c623b2f63c (#17791 via #17806) deleted 804 unit spec files. #17922 censused the ones whose subject never left this repository. #18052 took the first of the remaining owed set; this ticket takes check-theme-surfaces.
Measured at dev@3b19d7dca7, the owed set is now four: this one, WorkflowConcurrency, PublishReleaseNoteOrphan, and RebuildContentIndexesAndSeo.
The Problem
buildScripts/util/check-theme-surfaces.mjs has two live callers and zero unit coverage since 2026-08-27:
caller
trigger
.github/workflows/check-theme-surfaces.yml
PRs touching the guard or the theme surface
package.json → npm run check-theme-surfaces
local + manual
It enforces three contracts nobody is asserting: dark/light parity for every --workstation-* / --agent-dock-* token, token-only color consumption across the Workstation SCSS tree, and completeness of every consumed non-local token in both skins.
A guard with no spec fails in one direction silently. If its detection quietly stops matching — a regex narrowed, a branch inverted — the workflow keeps reporting green and the theme surface it protects drifts unobserved. That is the same shape as #17230 ("a theme surface whose token pattern matches nothing reads as clean"), which was filed against this very guard and is now unguarded again.
Why this is a rewrite, not a restoration
The deleted spec is 618 lines and must not be restored verbatim. Verified mechanically rather than inherited:
The subject's own header records why: "The application-specific Institution token language and drawer-frame contract left with the Institution product." Three of the five checks the deleted spec asserts — the --fm-* cockpit skin parity, the text-safe-ink contrast floor, and the drawer shell/pane frame seam — no longer exist in the guard. A verbatim restore would red on the majority of its arms for reasons that say nothing about whether the surviving guard works.
What survives is one export, collectThemeSurfaceFailures(surface), whose injectable surface argument makes the guard testable against fixtures without touching the repository's own theme files.
The Fix
Author test/playwright/unit/buildScripts/util/check-theme-surfaces.spec.mjs, beside the surviving sibling tree — not the old unit/ai/ prefix, which is what classified it as extracted substrate and would re-arm the same deletion at the next boundary change.
Drive the real export through its surface argument against SCSS written to a temp tree. Asserting against the repository's own theme files would pass for exactly as long as those files stay clean, and stop testing the guard the moment someone fixed them.
Cover all four failure classes plus every exemption: modeInvariant tokens, var() fallback literals, custom-property declarations, comments, quoted strings, and component-local tokens.
Pin the alias-resolution escape hatch (resolvesDifferently). #17200 closed on exactly this — "its parity rule flags correct aliases" — so the regression it fixed needs an arm, or the cheap string-equality version can return unnoticed.
drives collectThemeSurfaceFailures against temp-tree fixtures
none — a CI-gating guard without a spec is the defect
spec header
each check family red against a seeded mutation of the guard
Acceptance Criteria
AC-1 A spec exists at test/playwright/unit/buildScripts/util/check-theme-surfaces.spec.mjs and is green under npm run test-unit.
AC-2 Every check family the guard still owns — [surface], [parity], [token-only], [completeness] — has at least one positive arm.
AC-3 Each exemption is asserted: modeInvariant, var() fallback literals, custom-property declarations, line and block comments, quoted strings, component-local tokens.
AC-4 The alias-resolution rule from #17200 carries both an exempt arm and its falsifier (identical referent still fails), so the arm cannot pass vacuously.
AC-5 Fixtures are written to a temp tree; the spec reads none of the repository's own theme files.
AC-6 Mutation-proven: disabling each check family in the guard reds the arms that name it, and the recorded per-mutation counts are in the PR.
AC-7 The dropped Institution-era checks are named in the spec header, so the deletion is stated rather than done quietly.
Out of Scope
The other three owed rows (WorkflowConcurrency, PublishReleaseNoteOrphan, RebuildContentIndexesAndSeo) — each gets its own ticket, as #18052 established. Also out of scope: any change to the guard itself. If an arm proves the guard wrong, that is a separate ticket, not a fix folded into a coverage PR.
Avoided Traps
Restoring the deleted file because it exists. Its 618 lines describe a 734-line guard; four fifths of that contract left with the Institution product. A spec restored on the strength of being recoverable would have asserted a contract that no longer exists.
Asserting against the live theme tree. It passes today, which reads as coverage and is not: it stops testing the guard the moment the tree is clean, and reds on unrelated theme work.
Trusting the first green. The full suite passed on its first run before any mutation. A spec that has never been shown to fail on the defect it names is not evidence — see AC-6.
Live latest-open sweep: read the latest 20 open issues at 2026-09-03T06:58Z; no equivalent found. A2A in-flight claim sweep: latest 30 messages, all read-states — no overlapping [lane-claim] on buildScripts spec coverage. Own-assignment sweep: 9 open, none covering this guard. Memory Core rationale sweep: no prior decision on this guard's coverage.
Retrieval Hint: "check-theme-surfaces zero unit coverage, deleted spec describes the 734-line Institution guard, rewrite not restore, alias parity escape hatch #17200"
tobiu referenced in commit 9b34b72 - "test(build): the Workstation theme guard states its own contract (#18162) on Sep 3, 2026, 8:57 AM
Sub of #17922 · sibling of #17924, #17964, #17968, #18052
Context
c623b2f63c(#17791 via #17806) deleted 804 unit spec files. #17922 censused the ones whose subject never left this repository. #18052 took the first of the remaining owed set; this ticket takescheck-theme-surfaces.Measured at
dev@3b19d7dca7, the owed set is now four: this one,WorkflowConcurrency,PublishReleaseNoteOrphan, andRebuildContentIndexesAndSeo.The Problem
buildScripts/util/check-theme-surfaces.mjshas two live callers and zero unit coverage since 2026-08-27:.github/workflows/check-theme-surfaces.ymlpackage.json→npm run check-theme-surfacesIt enforces three contracts nobody is asserting: dark/light parity for every
--workstation-*/--agent-dock-*token, token-only color consumption across the Workstation SCSS tree, and completeness of every consumed non-local token in both skins.A guard with no spec fails in one direction silently. If its detection quietly stops matching — a regex narrowed, a branch inverted — the workflow keeps reporting green and the theme surface it protects drifts unobserved. That is the same shape as #17230 ("a theme surface whose token pattern matches nothing reads as clean"), which was filed against this very guard and is now unguarded again.
Why this is a rewrite, not a restoration
The deleted spec is 618 lines and must not be restored verbatim. Verified mechanically rather than inherited:
The subject's own header records why: "The application-specific Institution token language and drawer-frame contract left with the Institution product." Three of the five checks the deleted spec asserts — the
--fm-*cockpit skin parity, the text-safe-ink contrast floor, and the drawer shell/pane frame seam — no longer exist in the guard. A verbatim restore would red on the majority of its arms for reasons that say nothing about whether the surviving guard works.What survives is one export,
collectThemeSurfaceFailures(surface), whose injectablesurfaceargument makes the guard testable against fixtures without touching the repository's own theme files.The Fix
test/playwright/unit/buildScripts/util/check-theme-surfaces.spec.mjs, beside the surviving sibling tree — not the oldunit/ai/prefix, which is what classified it as extracted substrate and would re-arm the same deletion at the next boundary change.surfaceargument against SCSS written to a temp tree. Asserting against the repository's own theme files would pass for exactly as long as those files stay clean, and stop testing the guard the moment someone fixed them.modeInvarianttokens,var()fallback literals, custom-property declarations, comments, quoted strings, and component-local tokens.resolvesDifferently). #17200 closed on exactly this — "its parity rule flags correct aliases" — so the regression it fixed needs an arm, or the cheap string-equality version can return unnoticed.Contract Ledger Matrix
test/playwright/unit/buildScripts/util/check-theme-surfaces.spec.mjscheck-fixed-sleeps.spec.mjscollectThemeSurfaceFailuresagainst temp-tree fixturesAcceptance Criteria
test/playwright/unit/buildScripts/util/check-theme-surfaces.spec.mjsand is green undernpm run test-unit.[surface],[parity],[token-only],[completeness]— has at least one positive arm.modeInvariant,var()fallback literals, custom-property declarations, line and block comments, quoted strings, component-local tokens.Out of Scope
The other three owed rows (
WorkflowConcurrency,PublishReleaseNoteOrphan,RebuildContentIndexesAndSeo) — each gets its own ticket, as #18052 established. Also out of scope: any change to the guard itself. If an arm proves the guard wrong, that is a separate ticket, not a fix folded into a coverage PR.Avoided Traps
Live latest-open sweep: read the latest 20 open issues at 2026-09-03T06:58Z; no equivalent found. A2A in-flight claim sweep: latest 30 messages, all read-states — no overlapping
[lane-claim]on buildScripts spec coverage. Own-assignment sweep: 9 open, none covering this guard. Memory Core rationale sweep: no prior decision on this guard's coverage.Retrieval Hint: "check-theme-surfaces zero unit coverage, deleted spec describes the 734-line Institution guard, rewrite not restore, alias parity escape hatch #17200"