Skip to content

Finish ClinicalDashboard admin decomposition: SettingsDialog + dashboard-nav cutover, dead-code cleanup - #336

Merged
BigSimmo merged 5 commits into
mainfrom
claude/dashboard-decomp-final
Jul 7, 2026
Merged

Finish ClinicalDashboard admin decomposition: SettingsDialog + dashboard-nav cutover, dead-code cleanup#336
BigSimmo merged 5 commits into
mainfrom
claude/dashboard-decomp-final

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Summary

Completes the deferred admin-surface phase of the ClinicalDashboard decomposition (debt tracked in docs/process-hardening.md Phase 3). Monolith: 4,373 → ~3,450 lines; only the orchestrator, its data/state hooks, and small render/stream helpers remain.

  • ToolsHub + MobileSectionFab → clinical-dashboard/dashboard-nav.tsx (move-only). The prepared refactor: extract SettingsDialog + dashboard-nav overlays from ClinicalDashboard (move-only) #250 sibling had drifted from the live monolith (missing prop members, stale colour tokens) and was regenerated verbatim from the current monolith block before wiring. Back-imports (ApplicationsLauncherWorkspace, navigationHashes, mobileSectionFabMediaQuery) follow the existing benign back-edge pattern.
  • SettingsDialog cluster (+ 7 Settings* helpers) → clinical-dashboard/settings-dialog.tsx (move-only). The refactor: extract SettingsDialog + dashboard-nav overlays from ClinicalDashboard (move-only) #250 sibling predated the auth-email sign-in flow (useAuthSession/settingsEmail/submitSettingsEmail absent) — wiring it as-is would have regressed settings sign-in — so it too was regenerated verbatim. Both consumers repointed: the monolith render and global-mockup-search-shell.tsx. 24 now-orphaned monolith imports stripped.
  • Deleted the dead clinical-dashboard/document-admin/ directory (document-drawer.tsx, label-helpers.ts, index.ts) — shadowed by the live document-admin.tsx file in module resolution; imported by nothing.
  • Merge with main's type-scale migration (c09b3ee): the migration had rewritten font-size classes inside the monolith blocks this PR moves, so the cutover was redone against main's migrated blocks — the extracted modules carry the new text-2xs/text-sm-minus/text-base-minus classes; no design regression.
  • Debt log updated (docs/process-hardening.md Phase 3 now reflects refactor: extract SettingsDialog + dashboard-nav overlays from ClinicalDashboard (move-only) #250/refactor: extract document-admin — completes ClinicalDashboard decomposition (move-only) #251 landed + this cutover); added docs/archive/operator-decisions-2026-07-06.md recording operator approval to apply the two pending live migrations (M13, storage_cleanup_jobs index reconcile) and deploy the indexing-v3-agent edge function from any live-connected session.

Move integrity: every block moved verbatim (diff-verified), and the live-surface data-testid/aria-label corpus was proven byte-identical before/after each move and across the main merge (sorted-attribute sha1).

Verification

  • npm run verify:cheap — pass (runtime, sitemap, lint [5 pre-existing warnings, 0 errors], typecheck, 1,181 unit tests)
  • npm run verify:ui109/109 passed post-merge (settings dialog via ui-smoke account-settings assertions; ToolsHub via ui-tools*; MobileSectionFab via ui-overlap)
  • npm run verify:release before release or handoff confidence claims
  • npm run format:check — pass
  • npm run eval:retrieval:quality — N/A: no retrieval, ranking, selection, chunking, or scoring change (UI move-only)
  • npm run eval:rag / eval:quality — N/A: no answer-generation change
  • npm run check:production-readiness — N/A: no clinical workflow, privacy, environment, Supabase, or deployment behavior change
  • npm run check:deployment-readiness — N/A: no deployment startup change

Clinical Governance Preflight

UI module reorganization only; no ingestion/answer/search/source/privacy changes.

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy)
  • Service-role keys and private document access remain server-only
  • Demo/synthetic content remains clearly separated from real clinical sources
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative
  • Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed — no clinical decision-support behavior changed

