Skip to content

Import WA psychiatric services catalogue and clinical registry snapshots - #261

Merged
BigSimmo merged 19 commits into
mainfrom
codex/pr-258-header-only-final
Jul 5, 2026
Merged

Import WA psychiatric services catalogue and clinical registry snapshots#261
BigSimmo merged 19 commits into
mainfrom
codex/pr-258-header-only-final

Conversation

@BigSimmo

Copy link
Copy Markdown
Owner

Summary

  • Import 219 WA psychiatric services from the services-information export into data/services-snapshot.json, with catalog mapper, registry fixtures, and catalog_payload migration.
  • Add parallel medication and differential catalogue pipelines (snapshots, import scripts, API routes, migrations, and tests).
  • Refactor services.ts / registry seeding so signed-in users load owner-scoped records from Supabase instead of hardcoded fixtures.

Verification

  • Signed-in browser/API check as josh@stoicable.com: /api/registry/records?kind=service returns 200 with 222 owner records (includes 13yarn); /services renders catalogue UI.
  • Vitest: tests/services-catalog.test.ts, tests/services.test.ts, tests/registry-records-route.test.ts, tests/supabase-schema.test.ts (55 tests passed).
  • Live Supabase seed: 219 service rows upserted for owner 4f1b3c19-3c39-4597-b9df-168c8e6007ff.
  • npm run verify:cheap (not run in this session)

Test plan

  • Sign in and open /?mode=services — confirm 219 catalogue records and search (e.g. 13YARN).
  • Run npm run registry:seed -- --kind service --write --confirm --owner-id <uid> on a fresh owner if needed.
  • Apply migrations 20260705030000_registry_catalog_payload.sql and 20260705120000_differential_records.sql if remote is behind.
  • Run npm run verify:cheap before merge.

@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.

@supabase

supabaseBot commented Jul 5, 2026

Copy link
Copy Markdown

Updates to Preview Branch (codex/pr-258-header-only-final) ↗︎

DeploymentsStatusUpdated
DatabaseSun, 05 Jul 2026 10:18:42 UTC
ServicesSun, 05 Jul 2026 10:18:42 UTC
APIsSun, 05 Jul 2026 10:18:42 UTC

Tasks are run on every commit but only new migration files are pushed.
Close and reopen this PR if you want to apply changes from existing seed or migration files.

TasksStatusUpdated
ConfigurationsSun, 05 Jul 2026 10:18:44 UTC
MigrationsSun, 05 Jul 2026 10:18:46 UTC
SeedingSun, 05 Jul 2026 10:18:47 UTC
Edge FunctionsSun, 05 Jul 2026 10:18:47 UTC

View logs for this Workflow Run ↗︎.
Learn more about Supabase for Git ↗︎.

BigSimmoand others added 4 commits July 5, 2026 16:10
Wire Psych Differentials v10 into differential_records, snapshot loaders, API routes, presentation workflows, and seed scripts. Fix option label parsing for hyphenated names like Post-ictal confusion.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add medication and services snapshots, Supabase migrations, API routes, registry catalog payload, document admin drawer, and cross-mode search integration.
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…cope.
Restore unauthenticated fixture responses for medication routes and drop redundant local-no-auth publicOnly branching in search scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
@BigSimmo
BigSimmoforce-pushed the codex/pr-258-header-only-final branch from 0a87719 to 065d95fCompareJuly 5, 2026 08:15
BigSimmoand others added 7 commits July 5, 2026 16:18
Co-authored-by: Cursor <cursoragent@cursor.com>
Drop before recreating the text retrieval RPC when its OUT signature changes, and keep retrieval_synopsis through the lexical_score migration so fresh branch replays do not hit SQLSTATE 42P13.
Co-authored-by: Cursor <cursoragent@cursor.com>
Guard retention cron migrations so Supabase Preview branch replays succeed without cron.job, and neutralize the duplicate rag_queries retention migration.
Co-authored-by: Cursor <cursoragent@cursor.com>
…rations
Document fresh-replay invariants and assert match_document_chunks_text drops and cron schedules are guarded.
Co-authored-by: Cursor <cursoragent@cursor.com>
Split snapshot loading into medication-snapshot.ts so client components do not bundle the full medications JSON. Remove an invalid query prop from MobileEvidenceSheetContent.
Co-authored-by: Cursor <cursoragent@cursor.com>
@BigSimmo

