diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 01d05a904..832a57445 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -724,6 +724,7 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-21 | claude/clinical-kb-pwa-review-asi3wb (Option A wave verdict — no code change; #1040 merged as cde6c5c) | canary run 29827012719 (#61, main cde6c5c) vs banked #60 (29800029819) | OPTION A WAVE ADOPTED — FIRST FULLY-GREEN 44-CASE CANARY IN PROGRAM HISTORY (Blocking failures: None). (1) Option A payoff EXCEEDED: citation_failure_rate 0.0227→0; the neuroleptic-side-effect-escalation case flipped from wrong-doc→failed-generation→1-citation-fallback to **strong route, successful gpt-5.6-sol generation, passed in 15.4s with no fallback marker** — the rescued S3 retrieval fixed generation itself, not just the citation count; expected_source_hit 0.6364→0.6591. (2) Golden held exactly as the blast-radius analysis promised: 36/36 PASS, content_recall 1.0, mrr@10 0.8921 BYTE-IDENTICAL to the pre-wave baseline (zero ordering movement — no golden case fires the predicate), irrelevant@10 0.1083→0.0917 (slightly better). (3) Parity payoff PARTIAL: monitoring targeting 1/5→2/5 (olanzapine-lai flipped — previously called a retrieval-depth residual; quetiapine-dose also flipped on the dose side); lithium-range (232ch) + metabolic (73ch, byte-identical answer to #60) did NOT flip despite offline-proven fixes — their live chunk sets evidently contain no admissible schedule sentence even under the widened gate → reclassified as retrieval-depth/live-content residuals joining adhd; below the ≥3/5 target but strictly improved, no regression anywhere. Dose 2/5 vs 2/4: same passing count, applicable set grew (new quality-metformin-renal-dosing miss = eval-set churn, not regression). (4) No-worse EXCEEDED: relevance 0.5333→0.6 (the two-step watch-item slide FULLY REVERSED to the #58 level), targeting_rate 0.6667→0.6957, fail_closed 0.9 held, readability/artifact_leaks 1.0, route ceilings 0, grounded 1.0, unsupported_correct 1.0, numeric 0, p95 22.8s, red_result 3/3. Adoption per the measured-gain rule: primary goal achieved, three case flips, relevance recovered, zero regressions. Residual queue: monitoring retrieval-depth trio (lithium-range/metabolic/adhd), E-3d H2 discards, weekly ANSWER_CASE_LIMIT 8→44 raise now unblocked (gate would be green), comparison-class coverage. Wave spend +~$2-4 → Phase E + Option A total ~$12-20 of ≤$20. | Evidence: run #61 job log read (Threshold Status: None; Answer Metrics; neuroleptic diagnostics row; targeting metric_rates + 6-miss list; golden 36 PASS lines + summary). Revert drill NOT triggered. | | 2026-07-21 | claude/clinical-kb-pwa-review-asi3wb (PR: I9 weekly coverage raise) | see PR head | ADDENDUM 5 post-green item I9 (plan-authorized "after reds fixed"): weekly scheduled canary ANSWER_CASE_LIMIT default 8→44 — the Sunday 18:00 UTC cron now guards the FULL answer-quality case set instead of the first 8 (both #57 blocking reds historically lived OUTSIDE the first 8, leaving the weekly gate blind to them). Unblocked by run #61 proving the citation gate green on the full 44. Cost: est +$1-2/week (user-authorized in the plan). Contract test pin updated in lockstep (eval-canary-workflow.test.ts). Dispatch shapes unchanged (input override still wins); operational-risk diff, plain-revert rollback. | check:github-actions PASS; check:ci-scope PASS; check:gate-manifest PASS (20/20); eval-canary-workflow contract 4/4; prettier clean; no provider calls | | 2026-07-21 | claude/database-governance-audit-10b6ed (PR #1051: source-governance audit — safe subset) | cee396730 | Governance-metadata observability + UI display + provenance flow test; no ranking/retrieval/generation surface touched. | IMPLEMENTED + handed off (not a review of prior work). Resolved audit #1 (logger.warn on unrecognized enum values; return value unchanged), #2 (review_due_source added to frontendVisibleWarningCodes → answer-level badge; warning-severity, no refusal impact), #9 (source_metadata retained on safety-finding citations + governance pill in SafetyFindingsListContent), #13 (new tests/provenance-flow.test.ts: DB-normalize→governance→client payload sources+safety citations→render policy). Deferred #4/5/6/8/10 (RAG-protected ranking/selection/LLM-context/cache — need live eval-canary+approval), #11/#5 flag debt (D5/D4), #3 (is_public schema/RLS), #7 (conflict-detection scope), #12 (canary automation). Rebased onto origin/main (was 18 behind; conflict-free — none of the 18 commits touched the 8 files). PR-policy CI green (confirmed no ragRankingPatterns match). | verify:pr-local exit 0 (351 files/3129 tests, production build, client-bundle secret scan, offline RAG fixtures 36/36); typecheck + lint + prettier green. verify:ui NOT run locally: pre-existing globals.css Tailwind/Turbopack dev-compile error (git-clean, unrelated; prod build passed) — CI Production UI job covers it. check:production-readiness deferred (offline env/config validator; PR changes no env/secret/config inputs; secretless worktree). No provider calls. | +| 2026-07-24 | codex/apply-phone-layout-to-all-home-pages (PR #1124) | e3dc87000 | Run PR sweep: CI fix + threads + drift | Before: PR required green, 1 unresolved guest-favourites dock thread, branch behind main. After: merged origin/main cleanly; gated favourites hero composer slot on favouritesAccessible; thread resolved via GraphQL; reply mutation 403 noted in commit e3dc8700. | node scripts/run-vitest.mjs run --reporter=dot tests/mobile-composer-reserve.test.ts PASS (9/9); git diff --check PASS; no Supabase/OpenAI/live eval gates run. | | 2026-07-24 | repo-auditor (detached HEAD 037b4808 — explicit full-repo structural audit) | 037b4808100946f96aee28b8ff51939ae2986851 | Full-repo structural audit: broken imports, dead files, consolidation. Scope: src/, scripts/, worker/, tests/. Pure triage — no files mutated. | P0/P1: none. P2: confirmed dead file src/components/clinical-dashboard/prior-answer-turn-surface.tsx (115 lines, zero importers; superseded by answer-thread-turn.tsx per its 'Extracted from ClinicalDashboard.tsx maturity X3' comment; old file exports PriorAnswerTurn type + PriorAnswerTurnSurface component — both replaced). P3: three duplicate package.json script pairs (test:e2e = test:e2e:all; promote:public-documents = promote:public-documents:batch; postinstall = hooks:install — last pair intentional lifecycle). Unused public exports: authorization.ts (administratorRoleClaim/Value internal-only), api-client-error.ts (ApiClientError — only parseApiErrorResponse imported), rag/rag.ts (answerQuestion — superseded by answerQuestionWithScope), supabase/project.ts (isExpectedSupabaseProjectConfig + 2 types). RAG surface note only (do not touch): rag-extractive-first.ts exports hasValidatedRoutineExtractiveRecovery/hasValidatedGenericLaiManagementExtractiveAnswer with no external consumers. Knip false positives: taskkill (win32-gated), railway (opt-in --railway flag). No broken imports (typecheck + lint both exit 0, zero relative ../ imports in src/). | npm run typecheck (exit 0); npm run lint (exit 0); npm run check:knip (exit 0); knip --reporter json full export/file/type analysis; rg import-graph probes across all source trees | | 2026-07-24 | cursor/search-interactive-perf-af54 (PR #1138) | 46597a9b | Explicit performance + frontend-ui review of search/interactive surfaces; low-risk client deferral/cache/abort/progressive-reveal pass | Prior document/universal search latency work retained (NDJSON stream, LRU, lazy PDF, content-first detail). New work: differential debounce+abort+LRU; useDeferredValue on catalogue ranking; document results Show more window; RelatedDocumentsPanel memo; universal LRU 100+TTL; deferred registry search extracted from ClinicalDashboard. No RAG/retrieval/ranking edits. No high-confidence P0–P2 defect found in the shipped scope; residual risk = deferred paint lag on large catalogues and progressive reveal missing deep cards until Show more. | Focused Vitest 10/10 (differential + universal + performance boundaries); verify:cheap exit 0 (3262 tests); typecheck clean; verify:ui exit 0 (Chromium). No provider calls. | | 2026-07-24 | cursor/search-interactive-perf-af54 (PR #1138 follow-up) | 7e2ccee0 | Bugfix pass on search interactive performance diff | Fixed P1 auth-stale differential matches; P2 progressive-reveal hiding selected card; P2 deferred empty/full-catalogue flash on services/forms/formulation/therapy-compass; Prettier CI failure on universal-search test. No remaining high-confidence P0–P2 in scoped diff. Residual: differential debounce skeleton flicker; RelatedDocumentsPanel memo limited by unstable callbacks. | Focused Vitest 11/11; typecheck; format:check; maintainability budgets. No provider calls. | @@ -745,21 +746,28 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-24 | cursor/search-performance-review-4ee9 (PR #1134) | 9311d01212fe42bd41ffb22a83bfa51f1a4d19f2 | Run PR re-sync sweep | Re-check: CONFLICTING on use-differential-catalog.ts (+ related). Not cheap; merge aborted, no push. | merge origin/main and/or conflict re-check only; no provider-backed checks run | | 2026-07-24 | cursor/comprehensive-repo-review-ledger-d9a1 (PR #1150) | 60a3c3a83a31e65ec2759540629687e7113e2489 | Run PR sweep: CI fix + threads + drift | Before: CONFLICTING, CI green, 0 threads. After: merged origin/main cleanly (ledger auto-merge); pushed 60a3c3a83. Threads: none. Residual: CI re-running. | merge origin/main only; no provider-backed checks run | | 2026-07-24 | cursor/search-interactive-perf-af54 (PR #1138) | ff4b293d95f922e70ebf5ee9b0c156c41a8bff3b | Run PR sweep: CI fix + threads + drift | Before: CONFLICTING, CI green, 0 threads. RAG impact: no retrieval behaviour change — PR is client deferred-search/UI only (no src/lib/rag/**). After: merged origin/main; conflict resolved in src/components/ui/sheet.tsx by keeping main restoreTimersRef/unmountingRef focus-restore fix; pushed ff4b293d9. Threads: none. | merge origin/main only; no provider-backed checks run | +| 2026-07-24 | codex/apply-phone-layout-to-all-home-pages (PR #1124) | 2cb98b40e2fd36650e293b27a97996c069971db3 | Run PR sweep: CI fix + threads + drift | Before: behind main by 35. After: merged origin/main cleanly (no conflicts). Threads: non-P0/P1 left open. CI not waited. | merge origin/main only; thread scan read-only; no provider-backed checks run | +| 2026-07-24 | codex/apply-phone-layout-to-all-home-pages (PR #1124) | e061ce29300cd80df5c8cd95d2ea8ecfeef1e2b0 | Run PR babysit: CI/threads/drift | Run PR babysit: Codex P2 prescribing typed-draft dock path fixed + thread resolved. Before: CI mostly green, 1 unresolved Codex P2 (3644919028). After: showDesktopHomeComposer for prescribing requires !query.trim(); documents half dispositioned as not holding (home slot stays pre-submit); reply+resolve PRRT_kwDOSh5Fis6Th-6B. Not behind main. | npx vitest run tests/mobile-composer-reserve.test.ts PASS (9/9). No provider-backed checks run. | +| 2026-07-24 | codex/fix-next.js-startup-failure-and-verify-pages (PR #1149) | 8ddddbab2a29a94b3f993cbd114889f72c95f4f1 | Run PR sweep: CI fix + threads + drift | Before: behind main. After: merged origin/main cleanly (no conflicts). Unresolved review threads left as non-P0/P1. CI not waited. | merge origin/main only; thread scan read-only; no provider-backed checks run | +| 2026-07-24 | codex/apply-phone-layout-to-all-home-pages (PR #1124) | bbd5aafaadc7334107bfe531eef291615b26ed4b | Run PR babysit: CI/threads/drift | Post-fix merge origin/main (clean). Prescribing dock P2 fixed+resolved earlier; CI re-running. | merge origin/main; vitest mobile-composer-reserve 9/9 earlier; no provider-backed checks run. | | 2026-07-24 | cursor/repo-auditor-ledger-4132 (PR #1147) | 030723c7716da98a6cf166327e5b7a72c2991b8b | Run PR sweep: CI fix + threads + drift | Before: behind main by 64. After: merged origin/main cleanly (no conflicts). Threads: non-P0/P1 left open. CI not waited. | merge origin/main only; thread scan read-only; no provider-backed checks run | | 2026-07-24 | remediate-audit-system-issues (PR #1160) | 8b2359589fe61c19c78fb02be50316c8f29d7e18 | Run PR sweep: CI fix + threads + drift | before: CONFLICTING; Static PR checks + Unit coverage + PR required FAIL (stale docs/site-map.md). after: merged origin/main cleanly (2e7b034d1); regenerated site-map (8b2359589fe61c19c78fb02be50316c8f29d7e18); no unresolved review threads; CI re-running expected green for static-pr/coverage/pr-required | vitest tests/site-map.test.ts pass (6); sitemap:check pass; no provider-backed checks run | | 2026-07-24 | remediate-audit-system-issues (PR #1160) | 992ebefa296d6894d5448c1381f1b0b95580e529 | Run PR sweep: CI fix + threads + drift | supersedes prior #1160 row: final HEAD after prettier site-map; merge origin/main clean; stale sitemap fixed; no threads | vitest site-map pass; sitemap:check pass; no provider-backed checks run | | 2026-07-24 | remediate-audit-system-issues (PR #1160) | 04201a87cc7ad7dd1477d17cd2b96544b7379789 | Run PR sweep: CI fix + threads + drift | re-merge origin/main after CONFLICTING relapse; resolved scripts/run-eval-safe.mjs (kept main taskkill /T /F + validPids); sitemap Prettier format() retained from fe0588b86; no unresolved threads; no conflict markers | merge + conflict resolve only; no provider-backed checks run | -| 2026-07-24 | codex/fix-next.js-startup-failure-and-verify-pages (PR #1149) | 8ddddbab2a29a94b3f993cbd114889f72c95f4f1 | Run PR sweep: CI fix + threads + drift | Before: behind main. After: merged origin/main cleanly (no conflicts). Unresolved review threads left as non-P0/P1. CI not waited. | merge origin/main only; thread scan read-only; no provider-backed checks run | | 2026-07-24 | codex/query-ribbon-search-headings (PR #1166) | cbb493c2b51dd7120df39cfbcd5f19b73f882d43 | Run PR babysit: CI/threads/drift | Before: not behind at start, then main advanced (8 behind); required checks green except Production UI still pending on prior head; 0 unresolved review threads; no Bugbot findings. After: merged origin/main cleanly (docs/branch-review-ledger.md + src/app/layout.tsx auto-merge). No code fix needed. | merge origin/main only; no provider-backed checks run. | | 2026-07-24 | codex/query-ribbon-search-headings (PR #1166) | ad3d38c62a19f5fa2a7e8356021795937c5b0f66 | Run PR babysit: CI/threads/drift | Supersedes prior #1166 row in this push: post-merge+ledger HEAD after syncing origin/main (clean auto-merge). 0 threads; required CI re-running. | merge origin/main; ledger append; no provider-backed checks run. | | 2026-07-24 | `codex/query-ribbon-search-headings` (PR #1166) | `37cfa5553ccb784ee5e9f47ded1ad69914c053ed` + reviewed correction diff | Correction: universal Query Ribbon implementation and responsive search-heading review | SUPERSEDES the earlier row that named non-existent pre-amend SHA `16ce57d9615708528e7924b41837210a24414722`. This resolvable reviewed tip contains functional commit `0b67944b0d2973d612833422fb4074aeacdb6c8c`, current-main syncs, and the append-only ledger correction. The prior APPROVE outcome and residual-risk statement are unchanged; no P0-P2 finding remains. | Query Ribbon DOM 4/4 after each main sync; exact-head hosted policy, static checks, unit coverage, build, advisory UI, Production UI, safety/config, Semgrep, Gitleaks, GitGuardian, and `PR required` passed before the final docs-only correction. No OpenAI, Supabase, Railway, deployment, production-data, or clinical provider workflow ran. | | 2026-07-24 | codex/query-ribbon-search-headings (PR #1166) | 37cfa5553ccb784ee5e9f47ded1ad69914c053ed | Run PR babysit: CI/threads/drift | Final HEAD after merge origin/main + ledger bookkeeping. 0 unresolved threads; no Bugbot actionable findings; required CI re-running on this SHA. | merge origin/main; no provider-backed checks run. | | 2026-07-24 | codex/query-ribbon-search-headings (PR #1166) | 9eac2e252bcc5c548aa919b69faeb79b9ff7d2cf | Run PR babysit: CI/threads/drift | Merged origin/main; Codex ledger-SHA P2 dispositioned+resolved (append-only supersede already in 9eac2e252). 0 unresolved threads. | merge origin/main; thread resolve only; no provider-backed checks run. | | 2026-07-24 | remediate-audit-system-issues (PR #1160) | bdf530fc8c6faaa4491c510396b47872fc39bf25 | Run PR sweep: CI fix + threads + drift | second re-merge after main moved to 2e68888f3 during first push; clean ort merge (ledger + layout.tsx); taskkill /T retained; sitemap prettier retained | merge only; no provider-backed checks run | +| 2026-07-24 | codex/apply-phone-layout-to-all-home-pages (PR #1124) | d0a96caa01b90dffa40e60770b7700f36e8ba43f | Run PR re-sync sweep | Before: CONFLICTING @8adaaf229. After: merged origin/main clean (ort). CI re-running. Threads: not re-triaged. | merge origin/main and/or conflict re-check only; no provider-backed checks run | | 2026-07-24 | cursor/repo-auditor-ledger-4132 (PR #1147) | 2f47746ad7e13e8cb848da71fb2647644388da8f | Run PR re-sync sweep | Before: CONFLICTING. After: merged origin/main clean. | merge origin/main and/or conflict re-check only; no provider-backed checks run | | 2026-07-24 | cursor/frontend-ui-review-docs-e8d9 (PR #1146) | 2b17f5fcbe06307e7ff50ad0373789daa8f07a01 | Run PR re-sync sweep | Before: CONFLICTING. After: merged origin/main clean. | merge origin/main and/or conflict re-check only; no provider-backed checks run | | 2026-07-24 | codex/query-ribbon-search-headings (PR #1166) | c94e89f392f578c4b2c749195dd485b74959074c | Run PR re-sync sweep | Before: CONFLICTING. After: merged origin/main clean. CI re-running. | merge origin/main and/or conflict re-check only; no provider-backed checks run | | 2026-07-24 | cursor/comprehensive-repo-review-ledger-d9a1 (PR #1150) | 345c02cdbaefb13aeb951a14674aedfe4648a50e | Run PR re-sync sweep | Before: CONFLICTING. After: merged origin/main clean. | merge origin/main and/or conflict re-check only; no provider-backed checks run | +| 2026-07-24 | codex/apply-phone-layout-to-all-home-pages (PR #1124) | c59ae820db3034ef3cf74ae6d0030a0472a3a264 | Run PR babysit: merge/CI/threads | Before: CONFLICTING + Static PR checks FAIL (duplicate #1149 ledger row) + 2 unresolved Codex P2s. After: merged origin/main; removed duplicate ledger row; Differentials hero slot gated on !(query.trim() && documentMatches.length); threads resolved. | vitest mobile-composer-reserve 9/9; check:branch-review-ledger pass; maintainability 4132/4140. No provider-backed checks run. | +| 2026-07-25 | codex/document-clinical-summary-20260725 (PR #1169) | 6bbce2b97477cb4497624abe2a37c74864e872c8 | Open-PR maintenance: review-thread verification | Before: 2 unresolved Codex threads; branch current with main and required CI running. After: both persisted-profile/placeholder-summary fixes confirmed on the exact head and ready for reply-then-resolve; no further code change required. | `node scripts/run-vitest.mjs run tests/document-clinical-summary.test.ts tests/document-clinical-summary.dom.test.tsx --reporter=dot` pass (5/5); `git diff --check` pass; no provider-backed checks run. | +| 2026-07-25 | cursor/pr-babysit-bugbot-agents-6c52 (PR #1167) | ee44812aae9dad1973d8302eba5bfca5000dffb6 | Open-PR maintenance: review-thread fixes | Before: 8 unresolved Codex/CodeRabbit threads; branch current with main. After: target-head pinning, fresh-main verification, exact `cursor[bot]` identity checks, explicit mutation/provider authorization, direct reply-then-resolve semantics, and no-op ledger bookkeeping are documented. | Prettier check on both agent files pass; `git diff --check` pass; GitHub author probe confirmed `cursor[bot]` account type `Bot`; no provider-backed checks run. | | 2026-07-24 | cursor/pr-babysit-bugbot-agents-6c52 (PR #1167) | 6ec7a852 | Babysit sweep: CI fix + Codex/CodeRabbit threads | Before: mergeable, PR required green, 8 unresolved agent-guidance threads. After: fixed pr-babysit/pr-bugbot agents (fetch origin/main, no Run PR live-gate auth, pin target head SHA, exact bot identity, ledger-after-every-sweep). Thread reply/resolve 403 on this token — fixes pushed. | typecheck on agent files; no provider-backed checks run | | 2026-07-24 | codex/fix-merge-conflicts-and-ci-on-open-prs (PR #1170) | 48fcb485 | Babysit sweep: CI fix | Before: Static PR checks FAIL (docs:check-links missing legacy route paths). After: expanded check-docs-links allowlist for pre-(search-app) paths. Production UI re-running. | npm run docs:check-links PASS; no provider-backed checks run | | 2026-07-24 | codex/document-clinical-summary-20260725 (PR #1169) | ef4ffca5 | Babysit sweep: Codex P2 threads | Before: 2 unresolved Codex P2s (missing pages guard, placeholder summary filter). After: profileItemPages helper + usefulSummaryText placeholder filter. CI re-running. | npm run typecheck PASS; no provider-backed checks run | @@ -771,10 +779,9 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-25 | codex/fix-merge-conflicts-and-ci-on-open-prs (PR #1170) | e979fc892f11a17b1a8f2ef1ab058ef40d629182 | Open-PR maintenance: CI fix + threads + drift | Before: Static PR checks failed because route-group moves made nine valid legacy `src/app/*` documentation references appear missing; 0 unresolved threads; branch already contained current main. After: docs-link resolution checks known App Router route groups and the focused failure is fixed. | `node scripts/check-docs-links.mjs` pass (1162 references); Prettier check pass; `git diff --check` pass; no provider-backed checks run. | | 2026-07-24 | open-PR conflict sweep (13 clean + 5 conflicted) | multi-head | Conflict fix sweep | Merged origin/main into #1124 #1140-1142 #1146-1148 #1153 #1156-1158 #1167 #1169 #1171 #1172 (clean). Resolved conflicts on #1131 #1134 #1153 #1167 #1162. All 18 open PRs mergeable after sweep. | merge-tree classify + per-PR merge; focused private-access tests on #1162; Bugbot on #1162; no provider-backed checks | | 2026-07-24 | execute-audit-code-remediation (PR #1162) | 3cb7c977 | Conflict fix + Bugbot + local review | Before: CONFLICTING (21 files). After: mergeable. Restored atomic upload RPC; aligned private-access tests (133/133). Bugbot 2 medium left open. | private-access-routes 133/133; no provider-backed checks | -| 2026-07-25 | cursor/pr-babysit-bugbot-agents-6c52 (PR #1167) | ee44812aae9dad1973d8302eba5bfca5000dffb6 | Open-PR maintenance: review-thread fixes | Before: 8 unresolved Codex/CodeRabbit threads; branch current with main. After: target-head pinning, fresh-main verification, exact `cursor[bot]` identity checks, explicit mutation/provider authorization, direct reply-then-resolve semantics, and no-op ledger bookkeeping are documented. | Prettier check on both agent files pass; `git diff --check` pass; GitHub author probe confirmed `cursor[bot]` account type `Bot`; no provider-backed checks run. | -| 2026-07-25 | codex/document-clinical-summary-20260725 (PR #1169) | 6bbce2b97477cb4497624abe2a37c74864e872c8 | Open-PR maintenance: review-thread verification | Before: 2 unresolved Codex threads; branch current with main and required CI running. After: both persisted-profile/placeholder-summary fixes confirmed on the exact head and ready for reply-then-resolve; no further code change required. | `node scripts/run-vitest.mjs run tests/document-clinical-summary.test.ts tests/document-clinical-summary.dom.test.tsx --reporter=dot` pass (5/5); `git diff --check` pass; no provider-backed checks run. | | 2026-07-25 | codex/document-clinical-summary-20260725 (PR #1169) | 605a47b551a03774fab41416bf980dfbc9610221 | Open-PR maintenance: malformed persisted profile guard | Before: one actionable thread showed non-array or malformed persisted summary groups could throw during render. After: every priority group is normalized through an array/item guard and malformed values are ignored while valid items still render. | Focused Vitest 7/7 pass; Prettier and diff checks pass; no provider-backed checks run. | | 2026-07-25 | cursor/search-performance-review-4ee9 (PR #1134) | 692834a86e612cc8b311dc6895e007f182f5c5b8 | Open-PR maintenance: superseded docs-link thread and clean main sync | Before: branch was behind current main with one outdated docs-link thread; its product tree already matched main. After: merged current main cleanly and verified the route-group-aware docs-link fix now covers legacy route references. RAG impact: no retrieval behaviour change — history sync and docs tooling verification only. | `node scripts/check-docs-links.mjs` pass (1154 references); clean merge-tree; no live RAG canary or provider-backed check run. | | 2026-07-24 | open-PR conflict sync (20 PRs) | multi-head | Conflict resolution pass | Before: 8 PRs behind/dirty (#1124 #1131 #1162 #1169 #1174-1177). After: merged origin/main into all; all 20 open PRs MERGEABLE behind=0 (BLOCKED only by CI/reviews). | merge origin/main per branch; no provider-backed checks run | | 2026-07-24 | open-PR conflict sync (22 PRs) | multi-head | Conflict resolution pass | Before: all 22 open PRs behind/dirty vs main (several CONFLICTING/DIRTY). After: merged origin/main into every open head; all pushes OK; merge-tree classified 22/22 clean. | merge origin/main per branch; check:branch-review-ledger on #1172; no provider-backed checks run | | 2026-07-24 | cursor/pr-queue-hygiene-72ec | pending-push | PR queue hygiene | Add pr-branch-sync workflow + sync:pr-branches helper; bump postcss to clear npm audit high; document anti-churn guidance in AGENTS/process-hardening/pr-babysit/run-pr. | check:github-actions PASS; docs:check-scripts/index PASS; vitest sync-open-pr-branches 3/3; npm audit high clean; no provider-backed checks run | +| 2026-07-24 | codex/apply-phone-layout-to-all-home-pages (PR #1124) | pending | Babysit: ledger dedupe + merge readiness | Before: Static PR failed on exact duplicate ledger rows after main sync. After: removed duplicate rows; squash auto-merge armed. | check:branch-review-ledger PASS; no provider-backed checks run | diff --git a/src/components/ClinicalDashboard.tsx b/src/components/ClinicalDashboard.tsx index b8bb4a620..21ac1e487 100644 --- a/src/components/ClinicalDashboard.tsx +++ b/src/components/ClinicalDashboard.tsx @@ -3054,15 +3054,24 @@ export function ClinicalDashboard({ const showDesktopHomeComposer = !error && (activeModeResultKind === "tools" || - activeModeResultKind === "favourites" || + (activeModeResultKind === "favourites" && favouritesAccessible) || (!loading && (showAnswerHome || (searchMode === "documents" && activeModeResultKind === "documents" && documentMatches.length === 0 && !modeSearchSubmitted) || - (searchMode === "prescribing" && activeModeResultKind === "documents" && !modeSearchSubmitted) || - (activeModeResultKind === "differentials" && !modeSearchSubmitted)))); + // Prescribing home unmounts as soon as the query is non-empty, so keep + // the hero/phone-composer slot only while MedicationHome actually mounts. + (searchMode === "prescribing" && + activeModeResultKind === "documents" && + !modeSearchSubmitted && + !query.trim()) || + // DifferentialsHome leaves ModeHomeTemplate when a draft query coincides + // with stale evidence matches — keep the hero slot only while home mounts. + (activeModeResultKind === "differentials" && + !modeSearchSubmitted && + !(query.trim() && documentMatches.length > 0))))); const desktopHomeComposerSlotId = showDesktopHomeComposer ? modeHomeDesktopComposerSlotId : undefined; // Favourites and Tools are content-rich hubs: they share the centred hero but // stay top-aligned so their lists start in a stable position. @@ -3076,6 +3085,7 @@ export function ClinicalDashboard({ ((searchMode === "services" || searchMode === "forms") && !modeSearchSubmitted && !query.trim() && !loading); const differentialsCompareAddonActive = searchMode === "differentials" && modeSearchSubmitted && Boolean(query.trim()); + const heroOwnsPhoneComposer = Boolean(desktopHomeComposerSlotId); // Hidden dock pad must stay at 0rem — Safari toolbar safe-area recreates a blank band. const mobileComposerReserve = resolveMobileComposerReserve( bottomComposerHidden, @@ -3083,6 +3093,7 @@ export function ClinicalDashboard({ searchMode, hasAnswerFollowUps: answerFollowUpSuggestions.length > 0, differentialsCompareAddonActive, + heroOwnsPhoneComposer, }), ); const renderDegradedNotice = () => ( @@ -3362,10 +3373,9 @@ export function ClinicalDashboard({ differentialsCompareAddonActive ? differentialsMobileCompareAddonSlotId : undefined } desktopHomeComposerSlotId={desktopHomeComposerSlotId} - // Only the answer home ("How can I help?") keeps the in-flow hero - // pill + privacy notice on phones; every other mode home docks the - // compact pill to the bottom edge below sm. - heroComposerBreakpoint={showAnswerHome ? "all" : "sm-up"} + // Mode homes keep the composer in the centred hero at every breakpoint, + // sharing the phone/tablet structure instead of switching to a bottom dock. + heroComposerBreakpoint={heroOwnsPhoneComposer ? "all" : "sm-up"} // Answer view: the header overlays the scrolling
at every width // (main reserves matching top padding) so content frosts under the // glass bar, and it slides away/returns with scroll direction. Other diff --git a/src/components/clinical-dashboard/mobile-composer-reserve.ts b/src/components/clinical-dashboard/mobile-composer-reserve.ts index f0271834b..899f2f50b 100644 --- a/src/components/clinical-dashboard/mobile-composer-reserve.ts +++ b/src/components/clinical-dashboard/mobile-composer-reserve.ts @@ -58,7 +58,15 @@ export function resolveDashboardVisibleMobileComposerReserve(input: { searchMode: string; hasAnswerFollowUps: boolean; differentialsCompareAddonActive: boolean; + /** Hero owns the phone composer (no fixed bottom dock) — match shell idle pad. */ + heroOwnsPhoneComposer?: boolean; }): string { + // Mode homes / answer home keep the in-flow hero pill on phones, so there is + // no floating dock to clear — only the idle content pad (same as standalone + // shell mode homes). Using the dock reserve here opens a blank bottom band. + if (input.heroOwnsPhoneComposer) { + return mobileComposerIdleReserve; + } if (input.searchMode === "answer") { return input.hasAnswerFollowUps ? mobileComposerVisibleReserve.dashboardAnswerWithFollowUps diff --git a/src/components/mode-home-template.tsx b/src/components/mode-home-template.tsx index b0e77b08d..76fa14b00 100644 --- a/src/components/mode-home-template.tsx +++ b/src/components/mode-home-template.tsx @@ -261,7 +261,7 @@ export function ModeHomeTemplate({ {actions?.length ? (
{actions.map((action, index) => { const ActionIcon = action.icon; @@ -285,8 +285,10 @@ export function ModeHomeTemplate({ ); const actionClassName = cn( - "mode-home-action group grid min-h-[4.4rem] w-full grid-cols-[2.5rem_minmax(0,1fr)_1.25rem] items-center gap-3 bg-[color:var(--surface)] px-4 py-3 text-left transition hover:bg-[color:var(--surface-subtle)] focus-visible:relative focus-visible:z-10 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-[color:var(--focus)] disabled:cursor-wait disabled:opacity-60 sm:min-h-[4.75rem] sm:grid-cols-[2.75rem_minmax(0,1fr)_1rem] sm:gap-3 sm:rounded-lg sm:border sm:border-[color:var(--border)] sm:px-4 sm:py-3.5 sm:shadow-[var(--shadow-card)] lg:min-h-[4.75rem] lg:px-5", - index > 0 && "border-t border-[color:var(--border)] sm:border-t-[color:var(--border)]", + "mode-home-action group grid min-h-[4.4rem] w-full grid-cols-[2.5rem_minmax(0,1fr)_1.25rem] items-center gap-3 bg-[color:var(--surface)] px-4 py-3 text-left transition hover:bg-[color:var(--surface-subtle)] focus-visible:relative focus-visible:z-10 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-[color:var(--focus)] disabled:cursor-wait disabled:opacity-60 sm:min-h-[4.4rem] sm:grid-cols-[2.5rem_minmax(0,1fr)_1.25rem] sm:px-4 sm:py-3 lg:min-h-[4.75rem] lg:grid-cols-[2.75rem_minmax(0,1fr)_1rem] lg:gap-3 lg:rounded-lg lg:border lg:border-[color:var(--border)] lg:px-5 lg:py-3.5 lg:shadow-[var(--shadow-card)]", + // Phone stack separator only — at lg each action is its own card + // with `lg:border`, so do not zero the card's top edge. + index > 0 && "max-lg:border-t max-lg:border-[color:var(--border)]", ); if (action.href) { diff --git a/tests/mobile-composer-reserve.test.ts b/tests/mobile-composer-reserve.test.ts index 960d95a88..b7c6f5874 100644 --- a/tests/mobile-composer-reserve.test.ts +++ b/tests/mobile-composer-reserve.test.ts @@ -1,3 +1,6 @@ +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; + import { describe, expect, it } from "vitest"; import { @@ -12,6 +15,10 @@ import { resolveShellVisibleMobileComposerReserve, } from "@/components/clinical-dashboard/mobile-composer-reserve"; +function source(relativePath: string): string { + return readFileSync(resolve(process.cwd(), relativePath), "utf8"); +} + describe("mobile composer reserve contract", () => { it("collapses to zero hidden pad without Safari toolbar safe-area", () => { expect(mobileComposerHiddenReserve).toBe("0rem"); @@ -54,7 +61,7 @@ describe("mobile composer reserve contract", () => { ).toBe(mobileComposerIdleReserve); }); - it("uses the compact dock reserve for every non-answer dashboard dock (mode homes included)", () => { + it("uses the compact dock reserve for non-answer dashboard docks when the hero does not own phones", () => { for (const searchMode of ["documents", "services", "forms", "tools", "favourites"]) { expect( resolveDashboardVisibleMobileComposerReserve({ @@ -66,6 +73,66 @@ describe("mobile composer reserve contract", () => { } }); + it("keeps only the idle content pad when the dashboard hero owns the phone composer", () => { + expect( + resolveDashboardVisibleMobileComposerReserve({ + searchMode: "documents", + hasAnswerFollowUps: false, + differentialsCompareAddonActive: false, + heroOwnsPhoneComposer: true, + }), + ).toBe(mobileComposerIdleReserve); + expect( + resolveDashboardVisibleMobileComposerReserve({ + searchMode: "tools", + hasAnswerFollowUps: false, + differentialsCompareAddonActive: false, + heroOwnsPhoneComposer: true, + }), + ).toBe(mobileComposerIdleReserve); + expect( + resolveDashboardVisibleMobileComposerReserve({ + searchMode: "answer", + hasAnswerFollowUps: false, + differentialsCompareAddonActive: false, + heroOwnsPhoneComposer: true, + }), + ).toBe(mobileComposerIdleReserve); + }); + + it("derives hero phone ownership from the mounted hero slot, not answer-home alone", () => { + // Answer-home + !canRunSearch keeps showAnswerHome true while the hero slot + // is unset (showDesktopHomeComposer requires !error). Ownership must follow + // the slot so the dock reserve stays and the fixed composer cannot cover the + // setup/error message. + const dashboard = source("src/components/ClinicalDashboard.tsx"); + const header = source("src/components/clinical-dashboard/master-search-header.tsx"); + expect(dashboard).toContain('(activeModeResultKind === "favourites" && favouritesAccessible)'); + expect(dashboard).toContain("const heroOwnsPhoneComposer = Boolean(desktopHomeComposerSlotId);"); + expect(dashboard).not.toContain("const heroOwnsPhoneComposer = showDesktopHomeComposer || showAnswerHome;"); + // Prescribing leaves MedicationHome as soon as the draft query is non-empty; + // keep the hero slot (and idle phone reserve) only while that home mounts. + expect(dashboard).toMatch( + /searchMode === "prescribing" &&\s*activeModeResultKind === "documents" &&\s*!modeSearchSubmitted &&\s*!query\.trim\(\)/, + ); + // DifferentialsHome shows results (no mode-home slot) when a draft query + // coincides with stale evidence matches after clearing a submitted search. + expect(dashboard).toMatch( + /activeModeResultKind === "differentials" &&\s*!modeSearchSubmitted &&\s*!\(query\.trim\(\) && documentMatches\.length > 0\)/, + ); + expect(header).toContain( + 'const heroComposerOwnsPhones = Boolean(desktopHomeComposerSlotId) && heroComposerBreakpoint === "all";', + ); + expect( + resolveDashboardVisibleMobileComposerReserve({ + searchMode: "answer", + hasAnswerFollowUps: false, + differentialsCompareAddonActive: false, + heroOwnsPhoneComposer: false, + }), + ).toBe(mobileComposerVisibleReserve.dashboardAnswer); + }); + it("keeps the answer dock reserve compact, growing only for the follow-up chip row", () => { expect( resolveDashboardVisibleMobileComposerReserve({ diff --git a/tests/therapy-compass-responsive-contract.test.ts b/tests/therapy-compass-responsive-contract.test.ts index db14838d3..79216871b 100644 --- a/tests/therapy-compass-responsive-contract.test.ts +++ b/tests/therapy-compass-responsive-contract.test.ts @@ -61,7 +61,7 @@ describe("Therapy Compass responsive contract", () => { expect(responsiveStackCount(therapyCardSource)).toBeGreaterThanOrEqual(2); expect(homeSource).toContain("ModeHomeMain"); expect(homeSource).toContain("ModeHomeTemplate"); - expect(modeHomeTemplateSource).toContain("sm:grid-cols-[repeat(auto-fit,minmax(15rem,1fr))]"); + expect(modeHomeTemplateSource).toContain("lg:grid-cols-[repeat(auto-fit,minmax(15rem,1fr))]"); expect(modeHomeTemplateSource).toContain("sm:flex-wrap"); expect(homeSource).toContain("desktopComposerSlotId={modeHomeDesktopComposerSlotId}"); expect(homeSource).toContain("ModeHomeVerificationFooter"); diff --git a/tests/ui-smoke.spec.ts b/tests/ui-smoke.spec.ts index 0566ed5df..d4b8d0832 100644 --- a/tests/ui-smoke.spec.ts +++ b/tests/ui-smoke.spec.ts @@ -2965,9 +2965,11 @@ test.describe("Clinical KB UI smoke coverage", () => { expect(startHereBox).not.toBeNull(); expect(documentsHeadingBox).not.toBeNull(); expect((documentsHeadingBox?.y ?? 0) + (documentsHeadingBox?.height ?? 0)).toBeLessThan(searchInputBox?.y ?? 0); - // Phones dock the compact composer at the bottom edge, below the hero content. - expect(searchInputBox?.y ?? 0).toBeGreaterThan(startHereBox?.y ?? 0); - await expect(page.locator('form.answer-footer-search-dock[data-footer-variant="compact"]')).toHaveCount(1); + // Phones keep the compact composer in the mode-home hero (above Start here), + // matching every other mode home — no fixed bottom dock on the empty home. + expect(searchInputBox?.y ?? 0).toBeLessThan(startHereBox?.y ?? 0); + await expect(page.locator('form.answer-footer-search-dock[data-footer-variant="compact"]')).toHaveCount(0); + await expect(page.locator(".mode-home-composer-slot").getByTestId("global-search-input")).toHaveCount(1); const recentDocumentsButton = page.getByRole("button", { name: /Recent documents/i }).first(); const browseLibraryButton = page.getByRole("button", { name: /Browse library/i }).first(); const sourcePdfButton = page.getByRole("button", { name: /Open a source PDF/i }).first(); diff --git a/tests/ui-tools.spec.ts b/tests/ui-tools.spec.ts index 2ed0fc9bc..58bf44b7e 100644 --- a/tests/ui-tools.spec.ts +++ b/tests/ui-tools.spec.ts @@ -386,8 +386,9 @@ test.describe("Clinical KB tools launcher", () => { await expect(page.getByLabel("Mode Tools")).toBeVisible(); await expect(visibleGlobalSearchInput(page)).toHaveCount(1); if (viewport.name === "mobile") { - // Phones dock the compact shared search at the bottom edge. - await expect(page.locator("form.answer-footer-search-dock").getByTestId("global-search-input")).toBeVisible(); + // Phones keep the compact shared search in the tools-home hero slot. + await expect(page.getByTestId("tools-home").getByTestId("global-search-input")).toBeVisible(); + await expect(page.locator("form.answer-footer-search-dock")).toHaveCount(0); } else { await expect(page.getByTestId("tools-home").getByTestId("global-search-input")).toBeVisible(); }