fix: restore answer thread on guest reload and stabilize UI specs - #262
Merged
Conversation
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Updates to Preview Branch (codex/pr-258-header-only-final) ↗︎
Tasks are run on every commit but only new migration files are pushed.
View logs for this Workflow Run ↗︎. |
Stop clearing persisted answer threads when auth resolves loading to signed_out (guest/demo). Align Playwright expectations with ModeHomeTemplate actions and differential catalogue counts. Co-authored-by: Cursor <cursoragent@cursor.com>
Add segment-level loading skeletons and error recovery UI for differential diagnosis, presentation workflow, and medication detail pages. Co-authored-by: Cursor <cursoragent@cursor.com>
BigSimmoforce-pushed
the
codex/pr-258-header-only-final
branch
from
July 5, 2026 11:01
cca7ade to
270d4cfCompareCo-authored-by: Cursor <cursoragent@cursor.com>
BigSimmo
enabled auto-merge (squash)
July 5, 2026 11:13
Uh oh!
There was an error while loading. Please reload this page.
BigSimmo pushed a commit
that referenced
this pull request
Aug 7, 2026
) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01823Ctwj4vinGhGuRNyK7oE
BigSimmo added a commit
that referenced
this pull request
Aug 8, 2026
…sured (#1719) * docs(issues): correct the re-measured A3 scope and B1 adoption count Replays a correction stranded in a local-only worktree when PR #1712 squash-merged and its branch was deleted before the commit could be pushed. Both figures were re-measured today against origin/main 2675e6e rather than copied forward, and one claim in the stranded version turned out to be wrong. #262: legacyShadowAliases counts SEVEN tokens, not one and not eight — the regex has matched tight|card|soft|hover|elevated|lux|lift since PR #1616 and has never included focus. Measured total 228 = tight 100, soft 72, elevated 17, hover 17, card 12, lux 8, lift 2. So --shadow-tight is 100 production sites across 55 files rather than the 155 or 229 previously recorded, clearing all 100 will not zero the ratchet, and #261's --shadow-focus does not appear in this metric at all. #266: adoption is 23 unadopted, not 24, since Button moved via #263 — its sole production importer is src/components/AccessibleTable.tsx. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(design-system): mark the TextField/SearchField/Select row done, it was stale Replays a correction stranded in a local-only worktree when PR #1712 squash-merged and its branch was deleted before the commit could be pushed. Re-measured today against origin/main 2675e6e: all three fold onto FormField, which renders hint AND error unconditionally (form-field.tsx:131-132) and merges aria-describedby caller -> hint -> error (form-field.tsx:107-109); TextField and SearchField each take an external id and a Ref<HTMLInputElement>, Select an external id and a Ref<HTMLSelectElement>. Landed in PR 13, not the PR 7 the row pointed at. Section 4's 'Do: fold TextField/SearchField/Select/Checkbox/RadioGroup onto this shell (PR 7)' was stale in a second way and is corrected too: Checkbox and RadioGroup deliberately do NOT fold onto FormField — a group keeps fieldset/legend and composes FieldHint/FieldError directly (choice.tsx:207-210). Left as written, that line asks a future session to 'finish' a fold that is finished and to break one that was a deliberate decision. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: correct future-dated re-measurement records to 2026-08-08 Codex review on PR #1719 flagged SCOPE/COUNT RE-MEASURED and COMPONENTS.md rows dated 2026-08-09 while the recording commits were authored on 2026-08-08. Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com> * docs(design-system): separate FormField integration from product adoption, re-measure the ui-primitives row Two review findings on PR #1719, both valid. Section 4 said 'Select/choice controls remain separate adoption work', which contradicted the section above it and was wrong on both axes for Select: select.tsx consumes FormField (folded in PR 13), and adoption-manifest.json gives Select 2 production importers, so it is adopted. The genuinely unadopted member of the trio is SearchField, at zero, which the sentence did not mention. Rewritten to state the two axes explicitly — integration is complete for TextField, SearchField and Select, and deliberately declined for Checkbox/RadioGroup; adoption is measured TextField 3, Select 2, SearchField/Checkbox/RadioGroup 0 — so a control cannot be read as pending on one axis because it is pending on the other. The ui-primitives row carried three stale figures. Re-measured on this head: the module is 698 lines, not 686; 157 production files import it, not 200 — that figure is 202 including mockups, the same mockup-inclusive mislabelling this PR already corrects for the shadow aliases; and 30 of the 53 registered components are adopted, not 27. Future-dated 2026-08-09 records were already corrected to 2026-08-08 by f3a91c6 before this commit; verified none remain under docs/. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
BigSimmo added a commit
that referenced
this pull request
Aug 8, 2026
* docs(design-system): stamp the 7 August handover superseded Nine open ledger rows (#261, #262, #264-#270) cite docs/design-system/HANDOVER-2026-08-07.md as their Source, and docs/design-system/README.md sent anyone picking the work up cold straight to it as "measured state, the ordered plan". Four of its figures have since been disproved, and the corrections were written into the ledger rows rather than the document, so the document still asserts the originals. A session scoping from it re-derives work that is already known wrong. Recorded as #277. A banner rather than a rewrite, which is what #277 asks for: the corrections already live in the rows, and duplicating them re-creates the drift this fixes. The banner names the four measured errors -- the "229 --shadow-tight aliases" that is really a seven-token total mislabelled as one token (100 sites across 55 files, 228 total), the 24-vs-23 unadopted count, the "baselines cannot be generated on Windows" conclusion that overlooked the ubuntu CI job now used under #118, and #270's 22-site premise that has zero same-variant pairs -- then points at the rows. The README now sends a cold start to docs/outstanding-issues.md and marks the handover superseded in the link text itself, where the misdirection was. Deliberately not done: the document is not deleted, moved, or corrected in place. The nine Source citations, the PR and commit record, and its verification and gotcha sections are provenance the ledger is meant to preserve, and silently correcting it would leave those rows citing a document that no longer says what they were derived from. Verified: npm run docs:check-links -- "docs link check passed: 1667 repo path references resolve"; npm run docs:check-inventory -- current; npm run format:check -- "All matched files use Prettier code style!". Refs #277 * docs(design-system): drop false #118 baseline-adoption claim The superseded banner said all six visual baselines were adopted under #118, but the ledger still has #118 open and tests/__screenshots__/ holds only README.md. Keep the accurate Ubuntu-CI generation point without implying visual-regression protection exists yet. Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
BigSimmo added a commit
that referenced
this pull request
Aug 9, 2026
…1770) * feat(design-system): gate the ungated prohibitions and close gate 4 Closes#264 and the gate-4 half of #265. Measured against origin/main 8db1e53 rather than scoped from the handover, which mattered: four of #264's six prohibitions were already gated while GATES.md §3 read `planned`, and that understatement is what deferred this task twice. Tighten two ratchets carrying stale slack to their measured values: edgeOwnershipConflicts 28 -> 27 and legacyShadowAliases 231 -> 224. Seven files had paid debt down without a baseline refresh, so up to seven new violations would have passed. Every other metric and debtByPath entry was asserted unchanged first. The remaining 224 aliases are #262's cleanup. Add three checks to check:design-system-contract: - statusColouredNumerals (ratcheted 2) — a text-* status hue on an element whose children are all figures. - colourOnlyStatusIndicators (ratcheted 4) — gate 4's repository-wide enumeration. A status hue on a box with no children, no accessible name on it or any ancestor, no sibling text, and not a StatusMark. Also flags shared swatch recipes, since the analyzer is per-file and cannot follow an imported statusDotReady to its call sites. - imageInversions (pinned at zero) — CSS filter/backdrop-filter and the Tailwind invert/hue-rotate utilities. All three are mutation-verified, as is the tightening: reintroducing one var(--shadow-tight) now fails with `legacyShadowAliases increased from 224 to 225` plus the per-path assertion, where the same edit passed at 231. The two status metrics were re-measured three times before the baseline was written. The first draft found 19 colour-only indicators and 3 numerals; 15 were false positives — a name test loose enough that `size` read as a numeral, an ancestor walk checking for JsxOpeningElement when ancestors are JsxElement, and no sibling-text rule, which condemned the ordinary legend pattern. All six recorded entries were read in source and confirmed real. Gate 2 is NOT closed. A rendered-interactive tap enumeration was written, shown to find genuine defects, and reverted: six runs against one production build returned 6, 5, 4, 3, 3 and 9 distinct sub-floor shapes because the audit races the async render, and networkidle plus shape deduplication did not settle it. ui-style-contract.spec.ts runs in the required Production UI job, so an intermittent version would have blocked every merge. Recorded with the defect it found as #289. Correct GATES.md throughout, including the gate 2 row's claim that test:e2e:style-contract needs wiring into verify:cheap — the spec already runs in required CI via productionSpecPattern and playwright-pr-shards, and adding it to verify:cheap:internal would trip check:gate-manifest, which requires every gate in that chain to also run in the browser-less static-pr job. Close#277, verified already satisfied on main. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(ledger): record the M2 design-system gates review Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(ledger): supersede the M2 row after renumbering #289 to #291 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix: expand NUMERAL_TEXT regex to accept clinical unit suffixes Co-authored-by: BigSimmo <87357024+BigSimmo@users.noreply.github.com> * fix(design-system): close seven gaps found in review of the new gates All seven review findings reproduced against a probe before any change, and every one was real. Two mattered: - Neither status pattern accepted Tailwind's `/NN` opacity modifier, and 83 status-token utilities in `src` carry one. The anchored patterns rejected every one before the semantic checks ran, so a colour-only indicator written `bg-[color:var(--danger)]/90` walked past a ratchet described as repository-wide. - `NUMERAL_TEXT` rejected every letter while its own comment claimed units were covered, so a dose painted in a status colour — the case the rule exists for — passed silently. Replaced with `isNumeralTextFragment`, which accepts an explicit unit list and still rejects prose. It carries no digit requirement, because `{dose} mg` splits into an expression and a unit-only text child. Also: - `isNumeralExpression` now requires an arithmetic operator, rejects any string/template operand anywhere in the tree, and follows only `+`/`-` prefix unaries, so `{count + " errors"}` is no longer a numeral. The previous comment claiming a concatenation "would have a string literal rather than a numeric one" was wrong: one numeric side was enough. - `INVERSION_FUNCTION` matches the `invert(`/`hue-rotate(` call itself, covering `filter-[invert(1)]`, `[filter:invert(1)]` and both `backdrop-` forms. - `IMAGE_INVERSION_UTILITY` excludes `-0`; `invert-0` disables inversion and a hard-zero gate must not reject the reset. - `rendersVisibleText` no longer counts `{null}`, `{false}`, `{undefined}` or a bare self-closing element as a text channel, and judges markup-building expressions by that markup. - `hasNonEmptyAccessibleName` replaces the attribute-presence test, so `aria-label=""` no longer exempts an indicator. - The Gate 9 row said a layout-property lint was "planned" while `layoutTransitionExceptions` already ships and ratchets per path — the exact contradiction this branch exists to remove. Fixing these surfaced two false positives of my own, both caught by re-verifying every baseline entry in source rather than trusting the count: the sibling walk stopped at the first non-JSX parent, so a conditional badge with a label was reported colour-only; correcting that then let a sibling `{started ? <div/> : null}` — another coloured div — count as text. Baseline unchanged at 4 colour-only / 2 numerals / 0 inversions, and unchanged by the unit widening, so nothing in the repo paints a dose in a status colour. `legacyShadowAliases` 224 -> 220 from the main merge. Every new form is mutation-verified. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(ledger): supersede the M2 row after the review round and main merge Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
BigSimmo added a commit
that referenced
this pull request
Aug 9, 2026
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
BigSimmo added a commit
that referenced
this pull request
Aug 9, 2026
…lection (#262 parts 2 and 3) (#1780) * docs(issues): close#218 and #270, both shipped before this session Both rows were still open in docs/outstanding-issues.md while their work was already live on main, which had scoped a third session from them. #218 (cn() lacks tailwind-merge) shipped in PR #1678, aeba5a2. src/components/ui-primitives.tsx:37 is twMergeClinical(...) rather than a plain join, package.json carries tailwind-merge ^3.6.0, and src/lib/tailwind-merge.ts declares the repo's @theme scales to twMerge. #270 (declare the tap spacing token) shipped in PR #1738, 80cf781, an ancestor of origin/main. "tap" is present in CLINICAL_TWMERGE_THEME.spacing, and tests/tailwind-merge-config.test.ts was inverted rather than deleted so the merge behaviour is now asserted rather than pinned out. Verified in source at origin/main 7aaf934, not inferred from the handover. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(design-system): ratchet raw padding, radius and line-height literals (#262 part 3) The design-system contract ratcheted colour, shadow, tap and tracking but not spacing, radius or line-height, so a value could bypass the scale as a bare literal in either a class or a stylesheet and nothing objected. Adds three per-path ratchets, covering both halves the way the colour and legacy-shadow metrics already do: rawPaddingLiterals 67 (17 CSS declarations, 50 class utilities) rawRadiusLiterals 24 (22 CSS declarations, 2 class utilities) rawLineHeightLiterals 3 (3 CSS declarations) The exemption is deliberately "contains no CSS function", not the narrower `(?!var\()` the tracking rule uses. Padding is not only ever a token or a literal: production ships pb-[env(safe-area-inset-bottom)], pt-[max(0.75rem,var(--safe-area-top))], pt-[clamp(1.5rem,5vh,3rem)] and pb-[calc(7rem+env(safe-area-inset-bottom))]. Those are computed from the viewport or the safe-area inset, cannot be spelled as a scale step, and a `var(`-only lookahead would have flagged every one of them. On the CSS side, zero in any unit, the CSS-wide keywords and custom-property declarations (the token definitions themselves) are exempt for the same reason. Every one of the 94 baseline entries was verified present at its cited line before pinning, and the baseline change is additive: all fifteen pre-existing metrics and every pre-existing debtByPath entry are byte-identical. Mutation-tested rather than assumed. Class side, in a file with no prior debt: - rawPaddingLiterals increased from 67 to 68 - rawPaddingLiterals at src/components/ui-primitives.tsx increased from 0 to 1 - rawRadiusLiterals increased from 24 to 25 - rawRadiusLiterals at src/components/ui-primitives.tsx increased from 0 to 1 - rawLineHeightLiterals increased from 3 to 4 - rawLineHeightLiterals at src/components/ui-primitives.tsx increased from 0 to 1 The CSS half fails the same way. Both probes also carried the sanctioned computed forms, and each count rose by exactly one, so the exemptions are proved by the same runs rather than argued. No new npm script: the metrics live inside check:design-system-contract, so docs:check-inventory and check:gate-manifest are untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(design-system): gate type-step selection on the decidable half (#262 part 2) check:type-scale blocks arbitrary text-[12px] values. Nothing has stopped the scale itself growing a step no surface ever picks, which is the drift that makes a wrong selection possible in the first place. Whether a heading should have chosen text-sm over text-sm-minus is not mechanically decidable, and this does not pretend otherwise. A step that is declared and consumed by nobody is decidable, and there is one today: --text-2xl-compact (globals.css:112) has zero consumers -- no utility use, no var() use -- while the next-rarest step, text-hero, has one real consumer. The analyzer reports every bare text-<name> it sees and does not decide which names are steps; the checker intersects that against the @theme block it parses from globals.css. So the scale is never written down twice, and a step added to globals.css is covered without touching this gate. Retiring the dead step edits @theme, so it gets its own revertible PR rather than riding along here: it is carried in UNUSED_TYPE_STEP_EXEMPTIONS and tracked as docs/outstanding-issues.md #295. The exemption cannot rot silently -- the gate also fails if an exempted step stops being declared or gains a consumer. Mutation-tested, three ways: - type steps are declared in globals.css @theme but no production surface selects them: --text-2xl-compact (text-2xl-compact). Retire the step or use it; do not leave the scale carrying a step nobody picks. - (a newly added --text-probe-step fails identically, so this catches future drift rather than only today's known case) - --text-2xl-compact is exempted as unused but production now selects text-2xl-compact -- drop the exemption Measurement note, since three different figures were in circulation for this row: the "1318 sites" is a repo-wide grep INCLUDING mockups, which the gate excludes (1360 at this HEAD). Production consumers of the nine non-standard steps total 705 -- text-2xs 421, sm-minus 160, base-minus 57, 3xs 42, 2xl-minus 9, 3xl-minus 9, lg-minus 6, hero 1, 2xl-compact 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(design-system): correct GATES.md for the two new scale gates GATES.md's own §1 is the list of what actually runs, and this series' recurring failure is that list lagging the code: four of #264's six prohibitions were already gated while it said "planned". Records the padding/radius/line-height ratchets and the type-step selection rule in the contract row, and rewrites the type-scale callout, which claimed a step-selection lint "does not exist". The decidable half now ships; the half that asks whether text-sm-minus was the right pick over text-sm still does not, and cannot. Also corrects the "1 318 call sites" figure quoted there. It was a repo-wide grep including src/app/mockups/**, which every one of these gates excludes (1 360 at 7aaf934). Production consumers total 705, and there are nine non-standard steps, not eight. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(ledger): record the #262 parts 2/3 gate work (PR #1780) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(design-system): close scale-ratchet and unused-step review gaps Cover Tailwind arbitrary-property forms and modern CSS zero units in the raw scale ratchets, and validate unused-step exemptions against the same class-or-CSS consumer predicate used for ordinary steps. Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
BigSimmo added a commit
that referenced
this pull request
Aug 9, 2026
…1786) * feat(design-system): build ErrorState, the gate with nothing behind it GATES.md §3 lists the prohibition "Render '0 matches' after a failed request" with the gate "ErrorState adoption + check", status planned. Measured at origin/main 199b303, ErrorState existed nowhere in src or tests — only in COMPONENTS.md, GATES.md and SPEC.md. This builds it. The invariant is clinical, not cosmetic. A search that failed has no count to report, so reporting zero is a false statement about the corpus: on the services page "0 matches" asserts there are no crisis services when the search never ran, and on favourites it reads as "you have saved nothing" rather than "we could not load them". COMPONENTS.md:322 draws the same line from the other side — "no result count is available" is not a MissingValue. The component therefore takes no count and no children. There is no prop through which a number can arrive, and the generated dtsPropsFor entry now records that as the published API. The one remaining route, a caller writing a count into title or body, is covered by a development-time tripwire that matches a figure against a counted noun ("0 matches", "no results") so an error code or a duration does not trip it. It warns and never throws: on the one screen already reporting a failure, a thrown error is a blank page. Requirements came from the surfaces that hand-roll this guard today. Three do, and their comments state the rule outright: search-results-header-band.tsx:210 ("no number may reach the DOM"), services-navigator-page.tsx:634 ("a blocked registry must not reach the band as '0 matches'") and favourites-command-library-page.tsx:1182. They are correct, just not shared; converting them is a live-look change and deliberately not here. Three further sites carried into this task as hand-rolled guards are not that, measured at this HEAD, and are recorded so the next reader does not convert them: differentials-home.tsx:716,729 renders "0 matches"/"No matches" when sourcesChecked is true, i.e. a legitimate zero after a search that SUCCEEDED; specifiers-home-page.tsx:211 is a comment about not showing a stale zero above real catalogue results, and lives in src/components/specifiers/, not clinical-dashboard/; document-search-results gates on recordStatus for loading, not for a failed count. Registered per gates 11 and 12: source, design-sync export, preview, prop contract, publication test entry, behavioural DOM tests, adoption-contract family, and both generators regenerated (54 components, 59 roots). Uses the shared floatingControl recipe rather than a hand-rolled control, so the tap floor, focus ring and forced-colors border come from one owner. Adds zero arbitrary padding/gap/radius/line-height, so the ratchet landed in the previous commit is unaffected and the two stay separately revertible. Scope: ErrorState only. OfflineState, PermissionDeniedState, NotFoundState and UnavailableState share the pattern but have no gate pointing at them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(design-system): ratchet raw gap literals, the family #1780 left uncovered #1780 landed rawPaddingLiterals, rawRadiusLiterals and rawLineHeightLiterals for #262 part 3. Gap was the one remaining spacing surface a hand-picked value could hide in: gap-[9px] and `gap: 18px` were counted by no ratchet at all. This adds rawGapLiterals on that commit's own predicate and wiring. Measured against origin/main ef9bb51: 34 sites across 9 files — 21 Tailwind utilities, every one under src/components/therapy-compass/, plus 13 CSS declarations in globals.css that a utility-only scan misses. Covering both spellings is the same reason #1780 counts both: otherwise a literal escapes by moving from a class into globals.css. Kept as its own metric rather than folded into rawPaddingLiterals so the therapy-compass cleanup can be paid down and re-pinned independently of the padding debt, which is spread across fifteen unrelated files. Reuses RAW_LITERAL_VALUE unchanged, so a value containing a CSS function (env(, clamp(, max(, calc() stays a sanctioned computed form and is exempt. Also corrects the §3 prohibition row, which #1780 left reading "implemented-partial (colour/shadow/tap literals only)" and which named none of the metrics it had just shipped. A row that understates shipped work is what sends the next session to rebuild it — this change was itself started as a duplicate of #262 part 3 for exactly that reason. Mutation-verified in both halves, each naming the metric and the path: a gap-[3px] utility gives "rawGapLiterals increased from 34 to 35" plus "at src/components/ui/missing-value.tsx increased from 0 to 1"; a `gap: 19px` declaration gives the same total plus "at src/app/globals.css increased from 13 to 14". Baseline diff is additive only. legacyShadowAliases measures 218 against its pinned 220 on main; that slack is left exactly as found. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(issues): capture the ErrorState and duplicate-work follow-ups Four rows (#298-#301), added with npm run issues:add. Ids 295-297 were claimed by main while this branch was open, which is ledger #156's read-modify-write race behaving exactly as recorded. - #298 (P2 task) the ErrorState enforcement check. GATES.md still reads "planned" for the 0-matches prohibition and nothing in scripts/ or eslint-rules/ references ErrorState, so the component exists but is not required. Deliberately not flipped to implemented. - #299 (P3 task) adopting ErrorState at the three surfaces that genuinely hand-roll the guard. Live-look change, downstream of the redesign. - #300 (P2 issue) three sites miscarried into M4 as guards that are not, so the next reader does not convert them. differentials-home renders its zero after a search that SUCCEEDED. - #301 (P3 issue) two sessions built #262 part 3 in parallel because the §3 row understated what had shipped. Proposes asserting that every baseline metric key appears in GATES.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(design-system): restrict the ErrorState copy tripwire to development Codex review on #1786 (P2). The doc comment described the tripwire as development-only, but the emitter only silenced NODE_ENV === "test", so a production caller supplying count-bearing title/body copy had the full caller-provided string written to console.warn. On a clinical surface that copy can quote the query — "0 results for <query>" — which turns a copy defect into a disclosure risk. Nobody reads a production browser console for design-system warnings, so the emit is now development-only and an unset NODE_ENV is treated as production: fail quiet. The gate is an exported predicate rather than an inline comparison because an inline comparison is untestable here. Vite statically replaces process.env.NODE_ENV inside src/ modules, so under Vitest the check compiles to `"test" === "development"` and no stubEnv can move it. The review asked for a production console-spy check; written that way it would have passed while proving nothing, staying silent for the wrong reason and continuing to pass even if the guard were deleted. shouldEmitErrorStateDiagnostic is asserted directly instead — development true; production, test and unset false — with the console spy kept alongside as the weaker check that catches an emitter which warns unconditionally. Verified: typecheck 0 errors, lint 0, format:check 0, check:design-system-contract 0, Tests 70 passed (70) across error-state.dom and design-sync-visual-exports. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
BigSimmo pushed a commit
that referenced
this pull request
Aug 12, 2026
…system rows Archived as verified-delivered or duplicate: #303, #284 (dup of #296), #149, #159, #286, #287, #288, #256, #261, #291. Re-scoped with re-measured evidence: #210 (gate was never missing; the defect is the .next/dev types include), #262 (aliases 228 -> 193), #266 (counts moved, 23 unadopted holds), #275 (premise 'only one implementation' is false — the values leaked to five files). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017paT42ZVMf8jaLtkjFxdy5
BigSimmo pushed a commit
that referenced
this pull request
Aug 12, 2026
… rows Repairs. Two main-merges silently reverted edits from this branch, and the re-apply script missed them because it was hand-enumerated from round 1's second commit only: - #293 the entire min-h-tap refutation was gone (grep sm:min-h-0 returned 0); the row was back to asserting a defect that does not exist. Restored from a6bfc6f. This was the live hazard — as reverted it directed a reader to strip desktop step-downs and pin every control to 48px. - #266 restored the 2026-08-12 counts (54/31/23) over main's older 53/30/23. - #262 deliberately NOT restored: main's version is newer than mine and records --shadow-tight retired outright on 2026-08-10. - #289 archived; verified fixed in round 1 and never actually closed. Stamps. Sixteen rows were checked during the sweep, found correct, and left unchanged — which left no trace, so they were indistinguishable from rows nobody had opened. Each now carries VERIFIED CORRECT 2026-08-12 plus the measurement that settled it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017paT42ZVMf8jaLtkjFxdy5
BigSimmo pushed a commit
that referenced
this pull request
Aug 12, 2026
Archived #303: probed the real CLI rather than reading it — ledger:append now accepts a --scope value beginning with a double dash. (The probe row it wrote was removed immediately and the guard re-run clean.) #302 re-measured and WORSE, which strengthens it: the legacyShadowAliases ceiling is still pinned at 220 while the gate measures 193 — 27 units of unguarded slack, up from 3 when the row was written. Paying #262's debt down bought headroom instead of tightening the gate, which is exactly the row's thesis. #215 re-scoped: one of its three items is done (image-lightbox has decoding=async) and the row's path was misleading — the file is under clinical-dashboard/, so a root-level search reads as 'already fixed'. Twenty stamped VERIFIED CORRECT with the measurement that settled each. Coverage: 58 of 119 open rows now carry sweep evidence, up from 34. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017paT42ZVMf8jaLtkjFxdy5
BigSimmo pushed a commit
that referenced
this pull request
Aug 12, 2026
…d rows The conflict was one hunk. Resolved by keeping this branch's #302 (re-measured 2026-08-12, newer than main's) and all four of main's genuinely new rows (#304, #305, #306, #307), dropping only main's older #302 and its #303, which this branch had already archived as delivered after probing the CLI. Correcting an earlier claim in this branch's history: #302 and #303 were NOT lost by main. Main renumbered them to #306 and #307 after the id collision, which is exactly what the guard advises. Both are re-filings of work already done, and both are archived here with evidence — style-contracts.ts:352-354 is clean, and the marker reads 308 against a highest declared id of 307. The derived loss detector reports one exception, #262, which is intentional: main's version of that row is newer than this branch's. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017paT42ZVMf8jaLtkjFxdy5
BigSimmo added a commit
that referenced
this pull request
Aug 14, 2026
…bt rows (#1942) * refactor(tokens): re-land the --shadow-tight retirement onto --e1 PR #1803 retired the --shadow-tight role alias in favour of the --e1 elevation tier across 49 files and squash-merged as 9d8370a on 2026-08-10. The acf78bf merge on 2026-08-11 silently reverted it, along with six other PRs. This re-applies the retirement against current main: 130 call sites across 67 files, plus both declarations. The alias was a pure pass-through -- `--shadow-tight: var(--e1)` in the light and dark role blocks -- so the substitution is value-preserving. Confirmed for forced-colors too rather than assumed: the `@media (forced-colors: active)` block scopes `:root, .dark`, the same `html` element the alias is declared on, so `--shadow-tight` already resolved through the flattened `--e1: none` there. The .ckb-v2 redeclaration hazard does not bite for the same reason -- .ckb-v2 sits on <html> and .ckb-v2.ckb-v2 outspecifies :root, so both spellings substitute against the winning v2 tier. Two comments survived acf78bf while the code they describe did not: the globals.css note that "the resting-hairline role is gone", and the token test's "unlike the --shadow-tight assertion above". Both are accurate again. The token contract test now sweeps the tracked src tree for both spellings (declaration and var() consumer) instead of only asserting the declaration. A declaration-only check would have caught this particular revert, but only because the declarations happened to come back with the call sites; sweeping both makes the gate independent of which half of a bad merge lands. Mutation-verified in both directions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XsBPUoxpsvTwvFMJzrNoyz * chore(design-system): re-pin the contract ratchets to their measured values `scripts/design-system-contract-baseline.json` is a ceiling, so paying debt down leaves silent headroom behind. Ledger #302 records that pattern: legacyShadowAliases was pinned at 220 against a measured 193, 27 units of unguarded slack, up from 3 units on 2026-08-10. With the previous commit's --shadow-tight retirement applied the gap is wider still -- 220 pinned against 119 measured -- because the reland pays down the debt the acf78bf revert had re-hidden. Four other ratchets had accumulated slack from unrelated work in the same window. legacyShadowAliases 220 -> 119 edgeOwnershipConflicts 27 -> 25 rawPaddingLiterals 67 -> 63 rawGapLiterals 34 -> 32 layoutTransitionExceptions 12 -> 11 Regenerated with --print-debt-baseline rather than hand-edited, so the per-path debtByPath counts move with the totals -- those are what findDebtPathRegressions compares, and the retirement moved them wholesale. Every metric in the diff decreases; nothing is absorbed upward. This is not the baseline refresh #262 warns against. That stop rule forbids refreshing to hide the movement; this pins the movement in so it cannot silently drift back a second time. Mutation-verified: reintroducing one alias in button.tsx now fails at both the total (119 -> 120) and the per-path level. Under the old 220 ceiling the same addition passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XsBPUoxpsvTwvFMJzrNoyz * refactor(tokens): hold the search-band count bubble in a spacing token The active-filter badge sized itself with a raw `h-[1.0625rem] min-w-[1.0625rem]` pair. Ledger #275 tracks that value as leaked debt: it had reached five files, so the fix has always been to tokenise once rather than edit a call site. Re-measured on merged main, the badge role is down to a single call site. #170's convergence landed in the meantime -- document-search- results.tsx now renders the shared control and therapy-compass/ filter-sheet.tsx was deleted outright -- so the leak this row was written about has already been reabsorbed by the extraction. Holding the value in @theme is what stops it leaving again. Two arbitrary values in the same component are deliberately left raw: pr-[0.6875rem] and min-[414px]:max-[429px] -- the repo defines no --breakpoint-* tokens at all, and eight peer sites use the same raw min-[]/max-[] form (359px, 389px, 414px). Naming one window while the peers stay raw is the same drift #275 warns about on another axis, and Tailwind named breakpoints would add variants across the whole utility surface. That belongs in a repo-wide decision, filed separately. The three remaining 1.0625rem hits in mode-nav.tsx and nav-slot-ink.tsx are NOT this token. They size <Icon> glyphs -- a 17px icon against a 12/14/16/20/24 --spacing-icon-* scale -- so folding them under a badge token would merge two roles that only happen to share a number. check:icon-scale deliberately does not flag arbitrary h-[Nrem], so they are a real but separate finding, filed rather than guessed at. The token is also registered in CLINICAL_TWMERGE_THEME.spacing, which tests/tailwind-merge-config.test.ts asserts against the @theme block -- without it `cn()` cannot resolve a conflict on the new utility. Safe by that file's own `tap` reasoning: the single call site is a static string carrying no competing h-*/min-w-* class and never passes through `cn()`, so there is no same-variant pair for declaration to hand to the later class. The entry is protective for future use, not load-bearing today. Value-preserving, and proven rather than inferred: compiling globals.css through @tailwindcss/postcss emits .h-search-band-badge { height: var(--spacing-search-band-badge) } .min-w-search-band-badge { min-width: var(--spacing-search-band-badge) } No ratchet moved, so the ceilings pinned in the previous commit still sit at zero slack. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XsBPUoxpsvTwvFMJzrNoyz * docs(design-system): close out DS Track A3 and refresh the stale gate rows Track A3 is `#262`. Its three parts are now all settled, each checked against code rather than against the row that describes it. Part 1 is the --shadow-tight retirement re-landed earlier in this PR. Part 3 shipped in PR #1780 per `#301`: rawPaddingLiterals, rawRadiusLiterals and rawLineHeightLiterals are live baseline keys enforced over both the class and CSS-declaration spellings, plus rawGapLiterals beyond the original ask. Part 2 needs no work, and that had already been adjudicated -- GATES.md section 3 records it, which is why nothing here builds it. The decidable half of step selection shipped on 9 Aug inside check:design-system- contract: a declared @theme step no production surface selects fails the build. The remaining half -- which existing step a component picks -- is documented there as something "nothing mechanical can" gate, being a judgement about the rendered design rather than a property of the source, with a standing instruction not to duplicate the arbitrary-value check check:type-scale already ships. Reading `#262` alone would have sent a session to build it; that is the `#301` failure mode, so the closure record says so explicitly. Section 3's live status rows carried numbers this PR moved. `#301`'s lesson is that a row understating shipped work is a duplicate-work generator, so they are corrected in the same change: legacyShadowAliases 224 -> 119, and the alias is now retired outright rather than "224 left to retire" edgeOwnershipConflicts 27 -> 25 rawPaddingLiterals 67 -> 63 rawGapLiterals 34 -> 32 layoutTransitionExceptions 12 -> 11 Section 5 is left alone deliberately: it is a dated record measured against 8db1e53, not a live status surface, and rewriting its figures would destroy the provenance it exists to hold. Ledger records are queued as immutable inbox requests: `#262`, `#302` and `#275` closed; two carve-outs split out of `#275` filed as their own rows (the repo-wide breakpoint-token decision, and three 17px mode-nav icon glyphs that sit off the --spacing-icon-* scale with no gate covering them). The queued re-land request 210e3db5 is cancelled rather than reconciled -- its headline "67 files on main still use the retired alias" is false as of this branch, so it would open a row wrong on arrival. The request file and the cancellation both survive as provenance for the acf78bf merge loss. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XsBPUoxpsvTwvFMJzrNoyz * chore(ledger): record the design-token relands review Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XsBPUoxpsvTwvFMJzrNoyz * fix(issues): retarget the reland record after main reconciled it mid-flight CI failed `docs:check-links` on this branch with Error: cancel request 2e791c01... targets missing pending request 210e3db5... `check-docs-links.mjs` replays the inbox batch to resolve link targets, so an unresolvable request fails it. The cause was a race, not a bad record: PR #1936 reconciled 75 queued requests -- 210e3db5 among them -- while this branch was already in flight. Reconciling moves the request file into `docs/outstanding-issues-inbox/applied/` and allocates it a canonical row, so by the time this branch merged main there was no pending request left for the cancellation to name. Cancelling was the right call against a pending request and is the wrong one against a reconciled row. The cancel is dropped and replaced with a `done` against `#319`, the row 210e3db5 became. That is also the better record: the work is finished rather than withdrawn, so the ledger should carry its outcome and its guard, which a cancellation would have thrown away. Also merges origin/main (this branch was 3 behind) and files two findings the PR preflight surfaced, both deliberately not fixed here: - `check:medication-lexicon-report` has been failing on main for every local `verify:pr-local`, and no CI job runs it -- a grep over .github/workflows finds nothing. It is the last step of the local chain, so it fails preflights while CI stays green. The stale file is a clinical-facing generated document; regenerating it inside a CSS-token PR would bundle a clinical-risk artefact with unrelated chores. - Claude Code web containers can ship Node 22 with no node_modules, which fails `npm ci` on engine-strict before any repo script can run. Re-verified after the merge: the tracked tree still holds zero `--shadow-tight` references, and every pinned ratchet still measures exactly its baseline, so the merge moved no metric and the pins stay honest. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XsBPUoxpsvTwvFMJzrNoyz * docs(design): retire shadow-tight guidance * docs(design): retire shadow-tight guidance * docs(design): retire shadow-tight guidance * docs(design): retire shadow-tight guidance --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
loading → signed_outclearing persisted answer threads on every page reloadContext
Follow-up to #261 (already merged). Only 3 files differ from
main.Test plan
npm run verify:cheappassed locally (1059 tests)npm run verify:uipassed locally (97 Chromium tests)