From 54ab4cf17ab93eaa2aafb59a424c680350936738 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 7 Aug 2026 18:24:22 +0000 Subject: [PATCH 1/4] Fix safety plan phone header safe-area collision Own the OS top inset on the standalone tool header and restack the phone chrome so the back control sits below the status bar. Co-authored-by: BigSimmo --- src/components/patient-safety-plan.tsx | 67 ++++++++++++++++---------- tests/patient-safety-plan.dom.test.tsx | 13 +++++ 2 files changed, 54 insertions(+), 26 deletions(-) diff --git a/src/components/patient-safety-plan.tsx b/src/components/patient-safety-plan.tsx index 80b9f74d20..c6cfecf929 100644 --- a/src/components/patient-safety-plan.tsx +++ b/src/components/patient-safety-plan.tsx @@ -593,32 +593,47 @@ export function PatientSafetyPlan() { "focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-[color:var(--focus)]", )} > - {/* Tool header */} -
-
-
- { - if (!isDirty) return true; - return window.confirm( - "Leave this safety plan? Your entries are only in this browser tab and will be lost.", - ); - }} - /> - - -
-

Clinical KB · Clinical tool

-

- Safety plan generator -

-

- Build an identifier-free safety plan with your patient — the six prioritised steps — then - export it through your approved clinical workflow. -

+ {/* + Safety plan sits outside the search shell, so this header owns the OS top + inset via max(safe-area-top) — same contract as /privacy and colour-coding. + Avoid axis py-* here so it cannot fight the side-specific pt-* pad. + */} +
+
+ {/* + Phone: back on its own min-h-tap row, then shield + title. + lg+: lg:contents folds back/shield/title into one three-column row. + */} +
+
+ { + if (!isDirty) return true; + return window.confirm( + "Leave this safety plan? Your entries are only in this browser tab and will be lost.", + ); + }} + /> +
+
+ + +
+

Clinical KB · Clinical tool

+

+ Safety plan generator +

+

+ Build an identifier-free safety plan with your patient — the six prioritised steps — then + export it through your approved clinical workflow. +

