ADR-0029 Themed coverage is the roster, and a scene with no baseline entry fails
#Context
Two gaps, found together, with one shape between them: a check that was scoped to a set, and the set moved.
tools/make-themed-fixtures.js opened with "Compose each Phase 0 fixture scene against the generated semantic tokens, once per theme (spec §9 step 1)." That was true when it was written and the Phase 0 scenes were the whole fixture set. They are now four of twenty-five. The census against disk: twenty-five scenes on the CLI's roster, four with a full themed set, twenty-one with none. Every component scene — 09 through 19 and 22 through 28, each one the only measurement its component's record rests on — was measured in field-night alone, and every per-component ADR from 0021 through 0028 rests on a number taken in one theme out of four.
src/cli.js's run() compared a scene to baseline.json inside if (baseline && baseline.scenes[scene.file]) and had no else. A scene added to SCENES and never baselined was measured, printed, and passed, and the suite was green because nothing had compared it to anything. Four scenes were in that state on disk: 17, 18, 19 and 21. 21 is the proof fixture — the one scene whose whole purpose is to fail when the product's screen changes, and it could not have. The themed path has carried the equivalent else branch since it was written; the unthemed path, which is the older of the two, never had it.
#Decision
Themed coverage is the roster, not a phase. make-themed-fixtures.js generates a variant for every scene the budget CLI's SCENES table names, in every theme that scene does not already carry, and a scene left out is named in the tool with the reason read off the scene's own file. A scene present in SCENES with no entry in baseline.json fails the run and names itself; the same rule the themed path already enforced. New entries are written with --fill-baseline, which writes only where no value exists and refuses to write at all if a committed value would change — --write-baseline re-measures everything and spends ADR-004's cross-platform evidence to add one scene to a file.
The roster holds two shapes of scene and they are themed two ways. A Phase 0-shaped scene carries no token stack of its own, so the theme is prepended and the scene stylesheet link is pointed at the tokenised sheet — the original path, unchanged. A component scene arrives from its own generator with the stack already inlined, so nothing is recomposed: its theme block is exchanged for another theme's block, the Substrate's declaration is rewritten to match, and every other byte is the source fixture's. Each substitution asserts exactly one match and throws on none or two. A component scene's own theme is therefore not duplicated here — that measurement is the fixture's own row in baseline.json, and a fifth variant would be a baseline entry whose only content is that it agrees with the file it was copied from.
Three scenes are excluded, each for a reason on its own disk:
21-proof-supervisor-queue.html. make-proof-fixture.js says it in its own header: "a fixture that re-composed its screen from the same components would be measuring this file's idea of the screen — it would keep passing after the app changed, which is the one thing a proof fixture may not do." A themed sibling is that recomposition. The app declares its own theme on its own Substrate; rewriting that declaration here produces a screen the product never rendered. The scene stays at the one theme the product ships.
06-overlay-violation.html keeps its existing restriction to interior-light, unchanged: a scrim's cost is maximal there (ADR-0014), and generating it in the dark themes would produce scenes declared must-fail that legally pass.
04-calm-light.html is named here rather than excluded, because it was themed before this decision and removing coverage would spend the evidence the widening exists to add to. The finding stands on the record: 04's identity is its stylesheet. fixtures/_scene.css and fixtures/_scene-light.css are the only two scene stylesheets in the set with hard-coded colour and no token — eighteen literals each, zero var(--wr-*) — and 04 is 01 with the second one swapped for the first, a controlled pair on the direction term. Theming 04 replaces that stylesheet with the tokenised sheet, which is the theme overriding the thing under test, and the four committed themed entries for 04-calm-light are byte-identical to 01-calm's in all four themes. They measure 01 twice. They are left in place and they prove nothing.
#Rejected options
Leave the themed set at Phase 0 and theme components later. It had the argument that held for a year: the themed set is calibration for ADR-0011's ceilings, the ceilings are derived from the Phase 0 ramps, and a component scene is not part of that derivation. It lost because coverage and calibration are not the same job. The derivation may legitimately want four scenes; the *suite* wants every scene it has, and twenty-one scenes measured in one theme is not a set that has been checked in four themes — it is a set nobody looked at. The two scenes this widening immediately caught are the argument against waiting.
**Recompose component scenes from their components, the way each make-*-fixtures.js does.** This is the obvious shape and it is what the tool already did for Phase 0. It had real merit: one composition path instead of two, and the themed variant would be built the same way its source was. It lost on the same principle make-proof-fixture.js states — a second composition is a second opinion. This tool would have had to know each component's stylesheet order, and it would have kept emitting valid-looking fixtures after a component generator changed. Exchanging one CSS block inside the file the real generator wrote cannot drift, because there is nothing here to drift from.
Generate all four themes for every scene, including a component scene's own. Uniform, and it would make "a full themed set" mean one thing everywhere. It lost to the evidence already in the tree: 04-calm-light's four themed entries are byte-for-byte 01-calm's, which is what a duplicate baseline row looks like from the inside. Twenty-eight more of those would be twenty-eight rows that can only ever restate their source.
Fix the absent-baseline hole with --write-baseline. It exists, it works, and it would have taken one command. It lost because it re-measures all twenty-five scenes and replaces the file, so adding four entries rewrites twenty-one committed values on whatever machine happens to run it. ADR-004's platform bound of 1.0e-3 is evidence attached to specific recorded numbers. --fill-baseline adds and never overwrites, and aborts if a committed value would move.
Restrict 14-queue-calm and 27-checkbox-calm out of field-day once they were found to exceed that ceiling, the way 06 is restricted out of the dark themes. It would have left the suite green. It lost for the reason the generator's own comment gives about theme restriction — it teaches the reader that a scene's verdict is a coin toss — and here it is worse than that: it would hide a calm scene that does not fit its budget behind a mechanism meant for scenes that were built not to.
#Consequences
The themed set goes from 21 renders to 83, and the themed baseline from 21 entries to 83. Sixty-two of those are first records, written this pass and compared to nothing; twenty-one are comparisons against committed values, and all twenty-one were carried through unchanged. baseline.json goes from 21 entries to 25, four of them first records. The suite's vocabulary now distinguishes the two: FIRST RECORD and COMPARISON on the unthemed path, FIRST RECORD on the themed one, and a first record is never reported as a passing check.
fixtures/_scene-tokens.css gains a .directed rule. It had none, and 08-undecided is the one scene in the set that uses the class, so a themed 08 would have drawn its two directed Emitters as unstyled text and passed — the same silent-success failure mode this ADR closes on the baseline side. The rule takes its fill from the inset surface and carries the role in its glyph, which is .chip's rule one rung up rather than .demand's rule one rung down; the Phase 0 sheet's own numbers say the same thing, since .directed's #2a3a46 is a mild step above the .scope ground it sits on where .demand's #ff5a4d is the far end of the ramp. Filling the block with the directed role instead put 08 over its emission ceiling in all four themes.
The widening leaves the themed suite red, and this is the finding rather than a defect in it. Two calm component scenes exceed the field-day ceiling of 0.120386: 27-checkbox-calm at 0.12331 and 14-queue-calm at 0.12181. Two more sit just under it — 26-button-calm at 0.11927, 28-radio-button-calm at 0.11332 — where the same four scenes hold 0.028 to 0.044 of headroom in interior-light and interior-dark. The candidate cause is in tools/derive-ceiling.js, which derives every one of the four ceilings from 01-calm, 02-one-demand, 03-violations and 04-calm-light: no component scene has ever entered the calibration, and the Phase 0 scene is the lighter one. Whether the ceiling is wrong, the components are, or field-day is simply where a component set runs out of budget is a measurement this unit did not make. It is left open, named, and red.
Enforcement table: budget gains the absent-baseline check on the unthemed path and the first-record vocabulary on both. themed exits 1 until the field-day question above is settled.