Skip to content

Also matches: rebuild the cross-mode panel and put it on every mode - #2640

Merged
BigSimmo merged 11 commits into
mainfrom
claude/gifted-franklin-8jdn1h
Sep 5, 2026
Merged

Also matches: rebuild the cross-mode panel and put it on every mode#2640
BigSimmo merged 11 commits into
mainfrom
claude/gifted-franklin-8jdn1h

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Summary

  • Rebuilt the "Also matches" cross-mode panel so it reads as a distinct, lower layer beneath the results rather than a flat strip pinned under them, and tuned the layout for phone, tablet and wide desktop separately.
  • Gave the panel to the seven modes that never had it: DSM, Specifiers, Calculators, Factsheets, Dictionary, Sources and On Call. Prescribing is no longer suppressed. Answer keeps its own CrossModeLinksSection and is the one recorded exemption.
  • Added a coverage contract test that registers all seventeen modes, so a mode either mounts the panel or carries a written reason.

Design changes

  • The section is a recessed tray on --surface-subtle holding raised mode cards on --surface-raised + --e1. Previously it was border-lux + --shadow-inset, which card-recipes.ts already documents as reading flat: two edge treatments on one surface.
  • Each mode card is vertical now — identity row, full-width result rows, footer link. The old horizontal tile | titles | View all row squeezed titles into the middle third and truncated them at every width, which is why "Mental Health Hospital in the Home" arrived as "Mental Health Hospital in th…". Titles get the whole card and clamp at two lines.
  • Result rows are real 48px tap rows with their own hover, focus and chevron affordance (visible on touch, hover-revealed from sm up). The footer reads "View all in <Mode>", matching the cross-mode typeahead's existing wording.
  • Loading is a skeleton grid in the real card geometry, so the tray no longer collapses to a single line of text and snap-opens to a four-card grid when matches land.
  • Columns are 1 / 2 / 4 across phone, tablet and xl. Four columns inside a 1024px content well leaves no room for a two-line clinical title, so the tablet band holds two columns all the way up to xl.
  • The visible aria-live node is replaced by an sr-only role="status" announcer (SPEC §9.2 — a live region is never visible content). It reports pending / count / empty rather than one fixed string.
  • Accent follows the recorded option-C study: tint on the icon tile and the short code chip, mode title in heading colour, no rail. also-matches-accent-mockups.tsx is annotated as shipped.

Prescribing — and the two defects found while verifying

Both were mine, both were caught by running the gates rather than by reasoning, and both are fixed in the commits on this branch. Recording them because the second one changes the stated reason for a decision this repo already had.

  1. The announcer contradicted the screen (b2c5610). The role="status" node rendered one fixed string, so a screen reader was told "No additional matches in other modes." while four populated mode cards were on screen. It now reports the real state.

  2. Prescribing mounted the panel twice (96d9749). Prescribing declares resultKind: "documents" deliberately — it searches the indexed sources, not a forms table — so ClinicalDashboard's documents arm matched it and mounted a second panel over the one MedicationPrescribingWorkspace already renders under the medication list. /?mode=prescribing shipped two.

    This is the half of the original modeId !== "prescribing" early return that the first commit read wrongly. That commit described the suppression as purely positional (the panel used to sit above the medication results). It was also the de-duplication for this shared result kind. The workspace now owns the single mount, because only it knows where the medication result list ends, and the exclusion names the mode rather than the result kind, because the kind is shared and the ownership is not.

    tests/ui-stress.spec.ts caught it by asserting the count is exactly one. The coverage test also carries an offline guard for the same duplicate, and that guard was mutation-verified: removing the exclusion fails it with expected '…' to contain 'searchMode !== "prescribing"'.

Verification

