rastrillo / fiesta Public

Branch review round three: unmatched brackets, braceMatchEnd, quoted motion declarations, and every CSS reader in check; red then green open

On branch fiesta-1-1

Fixes Astra's round-three findings 4, 5 and 6, and the flaw behind them: CSS readers in check that do not read through the shared scanner, or that guess at structure. Model: Claude Opus 5.5 (claude-opus-5-5), default reasoning.

Commits:

  • d37c5b9: brackets
  • 94c6b3e: motion gate
  • 072d79e: rules, selectors and headings
  • 9797a6f: themes, and the gallery's highlight gate moved into check
  • 31caf70: tokens.css presence gates
  • a92e384: the no-anchor tokens.css

Hub CI passed on a92e384: gofmt, no-rastrillo, size, history and 50-suite@48. Sizes are unchanged: 23,823 / 23,104 / 23,217 gzipped. Nothing under css/, js/, i18n/, icons/ or releases/ changed.

None of the 26 stylesheets is refused (css/, both releases, the seed). Their declaration records are byte for byte the same before and after.

Astra's inputs as rejection controls

| Finding | Input | Before | After | |---|---|---|---| | 4 | :root { --rst-review-probe: ); color: red; } | passes TestTheNewRuleGateHasNoKnownHoles | refused: "a ) closes the {" | | 5 | quoted "{" and "}" across two rules around the reduce block | motion gate passes .missed | names .missed | | 6 | .moving { --rst-proof: "; transition: none;"; } | motion gate passes | names .moving |

Red run, by assertion, on 2ed4142's readers:

baseline_test.go:898: appending :root { --rst-review-probe: ); color: red; } to 1.0.0's tokens.css passes the gate; it changes 1.0 pages
baseline_test.go:1028: declarationsOf("a { --b: ); color: red }"): <nil>, want an error naming the a ) closes
  (and 8 more bracket inputs read without error)
css_test.go:557: the motion gate passes .missed, which still moves under reduced motion
css_test.go:557: the motion gate passes .moving, which still moves under reduced motion
css_test.go:573: braceMatchEnd("@media x { a { b: \"}\"; } c { d: \"{\"; } } e { f: \"}\" }", 10) = 23, want 39

Green: go test ./check, then the hub.

What changed. There is one bracket reader on cssSpan:

  • checkCSSBrackets, called from cssWithoutComments;
  • braceMatchEnd, which now returns an error for an unmatched block;
  • cssItems and cssDeclarations, which step over brackets with it.

A { inside ( or [ is refused. The motion gate reads declarations with declarationsOf. It now checks 34 declarations in tokens.css where it checked 36: the old comma split cut two :has(a, b) selectors in half. A capitalised TRANSITION was also missed before, and property names are now read in lower case.

Mutations, each red then reverted:

  • M1: no bracket check in the comment reader
  • M2: cssDeclarations lets a stray closer through
  • M3: braceMatchEnd steps one byte
  • M4: a closer with nothing open let through
  • M5: a closer of the wrong kind let through
  • M6: { inside brackets let through
  • M7: a disable read from any value holding "transition: none"
  • M8: property names read as written

The sweep

Each CSS reader in check, and in any other _test.go that reads css/ or releases/, is now either routed through the scanner and declaration reader or noted as unable to make a gate pass wrongly. The full table is in the report.

Routed:

  • leafRules (built from the declaration reader)
  • splitSelectorList, endsOnControlElement
  • cssRules (refuses what readStylesheet refuses)
  • class and attribute selector reading, tokensStyle
  • floorBlock (commentAt)
  • themeTokens, the scheme-number gate, the theme gates, themePropSet, pressNumber, splitLightDark
  • the gallery highlight gate, moved into check
  • shapeLeaks
  • the token-reference gate (varRefs)
  • the modal, focus ring, sr-only, colour-literal, hidden-button, stage-art, sign-in and face gates
  • the forced-colours and print blocks, the retired header rule
  • zoom floors, type step, target floor
  • search landmark, profile placeholder, anchor contract
  • styleProperties, stripHas
  • the no-anchor rewrite

Noted: absence checks (fail closed), value parsers fed by the reader, mutations with browser controls, and drives that only serve bytes.

Red on the old readers (probes at 2ed4142):

OLD splitSelectorList("[a=\"]\"], [b]") = ["[a=\"]\"], [b]"]
OLD endsOnControlElement("[title=\"]\"] a") = false
OLD styledClasses: rst-y=true rst-w=true  (only a string names them)
OLD tokensStyle: rst-z=true rst-v=true    (only a string names them)
OLD strings.Index for the heading = 99, want 119
OLD readLeafRules(nested) = [{selector:cursor: pointer; [rst-d] body: color: red }], err <nil>
OLD parseCSSRules("@import \"x.css\"; [ rst-PAGE] { padding: 0; }") = one selector "@import \"x.css\"; [ rst-PAGE]"
OLD parseTokens(blockBody(:root)) --rst-x = "2"   (the commented-out one)
OLD splitLightDark("light-dark(\"a, b\")") = "\"a", "b\"", true
OLD schemeNumberProblems(a toggle only in a string) = 2 blocks, problems []
OLD var pattern finds quoted --rst-a, misses var( --rst-b )
OLD stripHas dropped 0 and left the :has() rule

Mutations, each red then reverted:

| Group | Mutations | |---|---| | Shared readers | S1 to S8 | | Theme readers | T1 to T5 | | tokens.css readers and the no-anchor rewrite | U1 to U5 |

Some first tries failed to compile or survived, and were replaced. The report says which.

Equivalence on the shipped files:

  • leaf, interactive and floor rules, classes and vocabulary: identical;
  • theme tokens, token sets and dark numbers: identical;
  • stripHas: drops the same 48 rules and leaves the same 5825 declarations.

Browser drives run locally: the footer pair, the card escape, both spellings, the floor, the console :has() drive, the two accent drives, the button stack and the no-anchor dropdown.

Concerns

  • internal/tokens' production blockBody and parseTokens, behind the gallery's tokens page, read theme :root blocks the old way. They are production code, so I left them: worth a follow-up.
  • Selector substring checks (tap inventory, icon rule, depth_media) could still match a key inside a quoted attribute value in a selector. No shipped selector does this.
  • Some checks now refuse more than before: brackets, nested rules and statements in cssRules, exact values where substrings were, and :after. All shipped files pass.