diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 56369e4247..f8b002e788 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -641,10 +641,17 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-08-05 | codex/v2-design-system-completion | 8863cea53bf4df59e8795dcaab1fa420b5109516 | PR #1616 v2 design system CI+reviews | fixed typecheck + review defects; baselines remain not-committed by design | tsc; vitest ui-v2/accessible-table/ui-primitives/design-system-adoption | | 2026-08-05 | codex/v2-design-system-completion | 8b49bfa2b66ed78577b08e1a50414db55898f2df | PR #1616 v2 design system CI+reviews | fixed typecheck + review defects; baselines remain not-committed by design | tsc; vitest ui-v2/accessible-table/ui-primitives/design-system-adoption | | 2026-08-05 | codex/v2-design-system-completion | 1ea30c19c48b3791519659abf85b20c9e8c7faab | PR #1616 review feedback disposition | COMPLETED: fixed mode-home empty composer reserve + chip category v2 tokens; dispositioned remaining Devin analysis findings to outstanding-issues; unresolved threads 0 | vitest search-route-ownership 12/12; ckb-v2-token-contract+related green; CI in progress on tip | -| 2026-08-05 | claude/review-open-prs-fewxlh | 4e384f4cef1a0c3f15bc8ca3d907920b7c097541 | Run PR sweep | merged main; fixed 3 Devin findings (busy heuristic, typecheck excludes, scripts-index) | vitest: tests/guard-push.test.ts 23 passed; self-test passed | -| 2026-08-05 | cursor/phone-mode-sheet-yes-05c0 | b579d68491980388ff3e4ce8aba85530e87a9d84 | Run PR sweep | threads already resolved; synced main via update-branch; CI was green pre-sync | CI: PR required SUCCESS pre-sync | -| 2026-08-05 | cursor/phone-mode-sheet-yes-05c0 | 9152e239076ff823ad3bf812d282ffc87c3295b7 | Run PR sweep | threads already resolved; merged origin/main; CI was green pre-sync | CI: PR required SUCCESS pre-sync; merge-tree clean | +| 2026-08-05 | claude/privacy-notch-safe-area | efc29559ede819daeec99d46e9147fe08a73bdb0 | PR #1621 babysit standalone-shell review fixes | fixed: shared searchPageShellStandalone + min-h-tap + contract; main synced; CI green CLEAN; Bugbot clean; no open threads | test:5331-pass; build:pass; lint:pass; typecheck:pass; rag-fixtures:pass; CI#31025767142:pr-required-success; bugbot:no-bugs | +| 2026-08-05 | claude/privacy-notch-safe-area | ef92d628683cc6a47c07b1a901b73f22d9474149 | PR #1621 babysit standalone-shell review fixes | fixed: shared searchPageShellStandalone + min-h-tap + contract; main synced; CI green CLEAN; Bugbot clean; no open threads; ledger closeout tip | test:5331-pass; build:pass; lint:pass; typecheck:pass; rag-fixtures:pass; CI#31025767142:pr-required-success; bugbot:no-bugs | | 2026-08-05 | codex/editable-search-pins | 18c3069c7ad19b7e92e3ec9e0b65b5d845afe9c1 | editable search pins menu production integration and mockups | no findings; ready for draft review | verify:pr-local; focused pin tests; typecheck; responsive browser QA; phone-chrome contracts and journey | | 2026-08-05 | codex/editable-search-pins | 193afe637481c0ed2fa82dbe9bb1f1d1c8bcc4d8 | editable search pins menu review follow-up | fixed remaining review defects; unresolved threads cleared; auto-merge armed | vitest:search-pins 18/18; tsc clean on touched surfaces; review threads 0 unresolved | | 2026-08-05 | codex/editable-search-pins | a0e2801fb291a672274c9a723108637d3cdb43f9 | editable search pins menu review follow-up | fixed remaining review defects; lint setState-in-effect; unresolved threads cleared; auto-merge armed | vitest:search-pins 18/18; eslint touched surfaces; review threads 0 unresolved | +| 2026-08-05 | cursor/phone-mode-sheet-yes-05c0 | b579d68491980388ff3e4ce8aba85530e87a9d84 | Run PR sweep | threads already resolved; synced main via update-branch; CI was green pre-sync | CI: PR required SUCCESS pre-sync | +| 2026-08-05 | cursor/phone-mode-sheet-yes-05c0 | 9152e239076ff823ad3bf812d282ffc87c3295b7 | Run PR sweep | threads already resolved; merged origin/main; CI was green pre-sync | CI: PR required SUCCESS pre-sync; merge-tree clean | | 2026-08-05 | codex/editable-search-pins | 67f2c71e8376fbc44615f765a73cb6a37c4ddb40 | editable search pins menu review follow-up | merged main; review threads cleared; auto-merge armed | vitest search-pins+mode-action+command-surface: Test Files 4 passed (4); Tests 33 passed (33); eslint max-warnings 0 on touched surfaces | +| 2026-08-05 | claude/review-open-prs-fewxlh | 4e384f4cef1a0c3f15bc8ca3d907920b7c097541 | Run PR sweep | merged main; fixed 3 Devin findings (busy heuristic, typecheck excludes, scripts-index) | vitest: tests/guard-push.test.ts 23 passed; self-test passed | +| 2026-08-05 | claude/privacy-notch-safe-area | dba8c9f72fdb84495736b7c8a5c723f4153b3612 | PR #1621 babysit standalone-shell review fixes | supersede: prior row HEAD ef92d628 was unresolvable; tip after main sync is this SHA; product fixes unchanged | ledger:append correction; merge-tree clean vs main | +| 2026-08-05 | claude/privacy-notch-safe-area | dba8c9f72fdb84495736b7c8a5c723f4153b3612 | Run PR sweep | merged main (stale DIRTY); superseded unresolvable ledger HEAD per Devin; no product threads open | merge-tree clean | +| 2026-08-05 | claude/privacy-notch-safe-area | 0fbb5686ace5368f6a21b07089db886e639c095a | PR #1621 babysit standalone-shell review fixes | supersede: prior row HEAD ef92d628 was unresolvable; tip after main sync is this SHA; product fixes unchanged | ledger:append correction; merge-tree clean vs main | +| 2026-08-05 | claude/privacy-notch-safe-area | a3967f8f0ee05b9a3ab922cd4a9feeebdf7efbbc | PR #1621 babysit standalone-shell review fixes | supersede: prior row HEAD ef92d628 was unresolvable; tip after main sync is this SHA; product fixes unchanged | ledger:append correction; merge-tree clean vs main | +| 2026-08-05 | claude/privacy-notch-safe-area | fe352adb18d3260f0646f64ef992376e1449b0de | Run PR sweep | merged main (stale DIRTY); superseded unresolvable ledger HEAD per Devin; no product threads open | merge-tree clean | diff --git a/src/app/privacy/page.tsx b/src/app/privacy/page.tsx index 505c1a9c3d..bbd2c26b65 100644 --- a/src/app/privacy/page.tsx +++ b/src/app/privacy/page.tsx @@ -10,7 +10,7 @@ import { raisedCard, searchPageCanvas, searchPageContainer, - searchPageShell, + searchPageShellStandalone, } from "@/components/ui-primitives"; import { privacyCopy } from "@/lib/ui-copy"; @@ -75,12 +75,18 @@ const SECTIONS: Section[] = [ export default function PrivacyPage() { return (
-
+ {/* + Privacy sits outside the search shell, so this page owns the OS top inset + via searchPageShellStandalone (max(safe-area-top) baked into the pad). + */} +
-
- }> - - +
+
+ }> + + +

