diff --git a/PR_POLICY_BODY.md b/PR_POLICY_BODY.md index 55f30f8bcf..a5ddb0aeec 100644 --- a/PR_POLICY_BODY.md +++ b/PR_POLICY_BODY.md @@ -1,33 +1,29 @@ ## Summary -- On phone viewports (`max-width: 639px`), the header Mode picker opens as the shared bottom `Sheet` so the full mode list is scrollable with backdrop dismiss, Escape, focus trap, and return-focus to the Mode button. -- Desktop (`sm:`+) keeps the existing anchored absolute dropdown, keyboard navigation, blur-leave dismiss, and outside-click dismiss via `useDismissableLayer`. -- Hardens shared `Sheet` backdrop dismiss (gesture must start on the dimmed area; no click-through) and Tab-trap handling for roving `tabindex="-1"` menus. -- Keeps PDF extractor process-tree termination reliable under suite load; Python binary resolution uses shared `python-bin` from main. -- Fixes phone hydration: Mode layout gate initializes SSR-safe and syncs from `matchMedia` after mount / on open (avoids React #418 on phone route coverage). +Hosted Production UI occasionally failed the differentials compare-dock tap assertion after scroll-reveal: a single `elementFromPoint` sample could miss while `translateY` was still easing. Poll the pointer-hit check through the reveal transition so the assertion waits for a stable hit target. -## Verification +The original phone Answer header gutter asymmetry is already fixed on `main` by #940 (`--header-edge-pad`); this PR does not change header spacing. -- [x] Focused Vitest — header/sheet/audit-navigation contracts green after hydration fix -- [x] Mode Playwright proofs — phone sheet open/scroll/select, backdrop dismiss + focus restore, desktop outside-click (no sheet), keyboard nav (5/5) -- [x] Previously failing Production UI route-coverage journeys — DSM/specifier/differential phone hydration (3/3) -- [x] `npm run check:production-readiness:ci` — READY (no blocking failures) -- UI verification not run: full `verify:ui` / `verify:release` not required beyond the focused Mode + route-coverage proofs above -- Provider-backed live evals not run: no answer-generation or retrieval scoring changes +## Verification -## Risk and rollout +- [x] `npm run verify:pr-local` +- [x] `npm run verify:ui` when UI, routing, styling, browser behavior, reduced-motion, or forced-colors behavior changed +- [ ] `npm run verify:release` before release or handoff confidence claims -- Risk: low — phone-only Mode open path plus shared Sheet dismiss/focus hardening; desktop Mode contracts preserved. Extractor change is fail-closed process cleanup only. -- Rollback: revert this PR; Mode menu returns to the previous floating phone panel and prior Sheet/extractor behaviour. -- Provider or production effects: None +UI verification: Chromium compare-dock test and prior full `verify:ui` on this branch lineage; hosted Production UI also passed after the poll harden on earlier heads. -## Clinical Governance Preflight +- [ ] **`npm run eval:retrieval:quality` (must stay 36/36) when retrieval, ranking, selection, chunking, or scoring behavior changed** — not applicable +- [ ] `npm run eval:rag -- --limit 15` + `npm run eval:quality -- --rag-only` when answer generation, the synthesis prompt, or answer post-processing changed — not applicable +- [ ] `npm run check:production-readiness` when clinical workflow, privacy, environment, Supabase, source governance, or deployment behavior changed — not applicable +- [ ] `npm run check:deployment-readiness` when deployment startup, hosting, or rollout behavior changed — not applicable -Touched extractors/document.ts (fail-closed process-tree cleanup only). No answer, citation, source-governance, privacy, or document-access behaviour change. +## Risk and rollout - +- Risk: Low. Test-only wait hardening for a mid-transition pointer sample; no product behavior change. +- Rollback: Revert the compare-dock `expect.poll` change in `tests/ui-tools.spec.ts`. +- Provider or production effects: None ## Notes -- Phone gate reuses `phoneSearchLayoutMediaQuery` (`max-width: 639px`). -- `id="app-mode-menu"` is preserved inside the sheet for existing restore-focus guards. +- Supersedes the component-level `px-3 sm:px-0` approach previously explored on this branch; #940 is the canonical phone header inset fix. +- Also replaces the leftover Mode-menu `PR_POLICY_BODY.md` from #935 on this head so Sync applies this PR's description. diff --git a/tests/ui-tools.spec.ts b/tests/ui-tools.spec.ts index 5676377e5c..e8de540445 100644 --- a/tests/ui-tools.spec.ts +++ b/tests/ui-tools.spec.ts @@ -1689,12 +1689,17 @@ test.describe("Clinical KB tools launcher", () => { await scrollPrimarySurface(page, 60); await expect(dock).not.toHaveAttribute("data-scroll-hidden", "true"); await expect(compareAction).toBeVisible(); - const restoredPointer = await compareAction.evaluate((element) => { - const rect = element.getBoundingClientRect(); - const hit = document.elementFromPoint(rect.left + rect.width / 2, rect.top + rect.height / 2); - return hit === element || element.contains(hit); - }); - expect(restoredPointer).toBe(true); + // Poll through the reveal transition: a single elementFromPoint sample can + // miss while translateY is still easing back into the viewport on CI. + await expect + .poll(async () => + compareAction.evaluate((element) => { + const rect = element.getBoundingClientRect(); + const hit = document.elementFromPoint(rect.left + rect.width / 2, rect.top + rect.height / 2); + return hit === element || element.contains(hit); + }), + ) + .toBe(true); await expectNoPageHorizontalOverflow(page); // The result cards and compare bar remain in their non-desktop layout up