diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 8896a0d8c5..ca5aa86778 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -621,3 +621,4 @@ Use this ledger to prevent repeated branch and PR reviews when the reviewed HEAD | 2026-07-18 | claude/clinical-kb-pwa-review-asi3wb (PR #896, plan Phase 5; content commit + this ledger follow-up) | a6c2b4e92374e9002fb00c547eb5677d01ce538c | Design-polish sweep: audit-then-fix (plan Phase 5, final phase) | Audit on post-#890 main: three strict design guards clean; full re-run of the 07-token-adoption-audit grep method shows all July 3 debt resolved (M1–M3 done, L4 reduced to the deliberate theme-aware `ring-white/N dark:ring-white/10` glass idiom, L5/L7 gone; production hex all legitimate print/brand/console/comment classes); 43-capture live sweep across 15 routes × desktop/phone + 320px spots + dark/reduced-motion/forced-colors spots found 0 overflow and 0 console errors. Three defects found and fixed: (1) forced-colors solid-button labels rendered as blank Canvas-on-Canvas backplate boxes (axe-invisible) — command controls flattened to the native HCM ButtonFace/ButtonText pairing and accent glyph tokens flipped to ButtonText inside the existing forced-colors block, regression-locked by a new ui-accessibility test; (2) tools desktop 6-up quick-action rail truncated card titles at 1440×1000 — card metrics tightened, all six titles verified unclipped; (3) privacy page rendered "systemand" from a JSX newline-adjacent-to-tag drop — explicit space, locked by a privacy-ui assertion. Dated July 18 run appended to docs/redesign/07-token-adoption-audit.md (archived design-qa.md not resurrected). | Guards + focused vitest 14/14; `verify:cheap` chain green to the known container-only pdf-extraction-budget artifact (2806/2809); `verify:ui` 220 passed/2 failed (the two long-baselined container artifacts, hosted-CI-green through #826/#835/#872/#890); `test:e2e:accessibility` 8/8 incl. the new forced-colors token test; production build + client-bundle secret scan passed; `check:bundle-budget` within tolerance vs the Phase 4 ratchet (1290.6 vs 1278.6 KiB baseline); `verify:pr-local` runtime/format/lint/typecheck/build/rag-fixtures green with the same sole unit-suite artifact. `verify:release` not run (provider-backed; awaits explicit confirmation). No provider-backed checks run. | | 2026-07-19 | all remote feature branches and registered worktrees against `origin/main` through PR #899 | 8242fa63d5f5b79fc770c9ae4f633e3a784b80e1 | branch/worktree cleanup, useful-work recovery, and protected-main merge closure | Deleted 122 stale or closed remote feature refs with exact SHA leases; four additional merged PR branches were removed by the protected-main PR workflow. Removed 32 obsolete, superseded, or merge-proven worktree registrations. Recovered useful dirty RAG work into PR #901 (deterministic and opt-in semantic reranking) and PR #902 (retrieval phase latency telemetry), preserved follow-up decisions in `docs/process-hardening.md`, and recovered four missing historical review rows. PRs #897, #899, #901, and #902 are merged with green exact-head checks and zero unresolved review threads. A detached full-repo-review worktree is deliberately retained because its ownership/activity could not be safely disproved; one unregistered `node_modules` junction residue is also retained because deletion was denied by local safety policy. | Fresh fetch/prune; full GitHub PR/check/thread inventory; `git worktree list --porcelain`; cherry-pick-aware right-only logs; exact leased remote deletes; exact-old-value local ref deletes; clean-worktree, path, and merged-PR proof before every removal. PR #899 local proof: focused Vitest 31/31, changed-file ESLint, `verify:cheap` 317 files / 2,879 tests, and `verify:ui` 239/239; exact-head hosted checks all passed. PR #901 local proof: `verify:cheap` 316 files / 2,870 tests; PR #902 focused Vitest 8/8 plus ESLint and typecheck. No OpenAI, Supabase, live clinical, deployment, or production-data workflow ran; provider-backed semantic canary evaluation remains approval-gated. | | 2026-07-19 | main / `codex/supabase-database-review` | 4034d2e60ebb6616130ff17bf3cb69368f36f8f6 + reviewed working diff | live `Clinical KB Database` security, migration, schema-drift, integrity, and performance review against current repo | Confirmed and remediated a P1 privacy defect: 601 private-document title-vocabulary rows were reachable by the service-role query corrector; the live public-only sync/backfill now reports zero private or out-of-scope rows. Applied the committed retrieval-count bound, audit-metadata minimization, registry cleanup/index, public-title corrector, and atomic summary-rate-limit migrations. The missing FK and registry indexes are present and no invalid indexes remain. A second P1 was found in the untracked live `ingestion-worker`: gateway JWT verification accepted any project JWT before privileged direct-Postgres job processing. Recovered the deployed source into the repo, restricted it to POST plus a gateway-verified `service_role` claim, expanded the Deno checker to every tracked Edge Function, and deployed exact-matching v13 with JWT verification enabled. Review also exposed a repo mirror/test gap: the count-clamp migration was not reflected in `schema.sql`; the branch now mirrors it and locks both sources in the focused test. Remaining hosted blocker: `postgres` cannot assume managed `supabase_admin`, so the fail-closed default-ACL migrations and final title-word constraint/trigger migration remain unapplied; the intentional service-role-only table still produces one INFO no-policy advisor. | Supabase connector project identity, migration and Edge Function inventory, full drift snapshot comparison, security/performance advisors, catalog integrity/ACL/index queries, Vault JWT-role compatibility check, post-apply invariants, exact deployed-source hashes, and unauthenticated live rejection (401); focused retrieval/schema/drift Vitest 82/83 with only manifest freshness failing; Edge/retrieval auth 9/9; Deno check for both functions; offline RAG 36 cases / 294 tests; function-grant guard; scoped ESLint, Prettier, and `git diff --check`. `check:supabase-project` was attempted but stopped before provider contact because local project env vars are unset. `drift:manifest` was blocked because Docker Desktop could not start and was cleaned up. `verify:cheap`, `verify:pr-local`, production-readiness, OpenAI, hosted CI, broader deployment, and commit/push were not run. | +| 2026-07-19 | cursor/safari-edge-to-edge-f46b (PR #933) | 15061964dd2fdf9665f72b7282f5cc81c736e57f | final Safari edge-to-edge / phone dock reserve review + merge readiness | No high-confidence P0-P1. Confirmed implementation: shared reserve module collapses to 0.75rem when dock hides; shell uses block scrollport + inner mobile-composer-reserve-pad so clearance contributes to scrollHeight; child dock-sized env(safe-area) pads removed; DocumentViewer owns its dock pad. Review polish: formulation/specifier max-sm:min-h-0 alignment, document-route ownership simplification, hidden-pad CSS token guard. Residual P2/P3 only: differentials compare zero-inset backdrop margin, idle 2rem vs max(2rem,safe-area) ~2px, unused-looking #main-content padding transition still needed by ClinicalDashboard. Merge-ready. | Local: format/lint/typecheck/knip/budgets pass; unit 2952 passed with only pre-existing pdf-extraction-budget (python ENOENT, also fails on clean main); production build + client-bundle secret scan pass; focused Chromium composer suite 6/6 (forms hide, tablet/desktop clearance, differentials compare, service-detail endpoint, document-viewer hide, long-answer dock). Hosted CI on prior head fully green including Production UI; polish head re-checked before merge. No OpenAI/live Supabase/provider calls. | diff --git a/src/app/globals.css b/src/app/globals.css index 20df4b99c0..eb75cb6667 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -267,6 +267,10 @@ --safe-area-right: env(safe-area-inset-right, 0px); --safe-area-bottom: env(safe-area-inset-bottom, 0px); --safe-area-left: env(safe-area-inset-left, 0px); + /* Keep in sync with mobile-composer-reserve.ts (phone dock clearance). */ + --phone-dock-hidden-pad: 0.75rem; + --phone-dock-differentials-compare-clearance: 12.5rem; + --phone-dock-differentials-compare-compact-clearance: 12.25rem; /* Radius tokens are the single source of truth in @theme above (they also generate the rounded-* utilities); do not redefine them here or var() and the utilities drift apart. */ @@ -1802,14 +1806,15 @@ summary::-webkit-details-marker { height: max(5rem, calc(var(--safe-area-bottom) + 4.4rem)); } - /* Differentials search results: compare action sits above the search pill in the dock. */ + /* Differentials search results: compare action sits above the search pill in the dock. + Heights track --phone-dock-differentials-compare-* (same as content reserve). */ .answer-footer-search-dock[data-footer-addon="differentials-compare"] .answer-footer-search-backdrop { - height: max(8.75rem, calc(var(--safe-area-bottom) + 7.5rem)); + height: max(var(--phone-dock-differentials-compare-clearance), calc(var(--safe-area-bottom) + 11.25rem)); } .answer-footer-search-dock.document-mobile-search-compact[data-footer-addon="differentials-compare"] .answer-footer-search-backdrop { - height: max(8.5rem, calc(var(--safe-area-bottom) + 7.25rem)); + height: max(var(--phone-dock-differentials-compare-compact-clearance), calc(var(--safe-area-bottom) + 11rem)); } .document-mobile-search-pill { @@ -2581,7 +2586,9 @@ html[data-motion="reduced"] .source-capsule-hit[aria-expanded="true"]:hover .sou } } -/* iOS Safari bottom padding transitions to match the composer's hide/show motion */ +/* iOS Safari bottom reserve transitions to match the composer's hide/show motion. + Shell routes animate the inner reserve pad; dashboard/answer animates + #main-content padding; DocumentViewer animates its own content pad. */ @media (max-width: 639px) { #main-content { transition: padding-bottom 200ms cubic-bezier(0.22, 1, 0.36, 1); @@ -2589,6 +2596,12 @@ html[data-motion="reduced"] .source-capsule-hit[aria-expanded="true"]:hover .sou #main-content[data-bottom-composer-hidden="true"] { transition: padding-bottom 240ms cubic-bezier(0.4, 0, 0.2, 1); } + #main-content [data-testid="mobile-composer-reserve-pad"] { + transition: padding-bottom 200ms cubic-bezier(0.22, 1, 0.36, 1); + } + #main-content[data-bottom-composer-hidden="true"] [data-testid="mobile-composer-reserve-pad"] { + transition: padding-bottom 240ms cubic-bezier(0.4, 0, 0.2, 1); + } [data-testid="document-viewer-content"] { transition: padding-bottom 200ms cubic-bezier(0.22, 1, 0.36, 1); } @@ -2596,3 +2609,14 @@ html[data-motion="reduced"] .source-capsule-hit[aria-expanded="true"]:hover .sou transition: padding-bottom 240ms cubic-bezier(0.4, 0, 0.2, 1); } } + +@media (max-width: 639px) and (prefers-reduced-motion: reduce) { + #main-content, + #main-content[data-bottom-composer-hidden="true"], + #main-content [data-testid="mobile-composer-reserve-pad"], + #main-content[data-bottom-composer-hidden="true"] [data-testid="mobile-composer-reserve-pad"], + [data-testid="document-viewer-content"], + [data-testid="document-viewer-content"][data-scroll-hidden="true"] { + transition: none; + } +} diff --git a/src/components/ClinicalDashboard.tsx b/src/components/ClinicalDashboard.tsx index 1f74aa26a8..90ba502134 100644 --- a/src/components/ClinicalDashboard.tsx +++ b/src/components/ClinicalDashboard.tsx @@ -91,6 +91,10 @@ import { } from "@/components/clinical-dashboard/answer-progress"; import { evidenceMapRowsFromRenderModel } from "@/components/clinical-dashboard/evidence-map-model"; import { MasterSearchHeader } from "@/components/clinical-dashboard/master-search-header"; +import { + resolveDashboardVisibleMobileComposerReserve, + resolveMobileComposerReserve, +} from "@/components/clinical-dashboard/mobile-composer-reserve"; import { UniversalSearchAlsoMatches } from "@/components/clinical-dashboard/universal-search-also-matches"; import { useScrollHideReporter } from "@/components/clinical-dashboard/use-hide-on-scroll"; import { SearchCommandProvider } from "@/components/clinical-dashboard/search-command-context"; @@ -3249,19 +3253,16 @@ export function ClinicalDashboard({ const compactMobileBottomSearch = hasMobileBottomSearch && modeSearchSubmitted; const differentialsCompareAddonActive = searchMode === "differentials" && modeSearchSubmitted && Boolean(query.trim()); - const visibleMobileComposerReserve = - searchMode === "answer" - ? answerFollowUpSuggestions.length > 0 - ? "calc(7.5rem + var(--safe-area-bottom))" - : "calc(5.25rem + var(--safe-area-bottom))" - : differentialsCompareAddonActive - ? "calc(8.75rem + var(--safe-area-bottom))" - : compactMobileBottomSearch - ? "calc(5rem + var(--safe-area-bottom))" - : "calc(5.25rem + var(--safe-area-bottom))"; - const mobileComposerReserve = bottomComposerHidden - ? "max(0.75rem, env(safe-area-inset-bottom))" - : visibleMobileComposerReserve; + // Hidden dock pad must stay at 0.75rem — Safari toolbar safe-area recreates a blank band. + const mobileComposerReserve = resolveMobileComposerReserve( + bottomComposerHidden, + resolveDashboardVisibleMobileComposerReserve({ + searchMode, + hasAnswerFollowUps: answerFollowUpSuggestions.length > 0, + differentialsCompareAddonActive, + compactMobileBottomSearch, + }), + ); const renderDegradedNotice = () => (