{privacyCopy.pageEyebrow}

diff --git a/src/app/reference/colour-coding/page.tsx b/src/app/reference/colour-coding/page.tsx index 4e8cb09c77..306d8b6a08 100644 --- a/src/app/reference/colour-coding/page.tsx +++ b/src/app/reference/colour-coding/page.tsx @@ -9,7 +9,7 @@ import { raisedCard, searchPageCanvas, searchPageContainer, - searchPageShell, + searchPageShellStandalone, } from "@/components/ui-primitives"; import { CONTENT_DOMAIN_META, CONTENT_DOMAIN_ORDER, flagsForDomain } from "@/lib/semantic-flags"; import { SEMANTIC_TONE_META, SEMANTIC_TONES } from "@/lib/semantic-tone"; @@ -41,10 +41,12 @@ export default function ColourCodingReferencePage() { "focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-[color:var(--focus)]", )} > -
+
- +
+ +

Reference

diff --git a/src/components/ui-primitives.tsx b/src/components/ui-primitives.tsx index 98aa714c8b..0b3a14f177 100644 --- a/src/components/ui-primitives.tsx +++ b/src/components/ui-primitives.tsx @@ -157,6 +157,12 @@ export const searchPageCanvas = "bg-[color:var(--background)] text-[color:var(-- // a second dock-sized safe-area pad into page shells. export const searchPageShell = "min-h-0 overflow-x-clip px-3 py-3 pb-4 sm:min-h-[calc(100dvh-var(--shell-header-h))] sm:px-5 sm:py-5 sm:pb-8 lg:px-6"; +// Standalone pages outside the search shell own the OS top inset themselves +// (apple-mobile-web-app-status-bar-style=black-translucent). Bake max(safe-area) +// into the top pad and omit py-* so cn() call sites never rely on Tailwind's +// side-vs-axis utility sort order to win over searchPageShell's py-3/sm:py-5. +export const searchPageShellStandalone = + "min-h-0 overflow-x-clip px-3 pt-[max(0.75rem,var(--safe-area-top))] pb-4 sm:min-h-[calc(100dvh-var(--shell-header-h))] sm:px-5 sm:pt-[max(1.25rem,var(--safe-area-top))] sm:pb-8 lg:px-6"; export const searchPageContainer = "mx-auto w-full max-w-[1500px]"; // Canonical content-page width. Detail pages (service / form / differential), // medication record + prescribing workspace, and the forms results view converge diff --git a/tests/search-page-shell-standalone.contract.test.ts b/tests/search-page-shell-standalone.contract.test.ts new file mode 100644 index 0000000000..be1c21ba63 --- /dev/null +++ b/tests/search-page-shell-standalone.contract.test.ts @@ -0,0 +1,62 @@ +import { readFileSync } from "node:fs"; +import { createElement } from "react"; +import { renderToStaticMarkup } from "react-dom/server"; +import { describe, expect, it, vi } from "vitest"; + +import PrivacyPage from "@/app/privacy/page"; +import ColourCodingReferencePage from "@/app/reference/colour-coding/page"; +import { searchPageShell, searchPageShellStandalone } from "@/components/ui-primitives"; + +vi.mock("next/navigation", () => ({ + useSearchParams: () => new URLSearchParams(), + useRouter: () => ({ + back: vi.fn(), + push: vi.fn(), + }), +})); + +const read = (relativePath: string) => readFileSync(new URL(`../${relativePath}`, import.meta.url), "utf8"); + +const STANDALONE_PAGES = ["src/app/privacy/page.tsx", "src/app/reference/colour-coding/page.tsx"] as const; + +describe("searchPageShellStandalone contract", () => { + it("owns the OS top inset without axis py-* that would fight side-specific pt-*", () => { + expect(searchPageShellStandalone).toContain("pt-[max(0.75rem,var(--safe-area-top))]"); + expect(searchPageShellStandalone).toContain("sm:pt-[max(1.25rem,var(--safe-area-top))]"); + expect(searchPageShellStandalone).toContain("pb-4"); + expect(searchPageShellStandalone).toContain("sm:pb-8"); + // Axis padding would reintroduce the Tailwind sort-order dependency that + // made per-page pt-* overrides fragile when cn() does not de-dupe utilities. + expect(searchPageShellStandalone).not.toMatch(/(?:^|\s)py-\S+/); + expect(searchPageShellStandalone).not.toMatch(/(?:^|\s)sm:py-\S+/); + // Shell pages keep the ordinary py rhythm; standalone is the only owner of + // max(safe-area-top) so future routes do not re-derive the values. + expect(searchPageShell).toMatch(/(?:^|\s)py-3(?:\s|$)/); + expect(searchPageShell).not.toContain("var(--safe-area-top)"); + }); + + it("is the shell used by every production standalone search-page route", () => { + for (const relativePath of STANDALONE_PAGES) { + const source = read(relativePath); + expect(source, relativePath).toContain("searchPageShellStandalone"); + expect(source, relativePath).not.toMatch(/\bsearchPageShell\b(?!Standalone)/); + } + }); + + it("renders the safe-area top pad and tap-token back row on /privacy", () => { + const markup = renderToStaticMarkup(createElement(PrivacyPage)); + expect(markup).toContain(searchPageShellStandalone); + expect(markup).toContain("pt-[max(0.75rem,var(--safe-area-top))]"); + expect(markup).toContain("sm:pt-[max(1.25rem,var(--safe-area-top))]"); + expect(markup).toContain("min-h-tap"); + expect(markup).not.toContain("min-h-12"); + }); + + it("renders the same safe-area pad and tap-token back row on /reference/colour-coding", () => { + const markup = renderToStaticMarkup(createElement(ColourCodingReferencePage)); + expect(markup).toContain(searchPageShellStandalone); + expect(markup).toContain("pt-[max(0.75rem,var(--safe-area-top))]"); + expect(markup).toContain("sm:pt-[max(1.25rem,var(--safe-area-top))]"); + expect(markup).toContain("min-h-tap"); + }); +});