All results below are from the current head (96d9749).

  • npm run lint — exit 0. [gate-receipts] recorded a pass for "lint:internal" (7028 input files).

  • npm run typecheck (tsc --noEmit -p tsconfig.json) — exit 0, no diagnostics.

  • npx vitest run — full offline suite: Test Files 3 failed | 1147 passed | 1 skipped (1151), Tests 3 failed | 16858 passed | 2 expected fail | 3 skipped (16866). The three failures (rag-plan-package-parity, privacy-readiness-contract, clinical-hazard-controls) were reproduced on a stashed clean tree and are pre-existing on main, not this PR's.

  • Focused re-runs after each fix: tests/universal-also-matches-mode-coverage.test.ts 20/20, plus audit-navigation-auth-regressions and medication-prescribing-workspace.dom — 3 files / 42 tests passed.

  • npm run check:design-system-contract — passed. It caught a real defect mid-change (a --text-soft decoration-alias consumer), corrected to --decoration-soft.

  • npm run check:type-scale, check:icon-scale, check:mockups, check:diff-integrity — all passed. diff-integrity: PASS — 11 changed test file(s), 115 -> 119 test case(s), against base 6cfaa94c8.

  • npx prettier --check . — whole tree: "All matched files use Prettier code style!", committed.

  • Browser (focused, not the full gate). npm run plan:browser selected level: full for this diff, so the complete Chromium suite is CI's to run and was not run locally. What did run locally, against an isolated production build:

    • tests/ui-universal-search.spec.ts20 passed (40.0s). Covers the panel's own contracts: after the owning results in the DOM, phone-deferred fetch until expansion, absent on Answer, once on Favourites and the legacy Tools URL.
    • tests/ui-tools-search-mode-mockup.spec.ts — 15 passed, including no horizontal overflow at 320 / 390 / 639 / 768 / 1440 / 1920.
    • tests/ui-stress.spec.ts3 passed (12.1s) after the duplicate fix. This is the file whose medication assertion this PR rewrites, and the run before the fix failed it: expect(locator).toHaveCount(expected) failed … Expected: 1 Received: 2.

    Caveat that matters: this container has Playwright chromium-1194 against the repo's pinned 1234, so the runner's preflight fails closed (feat(ui): compact phone bottom search bar on search/result views #255/chore(deps-dev): bump prettier from 3.9.1 to 3.9.4 #312). These runs used PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH pointed at the 1194 headless shell — a different browser build from the pinned one. Treat them as real evidence for behaviour, not as a substitute for CI's pinned run.

  • npm run verify:pr-localVerification not run: the local heavy-run coordinator was held by the browser runs, and it mirrors what pr-required runs on this PR. Every gate it aggregates for this scope is listed above, run directly.

Visual proof was also taken directly: the panel screenshotted at 390 / 834 / 1440 px in light and dark, plus the loading state, against the dev server with a mocked /api/search/universal. That is what surfaced the title truncation and a 2px row/footer misalignment.

Note on the red PR required on b2c5610: that run reported CANCELLED with no failing job: coverage production-ui. Pushing 96d9749 cancelled those two mid-flight (cancel-in-progress), and the aggregate stays red on a cancelled job because a cancelled job verified nothing. Nothing failed on it — static, safety, build, production-ui-critical, lighthouse-budget and caring-contacts-db all reported success. The workflow's own message says to look for the newer run on the current head SHA, which is the one that counts.

Risk and rollout

  • Risk: presentation, plus eight mount points (seven new, one unsuppressed). The panel fetches /api/search/universal only after submission, and on phones only after the reader expands it, so the added mounts add no speculative requests on narrow viewports. The realistic failure mode is cosmetic, or a duplicate mount — which is now covered offline and in the browser.
  • Rollback: revert any of the three commits independently, or the branch. No schema, no migration, no config.
  • Provider or production effects: None. No Supabase, OpenAI or Railway calls were made. The Supabase integration confirmed on this PR that it ignored it — no supabase/ changes.
  • RAG impact: none — no retrieval, ranking, selection or scoring surface is touched. universal-search-mode-context.ts and the retrieval libraries are unchanged.

Clinical Governance Preflight

Not applicable in substance — presentation and mount points only, not ingestion, answer generation, ranking, source rendering, document access or clinical output. For completeness:

  • Source-backed claims still require linked source verification before clinical use — unchanged.
  • No patient-identifiable document workflow was introduced or expanded.
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy) — no Supabase config touched.
  • Service-role keys and private document access remain server-only — unchanged.
  • Demo/synthetic content remains clearly separated from real clinical sources — unchanged.
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative — unchanged. The Favourites access gate that hides gated hrefs from guests is preserved verbatim.
  • Deployment classification/TGA SaMD impact — no clinical decision-support behaviour changed.

