Skip to content

fix(#915): derive theme lists, and classify scripthammer-forge as dark - #928

Merged
TortoiseWolfe merged 1 commit into
mainfrom
fix/915-derive-theme-lists
Aug 22, 2026
Merged

fix(#915): derive theme lists, and classify scripthammer-forge as dark#928
TortoiseWolfe merged 1 commit into
mainfrom
fix/915-derive-theme-lists

Conversation

@TortoiseWolfe

Copy link
Copy Markdown
Owner

Closes #915

The defect

Six test files each typed a theme list by hand against src/config/themes.ts, and every one missed scripthammer-forge — the third house theme, which shipped after they were written.

The sharpest instance is color-contrast.spec.ts, the AAA gate. #411 built it after a hand-written four-route list let a 6.44:1 eyebrow reach main behind 17 green checks. It enumerates its routes for exactly that reason — and hand-wrote its themes. Its comment read "Both custom themes covered", and "Both" was the completeness claim, stale the day forge landed. Same defect, same file, other axis.

Measured:

color-contrast.spec.ts   1,223 -> 1,834 cases
forge cases                  0 ->   611

A real bug fell out, and it is user-visible

scripthammer-forge ships base-100: #0f0d0b — near black — and was absent from DARK_THEMES in src/utils/theme-utils.ts, whose own docblock says it drives "map tiles, Disqus, Calendly, Cal.com, and Leaflet CSS."

On forge, every one of those rendered in light mode over a near-black page.

It surfaced only because embed-theme-contrast.spec.ts mirrored that list by hand and the new guard flagged the copy. The mirror had not drifted — 13 against 13. Both were wrong together, which is exactly why comparing a mirror to its source could never have revealed it.

What changed

src/config/themes.ts exports HOUSE_THEMES; HOUSE_THEME_COUNT derives from it
5 specs import instead of restating
src/utils/theme-utils.ts forge added to DARK_THEMES

The count in themes.ts was already correct. The prose beside it said "34 total, of which 2 are ours" — written for two house themes, in the one module whose stated job is stopping numbers from drifting.

depth-tokens.spec.ts had both patterns in one file: two hand-typed at :286 while :367 derived all three correctly. theme-switching.spec.ts already imported THEME_COUNT to assert the count, then listed 32 of 35 members beside it.

Two guards, and the second is the one that matters

The first forbids a hand-typed house theme in any of the five. It strips comments first, because several of those files now discuss the theme names while explaining why they derive them — matching that prose would fail a correct file.

The second computes each house theme's base-100 luminance and requires DARK_THEMES to agree. Asserting "forge is in DARK_THEMES" would pass the moment someone typed it in, and would say nothing about the fourth house theme.

mutation result
remove forge from DARK_THEMES (the actual bug) red — "luminance 0.006, DARK, is NOT in DARK_THEMES"
hand-type a house theme back into the AAA gate red
un-derive HOUSE_THEME_COUNT red

Deliberately not done

tests/e2e/pages/ThemePage.ts also holds a hand-typed list of 31, and the audit counted it as a sixth instance. The file is entirely unused — nothing outside ThemePage.ts references ThemePage. Its list is dead code, not a coverage gap, and deleting an unused page object belongs with #547's decision about unused components.

A note on risk

This adds 611 whole-page axe cases on a theme that has never been swept. I checked the token-level verdict first — src/config/theme-contrast.json rates forge AAA, textRatio 13.37, uiRatio 7.05 — so a red gate is unlikely. If one appears, it is a real accessibility finding on a shipped theme, which is the entire point of closing the hole.

Verification

type-check / lint     clean
test:scripts          539 passed
theme unit tests      79 passed (7 files)

🤖 Generated with Claude Code

Six test files each typed a theme list by hand against src/config/themes.ts, and
every one missed `scripthammer-forge` — the third house theme, which shipped
after they were written.

The sharpest instance is color-contrast.spec.ts, the AAA gate. #411 built it
after a hand-written FOUR-ROUTE list let a 6.44:1 eyebrow reach main behind 17
green checks; it enumerates its routes for exactly that reason and hand-wrote its
themes. Its comment read "Both custom themes covered" — and "Both" was the
completeness claim, stale the day forge landed. Same defect, same file, other
axis.

Measured, not asserted: color-contrast.spec.ts goes from 1,223 to 1,834 cases,
with 611 forge cases where there were 0.

A REAL BUG FELL OUT, and it is user-visible. `scripthammer-forge` ships
base-100 #0f0d0b — near black — and was absent from DARK_THEMES in
src/utils/theme-utils.ts, whose own docblock says it drives "map tiles, Disqus,
Calendly, Cal.com, and Leaflet CSS". On forge, every one of those rendered in
LIGHT mode over a near-black page. Found only because embed-theme-contrast.spec
mirrored that list by hand and the new guard flagged the copy; the mirror had NOT
drifted (13 against 13) — both were wrong together, which is precisely why a
mirror could never have revealed it.

- src/config/themes.ts exports HOUSE_THEMES, and HOUSE_THEME_COUNT now derives
  from it. The count was already correct; the PROSE beside it said "34 total, of
  which 2 are ours" — written for two house themes, in the one module whose
  stated job is stopping numbers from drifting.
- Five specs import instead of restating. depth-tokens.spec.ts had BOTH patterns:
  two hand-typed at :286 while :367 derived all three correctly.
- theme-switching.spec.ts already imported THEME_COUNT to assert the count, then
  listed 32 of 35 members beside it.

Two guards, both mutation-tested. The first forbids a hand-typed house theme in
any of the five, stripping comments first because several now DISCUSS the names
while explaining why they derive them. The second is the one that matters: it
computes each house theme's base-100 luminance and requires DARK_THEMES to agree,
so the answer is derived from the theme's own colours. Asserting "forge is in
DARK_THEMES" would pass the moment someone typed it in and would say nothing
about the fourth house theme.

Verified red on: removing forge from DARK_THEMES (the actual bug), hand-typing a
house theme back into the AAA gate, and un-deriving HOUSE_THEME_COUNT.

NOT DONE: tests/e2e/pages/ThemePage.ts also holds a hand-typed list of 31. It is
excluded because the file is entirely unused — nothing outside ThemePage.ts
references ThemePage — so its list is dead code, not a coverage gap. Deleting an
unused page object belongs with #547's decision about unused components, not
here.

type-check, lint clean. test:scripts 539 passed. theme unit tests 79 passed.

Closes #915

Co-authored-by: TurtleWolfe <TurtleWolfe@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@TortoiseWolfe
TortoiseWolfe enabled auto-merge (squash) August 22, 2026 07:39
@TortoiseWolfe
TortoiseWolfe merged commit 95e4ff4 into main Aug 22, 2026
46 of 50 checks passed
@TortoiseWolfe
TortoiseWolfe deleted the fix/915-derive-theme-lists branch August 22, 2026 08:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fourteen tests restate a list that exists in source, and have drifted — including the AAA contrast gate missing a house theme

2 participants