Copy link
Copy Markdown
OwnerAuthor

@copilot resolve the merge conflicts in this pull request

@BigSimmo
BigSimmo enabled auto-merge (squash) July 5, 2026 09:18

CopilotAI commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Resolved and pushed. Merge conflicts were fixed in tests/private-access-routes.test.ts and tsconfig.json in commit 26c8a2c.

auto-merge was automatically disabled July 5, 2026 09:22

Head branch was pushed to by a user without write access

Co-authored-by: Cursor <cursoragent@cursor.com>
@BigSimmo
BigSimmo enabled auto-merge (squash) July 5, 2026 09:37
Avoid a redundant 30s API wait on repeat navigations and give the prescribing workflow smoke test enough time for CI Chromium runs.
Co-authored-by: Cursor <cursoragent@cursor.com>
BigSimmoand others added 3 commits July 5, 2026 17:54
Route unauthenticated subject limiting through consume_api_subject_rate_limit so registry public-access tests pass, and teach the private-access mock to count subject RPC calls for burst-limit coverage.
Co-authored-by: Cursor <cursoragent@cursor.com>
Update mode-home, document-search, differentials, and command-surface expectations introduced by the WA catalogue import work.
Co-authored-by: Cursor <cursoragent@cursor.com>
Align ui-tools and ui-smoke assertions with snapshot-driven differential counts, hero search portaling, document-search layout order, and mobile settings fullscreen tolerance.
Co-authored-by: Cursor <cursoragent@cursor.com>
@BigSimmo
BigSimmo merged commit b65acdd into mainJul 5, 2026
5 checks passed
BigSimmo added a commit that referenced this pull request Aug 7, 2026
Resolve outstanding-issues conflict by keeping main's #261-#270 design-system tracks and renumbering this PR's SecondaryNavigation and addon-slot follow-ups to #271/#272. Preserve the confirmed #256 diagnosis from this branch.
Co-authored-by: Cursor <cursoragent@cursor.com>
BigSimmo pushed a commit that referenced this pull request Aug 7, 2026
…n-kind decision
#256 was 'suspected remaining' for form-overview and the
differential-presentation-* set. Both are now confirmed dead, so two live
routes draw no section nav at all: /forms/<slug> (one anchor is a testId
rather than an element id, the other five are rendered nowhere) and
/differentials/presentations/<slug> (all six absent, and the only dynamic
section id in that family uses a different prefix on a different route,
so nothing is hiding behind a sectionId prop).
Also captures #261: whether to delete SecondaryNavigationActionItem,
which lost its last live consumer in PR #1679 and was deliberately kept.
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 pushed a commit that referenced this pull request Aug 11, 2026
Handoff record for PR #1807 (DS Track A2, --shadow-focus retirement).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012spLo9ry4zjoDyZi7j89kT
BigSimmo pushed a commit that referenced this pull request Aug 11, 2026
DS Track A2. `--shadow-focus` packed a 3px accent halo in front of
`--shadow-soft`, so its one consumer — `.chat-composer-shell-delta:focus-within`
— painted a companion ring on top of the accent border swap. That is the second
focus affordance the shared `:focus-visible` treatment is written to prevent
(GATES Gate 3). The composer now uses `outline: 2px solid var(--focus)` at
`outline-offset: 2px`, and both theme declarations of the token are deleted.
Focus no longer overrides `box-shadow`. The retired token carried `--shadow-soft`
as its second layer, so leaving the resting elevation alone keeps the pill seated
at the same height focused as at rest. Measured in Chromium: light
`solid 2px rgb(29, 111, 184)`, dark `solid 2px rgb(116, 189, 240)`, box-shadow
byte-identical resting vs focused in both themes.
Corrects the premise inherited from HANDOVER-2026-08-07 A2: this is not a visible
production focus change. `chatComposerShell` is imported only by
`calculators/search-detail.tsx` and its mockup twin; production `/calculators`
renders `chatComposerShellBase` + `answer-footer-search-pill`, and
`CalculatorSearchHome` is reached only from two unrouted mockup exports. Probing
all 37 static production routes in Chromium found zero renders of the class; the
single live render is `/mockups/calculators-search`, where the look was taken.
The inherited note was right that a `--include=*.tsx` grep misses the consumer
(it is in CSS) but wrong about its reach. Corrections recorded in the handover's
own "Measured wrong so far" list, per that file's convention.
Guard: design-token-contract.test.ts rejects both a `--shadow-focus:` declaration
and a `var(--shadow-focus)` consumer, across globals.css and the v2 layer, and is
mutation-verified against both. Deliberately not the whole-file substring check
used for `--shadow-tight`, because the composer rule names the retired token in a
comment on purpose.
legacyShadowAliases 127 -> 125 (soft 71 -> 69), globals.css per-path pin
tightened 3 -> 1: the deleted declarations' VALUE ended in `var(--shadow-soft)`,
so they scored as two `soft` aliases — the indirection GATES documents. Left
un-ratcheted as pre-existing slack unrelated to this diff: rawPaddingLiterals
67 -> 65, rawGapLiterals 34 -> 32, layoutTransitionExceptions 12 -> 11.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012spLo9ry4zjoDyZi7j89kT
cursorBot pushed a commit that referenced this pull request Aug 12, 2026
Resolve docs/outstanding-issues.md by keeping this PR's archive of
#207/#226/#230/#261/#284/#296 and updated open-row notes; main had no
unique open ids beyond those already archived here.
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.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
docs/outstanding-issues.md has no merge driver by design, so this conflicted
in seven hunks. Resolved by taking main's file as the base — it carries main's
own archives (#207, #226, #230, #261, #284, #296) — then re-applying all 23 of
this branch's changes through scripts/outstanding-issues.mjs: 15 archives and
8 detail rewrites. Verified afterwards that every one landed and that the
guard reports no ids deleted from base.
Two rows are deliberately NOT resurrected. This branch's #302 (style-contract
line-break artifacts) and #303 (issues:next-id sync) no longer exist anywhere
on main under any id — main has since reused both numbers for entirely
different live rows. Both of this branch's rows were verified-delivered trivia,
so nothing actionable is lost, but the disappearance is itself evidence for
#156/#168: a main-side merge dropped rows, which is the hazard those rows
describe.
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 18, 2026
* feat(ui): one category-identity registry for card glyphs and accents
Icons and category colour were spread across ten independent maps and two of
them disagreed, so the same tool rendered differently depending on which screen
reached it.
- `launcherIconById` (applications-launcher-page.tsx) carried 13 tool ids;
`iconByToolId` (tools-search-results-page.tsx) carried 8 with a different
fallback, so `guidelines`, `care-plans`, `safety-plan`, `calculators` and
`monitoring` showed a real glyph on the launcher and a generic `Grid2X2` in
search results.
- Colour diverged the same way: the launcher tinted by tool area, the results
page painted every tile `--type-source`, so one list read as a single purple
family while the other grouped the same tools into five.
- `ShieldCheck` was assigned to `guidelines`, to `risk-safety`, and to the
"Source-backed" status chip — three unrelated meanings, one glyph, reachable
on a single card.
- `appIconTone` overrode the area map per id, routing `differentials` and
`forms` to a tone key named `differentials`, so the advertised "category
colour" was not a category colour.
`src/lib/category-identity.ts` is now the single source of truth. It is
framework-free (string glyph keys, no lucide) following the `semantic-tone.ts`
precedent, so data and server modules can name a glyph without the render layer;
`category-identity-icons.ts` is the only place that binds keys to components,
resolving through `createElement` as `factsheets-icons.ts` does to satisfy
`react-hooks/static-components`.
`ToolCatalogRecord.id` is narrowed from `string` to a `ToolCatalogId` union, so
`Record<ToolCatalogId, …>` cannot be under-filled: adding a tool without
choosing a glyph is now a type error rather than a silent runtime fallback.
`appModeIcons` keeps its name and shape but is derived rather than
hand-maintained, so its "keep in sync" comment is now a property of the type.
Accent delivery is `data-category-accent` → `--cat-accent`/`--cat-soft`/
`--cat-border` in globals.css rather than interpolated class names, which
Tailwind's scanner cannot see, and rather than inline styles, which bypass the
theme contract. Every accent aliases an existing non-semantic triad
(`--type-*`, `--tone-*`), so light, dark and forced-colors need no new
declarations.
`risk-safety` loses its permanent danger-red tile: red asserted caution about a
route rather than about a patient, spending the loudest colour in the system on
a navigation target. Safety is carried by the now-unique shield glyph and by the
danger-toned selected state, which is a real state.
Gates: typecheck, lint, `npm run test` (643 files, 6882 passed / 4 skipped),
check:design-system-contract, check:icon-scale, check:type-scale — all green.
No provider-backed check was run and none is required; no RAG surface touched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XGhewT2mmRoRTynjWfF1Vn
* fix(factsheets): stop painting content categories in semantic colours
`categoryTheme()` drew two of its four category accents from the SEMANTIC
palette: Therapies on `--success-text`/`--success-bg` and Tests & procedures on
`--warning-text`/`--warning-bg`.
Those tokens carry meaning. `src/lib/semantic-tone.ts` defines six tones where
the colour IS the claim — warning means "pause, check, adjust, review", success
means a check passed — and `docs/clinical-badge-system-guide.md` states the rule
as "meaning drives the colour, never the other way round". Applying warning-amber
to an entire category of patient handouts asserted caution about content that
nothing had reviewed, and it did so on the largest surface the factsheet has: the
hero band. It also spent a colour the badge system needs, so a genuine caution
badge had to compete with its own page chrome.
All four categories now sit on non-semantic identity triads, sourced from
`FACTSHEET_CATEGORY_IDENTITY`:
Medications --clinical-accent -> --type-form
Conditions --tone-indigo -> --type-source
Therapies --success-text -> --type-service
Tests & procedures --warning-text -> --type-table
Medications moves too, for a different reason: it was the same blue as every
selection state, focus ring and evidence marker on the page, so the biggest
category was the one with no identity of its own.
`FactsheetTheme`'s shape is unchanged, so the ~20 call sites passing these as
inline style values are untouched. `FactsheetCategory` is now a re-export of the
registry's union, so the accent map and the content model cannot disagree about
what the categories are.
Three guards added to tests/design-token-contract.test.ts, each mutation-verified:
- no `[data-category-accent]` rule may reference a semantic token
(verified: pointing "table" at --warning-text fails the test)
- `categoryTheme` must stay derived via `categoryAccentVars` and return no
semantic token (verified: reinstating the old --warning-* return fails it)
- `CategoryAccent` may not declare a semantic member, which is what makes the
mistake unrepresentable at every call site rather than caught per site
Gates: typecheck, lint, `npm run test` (643 files, 6885 passed / 4 skipped),
check:design-system-contract — all green. No provider-backed check run.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XGhewT2mmRoRTynjWfF1Vn
* refactor(ui): add the shared card recipe and absorb the two private forks
Four cards that sit side by side in the same shell had drifted on every axis:
ToolCard rounded-lg --shadow-card p-4 border + tint + hover lift
ServiceCard rounded-xl --shadow-inset p-3 sm:p-4 border + ring/35
CalculatorCard rounded-lg --shadow-inset p-4 border + --shadow-soft
Factsheet card rounded-xl --shadow-card - inline-style border-t-[3px]
Two radii, three resting elevations, and four different "this one is selected"
encodings, three of those expressed as fractional opacity on a token colour —
which is unreviewable, because the contrast an alpha lands on depends on
whatever surface happens to sit behind it in each theme.
`src/components/card-recipes.ts` holds the shared definitions: `cardSurface`,
`cardInteractive`, `cardSelected`, `cardAccentEdge`, `cardPadding`, and one
exported `focusRing` (that string is currently redeclared as a local const in a
dozen files and inline in dozens more).
Recipes rather than a registered component: COMPONENTS.md §0.4 measures 157
production importers of ui-primitives.tsx against 31 product imports across the
whole 54-component registry, and ledger #266 says adoption is demand-driven,
"never a race to 54/54". A function also sidesteps element polymorphism — these
cards are variously <article>, <button> and <Link>. A new module rather than
growing ui-primitives.tsx, which §0.4 already lists as over-budget and slated to
split.
Two deliberate departures from what the four cards did:
- `--shadow-inset` is dropped as a card elevation. It is the design-system
bevel, and SPEC §4.7 says an inset well uses a border or inset shading, not
both; pairing it with a border is why the services and calculator cards read
flatter than the tool cards beside them. Resting is border + --e1.
- The recipes name `--e1`/`--e2`/`--e3` directly rather than the `--shadow-card`
/ `--shadow-soft` / `--shadow-hover` role aliases. TOKENS.md schedules those
for retirement "inside the recipes first", and the contract ratchet counts
every use — a new consolidating module reaching for one would move the count
the wrong way. Caught by the gate on the first attempt (114 -> 115); now 113,
unchanged from baseline.
`specifierCard` and `formulationCard` were byte-identical copies of the same
string; both now name the shared recipe. This moves those two modes' cards from
`--surface` to `--surface-raised`, which is the SPEC §177 correction (true-white
cards against the near-white page) and is a visible, intended change.
Consumers for `cardInteractive`/`cardSelected`/`cardAccentEdge` land in the
following commit.
Gates: typecheck, lint, `npm run test` (643 files, 6885 passed / 4 skipped),
check:design-system-contract, check:knip — all green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XGhewT2mmRoRTynjWfF1Vn
* feat(tools): elevate the tool cards and merge the phone/desktop pair
The launcher rendered two components for the same content — `ToolCard` and
`MobileToolRow` — and they had already drifted: different resting elevation
(--shadow-card vs --shadow-inset), different selected tint (/50 vs /55), and a
hover lift on one but not the other. They are now one component with a `density`
prop. Both test ids are kept: `application-card-*` and `application-row-*` are
each asserted by ui-tools and ui-smoke, and ui-smoke is blocking at zero
retries. Verified in Chromium at 390px that the row renders visible and the card
hidden, with all 13 of each still in the DOM.
Craft changes, launcher and tools search results:
- The fake button is gone. `Details` was a <span> painted as a solid accent
button INSIDE the card's own <button>: it read as a nested control, was
announced as nothing, was the loudest element on the card, and — being
identical on every card — distinguished nothing. The card is the control; a
chevron on the decoration tier says so, taking the category accent on hover so
the affordance points at the card's own family.
- On the search results page the same button is real (that card is an <article>
and not itself clickable), so it stays — but as `floatingControl` rather than a
filled accent. Thirteen filled primaries down one list were thirteen primary
actions, none of them the page's actual primary action.
- Titles move from `text-base font-extrabold` to `text-lg font-semibold`. At
extrabold they matched the section heading above them, so a grid of cards read
as a wall of headings. Size carries the hierarchy; weight stops trying to.
SPEC §4.6 puts card titles at --text-lg.
- "Best for:" was a bold inline run inside the body copy, giving a label the same
emphasis as the clinical text it labels. It is a kicker, so it uses the shared
`eyebrowText` recipe.
- The magnifier beside "Best for" on the results page is dropped: "Best for" is
not a search, and it spent an accent-coloured glyph on a label.
- The selected rail on a results row takes the tool's own category accent rather
than the product blue, so it agrees with the tile beside it.
- `min-h-[9.25rem]` and `min-h-[5.25rem]` are replaced by content height with a
`min-h-tap` floor. Production tap targets stay at min-h-12.
- The local `focusRing` const in both files now imports the shared one.
`risk-safety` keeps a danger-toned SELECTED state via the new
`cardSelectedDanger` — selection is a real state, unlike the permanent red tile
retired earlier in this branch.
`legacyShadowAliases` fell 113 -> 111 as the cards moved off --shadow-card and
--shadow-inset onto the --e ladder.
Gates: typecheck, lint, `npm run test` (643 files, 6885 passed / 4 skipped),
check:design-system-contract, check:icon-scale, check:type-scale — all green.
Chromium: check:playwright-browser-revision was failing on the known #255 drift
(1194 installed against the pinned 1234); the pinned revision was installed, the
check now reports OK, and ui-tools + ui-hydration + the ui-smoke tools
assertions were run locally against it. Five ui-tools failures were investigated
and are pre-existing: all four reproduce on origin/main with this work stashed
(two /services/13yarn composer cases, two service-detail cases, and the tools
mobile detail-sheet case), and the tools one passes when driven manually, so it
is environmental rather than a regression.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XGhewT2mmRoRTynjWfF1Vn
* feat(ui): adopt the card recipe and category accents on factsheets, calculators and services
Factsheets
- The three inline `style` objects per card are gone. An inline value cannot be
remapped by the dark or forced-colors blocks, so the old cards carried their
light-mode tint into both. Verified in Chromium: the four accents now remap
correctly in dark, and under forced-colors they flatten to Canvas/CanvasText
as they should — identity colour is decoration, and the glyph and category
chip carry the meaning in high contrast.
- The category browse pills take the same accents, so a pill and the cards it
filters to now agree.
- Card titles move to `text-lg font-semibold`, and hover tints the title with
the card's own category accent rather than the product blue.
Calculators
- The directory tile was grey until a card opened, so a closed directory showed
five domains rendered identically and the domain was findable only by reading
the chip. It now carries the domain accent at rest — which is what the chip
beside it has always said in words.
- `CALCULATOR_DOMAIN_ACCENT` deliberately gives `risk` (suicide risk) an
identity accent, not `--danger`. The label already says "Suicide risk", and an
instrument is not itself a warning; a red tile on a directory row would claim
urgency about a tool rather than about a patient.
- Open/closed states move onto `cardSelected` and the --e ladder.
Services
- Adopts `cardSurface` + `cardSelected`, retiring the fourth "this one is
selected" encoding (`ring-1 …/35` — an alpha on a token colour, so what it
contrasted against depended on whatever surface sat behind it per theme).
- The leading tile deliberately stays a RANK rather than becoming a category
glyph: this is a ranked referral list, the number is what the "Best fit" pill
refers to, and it doubles as the shortlist checkmark. Services has no single
category axis either — records carry facets — so there would be nothing honest
to put there.
Ratchets moved the right way: legacyShadowAliases 111 -> 107, edge conflicts
19 -> 18.
Deferred, unchanged: therapy-compass/therapy-card.tsx (own SVG icon set, own
control recipes, own IconTile, and the open rawPadding/rawGap debt from #261),
the differentials-home card family, and the forms detail cards — those are
detail-panel compositions rather than category-bearing list items.
Gates: typecheck, lint, `npm run test` (643 files, 6885 passed / 4 skipped),
check:design-system-contract — all green. Chromium inspection at 390/1440 in
light, dark and forced-colors against the pinned revision 1234.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XGhewT2mmRoRTynjWfF1Vn
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto 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.

2 participants

@BigSimmo