+
diff --git a/tests/patient-safety-plan.dom.test.tsx b/tests/patient-safety-plan.dom.test.tsx index 2fe373ccc2..4c3de3b279 100644 --- a/tests/patient-safety-plan.dom.test.tsx +++ b/tests/patient-safety-plan.dom.test.tsx @@ -25,6 +25,19 @@ const exampleExportPattern = /\*\*\* EXAMPLE — SAMPLE SAFETY PLAN WITH NON-WORKING NUMBERS, NOT FOR PATIENT HANDOVER \*\*\*/; describe("PatientSafetyPlan — incomplete-plan draft guard", () => { + it("owns the OS top inset outside the search shell", () => { + // /safety-plan is a standalone tool route (no chrome-safe-area-top host), + // so the tool header must bake max(safe-area-top) into its top pad. + const { container } = render(); + const header = container.querySelector('[data-testid="safety-plan-tool-header"]'); + expect(header).toBeTruthy(); + const className = header?.getAttribute("class") ?? ""; + expect(className).toContain("pt-[max(0.75rem,var(--safe-area-top))]"); + expect(className).toContain("sm:pt-[max(1.25rem,var(--safe-area-top))]"); + expect(className).not.toMatch(/(?:^|\s)py-\S+/); + expect(screen.getByRole("heading", { name: /Safety plan generator/i })).toBeTruthy(); + }); + it("flags the patient copy as a draft until every step is complete", async () => { render(); From a10a7e93fefbdebea84b280de58c4edcd8f07c48 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 8 Aug 2026 06:28:21 +0000 Subject: [PATCH 2/4] fix(search): stop Scope Escape from reopening command listbox Focus restore onto the composer + trigger was reopening the documents actions panel via onFocusCapture, covering Browse library and failing the deferred-request @critical journey after the Button ref sync. Co-authored-by: BigSimmo --- .../clinical-dashboard/master-search-header.tsx | 1 + .../universal-search-command-surface.tsx | 10 +++++++++- tests/ui-smoke.spec.ts | 5 +++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/components/clinical-dashboard/master-search-header.tsx b/src/components/clinical-dashboard/master-search-header.tsx index 17d59eabdd..dad82f70e0 100644 --- a/src/components/clinical-dashboard/master-search-header.tsx +++ b/src/components/clinical-dashboard/master-search-header.tsx @@ -861,6 +861,7 @@ export function MasterSearchHeader({ function closeModeSurfaces() { setActionMenuOpen(false); + setCommandDropdownOpen(false); closeScope(false); setScopeSheetOpen(false); } diff --git a/src/components/clinical-dashboard/universal-search-command-surface.tsx b/src/components/clinical-dashboard/universal-search-command-surface.tsx index 4b62c4cca9..e3190ab5bd 100644 --- a/src/components/clinical-dashboard/universal-search-command-surface.tsx +++ b/src/components/clinical-dashboard/universal-search-command-surface.tsx @@ -1000,7 +1000,15 @@ export function UniversalSearchCommandSurface({ handleComposerKeyDown(event as unknown as ReactKeyboardEvent); } }} - onFocusCapture={() => { + onFocusCapture={(event) => { + // Only the search input opens the command panel. Focus restore onto the + // integrated + / pins trigger (e.g. after Escape closes Scope) must not + // reopen the listbox — that overlay covers mode-home actions such as + // Browse library and breaks the deferred-request critical journey. + const target = event.target; + if (!(target instanceof HTMLElement) || target.dataset.testid !== "global-search-input") { + return; + } // Focus can arrive before the post-hydration effect has synchronized // the conservative false initial state. Re-evaluate synchronously so // desktop input never loses its first command-panel interaction. diff --git a/tests/ui-smoke.spec.ts b/tests/ui-smoke.spec.ts index b80c24e52d..94c088fd48 100644 --- a/tests/ui-smoke.spec.ts +++ b/tests/ui-smoke.spec.ts @@ -3881,8 +3881,13 @@ test.describe("Clinical KB UI smoke coverage", () => { expect(requestCounts.batches).toBe(0); expect(requestCounts.quality).toBe(0); await page.keyboard.press("Escape"); + await expect(page.getByTestId("scope-command-popover")).toHaveCount(0); + // Scope restore can land on the composer + trigger; the command listbox must + // stay closed so it cannot cover Start-here actions (Browse library). + await expect(page.getByRole("listbox", { name: /search suggestions/i })).toHaveCount(0); await switchToDocumentSearchMode(page); + await expect(page.getByRole("listbox", { name: /search suggestions/i })).toHaveCount(0); await page .getByRole("button", { name: /Browse library/i }) .first() From 78c142054d5b32e51ec206f97a20f846650b83df Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 8 Aug 2026 08:43:16 +0000 Subject: [PATCH 3/4] test(safety-plan): reject sm:py axis padding in header guard Match the standalone shell contract so breakpoint-prefixed py utilities cannot bypass the safe-area top-pad regression check. Co-authored-by: BigSimmo --- tests/patient-safety-plan.dom.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/patient-safety-plan.dom.test.tsx b/tests/patient-safety-plan.dom.test.tsx index 4c3de3b279..9d6e417615 100644 --- a/tests/patient-safety-plan.dom.test.tsx +++ b/tests/patient-safety-plan.dom.test.tsx @@ -35,6 +35,7 @@ describe("PatientSafetyPlan — incomplete-plan draft guard", () => { expect(className).toContain("pt-[max(0.75rem,var(--safe-area-top))]"); expect(className).toContain("sm:pt-[max(1.25rem,var(--safe-area-top))]"); expect(className).not.toMatch(/(?:^|\s)py-\S+/); + expect(className).not.toMatch(/(?:^|\s)sm:py-\S+/); expect(screen.getByRole("heading", { name: /Safety plan generator/i })).toBeTruthy(); }); From 9f3b5a29ca45f2017fff379d641068d13363ab71 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 8 Aug 2026 08:56:31 +0000 Subject: [PATCH 4/4] docs(ledger): record PR #1711 heavy review-and-fix Review outcome for the post-sync tip after the sm:py guard fix and late main sync (#1720). Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 2007d1c9b6..af2495be0b 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -749,3 +749,4 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-08-08 | claude/differentials-inpage-navigation-h2u8fq (PR #1715) | f0b27ec857a70130d1616ddc1ccae1c9952c697b | heavy review-and-fix PR #1715 differentials in-page navigation | merge-blocker cleared (origin/main sync); no P0/P1 findings; phone-chrome + verify:pr-local green on f0b27ec8 | merge-tree clean post-sync; verify:phone-chrome lock-parity+runtime PASS, contracts 119 passed, changed-browser ui-tools passed, focused-browser 7 passed (19.4s); verify:pr-local Test Files 523 passed (523), Tests 5547 passed \| 4 skipped (5551); lint+typecheck+build+rag fixtures green; 0 unresolved review threads | | 2026-08-08 | claude/ds-doc-corrections | b4051d21f38755f7d37dbc2b49994f689af801b5 | M1 stranded doc corrections, final reviewed head (adds the review-response commit: COMPONENTS.md section 4 integration-vs-adoption split and the re-measured ui-primitives row) | merged to main as 8cffad59a. Supersedes the 534405600 record, which was accurate at that head but predates the review pass. Three findings, all valid and all fixed: Codex caught four future-dated 2026-08-09 records (corrected to the 2026-08-08 authoring date by f3a91c67c, verified none remain); CodeRabbit caught 'Select/choice controls remain separate adoption work', wrong on both axes since select.tsx consumes FormField and Select has 2 production importers while SearchField has zero; CodeRabbit caught a stale '27 adopted', and re-measuring that row also corrected 686 to 698 lines and 200 to 157 production importers of ui-primitives (200 was close to the 202 mockup-inclusive figure) | prettier --check . pass whole-tree; check:outstanding-issues pass (274 rows, unique ids, no ids deleted from base); adoption figures read from the generated adoption-manifest.json; docs-only diff so no unit, lint, typecheck or browser gate applies to it | | 2026-08-08 | claude/ds-tap-and-linkaction | 6916c80526603514d91bd29d224959dd420af59c | M5 LinkAction tone refusal plus re-measured corrections to outstanding-issues #270, #118 and #269 — final reviewed head, adds the tone?: never fix, its type-contract test and both regenerated manifests | PR #1720, superseding the 824c1b74a record. Codex found the Omit form still accepted tone through a spread; verified with a focused tsc probe before changing anything (Omit accepted the spread with no diagnostic, tone?: never rejected it with TS2345), because excess-property checking only fires on object literals. Fixed with tone?: never plus a type-level contract test that stops compiling if the prop widens back. CodeRabbit's future-dated finding fixed in ff307cc5b. CodeRabbit's ledger-scope finding does not apply: that row records a different ref and head and was accurate as written, but a superseding row for the final #1719 head was appended anyway since its scope grew after the review pass | tsc -p tsconfig.typecheck.json --noEmit exit 0 zero diagnostics; lint exit 0; check:design-system-contract exit 0 (676 production files, legacy shadow aliases 228 confirming the #262 re-measure, adoption 53 components 55 roots, design-sync 53 components and 7 guidelines); check-icon-scale.mjs --strict exit 0; vitest threads pool 3 files 164 tests passed; check:outstanding-issues pass; check:branch-review-ledger pass; prettier --check . pass whole-tree; main merged in with merge-tree proven clean first and an id-set proof over both merge parents showing 274 ids each side, none lost, none invented | +| 2026-08-08 | cursor/safety-plan-phone-safe-area-624a (PR #1711) | ad1b1f5db24ed68ee4c0d5963620e4562829884e | heavy review-and-fix PR #1711 | fixed CodeRabbit sm:py guard parity; late-synced #1720 behind-but-clean; no P0/P1; Bugbot none; threads cleared; merge-tree clean; required CI green on 78c14205 pre-sync | vitest safety-plan+standalone 18p; verify:cheap 523/5582; verify:pr-local format+lint+typecheck+test+build+rag-fixtures; Production UI critical+(1)(2)(3)+PR required SUCCESS on 78c14205; no provider gates |