fix: Task 3 review round 1 - strip control chars before the sanitizer (S32a), S34 minors

Control characters now go on the raw value in AttrWriter::cleanNode and
VariableRef::setting; after the sanitizer only rejection remains ('<' + VT/FF
no longer turns into a tag). VALUE_KEY anchored with D, exact column-width
fractions, nested rows in columns, specialty columns refused, fullwidth by
catalog category, preset ambiguity in DesignSystem::modulePresetId().

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
This commit is contained in:
2026-09-26 14:26:18 +02:00
co-authored by Claude Opus 5.5
parent 4b72c2bb42
commit 8688a02402
13 changed files with 278 additions and 104 deletions
@@ -83,6 +83,20 @@ final class DesignSystemTest extends TestCase
self::assertSame('{{color:Nachtblau}}', DesignSystem::colorPlaceholder('gcid-a1b2', 'Nachtblau'));
}
/** Ruling S13a/S34: a preset name several presets of the module share fails with their ids. */
public function testAmbiguousPresetNamesFail(): void
{
$design = DesignSystem::fromDivi([], [], ['module' => ['divi/button' => ['default' => 'p-1', 'items' => [
'p-1' => ['id' => 'p-1', 'name' => 'Standard', 'moduleName' => 'divi/button'],
'p-3' => ['id' => 'p-3', 'name' => 'standard', 'moduleName' => 'divi/button'],
]]]], []);
self::assertSame('p-3', $design->modulePresetId('divi/button', 'p-3'));
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('p-1, p-3');
$design->modulePresetId('divi/button', 'STANDARD');
}
public function testToleratesEmptyOrOddInput(): void
{
$design = DesignSystem::fromDivi([], ['fonts' => 'x'], ['module' => 'y'], []);