Finish the PsychSift rename and re-cut the sidebar lockup as a header band - #2600
Conversation
The phone drawer and the expanded desktop sidebar both opened on a heading that read "Clinical Guide" — the product's old descriptive name — set as a plain row of text on the panel's own white. The main app header had already moved to the PsychSift lockup, so the two entry points to the same navigation disagreed about what the product is called. The heading is now BRAND_NAME, and the two surfaces share one lockup: - Type is the `shared-home-brand` setting from master-search-header, matched line for line rather than re-invented — 18px/800 on the display tracking step for the name, 12px/500 in --text-muted for the strapline. That call site's measured rejections carry over and are quoted where the classes live: --text-soft fails contrast at 3.07:1, and an 11px strapline reads bottom-light. - The strapline moves to BRAND_CATCHPHRASE_BARE. Under a wordmark it is an identity label, not a sentence, which is the position `@/lib/brand` documents the unpunctuated variant for. - The header becomes a band rather than a row: one corner wash behind the mark that dissolves before it reaches the close control, over a vertical surface fade that only resolves in dark. The desktop header bleeds to the edges of its aside so it reads as a header, not a card inside the padding. - The close control keeps its 48px tap target and resolves into the accent on hover instead of a neutral fill. The wash strength is a new --brand-band-wash role token rather than a mix written at the call site, because the themes need opposite recipes to land on the same read: --clinical-accent-soft is #eff5fc in light and vanishes against the white panel, while in dark it is already a deep blue that carries on its own. The divider stays a real border-b — a pseudo-element rule would disappear in forced-colors, which is where a divider matters. Re-measured the drawer strapline fit at 320px now that it renders at 12px rather than 10px: a 167px title column at 5.91px per character is 28 characters, down from the 32 the previous setting allowed. The bound in tests/brand-copy.test.ts is tightened to match and now guards the bare variant, which is what that surface actually renders. Verified: lint, typecheck, brand-copy/sidebar/design-system contract suites, type-scale, icon-scale and design-drift-ratchet all green; drawer and sidebar screenshotted in light and dark at 320/360/390 and 1440. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K8rMooUReCqvW8jLLx3k43
BRAND_NAME became "PsychSift" on 2026-08-28, but only the module and the surfaces that read it moved. Every hand-typed occurrence of the old name stayed where it was, so the product introduced itself by one name and labelled its own navigation with another: a sr-only page heading, five sidebar and drawer aria-labels, the sign-up title, the calculator empty state, four design-scratch mockups, and `public/llms.txt` — the file that tells agents what this product is. Renamed on every product surface: - The sr-only h1 on the dashboard, so the accessible page title matches the header above it. - "Search Clinical Guide", "Clinical Guide collapsed sidebar", "Clinical Guide sidebar", "Close Clinical Guide menu" and "Open Clinical Guide menu" — the labels a screen-reader user hears for the navigation whose visible heading already said PsychSift. - "Create your Clinical Guide account" on the sign-in panel. - "ask Clinical Guide below" in the calculator search empty state. - `public/llms.txt`, which had described the product under the old name since 2026-07-07 and is finding L86 of the 2026-09-02 repository audit. The mockups are renamed too, though the gates exclude them: a prototype of the sidebar that still carries the old name is exactly what the next version gets copied from. The recurrence guard is a new case in tests/brand-copy.test.ts. It cannot work the way the module's other guards work — those forbid retyping a line that has an export to import instead, and "PsychSift" is a literal in over a hundred files, so routing them through an import would be a migration rather than a guard. It runs the other way round: the retired name may not appear in any tracked src file or in llms.txt. "Clinical Guideline"/"Clinical Guidelines" pass, since those are document titles in the corpus and not the product's name. `public/llms.txt` also joins the existing retyped-line scan, because living outside src is how it escaped the brand gate for two months. Verified: format, lint, typecheck, brand-copy (now 1,429 cases), the sidebar, mockup and design-system contract suites, and focused selection over every changed source file — all green. Browser journeys next. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K8rMooUReCqvW8jLLx3k43
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
Bugbot couldn't run - usage limit reachedBugbot 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_596b7123-f460-458e-ac49-85cdf5faaa4f) |
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Team Run ID: 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. Comment |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Bugbot couldn't run - usage limit reachedBugbot 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_51b76146-2104-4fc7-9735-0e54ec0a7fd0) |
CI triageCI failed on this PR. Automated classification of the 2 failed job(s):
Compared with main CI run #15588 (success). That run's conclusion is an aggregate and did not exercise Classification is evidence routing, not permission to ignore a failure. Exact quarantined Playwright identities remain governed by the flake ledger. |
Production UI shard 1 went red on this branch after it merged main, on a
test this PR does not touch:
strict mode violation: getByTestId('sources-publishers-main')
resolved to 2 elements
1) …aka getByTestId('mobile-composer-reserve-pad').getByTestId(…)
2) …aka getByTestId('sources-publishers-main').nth(1)
Not this branch's. The same test fails identically on origin/main at
7ca2ea1 with none of this branch's changes — there on
`sources-topics-main`, which is the same defect landing on whichever browse
route the run reaches first. The Sources redesign (#2593) added this spec,
and its own push run on main was cancelled, so Production UI never returned
a verdict on it.
The mechanism is the one `tests/playwright-settlement.ts` already documents
as #93: Next streams the page, React first renders the `<main>` outside
`mobile-composer-reserve-pad` and then relocates it in, and for that window
both copies are in the tree. Measured on a local production build at 320px:
at `domcontentloaded` the element sits outside the pad with `offsetParent`
null, and by 1.5s the same test id resolves inside the pad. `page.goto` can
return inside that window, and Playwright strict mode fails on the second
element the moment it sees it — it does not retry.
So the seven page-root assertions in this spec now go through
`expectSingleSettledOwner`, which is the repo's own remedy for this class
and is a *stronger* assertion than the `toBeVisible()` it replaces: it
polls until the test id resolves to exactly one element and that element is
visible. A duplicate `<main>` is an accessibility defect in its own right,
so requiring convergence rather than filtering to the visible copy is the
right reading here — `visibleByTestId` would have passed while two landmarks
were still in the tree. Nothing is skipped, disabled or quarantined.
Reproduced and fixed, both on a local production build via the repo runner:
- before, on origin/main 7ca2ea1 in a clean worktree: 1 failed, the
strict-mode violation quoted above.
- after, on this branch: `tests/ui-sources.spec.ts` 7 passed (51.0s). The
previously failing case takes 28.1s, which is the settle poll waiting out
the relocation — evidence the duplicate is real and does converge, not a
locator dodged around it.
Also green: lint, typecheck, and check:diff-integrity (153 -> 154 cases).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K8rMooUReCqvW8jLLx3k43
|
Production UI (1) went red on The failure: — It is not this branch's. The same test fails identically on The mechanism is the one The fix, since no other PR carries one: the seven page-root assertions in that spec now go through Evidence, both on a local production build through the repo runner:
Also green on the change: No re-run was spent: the failure reproduces deterministically on the base branch, so it was never a candidate for one. Generated by Claude Code |
Bugbot couldn't run - usage limit reachedBugbot 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_b55ffd02-51c1-4f94-8c5d-2bd6c13b8063) |
Same strict-mode "resolved to 2 elements" shape seen on PR #2613's queued issue, now also on caring-contacts-phone-dock (PR #2600). Three unrelated PRs, three different testids — updates the priority signal for the existing queued investigation without duplicating it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015uRvqm44emrLDMyPF7Pame
Bugbot couldn't run - usage limit reachedBugbot 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_e50ab3ad-1197-4bb3-92aa-2cd9dc3a3354) |
PR #2600's Production UI failure on caring-contacts-phone-dock is a recurrence of the shared-shell testid duplication race, confirmed unrelated to this PR's diff. Unlike the two prior occurrences (service-actions-trigger, sources-topics-main), this one has a concrete mechanism: openWorkspace() in tests/ui-caring-contacts-workspace.spec.ts already documents and partially guards a streamed/placed shell duplicate for caring-contacts-rail, but never extends that settle-wait to caring-contacts-phone-dock, a sibling landmark in the same next/dynamic-loaded CaringContactsShell. Includes a cancel for an earlier queued request whose --detail picked up stray shell output from an unescaped backtick. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015uRvqm44emrLDMyPF7Pame
…laude/clinical-guide-redesign-ir5zcj Sync with the Documents/Medications consolidation (#2614) merged into this branch by another session, before pushing the CI-failure inbox notes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015uRvqm44emrLDMyPF7Pame
CI failure diagnosis:
|
Production UI shard 2 went red on this branch at `aff60bc3` on a test this
PR does not touch:
Template detail at 390px: the phone dock does not own navigation
strict mode violation: getByTestId('caring-contacts-phone-dock')
resolved to 2 elements
`openWorkspace()` already guards this exact defect, and its comment already
states the right rule: React streams the segment under `loading.tsx`'s
Suspense boundary into a hidden holder before moving it into place, so a page
sampled too early carries a second copy of the whole shell — and "a shell that
genuinely mounted twice would double every landmark on the page".
The implementation never matched that sentence. It settled `caring-contacts-rail`
alone. The two landmarks reconcile on different ticks and `toHaveCount(1)`
retries, so the wait can go green at a moment when the rail has settled and
the dock is still doubled; the frozen-layout sweep then asserts on the dock a
few lines later and trips strict mode on the second copy. That is why the
failure happened *despite* the guard passing on the same navigation.
So the wait now covers both landmarks. Safe at every width because neither is
conditionally rendered: `shell.tsx` draws the rail as `hidden … md:flex` (301)
and the dock as `md:hidden` (492), so exactly one of each is in the DOM
whatever the viewport, and the count is 1 regardless of which is displayed.
The guard says nothing about which is *visible* — that remains the sweep's job,
unchanged. Nothing is skipped, disabled or quarantined.
Credit where due: the mechanism was diagnosed by another Claude session on
this PR, which could not verify it (no browser in that sandbox) and left it
for whoever could. This session has one, so: verified by inspection against
`shell.tsx` for the always-rendered claim, and by running the suite.
Evidence, on a local production build through the repo runner:
- `tests/ui-caring-contacts-workspace.spec.ts` — 126 passed (2.5m).
- lint, typecheck and check:diff-integrity green.
Stated plainly: the race itself is not reproducible here — it needs the
timing of a full CI shard — so this is a reasoned fix against a defect
verified in the guard's own logic, not one demonstrated against a red test.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K8rMooUReCqvW8jLLx3k43
Bugbot couldn't run - usage limit reachedBugbot 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_22927a2e-d0d2-4b4d-8c84-6e0cfa8e0735) |
…cation (#2613) * docs: queue outstanding-issue for recurring shared-shell testid duplication Playwright strict-mode "resolved to 2 elements" failures on service-actions-trigger (PR #2536) and sources-topics-main (PR #2591, tests/ui-sources.spec.ts:88) share the same shape: one element nested inside GlobalSearchShell's mobile-composer-reserve-pad wrapper, one outside it. Confirmed unrelated to either PR's own diff. Queuing for a focused investigation/fix rather than patching a "one owner" shared shell component inside an unrelated PR. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015uRvqm44emrLDMyPF7Pame * docs: log a third occurrence of the shared-shell testid duplication bug Same strict-mode "resolved to 2 elements" shape seen on PR #2613's queued issue, now also on caring-contacts-phone-dock (PR #2600). Three unrelated PRs, three different testids — updates the priority signal for the existing queued investigation without duplicating it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015uRvqm44emrLDMyPF7Pame * docs: correct an unverified detail in the third-occurrence ledger entry The earlier note claimed the duplicate caring-contacts-phone-dock element was nested inside mobile-composer-reserve-pad by analogy with the first two occurrences. The actual job log for this instance only shows two sibling matches for the same testid; the nesting detail was not verified and is removed. Cancels and replaces the prior entry. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015uRvqm44emrLDMyPF7Pame --------- Co-authored-by: Claude <noreply@anthropic.com>
The desktop mode menu focuses its search box inside a requestAnimationFrame when it opens. The reopen half of this test focused an option immediately after asserting the dialog was visible, so on a slow runner the queued autofocus landed afterwards and pulled focus back into the search box — where Tab is not a dismiss key. The menu stayed open and the final toBeHidden assertion timed out, as it did on CI run 33892868281. The first half of the same test already waits for that autofocus; this makes the reopen do the same, and asserts the option really holds focus before Tab. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K8rMooUReCqvW8jLLx3k43
…b.com/BigSimmo/Database into claude/clinical-guide-redesign-ir5zcj
Summary
shared-home-brandsetting frommaster-search-header, matched line for line rather than re-invented — 18px/800 on the display tracking step for the name, 12px/500 in--text-mutedfor the strapline — so the drawer, the sidebar and the app header are one lockup. That call site's measured rejections carry over and are quoted where the classes live:--text-softfails contrast at 3.07:1, and an 11px strapline reads bottom-light.BRAND_CATCHPHRASE_BARE. Under a wordmark it is an identity label, not a sentence, which is the position@/lib/branddocuments the unpunctuated variant for.--brand-band-washrole token rather than a mix written at the call site: the themes need opposite recipes to land on the same read.--clinical-accent-softis#eff5fcin light and vanishes against the white panel; in dark it is already a deep blue that carries on its own. The divider stays a realborder-b— a pseudo-element rule would disappear in forced-colors, which is exactly where a divider matters.BRAND_NAMEbecame "PsychSift" then, but only the module and the surfaces reading it moved; every hand-typed occurrence stayed. Renamed: the sr-only dashboardh1; the five navigation aria-labels (Search…,… collapsed sidebar,… sidebar,Close … menu,Open … menu); the sign-up title; the calculator search empty state; four design-scratch mockups; andpublic/llms.txt, which had described the product under the old name since 2026-07-07 and is finding L86 of the 2026-09-02 repository audit. Until now the product introduced itself by one name and labelled its own navigation with another.tests/brand-copy.test.ts. It cannot work the way that module's other guards work — those forbid retyping a line that has an export to import instead, and "PsychSift" is a literal in over a hundred files, so routing them all through an import would be a migration rather than a guard. It runs the other way round: the retired name may not appear in any trackedsrcfile or inllms.txt. "Clinical Guideline"/"Clinical Guidelines" pass, since those are document titles in the corpus, not the product's name.public/llms.txtalso joins the existing retyped-line scan, because living outsidesrcis how it escaped the brand gate for two months.RAG impact: no retrieval behaviour change — this diff is brand copy, aria-labels and header styling; it touches no file under
src/lib/rag/, no retrieval RPC, no ranking or selection surface, and no golden fixture.Verification
npm run verify:pr-local— all 20 stages completed, none failed:check:runtime, check:installed-lock-parity, format:changed, check:diff-integrity, sitemap:check, check:repo-awareness-snapshot, docs:check-index, docs:check-inventory, docs:check-scripts, docs:check-links, check:branch-review-ledger, check:outstanding-issues, check:ledger-write-discipline, lint, typecheck, test, build, check:rag:fixtures, check:medication-interactions, check:medication-lexicon-report.A first run of this gate reported three failing test files —
clinical-hazard-controls,privacy-readiness-contract,rag-plan-package-parity. All three were the same artefact of this container's shallow clone (75 commits): each failure readreviewedCommit does not exist <sha>, andgit cat-file -econfirmed the three SHAs were absent locally while.git/shallowwas present. Aftergit fetch --unshallowthe three suites pass (27/27) and the whole gate is green. Nothing in this diff touches a governance manifest.UI verification not run:
npm run plan:browserescalates to the full suite becausesrc/app/globals.cssis a shared foundation, and CI re-runs the complete Chromium suite on this change — so a full localverify:uiwould buy a verdict GitHub is about to reach. What ran locally instead is a focused browser proof on Chromium, targeting every spec that referenced a renamed label:tests/ui-smoke.spec.ts(whole file) — 104 passed, 2 failedtests/ui-accessibility.spec.ts,ui-overlap.spec.ts,ui-route-coverage.spec.ts,ui-phone-scroll-submitted-root.spec.ts— 48 passed, 0 failedtests/ui-sidebar-live-mockup.spec.ts(chromium-mockups) — 2 passedThe two
ui-smokefailures aredocument frame stretches the canvas owner at phone and desktopanddocument viewer puts the PDF preview first with pinned evidence after it on mobile, both failing onexpect(locator).toBeVisible()forpdf-canvas-scroll canvas—element(s) not found. They are not this PR's. Re-ran both atbe15a2c, the commit this branch started from, in a separate worktree with none of these changes: they fail identically there. The PDF canvas does not render under this container's Chromium; the diff touches no document-viewer code.Design gates:
check:type-scale,check:icon-scale,check:design-drift-ratchet— green (inline styles 228/232, zero-importer components 16/16; neither ceiling moved).Visual check: drawer and expanded sidebar screenshotted in light and dark; strapline measured at 320/360/390px for truncation.
Not run:
npm run verify:release(no release or handoff confidence claim), and the live retrieval/answer evals (no retrieval, ranking, chunking or answer-contract surface is touched).Risk and rollout
tests/moved in this PR and the affected specs were run. No data, auth, API, migration, or dependency change. The one styling change with reach beyond the header is the new--brand-band-washtoken, added in both theme blocks and read by nothing else.src/lib/rag/, no retrieval RPC, no ranking, selection or chunking surface, and no golden fixture is touched.Clinical Governance Preflight
This change is brand copy, aria-labels and header styling. It touches no ingestion, answer generation, search or ranking, source rendering, document access, privacy, production environment, or clinical output — so each item below holds unchanged rather than by new mitigation. In particular, no clinical decision-support behaviour changed, so the deployment classification is unaffected.
Clinical KB Database(sjrfecxgysukkwxsowpy)Notes
sidebar-live-mockup,warning-line-mockups,warning-consolidation-mockups,calculator-mockups/search-detail-mockup,app/mockups/mode-dropdown) are excluded from the repo's other gates as design scratch, but were renamed anyway and are in scope for the new guard: a prototype of the sidebar that still carries the old name is exactly what the next version gets copied from.docs/archive/**,docs/redesign/**and the audit that reported this drift all describe the product as it was named at the time, and rewriting them would falsify the record. One line of live documentation (docs/design-system/ADOPTION.md, "the mobile Clinical Guide sidebar") was updated because it describes current behaviour.origin/mainwas merged in after the work was assembled (one late sync, cleangit merge-tree), and the rename was re-verified against the merged tree.🤖 Generated with Claude Code
https://claude.ai/code/session_01K8rMooUReCqvW8jLLx3k43
Note
Low Risk
Mostly copy, aria-labels, and header CSS plus test selector/wait updates; no auth, API, or data-path changes. Residual risk is missed stale "Clinical Guide" strings or brief CI timing sensitivity on other streamed shells not yet using the generalized settle wait.
Overview
Completes the PsychSift rename on hand-typed surfaces that still said Clinical Guide—sr-only dashboard heading, sidebar/drawer aria-labels and search copy, auth and calculator empty states,
public/llms.txt, and related Playwright expectations—while aligning live docs (ADOPTION.md) with the new name.Sidebar and mobile drawer now share one brand lockup (wordmark + bare strapline from
@/lib/brand, typography matched tomaster-search-header), a corner accent wash via new--brand-band-washinglobals.css, and quieter close/hover styling so both entry points read as the same product header.Brand regression guards expand:
tests/brand-copy.test.tsscansllms.txt, forbids the retired product name acrosssrc+ that file, and tightens the drawer strapline limit forBRAND_CATCHPHRASE_BAREafter the 12px strapline change.Flaky UI tests get targeted hardening for Suspense/streamed-shell duplicate DOM:
openWorkspace()in caring-contacts waits on bothcaring-contacts-railandcaring-contacts-phone-dock; Sources specs useexpectSingleSettledOwnerfor catalogue/browsedata-testidroots. Inbox JSON records the caring-contacts CI investigation (and cancels a malformed duplicate note).Reviewed by Cursor Bugbot for commit a90e369. Configure here.