Notes

  • Per the standing convention, the barrel clinical-dashboard/index.ts was not extended; modules are imported by deep path.
  • The docs/archive/operator-decisions-2026-07-06.md items (live migrations + edge deploy) still need execution from a live-connected session — this PR only records the approval.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XPVNBVo4cg9PEYtNhJZBQY


Generated by Claude Code

claude added 4 commits July 6, 2026 13:46
…d-nav module (move-only)

The prepared dashboard-nav.tsx sibling from #250 had drifted from the live
monolith (missing prop members, stale colour tokens), so it was regenerated
verbatim from the current monolith block (1072-1436) before wiring. Exports:
ToolsHub, buildMobileSectionFabState, MobileSectionFab; back-imports
(ApplicationsLauncherWorkspace, navigationHashes, mobileSectionFabMediaQuery)
follow the existing benign back-edge pattern. Monolith 4373 -> 4007 lines.
Live-surface data-testid/aria-label corpus verified byte-identical pre/post.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XPVNBVo4cg9PEYtNhJZBQY
…og module (move-only)

The prepared settings-dialog.tsx from #250 predated the auth-email sign-in
flow (useAuthSession/settingsEmail/submitSettingsEmail all absent), so it was
regenerated verbatim from the current monolith block (518-1070) before wiring.
Repointed both consumers: the monolith render and global-mockup-search-shell
(which now imports SettingsDialog from the module and only
recentQueryStorageKey from the monolith). Stripped 24 now-orphaned monolith
imports. Monolith 4007 -> 3454 lines. Live-surface data-testid/aria-label
corpus verified byte-identical pre/post.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XPVNBVo4cg9PEYtNhJZBQY
…log; record operator approvals

The document-admin/ directory (document-drawer.tsx, label-helpers.ts,
index.ts) was an unwired leftover shadowed by the live document-admin.tsx
file in module resolution — nothing imported it. The process-hardening
Phase-3 log now reflects that #250/#251 landed and the admin cutover is
finished. docs/archive/operator-decisions-2026-07-06.md records the
operator's approval to apply the two pending live migrations (M13,
storage_cleanup_jobs index reconcile) and deploy the indexing-v3-agent
edge function from any live-connected session.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XPVNBVo4cg9PEYtNhJZBQY
…mp-final

# Conflicts:
#	src/components/ClinicalDashboard.tsx
#	src/components/clinical-dashboard/document-admin/document-drawer.tsx
#	src/components/clinical-dashboard/settings-dialog.tsx
@supabase

supabase Bot commented Jul 6, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@BigSimmo
BigSimmo marked this pull request as ready for review July 6, 2026 18:18
@BigSimmo
BigSimmo enabled auto-merge July 6, 2026 18:18
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
@BigSimmo
BigSimmo merged commit 46217a6 into main Jul 7, 2026
5 checks passed
@BigSimmo
BigSimmo deleted the claude/dashboard-decomp-final branch July 8, 2026 16:10
BigSimmo added a commit that referenced this pull request Aug 18, 2026
BigSimmo pushed a commit that referenced this pull request Aug 18, 2026
Resolves the one real conflict GitHub reported (`mergeable_state: dirty`, base
at dc7e518): `tests/design-token-contract.test.ts`.

Both sides appended a new `describe` block to the end of the same file and the
two shared a trailing `});` pair, so git could not tell them apart:

- ours: "category accents stay out of the semantic palette" — the three guards
  keeping identity accents off the danger/warning/success/info families.
- theirs: "responsive breakpoint tokens (Task #336)" — the --bp-* / --breakpoint-*
  assertions.

There is no semantic overlap between them, so both are kept and each is closed
explicitly rather than sharing a terminator. Verified by content, not by the
merge exiting cleanly: the file now declares both describes, and the suite runs
40 tests where ours alone ran 39 and theirs added 1 — so neither side was
silently dropped.

Gates on the merged tree: typecheck, lint, `npm run test` (651 files, 7016
passed / 4 skipped).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XGhewT2mmRoRTynjWfF1Vn
BigSimmo added a commit that referenced this pull request Aug 18, 2026
BigSimmo pushed a commit that referenced this pull request Aug 18, 2026
PR #2081 (already merged) queued "update" flags for #222, #321, and
#336 noting they were still open after PR #2072. This PR queues its
own newer, more complete requests for the same three rows (two
"done" decisions, one updated "PARTIAL" status), which the ledger
inbox tool correctly refuses to auto-reconcile as a silent
overwrite — it requires an explicit cancel decision per row.

Cancels the three now-superseded #2081 flags in favor of this PR's
requests, fixing the docs:check-links / ledger-inbox failure in
Static PR checks.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qDHwBNZYyRLdJHqSkiYTg
BigSimmo added a commit that referenced this pull request Aug 18, 2026
BigSimmo pushed a commit that referenced this pull request Aug 18, 2026
Cancels four pending requests found while diagnosing the docs:check-links
CI failure — two were duplicates of already-applied requests (#222, #336,
each already closed by a different, more accurate applied request), and
two asserted "done" outcomes for work this branch's final state does not
actually contain after the fabricated-content revert (#318: lexicon
sign-off and loperamide exclusion; #309: filter-density fix). All four
are cancelled via immutable cancel requests rather than deleted, per
ledger-write discipline.
BigSimmo pushed a commit that referenced this pull request Aug 18, 2026
Cancels 88868df4-c310-4ac2-9e83-cd3ad7702a1d, another false "done" claim
from this branch's original batch (same pattern as #222/#318/#309/#336):
#183 asks for a live Sentry production metric alert, blocked on missing
credentials and unreachable via a code diff. This branch's actual final
diff contains no Sentry or alerting changes. Also resolves a collision
with a separate, legitimate concurrent update request on the same row
from an unrelated branch.
BigSimmo added a commit that referenced this pull request Sep 1, 2026
…, and pin two drifting design-system documents (#2497)

* fix(clinical): align decision-support copy with the product boundary; pin design-system figures and token drift

The app told users four times that it was clinical decision support while its own
privacy page says the opposite. Two design-system documents had also drifted far
enough from the code to mislead, and one silent trap had no gate at all.

Clinical copy (the reason this change exists)

- Differentials (3 sites) and Specifiers (1) said "Clinical decision support only"
  / "Use this as structured decision support". The privacy page and CLAUDE.md both
  scope this product as "a clinical reference prototype, not validated clinical
  decision support", and the PR preflight carries a device-classification item, so
  the clinical surfaces were the ones out of step.
- All four now read "Clinical reference - not validated decision support", the
  wording already recorded as the clinical-owner copy decision on #PM9SP1.
- The fifth site was the copied/exported differential text in
  src/lib/differential-detail.ts, which carried the same claim into anything a
  clinician pastes elsewhere. Its pinned assertion moved with it.
- Therapy Compass was already correct and is untouched.

New gate: token-layer divergence (scripts/token-layer-divergences.mjs)

layout.tsx mounts .ckb-v2 on <html>, so .ckb-v2.ckb-v2 (0,2,0) beats :root (0,1,0)
and for any role both stylesheets declare the v2 value paints while the globals.css
one is dead - editing it changes nothing, with no lint, type or screenshot signal.
53 roles are in that state today (29 light, 24 dark).

The v2 migration is deliberate and unfinished, so divergence is pinned rather than
banned: docs/design-system/token-layer-divergences.json is the reviewed set, and a
role that starts diverging OR stops diverging fails. The comparison covers globals'
@theme block as well as :root, which is where the structural roles live. Wired into
the existing check:design-system-contract, so no gate-manifest change was needed.
Mutation-verified both directions.

New gate: GATES.md figures are generated (scripts/generate-gates-figures.mjs)

GATES.md carried the same metric as three different hand-typed values in three
sections. Measured against the baseline: interactiveTapFloorDeclarations appeared
as 9, 32 and 2 (true value 2); rawMarginLiterals as 74 and 53 (34);
legacyShadowAliases as 231, 224 and 87 (34). Nearly every figure overstated
remaining debt, which costs a session in exactly the way the document's own section
5 warns about.

A generated section 0 is now the single current count, with the prose figures in
sections 2, 4 and 5 explicitly marked as the historical record. Comparison is
normalised cell-wise so Prettier's table padding cannot report false drift.
Mutation-verified both directions.

Breakpoint tokens: reconciled toward the code, not away from it

GATES.md 3b banned --breakpoint-* tokens; globals.css has five, added four days
after that decision. The obvious fix - delete the three device-band names, which
have zero product call sites - was attempted and reverted in the same session:
MIN_WIDTH_BREAKPOINT_BANDS in design-system-contract-utils.mjs hard-codes them as
same-threshold aliases of sm/md/lg, and they are the only fixtures exercising the
tap-floor gate's alias-collision path. "Nothing imports it" was necessary and
nowhere near sufficient, exactly as AGENTS.md warns. All five stay, with the real
reason recorded in both the stylesheet and 3b, and the wrong "(Task #336)"
attribution removed.

AGENTS.md bundle-budget route list

Documented five budgeted routes; the config has two. The config is right:
/therapy-compass, /dsm and /forms became redirect stubs rendering the same shared
home as /, so budgeting them measured / three more times (see
tests/check-lighthouse-budget.test.ts, COMMITTED_ROUTES). Corrected the document
and recorded why, so the next reader does not "fix" the config back.

Also: indexed the one mockup route missing from mockups/README.md
(answer-loading-redesign), taking that index to 79 of 79.

Verification

npm run verify:cheap exit 0. Full unit suite 934 files / 11,771 tests passed, 1
skipped, 0 failures. lint and typecheck clean. Clean-build bundle check passed
(production 1742.1 KiB gzip, / at 240.0 KiB); it reports a +5.2% drift warning
against a baseline commit that no longer resolves, which is pre-existing and
tracked as #QSHHGK, not caused here.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015uQ7pGjA4hBxi9TRNsfjAL

* fix(design-system): pin token-layer counts metadata against divergences

Codex P2: reject hand-edited/merge-skewed counts when divergences are unchanged.

* docs: refresh scripts-index inventory counts for two new design-system scripts

---------

Co-authored-by: Claude <noreply@anthropic.com>
BigSimmo added a commit that referenced this pull request Sep 1, 2026
…lose four holes in the new token-divergence gate (#2499)

* fix(clinical): align decision-support copy with the product boundary; pin design-system figures and token drift

The app told users four times that it was clinical decision support while its own
privacy page says the opposite. Two design-system documents had also drifted far
enough from the code to mislead, and one silent trap had no gate at all.

Clinical copy (the reason this change exists)

- Differentials (3 sites) and Specifiers (1) said "Clinical decision support only"
  / "Use this as structured decision support". The privacy page and CLAUDE.md both
  scope this product as "a clinical reference prototype, not validated clinical
  decision support", and the PR preflight carries a device-classification item, so
  the clinical surfaces were the ones out of step.
- All four now read "Clinical reference - not validated decision support", the
  wording already recorded as the clinical-owner copy decision on #PM9SP1.
- The fifth site was the copied/exported differential text in
  src/lib/differential-detail.ts, which carried the same claim into anything a
  clinician pastes elsewhere. Its pinned assertion moved with it.
- Therapy Compass was already correct and is untouched.

New gate: token-layer divergence (scripts/token-layer-divergences.mjs)

layout.tsx mounts .ckb-v2 on <html>, so .ckb-v2.ckb-v2 (0,2,0) beats :root (0,1,0)
and for any role both stylesheets declare the v2 value paints while the globals.css
one is dead - editing it changes nothing, with no lint, type or screenshot signal.
53 roles are in that state today (29 light, 24 dark).

The v2 migration is deliberate and unfinished, so divergence is pinned rather than
banned: docs/design-system/token-layer-divergences.json is the reviewed set, and a
role that starts diverging OR stops diverging fails. The comparison covers globals'
@theme block as well as :root, which is where the structural roles live. Wired into
the existing check:design-system-contract, so no gate-manifest change was needed.
Mutation-verified both directions.

New gate: GATES.md figures are generated (scripts/generate-gates-figures.mjs)

GATES.md carried the same metric as three different hand-typed values in three
sections. Measured against the baseline: interactiveTapFloorDeclarations appeared
as 9, 32 and 2 (true value 2); rawMarginLiterals as 74 and 53 (34);
legacyShadowAliases as 231, 224 and 87 (34). Nearly every figure overstated
remaining debt, which costs a session in exactly the way the document's own section
5 warns about.

A generated section 0 is now the single current count, with the prose figures in
sections 2, 4 and 5 explicitly marked as the historical record. Comparison is
normalised cell-wise so Prettier's table padding cannot report false drift.
Mutation-verified both directions.

Breakpoint tokens: reconciled toward the code, not away from it

GATES.md 3b banned --breakpoint-* tokens; globals.css has five, added four days
after that decision. The obvious fix - delete the three device-band names, which
have zero product call sites - was attempted and reverted in the same session:
MIN_WIDTH_BREAKPOINT_BANDS in design-system-contract-utils.mjs hard-codes them as
same-threshold aliases of sm/md/lg, and they are the only fixtures exercising the
tap-floor gate's alias-collision path. "Nothing imports it" was necessary and
nowhere near sufficient, exactly as AGENTS.md warns. All five stay, with the real
reason recorded in both the stylesheet and 3b, and the wrong "(Task #336)"
attribution removed.

AGENTS.md bundle-budget route list

Documented five budgeted routes; the config has two. The config is right:
/therapy-compass, /dsm and /forms became redirect stubs rendering the same shared
home as /, so budgeting them measured / three more times (see
tests/check-lighthouse-budget.test.ts, COMMITTED_ROUTES). Corrected the document
and recorded why, so the next reader does not "fix" the config back.

Also: indexed the one mockup route missing from mockups/README.md
(answer-loading-redesign), taking that index to 79 of 79.

Verification

npm run verify:cheap exit 0. Full unit suite 934 files / 11,771 tests passed, 1
skipped, 0 failures. lint and typecheck clean. Clean-build bundle check passed
(production 1742.1 KiB gzip, / at 240.0 KiB); it reports a +5.2% drift warning
against a baseline commit that no longer resolves, which is pre-existing and
tracked as #QSHHGK, not caused here.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015uQ7pGjA4hBxi9TRNsfjAL

* fix(design-system): pin token-layer counts metadata against divergences

Codex P2: reject hand-edited/merge-skewed counts when divergences are unchanged.

* docs: refresh scripts-index inventory counts for two new design-system scripts

* fix(clinical): correct two more over-claiming surfaces; close four holes in the new token-divergence gate

Two independent audits ran against the previous commit. Both found real defects,
and both are fixed here.

Clinical over-claim sweep — two more surfaces

The earlier fix found its sites by grepping one phrase. A sweep for the whole
class found two it missed, both on the medication/prescribing surface, which is
the highest-consequence screen in the app:

- `patient-profile-panel.tsx` (rendered by the prescribing workspace, the
  medication record page, and the patient-details dock) said "Decision support,
  not medical advice" under the panel where a clinician enters renal/hepatic
  function, allergies and a current medication list to get a per-patient
  interaction verdict. This asserted the feature IS decision support — worse than
  the five sites already corrected, which merely said "not ... decision support".
- `medication-record-page.tsx`'s standing footer claimed PsychSift "provides
  evidence summaries", an authority claim the product does not make anywhere
  else, and disclaimed with a generic "not medical advice" rather than the
  house governance phrasing every comparable mode uses.

Both now match the wording used on the other surfaces. Production is clean: the
only remaining "decision support" strings in src are the document-intent taxonomy
(classifying UPLOADED guideline documents, not the app's own voice) and the
privacy page's own boundary statement.

Token-divergence gate — four holes, three of them live

An adversarial review of the gate added in the previous commit demonstrated four
false passes. The parser has been rewritten:

1. **Forced colours was unmonitored.** Both stylesheets declare a third theme in
   `@media (forced-colors: active)`, and the same specificity trap applies there.
   Editing a real Windows High Contrast value produced no complaint. Three roles
   are genuinely diverging in it today and are now pinned:
   `--clinical-accent-border`, `--overlay-backdrop`, `--text-soft`.
2. **Indentation-sensitive parsing.** The old regex required exactly two leading
   spaces, so re-indenting a declaration — no rendered effect — dropped it from
   the comparison, and the tool then reported the divergence as RESOLVED. Acting
   on that advice removed a still-dead token from tracking and left the gate
   green. Parsing is now indentation-insensitive.
3. **`var()` aliases were never resolved.** Two layers can declare identical alias
   text and still paint different colours when the alias itself diverges: dark
   `--clinical-chat-document` is `var(--surface-inset)` on both sides while
   `--surface-inset` differs. Values are now resolved within their own layer
   before comparison, which also removes three false FAILS where different text
   resolved to the same value (dark `--text`, `--text-heading`,
   `--clinical-accent-soft`).
4. **Ambiguous markers in GATES.md** silently retargeted both the comparison and
   the `--write` overwrite at the wrong slice. Now a hard error.

Block extraction uses real brace matching instead of slicing to the next
line-initial `}`, so an `@media` wrapper is context rather than a terminator, and
comments are stripped before selectors are read. An empty result for any theme is
a hard error, never a quiet pass.

Counts move from light 29 / dark 24 to light 29 / dark 22 / forcedColors 3 — the
dark drop is the three false fails above, the forced-colours three are newly
visible real divergences.

One review claim was checked and NOT adopted: the review reported forced-colours
`--border`/`--border-strong`/`--border-lux` as diverging. globals.css declares
them twice in that media context and the later value already matches v2, so they
are not divergent; the manual diff had only read the first block.

Verification

Full unit suite 937 files / 11,796 tests passed, 1 skipped, 0 failures. lint,
typecheck and the design-system contract chain all clean. Each of the four holes
was re-attacked against the hardened code: the forced-colours edit and the
duplicated marker now fail as they should, and the re-indented declaration is
proven still tracked rather than silently dropped.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015uQ7pGjA4hBxi9TRNsfjAL

* fix(design-system): conditional media blocks no longer leak into the base theme comparison

Codex P2 on #2499, verified and confirmed. `collect()` filtered only on
`forced-colors`, so every OTHER `@media` block was admitted into the
unconditional theme map and its override replaced the base declaration.

Comparing a narrow-viewport override against an unconditional v2 declaration
compares two different contexts, and it reports "identical" for a pair that
diverges everywhere the condition does not apply.

Proven on real data, not argued: before this change the light compat map held
`--mode-home-copy-reserve` as the `(min-width: 412px)` override
(`calc(var(--text-hero) * …)`) rather than its unconditional declaration at
globals.css:1240 (`calc(2 * var(--text-hero) * …)`). globals.css has three such
`:root` blocks today — `--mode-home-copy-reserve` twice and
`--spacing-mode-home-composer-wide` once.

Reproduced as a false pass with the synthetic wide-viewport case the review asked
for: base compat `--radius-md: 0.7rem`, an override at `(min-width: 9999px)`
matching v2's `0.625rem`, v2 unconditionally `0.625rem`. The old predicate
reported no divergence; the new one flags it. Latent rather than live today only
because the v2 layer declares none of the three affected names.

Base themes now take unconditional blocks only. Conditional non-forced-colors
blocks are excluded rather than merged, which is the conservative direction: a
token declared only under such a condition goes uncompared instead of being
compared wrongly. Forced colours remains its own theme — it is a condition, and
it is modelled as one rather than treated as base.

Also fixes the return typing of `readLayers`, which inferred `{}` and broke
`npm run typecheck` for the new test's consumers.

Verification

Full unit suite 937 files / 11,798 tests passed, 1 skipped, 0 failures. lint,
typecheck and the design-system contract chain clean; pin unchanged at light 29 /
dark 22 / forcedColors 3, confirming this was latent. The new regression test is
mutation-verified: restoring the old predicate fails it, and it asserts against
the real stylesheet rather than a fixture, so it tracks the file as it changes.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015uQ7pGjA4hBxi9TRNsfjAL

---------

Co-authored-by: Claude <noreply@anthropic.com>
BigSimmo pushed a commit that referenced this pull request Sep 1, 2026
… pin design-system figures and token drift

The app told users four times that it was clinical decision support while its own
privacy page says the opposite. Two design-system documents had also drifted far
enough from the code to mislead, and one silent trap had no gate at all.

Clinical copy (the reason this change exists)

- Differentials (3 sites) and Specifiers (1) said "Clinical decision support only"
  / "Use this as structured decision support". The privacy page and CLAUDE.md both
  scope this product as "a clinical reference prototype, not validated clinical
  decision support", and the PR preflight carries a device-classification item, so
  the clinical surfaces were the ones out of step.
- All four now read "Clinical reference - not validated decision support", the
  wording already recorded as the clinical-owner copy decision on #PM9SP1.
- The fifth site was the copied/exported differential text in
  src/lib/differential-detail.ts, which carried the same claim into anything a
  clinician pastes elsewhere. Its pinned assertion moved with it.
- Therapy Compass was already correct and is untouched.

New gate: token-layer divergence (scripts/token-layer-divergences.mjs)

layout.tsx mounts .ckb-v2 on <html>, so .ckb-v2.ckb-v2 (0,2,0) beats :root (0,1,0)
and for any role both stylesheets declare the v2 value paints while the globals.css
one is dead - editing it changes nothing, with no lint, type or screenshot signal.
53 roles are in that state today (29 light, 24 dark).

The v2 migration is deliberate and unfinished, so divergence is pinned rather than
banned: docs/design-system/token-layer-divergences.json is the reviewed set, and a
role that starts diverging OR stops diverging fails. The comparison covers globals'
@theme block as well as :root, which is where the structural roles live. Wired into
the existing check:design-system-contract, so no gate-manifest change was needed.
Mutation-verified both directions.

New gate: GATES.md figures are generated (scripts/generate-gates-figures.mjs)

GATES.md carried the same metric as three different hand-typed values in three
sections. Measured against the baseline: interactiveTapFloorDeclarations appeared
as 9, 32 and 2 (true value 2); rawMarginLiterals as 74 and 53 (34);
legacyShadowAliases as 231, 224 and 87 (34). Nearly every figure overstated
remaining debt, which costs a session in exactly the way the document's own section
5 warns about.

A generated section 0 is now the single current count, with the prose figures in
sections 2, 4 and 5 explicitly marked as the historical record. Comparison is
normalised cell-wise so Prettier's table padding cannot report false drift.
Mutation-verified both directions.

Breakpoint tokens: reconciled toward the code, not away from it

GATES.md 3b banned --breakpoint-* tokens; globals.css has five, added four days
after that decision. The obvious fix - delete the three device-band names, which
have zero product call sites - was attempted and reverted in the same session:
MIN_WIDTH_BREAKPOINT_BANDS in design-system-contract-utils.mjs hard-codes them as
same-threshold aliases of sm/md/lg, and they are the only fixtures exercising the
tap-floor gate's alias-collision path. "Nothing imports it" was necessary and
nowhere near sufficient, exactly as AGENTS.md warns. All five stay, with the real
reason recorded in both the stylesheet and 3b, and the wrong "(Task #336)"
attribution removed.

AGENTS.md bundle-budget route list

Documented five budgeted routes; the config has two. The config is right:
/therapy-compass, /dsm and /forms became redirect stubs rendering the same shared
home as /, so budgeting them measured / three more times (see
tests/check-lighthouse-budget.test.ts, COMMITTED_ROUTES). Corrected the document
and recorded why, so the next reader does not "fix" the config back.

Also: indexed the one mockup route missing from mockups/README.md
(answer-loading-redesign), taking that index to 79 of 79.

Verification

npm run verify:cheap exit 0. Full unit suite 934 files / 11,771 tests passed, 1
skipped, 0 failures. lint and typecheck clean. Clean-build bundle check passed
(production 1742.1 KiB gzip, / at 240.0 KiB); it reports a +5.2% drift warning
against a baseline commit that no longer resolves, which is pre-existing and
tracked as #QSHHGK, not caused here.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015uQ7pGjA4hBxi9TRNsfjAL
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.

3 participants