From a1b1ff4a2b95a4dabf5aa0fca071fa43fb79c4f2 Mon Sep 17 00:00:00 2001
From: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
Date: Sat, 22 Aug 2026 23:29:59 +0800
Subject: [PATCH 1/3] fix(ui): add non-colour status semantics
---
...7201a1635e459105ba8fb0cda19d332a.record.md | 1 +
...07e83e51306dfd0f925f9da6057bcf85.record.md | 1 +
...84d36fdfaaf2afa95923f8ee8857522b.record.md | 1 +
...da97f2253fd6da239fd6bde1ca6c0a28.record.md | 1 +
...382993025a928ef253c4818a1663f06e.record.md | 1 +
docs/design-system/GATES.md | 101 +++--
.../design-system-live-convergence-plan.md | 366 ++++++++++++++++++
.../codex-cloud-design-status-semantics.md | 282 ++++++++++++++
scripts/design-system-contract-baseline.json | 13 +-
src/app/(search-app)/page.tsx | 16 +
src/components/ClinicalDashboard.tsx | 9 +-
src/components/calculators/calculator-ui.tsx | 15 +-
.../clinical-dashboard/visual-evidence.tsx | 2 +-
src/components/ui-primitives.tsx | 8 +-
src/lib/ui-copy.ts | 5 +
tests/shared-home-metadata.test.ts | 46 +++
tests/status-semantics-contract.test.ts | 42 ++
tests/ui-accessibility.spec.ts | 21 +
18 files changed, 863 insertions(+), 68 deletions(-)
create mode 100644 docs/branch-review-records/1233fbf36b5160345976009075cd18f47201a1635e459105ba8fb0cda19d332a.record.md
create mode 100644 docs/branch-review-records/343cc5158a797014c739bcb139022ae207e83e51306dfd0f925f9da6057bcf85.record.md
create mode 100644 docs/branch-review-records/66988d5ef89875ea4478e969fa246f6e84d36fdfaaf2afa95923f8ee8857522b.record.md
create mode 100644 docs/branch-review-records/99b9ea02fd5f93288c57c502619ad01fda97f2253fd6da239fd6bde1ca6c0a28.record.md
create mode 100644 docs/branch-review-records/a2d5013511e1efca2e90f83a38a3b72b382993025a928ef253c4818a1663f06e.record.md
create mode 100644 docs/plans/design-system-live-convergence-plan.md
create mode 100644 docs/prompts/codex-cloud-design-status-semantics.md
create mode 100644 tests/shared-home-metadata.test.ts
create mode 100644 tests/status-semantics-contract.test.ts
diff --git a/docs/branch-review-records/1233fbf36b5160345976009075cd18f47201a1635e459105ba8fb0cda19d332a.record.md b/docs/branch-review-records/1233fbf36b5160345976009075cd18f47201a1635e459105ba8fb0cda19d332a.record.md
new file mode 100644
index 0000000000..1e352a1cb2
--- /dev/null
+++ b/docs/branch-review-records/1233fbf36b5160345976009075cd18f47201a1635e459105ba8fb0cda19d332a.record.md
@@ -0,0 +1 @@
+| 2026-08-22 | work | a26747b1e8c7ac5a705b1beee94b61aaeddef74e | PR 1 clinical status semantics and baseline provenance | status semantics implemented with zero contract debt; no high-confidence diff findings; human screenshot provenance disposition remains approval-gated | focused status contract; design-system contract; desktop and forced-colour phone browser proof; production readiness; lint; typecheck; build; full unit suite has unrelated jq-less hook timeouts |
diff --git a/docs/branch-review-records/343cc5158a797014c739bcb139022ae207e83e51306dfd0f925f9da6057bcf85.record.md b/docs/branch-review-records/343cc5158a797014c739bcb139022ae207e83e51306dfd0f925f9da6057bcf85.record.md
new file mode 100644
index 0000000000..743f16c29b
--- /dev/null
+++ b/docs/branch-review-records/343cc5158a797014c739bcb139022ae207e83e51306dfd0f925f9da6057bcf85.record.md
@@ -0,0 +1 @@
+| 2026-08-22 | work | 697c74cade73c1cc670a1b82d1392959a2d8598d | adversarial review of design-system title fix and live shared-home UI | P2 client-side mode switches left document.title stale; fixed with shared title owner and browser regression. Corrected stale design-gate evidence/counts; no P0/P1 findings. | focused Vitest 45 pass; focused Playwright 1 pass; accessibility Chromium 17 pass; design-system contract pass; 320/390/639/768/1440/1920 overflow and forced-colors sweep |
diff --git a/docs/branch-review-records/66988d5ef89875ea4478e969fa246f6e84d36fdfaaf2afa95923f8ee8857522b.record.md b/docs/branch-review-records/66988d5ef89875ea4478e969fa246f6e84d36fdfaaf2afa95923f8ee8857522b.record.md
new file mode 100644
index 0000000000..343d116610
--- /dev/null
+++ b/docs/branch-review-records/66988d5ef89875ea4478e969fa246f6e84d36fdfaaf2afa95923f8ee8857522b.record.md
@@ -0,0 +1 @@
+| 2026-08-22 | work | 4c06617a4bac40dbafb9c07dc7468ea62adb559c | design-system live convergence programme plan and local handoff | No P0-P2 defect in the plan. Six independently revertible tranches, adversarial gates, Cloud/local boundaries, clinical stop conditions, and an operator handoff packet are specified. | flightplan docsOnly; clinical-proof docsOnly; docs links passed; docs script refs passed; focused Prettier passed; diff check passed |
diff --git a/docs/branch-review-records/99b9ea02fd5f93288c57c502619ad01fda97f2253fd6da239fd6bde1ca6c0a28.record.md b/docs/branch-review-records/99b9ea02fd5f93288c57c502619ad01fda97f2253fd6da239fd6bde1ca6c0a28.record.md
new file mode 100644
index 0000000000..a148ca5970
--- /dev/null
+++ b/docs/branch-review-records/99b9ea02fd5f93288c57c502619ad01fda97f2253fd6da239fd6bde1ca6c0a28.record.md
@@ -0,0 +1 @@
+| 2026-08-22 | work | b158b93532511db8077e226f00bdabeaa0c3ea85 | cloud design-status semantics implementation prompt | no high-confidence findings; bounded offline-first PR1 prompt with truthful provenance and local handoff gates | workflow:flightplan; docs:check-links; docs:check-scripts; format; git diff --check |
diff --git a/docs/branch-review-records/a2d5013511e1efca2e90f83a38a3b72b382993025a928ef253c4818a1663f06e.record.md b/docs/branch-review-records/a2d5013511e1efca2e90f83a38a3b72b382993025a928ef253c4818a1663f06e.record.md
new file mode 100644
index 0000000000..bae789631e
--- /dev/null
+++ b/docs/branch-review-records/a2d5013511e1efca2e90f83a38a3b72b382993025a928ef253c4818a1663f06e.record.md
@@ -0,0 +1 @@
+| 2026-08-22 | work | dea32b8d26b32f03827bf71c0814315d92a7cb5c | current design system and live UI | P2 shared-home route titles were not mode-specific; fixed locally with focused metadata coverage. Remaining recommendations documented in handoff. | design-sweep; local identity; phone/desktop visual inspection; focused Vitest |
diff --git a/docs/design-system/GATES.md b/docs/design-system/GATES.md
index 82fef19d73..e1e4fc4cae 100644
--- a/docs/design-system/GATES.md
+++ b/docs/design-system/GATES.md
@@ -16,19 +16,19 @@ than CI, two of them by the second reader; review does not scale past two carefu
**[verified against this worktree and the export]**
-| Check | What it enforces | Label |
-| ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------ |
-| `npm run check:design-system-contract` (token baseline + adoption + design-sync contracts) | Raw colour literals (ratchet against `design-system-contract-baseline.json`), literal `shadow-[…]` classes, the legacy tap class, interactive tap-size literals, therapy-compass `tc-btn` base-class use; border+ring edge conflicts, 1px shadow spreads, layout-property transitions and legacy shadow aliases (all ratcheted **per path**, so new use fails anywhere); status-coloured numerals and colour-only status indicators; raw padding, radius, gap and line-height literals in both classes and CSS declarations (ratcheted **per path**; named utilities and Tailwind arbitrary-property forms such as `[padding:22px]` both count; values computed with `var()`/`env()`/`calc()`/`clamp()`/`max()`, zero with any CSS unit identifier, and the CSS-wide keywords are exempt); declared-but-unselected type steps (**hard rule** with a gated exemption list); `dark:` colour overrides, legacy palette utilities and image-inversion filters (all pinned at **zero**, not ratcheted); deterministic adoption truth; local source/export/preview/design-sync parity. Mockups exempt. | implemented-blocking |
-| `tests/ckb-v2-token-contract.test.ts` | v2 layer stays class-scoped (no `:root` leak) · `--border-lux` solid and no lighter than `--border`, both themes · dark surface ramp monotonic and separated · `--surface-subtle` aliases up · text/muted/heading ≥4.5:1 on the light shell, muted ≥4.5:1 dark · `--text-soft` pinned **below** 4.5:1 and ≥3:1 (both sides, so the tier cannot be "fixed" away) · command pair ≥4.5:1 both themes · `--shadow-inset` true inset · elevation ladder carries no baked 1px hairline · tap floor ≠ chip height ≠ compact row · reduced-motion zeroes durations · every type step has its own line-height and tracking | implemented-blocking |
-| `tests/design-token-contract.test.ts` | Live-layer token relationships (companion to the v2 contract) | implemented-blocking |
-| `tests/source-badges-off-vocab.dom.test.tsx` | Enum resilience: off-vocabulary `clinical_validation_status` degrades to the neutral triad, logs once, never throws | implemented-blocking |
-| `tests/source-metadata-browser-safety.test.ts` | The `process is not defined` client-bundle crash stays fixed (server logger never ships to the browser) | implemented-blocking |
-| `tests/accessible-table.dom.test.tsx`, `tests/accessible-table-alignment.dom.test.tsx` | Table semantics, numeric alignment, expander `aria-controls` | implemented-blocking |
-| `tests/ui-primitives.dom.test.tsx`, `tests/ui-v2-components.dom.test.tsx` | Behavioural coverage for the tested subset (COMPONENTS §0) | implemented-blocking (subset) |
-| ESLint repo rules: `no-hardcoded-hex`, `require-button-wiring`, `require-lucide-icon-aria`, `require-z-index-ladder`, `restrict-suppress-hydration-warning` | Raw hex (`bg-`/`text-`/`border-` bracket-hex ONLY; `ring-`/`shadow-`/`outline-`/`from-`/`via-`/`to-`/`fill-`/`stroke-` and inline `style={{}}` are out of scope), un-wired buttons, icon aria (values imported directly from `lucide-react` only - the `icon: LucideIcon` prop form is structurally invisible), z-ladder discipline (see the scoped row below), hydration-warning abuse | implemented-blocking |
-| `tests/route-reachability.test.ts` | No orphan production routes | implemented-blocking |
-| `npm run verify:ui` (+ `verify:phone-chrome`) | Chromium production journeys; phone-chrome owners | implemented-blocking (journey scope) |
-| Visual-baseline harness (PR #1404) | Screenshot baselines exist but ship **zero committed baselines with `continue-on-error` on, deliberately** — a held gate until the design is declared final (issue `#118`). Includes the `--spacing-tap` probe floor. | implemented-partial (non-blocking by hold) |
+| Check | What it enforces | Label |
+| ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------ |
+| `npm run check:design-system-contract` (token baseline + adoption + design-sync contracts) | Raw colour literals (ratchet against `design-system-contract-baseline.json`), literal `shadow-[…]` classes, the legacy tap class, interactive tap-size literals, therapy-compass `tc-btn` base-class use; border+ring edge conflicts, 1px shadow spreads, layout-property transitions and legacy shadow aliases (all ratcheted **per path**, so new use fails anywhere); status-coloured numerals and colour-only status indicators; raw padding, radius, gap and line-height literals in both classes and CSS declarations (ratcheted **per path**; named utilities and Tailwind arbitrary-property forms such as `[padding:22px]` both count; values computed with `var()`/`env()`/`calc()`/`clamp()`/`max()`, zero with any CSS unit identifier, and the CSS-wide keywords are exempt); declared-but-unselected type steps (**hard rule** with a gated exemption list); `dark:` colour overrides, legacy palette utilities and image-inversion filters (all pinned at **zero**, not ratcheted); deterministic adoption truth; local source/export/preview/design-sync parity. Mockups exempt. | implemented-blocking |
+| `tests/ckb-v2-token-contract.test.ts` | v2 layer stays class-scoped (no `:root` leak) · `--border-lux` solid and no lighter than `--border`, both themes · dark surface ramp monotonic and separated · `--surface-subtle` aliases up · text/muted/heading ≥4.5:1 on the light shell, muted ≥4.5:1 dark · `--text-soft` pinned **below** 4.5:1 and ≥3:1 (both sides, so the tier cannot be "fixed" away) · command pair ≥4.5:1 both themes · `--shadow-inset` true inset · elevation ladder carries no baked 1px hairline · tap floor ≠ chip height ≠ compact row · reduced-motion zeroes durations · every type step has its own line-height and tracking | implemented-blocking |
+| `tests/design-token-contract.test.ts` | Live-layer token relationships (companion to the v2 contract) | implemented-blocking |
+| `tests/source-badges-off-vocab.dom.test.tsx` | Enum resilience: off-vocabulary `clinical_validation_status` degrades to the neutral triad, logs once, never throws | implemented-blocking |
+| `tests/source-metadata-browser-safety.test.ts` | The `process is not defined` client-bundle crash stays fixed (server logger never ships to the browser) | implemented-blocking |
+| `tests/accessible-table.dom.test.tsx`, `tests/accessible-table-alignment.dom.test.tsx` | Table semantics, numeric alignment, expander `aria-controls` | implemented-blocking |
+| `tests/ui-primitives.dom.test.tsx`, `tests/ui-v2-components.dom.test.tsx` | Behavioural coverage for the tested subset (COMPONENTS §0) | implemented-blocking (subset) |
+| ESLint repo rules: `no-hardcoded-hex`, `require-button-wiring`, `require-lucide-icon-aria`, `require-z-index-ladder`, `restrict-suppress-hydration-warning` | Raw hex (`bg-`/`text-`/`border-` bracket-hex ONLY; `ring-`/`shadow-`/`outline-`/`from-`/`via-`/`to-`/`fill-`/`stroke-` and inline `style={{}}` are out of scope), un-wired buttons, icon aria (values imported directly from `lucide-react` only - the `icon: LucideIcon` prop form is structurally invisible), z-ladder discipline (see the scoped row below), hydration-warning abuse | implemented-blocking |
+| `tests/route-reachability.test.ts` | No orphan production routes | implemented-blocking |
+| `npm run verify:ui` (+ `verify:phone-chrome`) | Chromium production journeys; phone-chrome owners | implemented-blocking (journey scope) |
+| Visual-baseline harness (PR #1404) | Six approved Linux baselines are committed for the canonical dashboard, phone, search-band, document-viewer, and Therapy surfaces (`tests/__screenshots__/linux/provenance.json`). Pixel drift remains advisory through the comparison step's scoped `continue-on-error`; the classifier still fails the job for missing baselines, setup/runtime failures, or non-drift assertions. This is visual-regression evidence for those six targets, not a blanket adoption claim for every production surface. | implemented-partial (drift advisory) |
⚠️ **Type-scale and icon-scale enforcement — corrected 6 Aug 2026.** The previous wording
here ("no such rule files exist in `eslint-rules/`… currently **unenforced**") was true about
@@ -73,14 +73,14 @@ because it contributed nothing.
| # | Gate | Status | Evidence / path to blocking |
| --- | --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | Contrast ≥4.5:1 for every text/background pair, both themes, live **and** v2; `--decoration-soft` asserted below 4.5 and never on a text node | **implemented-blocking** | v2 pairs + `--text-placeholder` + `--decoration-soft` tier + recipe AST (`decoration-on-text.contract.test.ts`); live full matrix remains follow-on. |
-| 2 | Tap targets ≥48px interactive; static pills never carry `min-h-tap`; no production target reduced | **implemented-blocking for new use** | Enforced today: `--spacing-tap` is 48px and pinned from both sides (`ckb-v2-token-contract` asserts the `@theme` knob is ≥48 and that `--tap-min` is its alias), `ui-style-contract` measures the rendered floor for declared `min-height` carriers in Chromium, and the legacy-class / literal ratchets still run. **Still open for the `h-10` case, and 9 Aug 2026 measured why.** An enumeration of _rendered interactive_ elements was written, shown to find genuine defects, and then **reverted rather than landed**: it is not deterministic on this route. Six runs against one production build returned 6, 5, 4, 3, 3 and 9 distinct sub-floor shapes, largely disjoint — one run saw the answer-suggestion chips and a sort band, another the settled results list. `waitForLoadState("networkidle")` plus deduplication to distinct shapes did not fix it, and two consecutive agreeing runs turned out to be coincidence. This spec runs in the required `Production UI` job, so an intermittent version of it would block every merge in the repo; that is a worse outcome than the gap it closes. Path to blocking: give the audit a deterministic surface — a static route or a fixed seeded state — before re-attempting it. **What the enumeration did establish, in every one of the six runs, is a live defect:** controls that carry `min-h-tap` compute `min-height: 0px` and render at 16–36px, six distinct shapes in total. The declared-carrier audit cannot report them by construction — it only measures elements already computing at or above the floor, so a floor overridden downward is skipped rather than flagged. Tracked as `#293`. **Correction, same date — "`test:e2e:style-contract` is not part of `verify:cheap`" was true and badly misleading, and it sent one session looking for a wiring bug that does not exist.** The npm script is only a convenience alias for running this one spec; the spec matches `productionSpecPattern` in `playwright.config.ts` and is listed explicitly in `scripts/playwright-pr-shards.mjs`, so it already runs in the `Production UI` job that `pr-required` demands on any UI-scoped PR. It must **not** be added to `verify:cheap:internal`: `check:gate-manifest` requires every gate in that chain to also run in `static-pr`, which has no browser and no server. Per-surface geometry stays in the held visual harness. **Both named gaps are now closed, 15 Aug 2026.** The rendered enumeration landed first: `885c613` (PR #1962) added the deterministic phone-viewport `min-h-tap` audit this row was waiting on — `/forms`'s fixed-array home rather than a live-search route, polled until three consecutive reads agree, explicitly sorted, and asserted identical across three full navigate-and-enumerate cycles. The `h-10` case is closed here by `interactiveTapFloorDeclarations` in `check:design-system-contract`: an interactive element (`a`, `button`, `input`, `select`, `summary`, `textarea`) declaring its own **unprefixed** `min-h-*` below the 48px token. Comparable arbitrary lengths and mutually exclusive/composed class branches are evaluated independently, so one safe branch cannot hide a sub-floor runtime path. Ratcheted at **40** across 16 files with per-path pins, so a new sub-floor control anywhere in `src/**` fails today. Mutation-verified: lowering one shortlist button to `min-h-9` produces both the total and matching per-path regression. **Deliberately scoped to `min-h-*`, not `h-*`/`size-*`:** a short `h-4` on an interactive element is routinely the visible box of a control whose hit area is owned by a tap-sized wrapper (`SelectionCheckbox` in `differentials-home.tsx`, whose label `ui-smoke` asserts still meets the floor), so flagging those would pad the baseline with non-defects — the §5 failure mode. **One known limit, recorded rather than hidden:** the walker sees intrinsic lowercase tags only, so a floor declared on `` or another component wrapper is invisible to it (the pre-existing `legacyTapClasses` check has the same blind spot). The 40 recorded sites are real debt still owed, tracked in `#265`. |
+| 2 | Tap targets ≥48px interactive; static pills never carry `min-h-tap`; no production target reduced | **implemented-blocking for new use** | Enforced today: `--spacing-tap` is 48px and pinned from both sides (`ckb-v2-token-contract` asserts the `@theme` knob is ≥48 and that `--tap-min` is its alias), `ui-style-contract` measures the rendered floor for declared `min-height` carriers in Chromium, and the legacy-class / literal ratchets still run. **Still open for the `h-10` case, and 9 Aug 2026 measured why.** An enumeration of _rendered interactive_ elements was written, shown to find genuine defects, and then **reverted rather than landed**: it is not deterministic on this route. Six runs against one production build returned 6, 5, 4, 3, 3 and 9 distinct sub-floor shapes, largely disjoint — one run saw the answer-suggestion chips and a sort band, another the settled results list. `waitForLoadState("networkidle")` plus deduplication to distinct shapes did not fix it, and two consecutive agreeing runs turned out to be coincidence. This spec runs in the required `Production UI` job, so an intermittent version of it would block every merge in the repo; that is a worse outcome than the gap it closes. Path to blocking: give the audit a deterministic surface — a static route or a fixed seeded state — before re-attempting it. **What the enumeration did establish, in every one of the six runs, is a live defect:** controls that carry `min-h-tap` compute `min-height: 0px` and render at 16–36px, six distinct shapes in total. The declared-carrier audit cannot report them by construction — it only measures elements already computing at or above the floor, so a floor overridden downward is skipped rather than flagged. Tracked as `#293`. **Correction, same date — "`test:e2e:style-contract` is not part of `verify:cheap`" was true and badly misleading, and it sent one session looking for a wiring bug that does not exist.** The npm script is only a convenience alias for running this one spec; the spec matches `productionSpecPattern` in `playwright.config.ts` and is listed explicitly in `scripts/playwright-pr-shards.mjs`, so it already runs in the `Production UI` job that `pr-required` demands on any UI-scoped PR. It must **not** be added to `verify:cheap:internal`: `check:gate-manifest` requires every gate in that chain to also run in `static-pr`, which has no browser and no server. Per-surface geometry stays in the held visual harness. **Both named gaps are now closed, 15 Aug 2026.** The rendered enumeration landed first: `885c613` (PR #1962) added the deterministic phone-viewport `min-h-tap` audit this row was waiting on — `/forms`'s fixed-array home rather than a live-search route, polled until three consecutive reads agree, explicitly sorted, and asserted identical across three full navigate-and-enumerate cycles. The `h-10` case is closed here by `interactiveTapFloorDeclarations` in `check:design-system-contract`: an interactive element (`a`, `button`, `input`, `select`, `summary`, `textarea`) declaring its own **unprefixed** `min-h-*` below the 48px token. Comparable arbitrary lengths and mutually exclusive/composed class branches are evaluated independently, so one safe branch cannot hide a sub-floor runtime path. Ratcheted at **33** across 14 files with per-path pins, so a new sub-floor control anywhere in `src/**` fails today. Mutation-verified: lowering one shortlist button to `min-h-9` produces both the total and matching per-path regression. **Deliberately scoped to `min-h-*`, not `h-*`/`size-*`:** a short `h-4` on an interactive element is routinely the visible box of a control whose hit area is owned by a tap-sized wrapper (`SelectionCheckbox` in `differentials-home.tsx`, whose label `ui-smoke` asserts still meets the floor), so flagging those would pad the baseline with non-defects — the §5 failure mode. **One known limit, recorded rather than hidden:** the walker sees intrinsic lowercase tags only, so a floor declared on `` or another component wrapper is invisible to it (the pre-existing `legacyTapClasses` check has the same blind spot). The 33 recorded sites are real debt still owed, tracked in `#265`. |
| 3 | Focus outline present, `--focus`, no companion ring | **planned** | Corrected 6 Aug 2026: the previous evidence ("`--focus` is referenced nowhere in the DS export", finding N3) was false — **[verified: grep]** 4 declarations (2 theme, 2 forced-colours) against **273** `var(--focus)` consumers, 260 of them in `.tsx`. The token is adopted; the check is what is missing. Path to blocking: assert a visible focus outline on every interactive role and reject a `ring-*` companion on the same node. A row that understates shipped work costs the document its authority as surely as one that overstates it, and nobody files a bug against pessimism. |
-| 4 | Non-colour encoding on every status indicator | **implemented-blocking for new use** | Blocked today by `ui-v2-answer-safety.dom.test.tsx`: an overdue `DoseLine` row is asserted to carry all three channels (amber inset rule **plus** the words "Source review overdue" **plus** a `StatusMark` shape), `MissingValue` is asserted never to contract to a dash at any density, `FieldError` is asserted to pair its text with an icon, and `RetrievalStateBanner` is asserted to carry its state in the headline text rather than the tone alone. Off-vocabulary status still degrades to a phrase (`source-badges-off-vocab.dom.test.tsx`, Gate 6). **Not blocked today:** there is no repository-wide enumeration of status indicators, so a _new_ colour-only indicator elsewhere in `src/components/**` — the bare `statusDot*` recipes are the obvious candidates — would not fail anything. **Closed 9 Aug 2026 — the repository-wide enumeration now ships.** `colourOnlyStatusIndicators` (`check:design-system-contract`) flags a status hue on a box that says nothing: no children, no `aria-label`/`aria-labelledby`/`title` on it or any ancestor, no text sibling, and not a `StatusMark`. It also flags shared _swatch recipes_ — a status hue plus a tiny round box and no text utility — because the analyzer is per-file and cannot follow an imported `statusDotReady` to its call sites, so the recipe is where the defect is catchable. Ratcheted at **4** with per-path pins: the two bare `statusDot*` recipes GATES.md named, plus a calculator risk band and a therapy meter fill. A _new_ colour-only indicator anywhere in `src/**` now fails. Still partial: those 4 recorded sites, and `--decoration-soft` is deliberately out of scope (it carries no state). |
+| 4 | Non-colour encoding on every status indicator | **implemented-blocking** | `colourOnlyStatusIndicators` scans production class roots and shared swatch recipes. The remaining calculator spectrum and shared status-marker debt was retired on 22 Aug 2026: severity bands now add distinct neutral edge patterns, ready/review/muted markers differ by outline/fill/geometry, and visible labels remain at call sites. The metric and per-path baseline are pinned at **0**; `StatusMark`, persistent text, and decoration-only `--decoration-soft` retain their documented roles. |
| 5 | Tables: semantic caption, associated headers, `aria-controls` on the expander | **implemented-blocking** | `AccessibleTableProps.caption` is required; DOM and alignment tests prove the semantic `
`, associated headers, and expander relationship. |
| 6 | Enum resilience — neutral fallback, never throws | **implemented-blocking** | `source-badges-off-vocab.dom.test.tsx`. |
| 7 | Elevation monotonicity — no child heavier than its parent | **implemented-partial** | Token-ramp monotonicity is tested; the per-surface child/parent rule is planned (needs a render-tree check). |
-| 8 | Edge ownership — no `border-*` + `ring-*` on one surface; no 1px spread in a drop shadow | **implemented-partial** | Corrected 9 Aug 2026: the previous evidence — "the per-surface co-occurrence lint and spread-term check are planned (PR 9)" — was **false**, and had been since before this row was written. Both ship. `edgeOwnershipConflicts` is an AST co-occurrence check over resolved class roots (`BORDER_WIDTH_UTILITY` × `RING_WIDTH_UTILITY`), and `onePixelShadowSpreads` reads the fourth length of every `box-shadow` layer. Both ratchet **per path**, so a new conflict on any surface fails today. What is partial is the recorded debt, not the check: **19 conflicts across 10 files**, and 2 spreads in `globals.css`. (Corrected 15 Aug 2026 — this cell said 27 across 15, which PR #1942 had already paid down to 25 across 12 without updating the prose. The §3 row below and `scripts/design-system-contract-baseline.json` both already said 25, and per `docs/design-system/README.md` the baseline outranks this document, so the prose was the defect. Ledger `#265` carried the same stale figure.) Path to blocking: retire that debt and pin both at zero. The spread form was chosen _because_ it is mechanically lintable, and it duly was. |
-| 9 | No layout-property animation; no hardcoded durations | **implemented-partial** | Corrected 9 Aug 2026: "a layout-property lint is still planned" was **false**, and leaving it would have preserved the exact contradiction this pass exists to remove. `layoutTransitionExceptions` ships and ratchets **per path** against `SAFE_TRANSITION_PROPERTIES`, so a new layout-property transition in any file fails today; the phone chrome's intentional `grid-template-rows` / `height` / `padding-bottom` are among the 12 recorded exceptions rather than a gap. The Tailwind duration form is a **hard zero** (`hardcodedMotionClasses`), on top of PR-Arch tokenising production `duration-*`/`ease-*` literals onto `--duration-*` / `--ease-*` (Gate 10). What stays partial is recorded debt, not a missing check: 12 layout transitions and 42 CSS `hardcodedCssMotionDurations`. Path to blocking: retire those and pin both at zero. |
+| 8 | Edge ownership — no `border-*` + `ring-*` on one surface; no 1px spread in a drop shadow | **implemented-partial** | Corrected 9 Aug 2026: the previous evidence — "the per-surface co-occurrence lint and spread-term check are planned (PR 9)" — was **false**, and had been since before this row was written. Both ship. `edgeOwnershipConflicts` is an AST co-occurrence check over resolved class roots (`BORDER_WIDTH_UTILITY` × `RING_WIDTH_UTILITY`), and `onePixelShadowSpreads` reads the fourth length of every `box-shadow` layer. Both ratchet **per path**, so a new conflict on any surface fails today. What is partial is the recorded debt, not the check: **18 conflicts across 9 files**, and 2 spreads in `globals.css`. (Corrected 15 Aug 2026 — this cell said 27 across 15, which PR #1942 had already paid down to 25 across 12 without updating the prose. The §3 row below and `scripts/design-system-contract-baseline.json` both already said 25, and per `docs/design-system/README.md` the baseline outranks this document, so the prose was the defect. Ledger `#265` carried the same stale figure.) Path to blocking: retire that debt and pin both at zero. The spread form was chosen _because_ it is mechanically lintable, and it duly was. |
+| 9 | No layout-property animation; no hardcoded durations | **implemented-partial** | Corrected 9 Aug 2026: "a layout-property lint is still planned" was **false**, and leaving it would have preserved the exact contradiction this pass exists to remove. `layoutTransitionExceptions` ships and ratchets **per path** against `SAFE_TRANSITION_PROPERTIES`, so a new layout-property transition in any file fails today; the phone chrome's intentional `grid-template-rows` / `height` / `padding-bottom` are among the 11 recorded exceptions rather than a gap. The Tailwind duration form is a **hard zero** (`hardcodedMotionClasses`), on top of PR-Arch tokenising production `duration-*`/`ease-*` literals onto `--duration-*` / `--ease-*` (Gate 10). What stays partial is recorded debt, not a missing check: 11 layout transitions and 41 CSS `hardcodedCssMotionDurations`. Path to blocking: retire those and pin both at zero. |
| 10 | Named z and motion tokens have real consumers; no unapproved `z-` value | **implemented-blocking** | PR-Arch: off-ladder `z-50` retired; toast at `--z-toast` (`z-[110]`, lint allowlist); motion literals map to named duration/ease tokens including phone-chrome hide/reveal pair. Dead `OverlayProvider` deleted. |
| 11 | Published-type conformance — every `dtsPropsFor` entry exactly matches source props | **implemented-blocking** | `generate-design-sync-contract.mjs` uses the TypeScript checker to derive every public property from the exported source `*Props` type; `--check` and `check-design-sync-contract.mjs` reject drift, missing named types, or machine-specific declarations. Zero-prop visual roots are explicit. |
| 12 | Publication coverage — every registered visual component has source, export, preview, prop contract and direct publication test | **implemented-blocking** | Registry/map/props parity and all 53 direct component rows are pinned by the design-sync contract and `design-sync-visual-exports.test.ts`. Behavioural, full visual-state, print, and browser acceptance remain separate gates; `DocumentFrame` is built shell-only in product but not yet among the 53 registered visual exports (print primitives remain specified-not-built). |
@@ -103,34 +103,34 @@ theme-list parity, and remote design-project publication remain separate concern
## 3 · The prohibition table
-| Never do this | Gate | Status |
-| -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| Reintroduce `.ckb-v2:not(.dark)` | Cascade computed-style test (PR 1) + contract-test selector filter update | implemented-blocking — landed #1538; contract parser + dark-ink regression guard |
-| Add `prefers-color-scheme` rules to the v2 layer | — (users pin a theme against OS preference) | **manual** |
-| Paint a numeral with a status colour; use amber/red decoratively | `check:design-system-contract` — `statusColouredNumerals` + `colourOnlyStatusIndicators` | **implemented-blocking** (9 Aug 2026) — ratcheted at 1 and 4 with per-path pins; see §5 |
-| Put `--decoration-soft`/`--text-soft` on a text node | Tier pinned both sides in the token contract; recipe AST (`decoration-on-text.contract.test.ts`) | implemented-blocking (recipes); broader AST follow-on |
-| Uppercase a dose unit | `Quantity` pins `normal-case` + unit test | implemented-blocking (branch) |
-| Invert a PDF, diagram or clinical image in any theme | `check:design-system-contract` — `imageInversions`, pinned at **zero**, not ratcheted | **implemented-blocking** (9 Aug 2026) — CSS `filter`/`backdrop-filter` plus the Tailwind `invert`/`hue-rotate` utilities; see §5 |
-| Border **and** ring on one surface, or a 1px spread in a drop shadow | `check:design-system-contract` — `edgeOwnershipConflicts` (19) + `onePixelShadowSpreads` (2) | **implemented-blocking for new use** — AST/CSS ratchets with per-path pins; the recorded debt itself is Gate 8's remaining half |
-| A child shadow heavier than its parent's | Gate 7 | implemented-partial |
-| Use `--shadow-tight`/any alias in new code | `check:design-system-contract` — `legacyShadowAliases`, ratcheted at 114 with per-path pins | **implemented-blocking for new use** — a new alias in any file fails. `--shadow-tight` itself is retired onto `--e1` and additionally pinned by a tracked-tree sweep in `tests/design-token-contract.test.ts` (it was silently reverted once by the `acf78bf` merge); the remaining 119 are the other six aliases, still `#262` |
-| Raw pixel size, padding, radius, gap or line-height in markup | `check:design-system-contract` — `rawPaddingLiterals` (63), `rawRadiusLiterals` (23), `rawGapLiterals` (31), `rawLineHeightLiterals` (3) | **implemented-blocking for new use** (9 Aug 2026) — per-path ratchets over both the utility and the CSS-declaration spelling, so a literal cannot move into `globals.css` to escape. Values containing a CSS function (`env(`, `clamp(`, `max(`, `calc(`) are sanctioned computed forms and exempt. Raw _size_ is still covered only for tap/shadow/colour |
-| Animate `width`, `height`, `grid-template-*`, `top`, `left`, `gap` | `check:design-system-contract` — `layoutTransitionExceptions`, ratcheted at 11 with per-path pins | **implemented-blocking for new use** — `SAFE_TRANSITION_PROPERTIES` carries the compositor-only allowlist; phone chrome's deliberate `grid-template-rows` is in the recorded 11 |
-| Hardcode a transition duration | `check:design-system-contract` — `hardcodedMotionClasses` (**zero**) + `hardcodedCssMotionDurations` (41) | **implemented-blocking** for the Tailwind `duration-*`/`delay-*`/`transition-all` form; the CSS form is a ratchet, so its 42 are debt |
-| A `z-[N]` literal outside the named rungs, in JS/TS only | `require-z-index-ladder` (ESLint) | implemented-blocking. Scope: `z-[N]` literals in JS/TS AST nodes. Plain `z-30` utilities and every `.css` / `.module.css` declaration are invisible to an ESLint rule - `sidebar-live-shell.module.css` carries an off-ladder `z-index: var(--z-sidebar-flyout, 50)` this cannot see. |
-| Ship a registered component without a direct publication test | Gate 12 | implemented-blocking |
-| Lower a production tap target below 48px | `check:design-system-contract` — `interactiveTapFloorDeclarations` (40) + Gate 2's phone-viewport `ui-style-contract` audit + `ui-smoke` flake history | **implemented-blocking for new use** (15 Aug 2026) — a new interactive control declaring a sub-floor `min-h-*`, including comparable arbitrary values and any reachable conditional branch, fails on both the total and its own path; the 40 recorded sites remain debt. Component-wrapper tags such as `` are a known blind spot |
-| Construct an enabled control with no action, destination or name | PR 4 discriminated unions + DOM contracts; `require-button-wiring` covers raw `