Notes

  • One thing worth a reviewer's eye: prescribing showing cross-mode suggestions at all is a judgement call. It is below the medication results and below the interaction verdicts, and collapsed on a phone, but if the preference is that a dosing surface stays free of them entirely, reverting 96d9749's mount is a one-line change and the coverage test's exemption map is where the reason would be recorded.
  • tests/universal-also-matches-mode-coverage.test.ts exists because this mount is the part that goes missing: a merge resolution silently dropped the Forms mount once before (recorded in the merge-loss audit for Remove cross-mode “Also matches in other modes” panel and related mounts/tests #1804), and nothing failed when it did.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JQ7yPfVVVh62WursxM6mbF


Note

Low Risk
Changes are presentation and mount wiring on search result pages; universal search still runs only after submission (and on phones after expand), with offline and browser tests guarding duplicate prescribing mounts.

Overview
Rebuilds the cross-mode “Also matches” panel so it reads as a recessed tray under primary results: new vertical mode cards (full-width titles, identity row as “view all”, skeleton loading), responsive 1 / 2 / 4 columns, and sr-only status text that matches pending vs populated vs empty states.

Rolls the panel out on DSM, Specifiers, Calculators, Factsheets, Dictionary, Sources, and On Call, and re-enables it for prescribing below the medication list in MedicationPrescribingWorkspace instead of suppressing fetches/render in the component. Prevents a duplicate panel on /?mode=prescribing by excluding prescribing from ClinicalDashboard's universal mount (prescribing shares the documents result kind). Answer stays exempt via its own cross-mode links.

Adds tests/universal-also-matches-mode-coverage.test.ts so every app mode either mounts UniversalSearchAlsoMatches or documents why not; page unit tests mock the panel to avoid auth coupling.

Reviewed by Cursor Bugbot for commit 33a8532. Configure here.

The panel that offers "here is where else this query lands" was carried by nine
of the seventeen modes and looked like a flat strip pinned under the results.
This makes it a proper surface and gives it to every mode.

Design
- The section is now a recessed tray on --surface-subtle holding raised mode
  cards, instead of a bevelled bar at the same elevation as the results above
  it. card-recipes.ts already records why the old border + --shadow-inset pair
  read flat: two edge treatments on one surface.
- Each mode card goes vertical — identity row, full-width result rows, footer
  link. The old horizontal `tile | titles | View all` row squeezed the titles
  into the middle third and truncated them at every width, which is why
  "Mental Health Hospital in the Home" arrived as "Mental Health Hospital in
  th…". Titles now get the whole card and clamp at two lines.
- Result rows are real 48px tap rows with their own hover and focus states, and
  the footer reads "View all in <Mode>", matching the cross-mode typeahead.
- Loading is a skeleton grid in the real card geometry, so the tray no longer
  collapses to one text line and snap-opens when matches land.
- Layout is 1 / 2 / 4 columns across phone, tablet and wide desktop. Four
  columns inside a 1024px well leaves no room for a two-line clinical title, so
  the tablet band holds two columns all the way to xl.
- The visible aria-live node is replaced by an sr-only role="status" announcer
  (SPEC §9.2 — a live region is never visible content), and the accent follows
  the recorded option-C study: tile plus code chip, title in heading colour.

Coverage
- Added the panel to DSM, Specifiers, Calculators, Factsheets, Dictionary,
  Sources and On Call.
- Prescribing no longer suppresses it. That early return was written when the
  panel sat ABOVE the medication results and displaced the count, patient strip
  and primary matches on a phone; the mount has since moved below the result
  list, so the reason is spent. ui-stress now pins its position under those
  results instead of pinning it out.
- Answer keeps its own CrossModeLinksSection on the answer surface and is
  recorded as the one deliberate exemption.
- tests/universal-also-matches-mode-coverage.test.ts registers all seventeen
  modes, so a mode either mounts the panel or carries a written reason. A merge
  resolution has silently dropped one of these mounts before (#1804).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQ7yPfVVVh62WursxM6mbF
The sr-only status node rendered one fixed string, so a screen reader was told
"No additional matches in other modes." while four populated mode cards sat on
screen. It now reports the pending state, the match count, or the empty state,
and the visible empty paragraph keeps the empty wording of its own.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQ7yPfVVVh62WursxM6mbF
@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.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 7cde6380-fd9a-4a57-9d25-0e2c5fe59d59


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cursor

cursor Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_47c5de5e-5e77-4286-8c8e-e3e08261506b)

@supabase

supabase Bot commented Sep 5, 2026

Copy link
Copy Markdown

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


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

Prescribing declares `resultKind: "documents"` on purpose — it searches the
indexed sources rather than a forms table — so ClinicalDashboard's documents arm
matched it and mounted a second panel over the one
MedicationPrescribingWorkspace already renders under the medication list.
`/?mode=prescribing` shipped two.

This is the other half of why the old `modeId !== "prescribing"` early return
existed. The previous commit read it as a positioning decision only, which was
incomplete: it was also the de-duplication for this shared result kind.

The workspace keeps the mount, because only it knows where the medication result
list ends. The exclusion names the mode rather than the result kind, because the
kind is shared and the ownership is not.

tests/ui-stress.spec.ts caught it by asserting the count is exactly one; the
coverage test now carries an offline guard for the same duplicate, verified to
fail with the exclusion removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQ7yPfVVVh62WursxM6mbF
@BigSimmo
BigSimmo enabled auto-merge September 5, 2026 10:12
@cursor

cursor Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_9c99190b-089e-49f0-a770-5e50b9273ca1)

BigSimmo and others added 6 commits September 5, 2026 18:18
Two rows per card, not three. The identity row is now itself the link into that
mode, which is where a reader would tap anyway, and the separate footer link is
gone with it. That removes a 48px row per card — four rows of pure chrome across
the panel on a phone — and the panel loses roughly a third of its height at every
width without touching a single tap target.

The short code chip drops out of the accessible name (aria-hidden): it abbreviates
the mode name the link already says in full, so it only made the name longer.
An sr-only "View all in " keeps the link's purpose in its name.

`sm:min-h-compact-meta` rather than `sm:min-h-0` on that row: stepping an
interactive control below the tap floor at the sm band is counted as new debt by
check:design-system-contract, which caught it. 40px is the sanctioned floor for a
dense meta row, and the 36px icon tile already sets the real height, so this is
the same pixels with the rule met rather than dodged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQ7yPfVVVh62WursxM6mbF
The tab said the same thing three ways. A tinted count badge sat beside the
label reading "4", the line under it read "4 related modes", and desktop wore a
third pill saying it again on the right. That stacked subtitle was also the only
reason the tab needed two rows.

One meta string now, right-aligned after a hairline rule: label, rule, status.
The rule is the editorial section-header device, and it is what lets the status
sit hard against the trailing edge at any width without a second line, so phone
and desktop finally wear the same header instead of two designs of it.

The mark is quieter too — a hairline square with the glyph carrying the accent,
rather than a saturated block competing with the four category tiles directly
below it. Chevron down to the icon scale's md step and pulled to the edge, so it
reads as an affordance rather than a button.

Copy: "Tap to open" replaces "Tap to browse related modes", which no longer fits
one line on a narrow phone.

No `duration-*` on the chevron transition: check:design-system-contract counts a
hardcoded motion utility as debt, and it caught the one I had written.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQ7yPfVVVh62WursxM6mbF
@cursor

cursor Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_cb093334-6dbf-40da-87c8-eee70e7211e2)

@BigSimmo
BigSimmo merged commit 988d38d into main Sep 5, 2026
29 checks passed
@BigSimmo
BigSimmo deleted the claude/gifted-franklin-8jdn1h branch September 5, 2026 11:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants