diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 3dfa10c3b9..114f425ae7 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -700,10 +700,20 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-08-07 | cursor/viewer-phase0-gesture-a11y-1db8 (PR #1660) | 097dfd245f798f8105eeb6c1cf4fc077f969496f | prlanded | MERGED; squash tip empty vs branch tip 810cfc9b4a1c476a0dcc995bffb00d7329a85686; Phase 0 gesture INP, preview a11y, image decode | content tree empty vs squash; no provider-backed checks run | | 2026-08-07 | claude/pr-handoff-loop-prevention-54y5zr (PR #1670) | dfe2946110e0ff93bd4acc571ae79c26b79a7a85 | PR #1670 heavy review-and-fix | synced origin/main (behind-but-clean DIRTY cleared); fixed CodeRabbit checks-cell to name exact #1649 gates + incomplete verify:pr-local/ui + no provider checks; Bugbot none; no P0/P1; #258/#ledger delta accurate; merge-tree clean; threads cleared | verify:cheap 519 files/5493 passed; verify:pr-local docs scope (format+docs+ledger+outstanding-issues); check:branch-review-ledger; check:outstanding-issues; no provider gates | | 2026-08-07 | claude/handover-review-nlhuln | 978623337c12dc1721fe5236eadbf9a5ad929f03 | mode nav remaining modes: factsheets adoption (PR #1674) | Adopted the shared ModeNav for factsheets (Topics + Search); replaced the action-only entry, added the activeId branch, q/category/run carry, BookOpenText icon; three pinned adopted-mode lists updated together; record-route protection pinned at render now the item-count protection has expired | lint clean; typecheck clean; test 518/519 files (pr-handoff-stop failure confirmed pre-existing via stashed re-run); focused 5 files 95 tests; ui-mode-nav-density 55 passed incl 7 new factsheets rows; two mutation checks confirmed red; format committed; verify:pr-local blocked at check:installed-lock-parity (playwright 1.62.0 vs 1.62.1) | +| 2026-08-07 | cursor/privacy-live-signal-variants-bc81 (PR #1676) | f63eba10f5e9b6db047302c34412ea5261cd410b | PR #1676 unblock | before: PR policy fail (missing Clinical Governance Preflight; privacy* mockup paths trip clinicalRisk), behind-but-clean then main advanced; after: PR body preflight completed (policy green), tip synced to main via merge f63eba10 (duplicate local merge discarded), merge-tree clean, 0 unresolved threads, required CI in progress on synced tip; no code fix needed | local evaluatePullRequestPolicy ok after body; PR policy run 31172248093/31172327100 success; merge-tree clean; no provider-backed checks run | | 2026-08-07 | claude/handover-review-nlhuln | 4ff613c10fbf734b1e740a31611296c17c791ec7 | mode nav remaining modes: vestigial strip removal (PR #1679) | Removed the single-button action strip from answer/documents/services/forms/favourites/prescribing/tools; deleted the registry index-0 fallback (TS2493-forced) and the dead documents clause; stripped modeItems/onSearch/modeAriaLabel/stickyTop from PageSecondaryNavigation, keeping the empty-registry return below the information-section branch; kept the action kind with a no-live-consumer note. Completes the 13-mode navigation rollout. | lint exit 0; typecheck clean; focused 5 files 97 tests; test 518/519 files (pr-handoff-stop re-confirmed pre-existing on this base via stashed re-run); ui-mode-nav-density + ui-accessibility 71 passed (landmark scan green); branch-order guard mutation-checked (hoisting it fails 2 tests); format committed; verify:pr-local blocked at check:installed-lock-parity (playwright 1.62.0 vs 1.62.1) | +| 2026-08-07 | cursor/pr-1676-unblock-ledger-ef51 (PR #1677) | dec8f7489b4e5492e924af30dec85859932af3bb | review-and-fix | synced origin/main (behind-but-clean DIRTY; merge-tree clean); Bugbot mid-table finding dispositioned (row was tip-append before #1679; post-merge order correct; ledger guard passed); no P0/P1; 0 threads; no code fix; merge left to user | check:branch-review-ledger pass; ledger:dedupe none; merge-tree clean vs origin/main; format unchanged; no provider gates | +| 2026-08-07 | cursor/pr-1676-unblock-ledger-ef51 (PR #1677) | 574702a681cbb4d455151da023428d16c22fb460 | review-and-fix | late-synced origin/main after CI green (brought #1678 cn/tailwind-merge; remote merge 574702a6); prior sync cleared DIRTY; Bugbot mid-table finding dispositioned (tip-append before #1679; post-merge order correct); no P0/P1; 0 threads; no code fix; merge left to user | check:branch-review-ledger pass; ledger:dedupe none; merge-tree clean; prior tip required CI green; format unchanged; no provider gates | | 2026-08-05 | HEAD | 2a8881afad230880166de60a533e17588d9920ec | seven-report repo-wide audit | P2 confirmed: live drift and live-only migration; mobile CLS; ACL runner; OOXML declared-size robustness; assertion false positives. Numerous report claims stale, narrowed, or unsafe. | verify:cheap (5088 pass, 3 skip, 1 timeout); focused retry 21/21 pass; build pass; bundle budget pass; offline RAG 574/574; assertions 98%; live drift 34; browser CLS 0.228/0.218 | | 2026-08-07 | codex/consolidated-ledger-updates (PR #1683) | 413e679bb92cb19717d6d8301764df44694eb73e | review-and-fix PR #1683 | synced origin/main (behind-but-clean DIRTY cleared); restored main ledger order + sole seven-report row; Bugbot none; no P0/P1; merge-tree clean | verify:pr-local docs scope PASS (format:changed Prettier; check:branch-review-ledger 648; docs links 1650; outstanding-issues 258); merge-tree clean | +| 2026-08-07 | cursor/pr-1676-unblock-ledger-ef51 (PR #1677) | fddf495b5176f570a5238b5c17326f64f333ecff | review-and-fix | synced origin/main (behind-but-clean DIRTY; merge-tree clean; main advanced with #1684/#1666); 0 review threads; no P0/P1; no code fix; merge left to user | merge-tree clean vs origin/main; ledger:dedupe none; prior tip required CI green except stale PR mergeability; format unchanged; no provider gates | | 2026-08-07 | cursor/inpage-nav-default-235a | c6d72e406c03e205bac86d23e84813c41332c205 | docs: default in-page nav DocumentViewer chrome + PhoneHeaderCollapsePortal | docs-only; verify:pr-local passed (low-risk docs scope) | verify:pr-local --files AGENTS.md,docs/search-chrome-behaviour.md | +| 2026-08-07 | claude/issues-256-section-nav-clean | 169323053db5c572d183d59c113ecd0c76e7aca5 | issues #256: forms section anchors + differentials presentation set (PR #1697) | Wired all six formSections anchors in form-detail-page.tsx (four direct ids, two breakpoint pairs via existing mobile wrappers and single-child desktop wrappers, no component signature change); deleted differentialPresentationSections and declared /differentials/presentations/ locally-owned instead, since three of its six sections declared a -mobile targetId ReviewPanels can never satisfy and the page owns MobileTabs below xl plus the xl review sidebar. Added a registered browser spec because source-text and jsdom guards both structurally cannot see breakpoint-variant resolution. | lint exit 0; typecheck clean; test 519/520 files (pr-handoff-stop confirmed pre-existing via stashed re-run); check:gate-manifest and check:ci-scope pass with the new spec in both playwright allowlists; ui-forms-section-nav + ui-accessibility 18 passed incl real-record nav with 6 links and exactly one variant per pair visible at 390px and 1280px; binding guard mutation-checked red on one removed id; browser spec observed failing when nav genuinely absent; format clean. Environment: npm ci blocked (main lockfile needs Node >=24.15, container has 24.13), tailwind-merge@3.6.0 materialised from tarball only | +| 2026-08-07 | claude/handover-review-nlhuln | de8b74e2fb94d1ec9b1982c15a2dea43421c3eee | outstanding-issues ledger capture after the mode-nav rollout (PR #1685) | Confirmed #256's two remaining suspected section sets are dead (/forms/ and /differentials/presentations/ draw no section nav; form-decision-context-mobile is a testId not an id; ruled out sectionId indirection in both files). Added #261 (delete-or-keep the consumer-less action kind) and #262 (addon-slot single-owner rule held by two lists agreeing by coincidence). #207/#226/#231 reviewed and deliberately left untouched as existing P1 rows. | check:outstanding-issues passed (260 rows, 119 open, unique ids, no ids deleted from base 1ff9ed206456); prettier --check clean on the changed file; rows written via scripts/outstanding-issues.mjs, never hand-edited; no code gates run - docs-only diff | +| 2026-08-07 | claude/handover-review-nlhuln | de8b74e2fb94d1ec9b1982c15a2dea43421c3eee | outstanding-issues ledger capture after the mode-nav rollout (PR #1685) (supersedes 2026-08-07) | Confirmed #256's two remaining suspected section sets are dead (/forms/ and /differentials/presentations/ draw no section nav; form-decision-context-mobile is a testId not an id; ruled out sectionId indirection in both files). Added #271 (delete-or-keep the consumer-less action kind) and #272 (addon-slot single-owner rule held by two lists agreeing by coincidence) — renumbered from this PR's original #261/#262 because main claimed #261-#270 via PR #1678 design-system tracks in the interim. #207/#226/#231 reviewed and deliberately left untouched as existing P1 rows. | check:outstanding-issues passed (270 rows, 129 open, 141 archived, unique ids, next-id=273 above the highest, no ids deleted from base d32dd549a3dd); prettier --check clean on the changed file; rows written via scripts/outstanding-issues.mjs, never hand-edited; no code gates run - docs-only diff | +| 2026-08-07 | cursor/document-citation-landing-7bc3 | 82378a2bb4b875f1b610ef60c0ec3c94ee461f10 | document-viewer citation landing | ship: PDF-first citation landing; excerpt chip; indexed text collapsed until inspect/search; phone overview condensed; rail pin removed | unit 5539 pass; playwright critical citation+mobile PDF-first 2 pass; browser QA desktop/phone pass; typecheck; lint; build ALLOW_BUILD_WITH_DEV_SERVER=1; eval:rag:offline 36 golden; verify:pr-local stages green (first run flaked design-system-adoption timeout, retry green) | +| 2026-08-07 | claude/search-bar-mobile-layout-buu0io | 9d64388c0ce530d0c20bb7efe8ffb32cd928319c | phone results-filter idiom: 7 modes off MobileResultFilterControl onto ResultFilterTrigger + ResultFilterSheet; band, docs, tests | changes-shipped | typecheck; lint; test 5538 passed (1 pre-existing pr-handoff-stop failure, baselined on unmodified tree); build; check:rag:fixtures; check:bundle-budget +6.3% within tolerance; targeted Playwright: ui-accessibility 16, ui-specifiers+ui-formulation 12, ui-tools 5, ui-smoke 2, ui-stress 3 | +| 2026-08-08 | claude/differentials-inpage-navigation-h2u8fq (PR #1715) | c67c4927d8a088be267b8bd280a06f300aad60bc | differentials detail: adopt PR #1688 default in-page navigation template | Header rebuilt to the four template slots (back / title + active-section chevron sheet / ellipsis actions / weighted segment track); new detail-section-index.ts shapes the five tabs as DocumentSections reusing DocumentSectionTrack + DocumentSectionList; labelled strip gated to sm+; max-sm:static -> relative so the absolutely-positioned track keeps a positioned ancestor; tab panel renamed via aria-label since the sm-hidden strip cannot label it; sheets kept as siblings of PhoneHeaderCollapsePortal; no scroll spy (discrete panels). Adoption manifest regenerated - the dropped Tabs/ui-tools association was a coincidental capital-T comment match, not lost coverage. | typecheck exit 0; lint clean --max-warnings 0; verify:pr-local 5557 passed, stops only at pre-existing pr-handoff-stop (confirmed by stashed re-run on clean base); verify:phone-chrome lock-parity+runtime PASS, contracts 119 passed, changed-browser ui-tools passed, focused-browser 7 passed; new differential-section-nav.dom 9 passed; 40 passed re-run post-format; live browser 0 h-overflow at 320/390/768, collapse matches DocumentViewer (data-scroll-hidden=true, stack bottom 0) | | 2026-08-07 | cursor/phone-mode-dense-production-05c0 (PR #1648) | 4e0cca2ccbc19ed676765b029642da0afea6215a | Run PR sweep: CI fix + threads + drift | before: behind 17, checks green, 0 threads → after: merged origin/main (conflicts: none), CI re-running; 0 threads | merge-tree clean; git merge origin/main + push; no provider-backed checks run | | 2026-08-07 | cursor/tools-search-mockups-72e1 (PR #1653) | a7fbc26a917b347d90c6bab1e6c1b2ede6422263 | Run PR sweep: CI fix + threads + drift | before: behind 17, checks green, 0 threads → after: merged origin/main (conflicts: none), CI re-running; 0 threads | merge-tree clean; git merge origin/main + push; no provider-backed checks run | | 2026-08-07 | cursor/ship-first-redesign-mockups-2398 (PR #1654) | 9d9eb0be47073a7f051a5885359b82f2ff978a85 | Run PR sweep: CI fix + threads + drift | before: behind 17, checks green, 0 threads → after: merged origin/main (conflicts: none), CI re-running; 0 threads | merge-tree clean; git merge origin/main + push; no provider-backed checks run | @@ -717,14 +727,6 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-08-07 | cursor/viewer-phase2a-frame-controls-1db8 (PR #1687) | 5c10730be1641a386ee8c8476778933588a822fc | Run PR sweep: CI fix + threads + drift | before: up to date with main, Static PR fail (format:changed pdf-canvas-viewer), 1 outdated CodeRabbit thread (ref sync) already fixed on head → after: prettier format fix pushed; thread left open (no review-write API as cursor[bot]); CI re-running | format:changed fail→prettier --write pdf-canvas-viewer.tsx; format:changed PASS locally; no provider-backed checks run | | 2026-08-07 | claude/search-bar-mobile-layout-buu0io (PR #1689) | a152ffd89c962e3589509c0c3740dc429264063a | Run PR sweep: CI fix + threads + drift | before: behind 1, required CI green (advisory lighthouse fail ignored), 1 CodeRabbit lighthouse baseline thread (human disagreement in progress) → after: waited for CI settle, disabled automerge, merged origin/main, pushed, re-enabled automerge; thread left open for human | settled CI then merge+push; no provider-backed checks run; advisory lighthouse not chased | | 2026-08-07 | claude/issues-256-section-nav-clean (PR #1697) | 2b99db1f00c42fd7c11c4bf8acbad904ef5003e1 | Run PR sweep: CI fix + threads + drift | before: DIRTY/PR mergeability fail, behind 3, merge-tree CLEAN, 0 threads → after: merged origin/main (conflicts: none), CI re-running; 0 threads | merge-tree clean; git merge origin/main + push; no provider-backed checks run | -| 2026-08-07 | cursor/privacy-live-signal-variants-bc81 (PR #1676) | f63eba10f5e9b6db047302c34412ea5261cd410b | PR #1676 unblock | before: PR policy fail (missing Clinical Governance Preflight; privacy* mockup paths trip clinicalRisk), behind-but-clean then main advanced; after: PR body preflight completed (policy green), tip synced to main via merge f63eba10 (duplicate local merge discarded), merge-tree clean, 0 unresolved threads, required CI in progress on synced tip; no code fix needed | local evaluatePullRequestPolicy ok after body; PR policy run 31172248093/31172327100 success; merge-tree clean; no provider-backed checks run | -| 2026-08-07 | cursor/pr-1676-unblock-ledger-ef51 (PR #1677) | dec8f7489b4e5492e924af30dec85859932af3bb | review-and-fix | synced origin/main (behind-but-clean DIRTY; merge-tree clean); Bugbot mid-table finding dispositioned (row was tip-append before #1679; post-merge order correct; ledger guard passed); no P0/P1; 0 threads; no code fix; merge left to user | check:branch-review-ledger pass; ledger:dedupe none; merge-tree clean vs origin/main; format unchanged; no provider gates | -| 2026-08-07 | cursor/pr-1676-unblock-ledger-ef51 (PR #1677) | 574702a681cbb4d455151da023428d16c22fb460 | review-and-fix | late-synced origin/main after CI green (brought #1678 cn/tailwind-merge; remote merge 574702a6); prior sync cleared DIRTY; Bugbot mid-table finding dispositioned (tip-append before #1679; post-merge order correct); no P0/P1; 0 threads; no code fix; merge left to user | check:branch-review-ledger pass; ledger:dedupe none; merge-tree clean; prior tip required CI green; format unchanged; no provider gates | -| 2026-08-07 | cursor/pr-1676-unblock-ledger-ef51 (PR #1677) | fddf495b5176f570a5238b5c17326f64f333ecff | review-and-fix | synced origin/main (behind-but-clean DIRTY; merge-tree clean; main advanced with #1684/#1666); 0 review threads; no P0/P1; no code fix; merge left to user | merge-tree clean vs origin/main; ledger:dedupe none; prior tip required CI green except stale PR mergeability; format unchanged; no provider gates | -| 2026-08-07 | claude/issues-256-section-nav-clean | 169323053db5c572d183d59c113ecd0c76e7aca5 | issues #256: forms section anchors + differentials presentation set (PR #1697) | Wired all six formSections anchors in form-detail-page.tsx (four direct ids, two breakpoint pairs via existing mobile wrappers and single-child desktop wrappers, no component signature change); deleted differentialPresentationSections and declared /differentials/presentations/ locally-owned instead, since three of its six sections declared a -mobile targetId ReviewPanels can never satisfy and the page owns MobileTabs below xl plus the xl review sidebar. Added a registered browser spec because source-text and jsdom guards both structurally cannot see breakpoint-variant resolution. | lint exit 0; typecheck clean; test 519/520 files (pr-handoff-stop confirmed pre-existing via stashed re-run); check:gate-manifest and check:ci-scope pass with the new spec in both playwright allowlists; ui-forms-section-nav + ui-accessibility 18 passed incl real-record nav with 6 links and exactly one variant per pair visible at 390px and 1280px; binding guard mutation-checked red on one removed id; browser spec observed failing when nav genuinely absent; format clean. Environment: npm ci blocked (main lockfile needs Node >=24.15, container has 24.13), tailwind-merge@3.6.0 materialised from tarball only | -| 2026-08-07 | claude/handover-review-nlhuln | de8b74e2fb94d1ec9b1982c15a2dea43421c3eee | outstanding-issues ledger capture after the mode-nav rollout (PR #1685) | Confirmed #256's two remaining suspected section sets are dead (/forms/ and /differentials/presentations/ draw no section nav; form-decision-context-mobile is a testId not an id; ruled out sectionId indirection in both files). Added #261 (delete-or-keep the consumer-less action kind) and #262 (addon-slot single-owner rule held by two lists agreeing by coincidence). #207/#226/#231 reviewed and deliberately left untouched as existing P1 rows. | check:outstanding-issues passed (260 rows, 119 open, unique ids, no ids deleted from base 1ff9ed206456); prettier --check clean on the changed file; rows written via scripts/outstanding-issues.mjs, never hand-edited; no code gates run - docs-only diff | -| 2026-08-07 | claude/handover-review-nlhuln | de8b74e2fb94d1ec9b1982c15a2dea43421c3eee | outstanding-issues ledger capture after the mode-nav rollout (PR #1685) (supersedes 2026-08-07) | Confirmed #256's two remaining suspected section sets are dead (/forms/ and /differentials/presentations/ draw no section nav; form-decision-context-mobile is a testId not an id; ruled out sectionId indirection in both files). Added #271 (delete-or-keep the consumer-less action kind) and #272 (addon-slot single-owner rule held by two lists agreeing by coincidence) — renumbered from this PR's original #261/#262 because main claimed #261-#270 via PR #1678 design-system tracks in the interim. #207/#226/#231 reviewed and deliberately left untouched as existing P1 rows. | check:outstanding-issues passed (270 rows, 129 open, 141 archived, unique ids, next-id=273 above the highest, no ids deleted from base d32dd549a3dd); prettier --check clean on the changed file; rows written via scripts/outstanding-issues.mjs, never hand-edited; no code gates run - docs-only diff | -| 2026-08-07 | claude/search-bar-mobile-layout-buu0io | 9d64388c0ce530d0c20bb7efe8ffb32cd928319c | phone results-filter idiom: 7 modes off MobileResultFilterControl onto ResultFilterTrigger + ResultFilterSheet; band, docs, tests | changes-shipped | typecheck; lint; test 5538 passed (1 pre-existing pr-handoff-stop failure, baselined on unmodified tree); build; check:rag:fixtures; check:bundle-budget +6.3% within tolerance; targeted Playwright: ui-accessibility 16, ui-specifiers+ui-formulation 12, ui-tools 5, ui-smoke 2, ui-stress 3 | | 2026-08-07 | cursor/remove-specifiers-back-arrow-f1c4 | 095791235d58a6309b21b139911a1aad9fe9086b | specifiers-search-results-breadcrumb | removed lone ← Specifiers crumb from search results; deep pages unchanged | format,typecheck,lint,ensure+phone-spot-check | | 2026-08-07 | cursor/phone-mode-dense-production-05c0 (PR #1648) | 1091b17933beba655dac3e37f0e5c1bc4cdfb679 | Run PR sweep: CI fix + threads + drift | No action needed: PR required green, no unresolved review threads, not behind main. Only advisory Lighthouse job failing (never chased). | get_check_runs (PR required: success), get_review_comments (0 unresolved threads) | | 2026-08-07 | cursor/tools-search-mockups-72e1 (PR #1653) | 1df72ba6119226aee92b203db8188f58851a6d3c | Run PR sweep: CI fix + threads + drift | No action needed: PR required green, no unresolved review threads, not behind main. Only advisory Lighthouse job failing (never chased). | get_check_runs (PR required: success), get_review_comments (0 unresolved threads) | @@ -744,9 +746,8 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-08-08 | claude/ds-a4-component-defects | a029a543f744eb80e608ec482aacdbdc5f5599c2 | unblock PR #1712 | Merged origin/main (ef28960e) to clear dirty mergeable_state: real conflict in docs/branch-review-ledger.md auto-merged via merge=ledger driver. Prior tip 9ba483d3 was 1 behind main. Static PR and PR required failures were dirty-state blockers (GitHub could not build refs/pull/1712/merge). Proved post-merge: merge-tree clean, check:branch-review-ledger, check:design-system-contract. | merge-tree clean; ledger:dedupe; check:branch-review-ledger; check:design-system-contract | | 2026-08-08 | claude/ds-a4-component-defects | d3a697aa8784c9cbdecfba24402cf2269bfe15d4 | heavy review-and-fix PR #1712 | Lint blocker fixed (react-hooks/refs in Pagination); CodeRabbit threads dispositioned; synced main (4a9d81d3 Lighthouse pin); merge-tree clean; verify:cheap 5567 passed; verify:pr-local green; check:design-system-contract passed | lint; typecheck; prettier --check .; verify:cheap (5567 passed); verify:pr-local; check:design-system-contract; check:branch-review-ledger; vitest ui-v2-components.dom (75 passed) | | 2026-08-08 | cursor/run-pr-sweep-ledger-d56c (PR #1698) | ccf7284cbbe2315e5dc6a1bf126403a8a2205fa7 | Run PR sweep: CI fix + threads + drift | before: DIRTY/CONFLICTING, PR mergeability fail, behind 33, 0 threads, fake single-parent merge tip → after: real merge origin/main (conflicts resolved: docs/outstanding-issues.md + lighthouse-budget.json took main), merge-tree clean, unique diff ledger-only, 0 threads | check:outstanding-issues pass; check:branch-review-ledger pass; ledger:dedupe none; merge-tree clean; format; no provider-backed checks run | -| 2026-08-08 | claude/ds-doc-corrections | 534405600dca67317b4d60266cda03ec95f028e7 | M1 stranded doc corrections (docs/outstanding-issues.md #262/#266, docs/design-system/COMPONENTS.md TextField row + section 4) | authored and handed off as PR #1719; every inherited figure re-measured against origin/main rather than copied forward, and the stranded version's 'eight shadow tokens, focus 2' claim was found wrong — LEGACY_SHADOW_ALIAS matches seven tokens and has never included focus | check:outstanding-issues pass (274 rows, unique ids, no ids deleted from base); prettier --check . pass whole-tree; legacyShadowAliases re-measured 228 via the contract's own analyzers; docs-only diff so no unit/lint/typecheck/browser gate applies | -| 2026-08-08 | claude/differentials-inpage-navigation-h2u8fq (PR #1715) | c67c4927d8a088be267b8bd280a06f300aad60bc | differentials detail: adopt PR #1688 default in-page navigation template | Header rebuilt to the four template slots (back / title + active-section chevron sheet / ellipsis actions / weighted segment track); new detail-section-index.ts shapes the five tabs as DocumentSections reusing DocumentSectionTrack + DocumentSectionList; labelled strip gated to sm+; max-sm:static -> relative so the absolutely-positioned track keeps a positioned ancestor; tab panel renamed via aria-label since the sm-hidden strip cannot label it; sheets kept as siblings of PhoneHeaderCollapsePortal; no scroll spy (discrete panels). Adoption manifest regenerated - the dropped Tabs/ui-tools association was a coincidental capital-T comment match, not lost coverage. | typecheck exit 0; lint clean --max-warnings 0; verify:pr-local 5557 passed, stops only at pre-existing pr-handoff-stop (confirmed by stashed re-run on clean base); verify:phone-chrome lock-parity+runtime PASS, contracts 119 passed, changed-browser ui-tools passed, focused-browser 7 passed; new differential-section-nav.dom 9 passed; 40 passed re-run post-format; live browser 0 h-overflow at 320/390/768, collapse matches DocumentViewer (data-scroll-hidden=true, stack bottom 0) | | 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 | 534405600dca67317b4d60266cda03ec95f028e7 | M1 stranded doc corrections (docs/outstanding-issues.md #262/#266, docs/design-system/COMPONENTS.md TextField row + section 4) | authored and handed off as PR #1719; every inherited figure re-measured against origin/main rather than copied forward, and the stranded version's 'eight shadow tokens, focus 2' claim was found wrong — LEGACY_SHADOW_ALIAS matches seven tokens and has never included focus | check:outstanding-issues pass (274 rows, unique ids, no ids deleted from base); prettier --check . pass whole-tree; legacyShadowAliases re-measured 228 via the contract's own analyzers; docs-only diff so no unit/lint/typecheck/browser gate applies | | 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 | claude/ds-close-276 (PR #1724) | 75c89993f3ea23b70a250f605b21437b4ea9aac8 | PR #1724 review-and-fix | fixed Codex P2 wrong #118 Lighthouse cause (150 overwrite vs 151 pin); dispositioned CodeRabbit #276 archive claim as false (issues:done move); merge-tree clean; required CI was green on prior tip 8ae8c48f; no Bugbot findings | check:outstanding-issues pass; prettier --check docs/outstanding-issues.md pass; no provider-backed checks | diff --git a/src/components/DocumentViewer.tsx b/src/components/DocumentViewer.tsx index 6ae3d4d28b..3a88717d21 100644 --- a/src/components/DocumentViewer.tsx +++ b/src/components/DocumentViewer.tsx @@ -104,7 +104,7 @@ const secondaryButton = floatingControl; * * @param documentId - The identifier of the document to load. * @param initialPage - The page to display initially in the source preview. - * @param chunkId - An optional indexed passage to pin and scroll into view. + * @param chunkId - An optional indexed passage to pin as a cited excerpt above the PDF. * @returns The document viewer interface. */ export function DocumentViewer({ @@ -164,6 +164,18 @@ export function DocumentViewer({ const [mobileActionsOpen, setMobileActionsOpen] = useState(false); const [sectionSheetOpen, setSectionSheetOpen] = useState(false); const [compactView, setCompactView] = useDocumentViewDensity(); + // Explicit inspect intent keyed to the current document+citation. A stale + // reveal from a prior deep-link must not survive into the next landing. + const citationLandingKey = `${documentId}::${activeChunkId ?? ""}`; + const [inspectRevealKey, setInspectRevealKey] = useState(null); + const [prevCitationLandingKey, setPrevCitationLandingKey] = useState(citationLandingKey); + if (citationLandingKey !== prevCitationLandingKey) { + setPrevCitationLandingKey(citationLandingKey); + // Chunk/document identity changed: drop any prior inspect latch so a + // revisit to the same citation does not auto-reopen the indexed dump. + if (inspectRevealKey !== null) setInspectRevealKey(null); + } + const inspectIndexedText = inspectRevealKey === citationLandingKey; const [composerChromeFocused, setComposerChromeFocused] = useState(false); const [shellScrollContainer, setShellScrollContainer] = useState(null); useEffect(() => { @@ -871,10 +883,18 @@ export function DocumentViewer({ }, []); const jumpToSection = useCallback( (id: string) => { + // Condensed view keeps IndexedTextPanel React-controlled. A raw + // `details.open = true` from the section jump would lose on the next + // render unless we also raise the inspect reveal for this citation. + // Jumping anywhere else must lower it again — otherwise IndexedTextPanel's + // controlled `open` stays true and the exclusive accordion group (native + // `name="document-viewer-section"`) closes the section this jump targets + // right back on the next render. + setInspectRevealKey(id === "source-text" ? `${documentId}::${activeChunkId ?? ""}` : null); selectSection(id); jumpToDocumentSection(id); }, - [selectSection], + [activeChunkId, documentId, selectSection], ); const generatedSummaryText = summary ? cleanClinicalSummaryText(summary.answer) : ""; const generatedAnswerIsSummary = summaryQuery === documentSummaryQuestion; @@ -892,10 +912,19 @@ export function DocumentViewer({ : []; useEffect(() => { if (!activeChunkId || loadingDocument) return; - window.document - .querySelector(`[data-source-chunk-id="${CSS.escape(activeChunkId)}"]`) - ?.scrollIntoView({ block: "center", behavior: resolveScrollBehavior() }); - }, [activeChunkId, loadingDocument, chunks.length]); + // Citation landing: keep the PDF as the first reading surface. Do not dump + // the viewport into the indexed source-passages list. + globalThis.document.getElementById("pdf-preview-section")?.scrollIntoView({ + block: "start", + behavior: resolveScrollBehavior(), + }); + }, [activeChunkId, loadingDocument]); + const inspectIndexedTextSection = useCallback(() => { + setInspectRevealKey(`${documentId}::${activeChunkId ?? ""}`); + window.requestAnimationFrame(() => { + jumpToDocumentSection("source-text"); + }); + }, [activeChunkId, documentId]); const retryPreview = () => { setViewerError(null); setPreviewError(null); @@ -944,6 +973,7 @@ export function DocumentViewer({ }; const submitSourceSearch = () => { if (normalizedSourceSearch.length < 2) return; + setInspectRevealKey(`${documentId}::${activeChunkId ?? ""}`); globalThis.document.getElementById("source-text")?.scrollIntoView({ block: "start", behavior: resolveScrollBehavior(), @@ -1410,15 +1440,14 @@ export function DocumentViewer({ -
-
- -
+
+ = 2} />
@@ -1445,7 +1475,6 @@ export function DocumentViewer({ onCompactChange={setCompactView} indexWarnings={indexWarnings} effectiveLoadingDocument={effectiveLoadingDocument} - selectedChunk={selectedChunk} document={document} summaryBadges={summaryBadges} formattedStoredSummary={formattedStoredSummary} diff --git a/src/components/document-viewer/document-overview-landing.tsx b/src/components/document-viewer/document-overview-landing.tsx index c7fe235234..faa2b5ac3a 100644 --- a/src/components/document-viewer/document-overview-landing.tsx +++ b/src/components/document-viewer/document-overview-landing.tsx @@ -1,7 +1,7 @@ // Overview landing for the document viewer: the compact header, quick actions, // and high-yield clinical summary. Extracted from DocumentViewer.tsx (maturity // X3) as a pure move. -import { Download, Loader2, Sparkles, Target } from "lucide-react"; +import { Download, Loader2, MoreHorizontal, Sparkles, Target } from "lucide-react"; import { documentDisplayTitle, documentOrganizationProfile } from "@/components/DocumentOrganizationBadges"; import { DocumentClinicalSummary } from "@/components/document-viewer/document-clinical-summary"; import { formatDocumentLabelDisplay } from "@/lib/document-tags"; @@ -69,18 +69,18 @@ export function DocumentOverviewLanding({ return (
-
-
+
+

{documentTypeEyebrow(document)}

-

+

{documentDisplayTitle(document)}

-
+ {/* Phone: primary reading actions only. Download / scope stay behind More + so the first viewport reaches the PDF and clinical summary faster. */} +
{signedUrl ? ( Open PDF ) : ( + Open preview + + )} + + Answer from this + +
+
+ + +
+ + {downloading ? "Preparing" : "Download"} + + + Add to scope + +
+
+
+ {signedUrl ? ( + + Open PDF + + ) : ( + Open preview @@ -112,14 +169,14 @@ export function DocumentOverviewLanding({ onClick={onDownload} disabled={downloading} icon={downloading ? Loader2 : Download} - className={cn(secondaryButton, "w-full min-h-12 px-2 text-xs sm:text-sm")} + className={cn(secondaryButton, "w-full min-h-12 px-2 text-sm")} > {downloading ? "Preparing" : "Download"} Add to scope @@ -127,7 +184,7 @@ export function DocumentOverviewLanding({ onClick={onAskFromDocument} disabled={!canSummarizeDocument} icon={Sparkles} - className={cn(secondaryButton, "w-full min-h-12 whitespace-nowrap px-2 text-xs sm:text-sm")} + className={cn(secondaryButton, "w-full min-h-12 whitespace-nowrap px-2 text-sm")} > Answer from this diff --git a/src/components/document-viewer/document-rail-panels.tsx b/src/components/document-viewer/document-rail-panels.tsx index bdd4162200..00ce9919fd 100644 --- a/src/components/document-viewer/document-rail-panels.tsx +++ b/src/components/document-viewer/document-rail-panels.tsx @@ -11,11 +11,10 @@ import { DocumentImage, DocumentSectionSummary, FormattedHighYieldSummary, - PinnedSourceEvidence, TableReviewPanel, } from "@/components/document-viewer/source-panels"; import { DocumentImageFilmstrip } from "@/components/document-viewer/document-image-filmstrip"; -import type { ChunkRow, DocumentIndexHealth, ImageRow, TableFactRow } from "@/components/document-viewer/types"; +import type { DocumentIndexHealth, ImageRow, TableFactRow } from "@/components/document-viewer/types"; import type { DocumentSection } from "@/components/document-viewer/section-index"; import { BadgeCluster } from "@/components/clinical-dashboard/clinical-badge"; import { @@ -43,7 +42,6 @@ export function DocumentViewerRail({ onCompactChange, indexWarnings, effectiveLoadingDocument, - selectedChunk, document, summaryBadges, formattedStoredSummary, @@ -70,7 +68,6 @@ export function DocumentViewerRail({ onCompactChange: (compact: boolean) => void; indexWarnings: string[]; effectiveLoadingDocument: boolean; - selectedChunk: ChunkRow | undefined; document: ClinicalDocument | null; summaryBadges: DocumentSummaryBadge[]; formattedStoredSummary: FormattedDocumentSummary; @@ -121,15 +118,6 @@ export function DocumentViewerRail({ ) : null} -
- -
- {document ? (
void; }) { + // Citation landing stays a short chip by default. Full extraction dump lives + // behind "Inspect indexed text" so PDF remains the primary reading surface. const displayContent = chunk ? flowIndexedText(sourceTextForDocumentViewer(chunk.content)) : ""; - const previewLimit = compact ? 220 : 300; + const previewLimit = compact ? 160 : 300; const [expandedChunkId, setExpandedChunkId] = useState(null); const isLong = displayContent.length > previewLimit; const expanded = !compact || (chunk?.id ? expandedChunkId === chunk.id : false); const showingPreview = compact && isLong && !expanded; - const visibleContent = showingPreview ? `${displayContent.slice(0, previewLimit).trim()}...` : displayContent; + const visibleContent = showingPreview ? `${displayContent.slice(0, previewLimit).trimEnd()}…` : displayContent; const chunkMeta = chunk ? [`Page ${chunk.page_number ?? "n/a"}`, `chunk ${chunk.chunk_index}`].filter(Boolean).join(" · ") : ""; @@ -573,7 +578,10 @@ export function PinnedSourceEvidence({

- + {loading ? ( - + ) : chunk ? (

@@ -605,13 +613,18 @@ export function PinnedSourceEvidence({ {chunk.section_heading && (

{chunk.section_heading}

)} -
+
{visibleContent || "No displayable clinical text was available for this indexed passage."}
{compact && isLong ? ( + ) : null}
- {compact ? ( -

- Full indexed page text remains available in the source text section. -

- ) : null}
) : (

@@ -783,6 +801,7 @@ export const IndexedTextPanel = memo(function IndexedTextPanel({ selectedChunkId, onSearchChange, compact = false, + revealRequest = false, }: { loading: boolean; selectedPage: PageRow | undefined; @@ -796,6 +815,11 @@ export const IndexedTextPanel = memo(function IndexedTextPanel({ selectedChunkId?: string; onSearchChange: (value: string) => void; compact?: boolean; + /** + * Explicit user intent to open the panel (e.g. "Inspect indexed text"). + * Citation deep-links alone must not force-open this dump over the PDF. + */ + revealRequest?: boolean; }) { const normalizedSearch = search.trim().toLowerCase(); const searchEligible = normalizedSearch.length >= 2; @@ -854,19 +878,24 @@ export const IndexedTextPanel = memo(function IndexedTextPanel({ // disclosures can stay React-controlled — imperative `.open = true` alone was // lost across re-renders and left deep-linked hits collapsed in Production UI. const [manualClosedDriver, setManualClosedDriver] = useState(null); - const [compactOpen, setCompactOpen] = useState(Boolean(selectedChunkId)); - // Deep-linked chunks and in-document search must keep the panel revealed even - // when the exclusive accordion briefly closes it (section jumps / sibling - // opens). Derive force-open from props so compactOpen cannot latch closed. - const forceReveal = Boolean(selectedChunkId) || Boolean(normalizedSearch); + const [compactOpen, setCompactOpen] = useState(false); + // In-document search and an explicit "Inspect indexed text" action keep the + // panel revealed through exclusive-accordion closes. Citation deep-links alone + // must not force-open — that stole the first viewport from the PDF. + const forceReveal = Boolean(normalizedSearch) || revealRequest; const [prevForceReveal, setPrevForceReveal] = useState(forceReveal); if (forceReveal !== prevForceReveal) { setPrevForceReveal(forceReveal); - if (forceReveal) setCompactOpen(true); + // Rising edge: latch open so exclusive-accordion closes cannot collapse an + // active inspect/search reveal. Falling edge: drop the latch so jumping to + // PDF/overview (or clearing revealRequest on the same citation) restores + // PDF-first instead of leaving the dump controlled-open. + setCompactOpen(forceReveal); } if (previousAutoOpenDriverRef.current !== autoOpenDriver) { previousAutoOpenDriverRef.current = autoOpenDriver; if (manualClosedDriver !== null) setManualClosedDriver(null); + if (!forceReveal) setCompactOpen(false); } const autoOpenSuppressed = Boolean(autoOpenDriver) && manualClosedDriver === autoOpenDriver; @@ -874,11 +903,15 @@ export const IndexedTextPanel = memo(function IndexedTextPanel({ if (!autoOpenDriver || !autoOpenTargetId || autoOpenSuppressed) return; const targetDisclosure = document.getElementById(`${idPrefix}-${autoOpenTargetId}`); if (!(targetDisclosure instanceof HTMLDetailsElement)) return; - if (topLevelDisclosureRef.current) topLevelDisclosureRef.current.open = true; + const top = topLevelDisclosureRef.current; + // Citation-only: leave the nested disclosure marked for when the user opens + // the panel, but do not open/scroll the dump over the PDF. + if (!forceReveal && !(top?.open || compactOpen)) return; + if (top) top.open = true; const wasOpen = targetDisclosure.open; - openNestedSourceDisclosure(topLevelDisclosureRef.current, targetDisclosure); + openNestedSourceDisclosure(top, targetDisclosure); if (!wasOpen) targetDisclosure.scrollIntoView({ block: "nearest", behavior: resolveScrollBehavior() }); - }, [autoOpenDriver, autoOpenTargetId, autoOpenSuppressed, idPrefix, targetAvailability]); + }, [autoOpenDriver, autoOpenTargetId, autoOpenSuppressed, compactOpen, forceReveal, idPrefix, targetAvailability]); function moveHit(delta: number) { if (visibleChunks.length === 0) return; @@ -932,8 +965,8 @@ export const IndexedTextPanel = memo(function IndexedTextPanel({ { expect(screen.getAllByRole("button")).toHaveLength(sections.length + 1); expect(screen.getByText("312 chunks")).toBeTruthy(); - const active = screen.getByRole("button", { name: /Pinned evidence/ }); + const active = screen.getByRole("button", { name: /Cited excerpt/ }); expect(active.getAttribute("aria-current")).toBe("true"); }); diff --git a/tests/document-section-summary.dom.test.tsx b/tests/document-section-summary.dom.test.tsx index 02fe0e22ee..a68b589554 100644 --- a/tests/document-section-summary.dom.test.tsx +++ b/tests/document-section-summary.dom.test.tsx @@ -49,8 +49,8 @@ describe("DocumentSectionSummary", () => { }); }); -describe("IndexedTextPanel condensed reveal", () => { - it("keeps deep-linked chunks revealed after exclusive-accordion close events", async () => { +describe("IndexedTextPanel citation landing", () => { + it("keeps citation deep-links collapsed so the PDF stays the first reading surface", () => { render( { />, ); + const panel = screen.getByTestId("source-chunk-indexed-text-panel") as HTMLDetailsElement; + expect(panel.open).toBe(false); + expect(panel.querySelector("summary")).not.toHaveAttribute("aria-disabled"); + }); + + it("opens on an explicit inspect request and keeps the cited chunk revealed", async () => { + render( + , + ); + const panel = screen.getByTestId("source-chunk-indexed-text-panel") as HTMLDetailsElement; expect(panel.open).toBe(true); const highlighted = screen.getByTestId("highlighted-indexed-source-chunk") as HTMLDetailsElement; - expect(highlighted).toBeVisible(); await waitFor(() => expect(highlighted.open).toBe(true)); expect(panel.querySelector("summary")).toHaveAttribute("aria-disabled", "true"); @@ -131,7 +154,7 @@ describe("IndexedTextPanel condensed reveal", () => { expect(screen.getByText("Hit 1 of 1")).toBeVisible(); }); - it("keeps the deep-linked nested chunk disclosure open under condensed view", async () => { + it("keeps the deep-linked nested chunk disclosure open under an inspect reveal", async () => { const props = { loading: false, selectedPage: basePage, @@ -153,6 +176,7 @@ describe("IndexedTextPanel condensed reveal", () => { selectedChunkId: "chunk-1", onSearchChange: vi.fn(), compact: true, + revealRequest: true, }; const { rerender } = render(); @@ -192,4 +216,59 @@ describe("IndexedTextPanel condensed reveal", () => { fireEvent.click(panel.querySelector("summary")!); await waitFor(() => expect(panel.open).toBe(false)); }); + + it("closes after inspect reveal clears on the same citation", () => { + const props = { + loading: false, + selectedPage: basePage, + chunks: [baseChunk], + search: "", + documentSearchResults: [] as [], + searchingDocument: false, + documentSearchError: null, + idPrefix: "source-chunk", + sectionId: "source-text" as const, + selectedChunkId: "chunk-1", + onSearchChange: vi.fn(), + compact: true, + revealRequest: true, + }; + const { rerender } = render(); + const panel = screen.getByTestId("source-chunk-indexed-text-panel") as HTMLDetailsElement; + expect(panel.open).toBe(true); + + rerender(); + expect(panel.open).toBe(false); + }); + + it("resets compact open when the citation changes without a reveal request", () => { + const props = { + loading: false, + selectedPage: basePage, + chunks: [ + baseChunk, + { + ...baseChunk, + id: "chunk-2", + chunk_index: 1, + content: "Lithium levels are checked 5 to 7 days after initiation", + }, + ], + search: "", + documentSearchResults: [] as [], + searchingDocument: false, + documentSearchError: null, + idPrefix: "source-chunk", + sectionId: "source-text" as const, + selectedChunkId: "chunk-1", + onSearchChange: vi.fn(), + compact: true, + revealRequest: true, + }; + const { rerender } = render(); + expect((screen.getByTestId("source-chunk-indexed-text-panel") as HTMLDetailsElement).open).toBe(true); + + rerender(); + expect((screen.getByTestId("source-chunk-indexed-text-panel") as HTMLDetailsElement).open).toBe(false); + }); }); diff --git a/tests/ui-smoke.spec.ts b/tests/ui-smoke.spec.ts index b80c24e52d..cd7889c63d 100644 --- a/tests/ui-smoke.spec.ts +++ b/tests/ui-smoke.spec.ts @@ -3987,10 +3987,13 @@ test.describe("Clinical KB UI smoke coverage", () => { await expect(page.locator("#source-evidence").getByTestId("highlighted-source-passage")).toContainText( "Escalate review when there is vomiting", ); + // Citation landing keeps the indexed dump collapsed so the PDF stays primary. + await expect(page.locator("#source-text")).toHaveJSProperty("open", false); + await page.getByTestId("inspect-indexed-text").click(); + await expect(page.locator("#source-text")).toHaveJSProperty("open", true); await expect( page.getByTestId("source-chunk-indexed-text-panel").getByTestId("highlighted-indexed-source-chunk"), ).toBeVisible(); - await expect(page.locator("#source-text")).toHaveJSProperty("open", true); await expect( page.getByTestId("source-chunk-indexed-text-panel").getByTestId("highlighted-indexed-source-chunk"), ).toHaveJSProperty("open", true); @@ -4125,14 +4128,13 @@ test.describe("Clinical KB UI smoke coverage", () => { await expect(evidence).toBeVisible(); await expect(evidence.getByText("Highlighted source passage")).toBeVisible(); await expect(page.locator("#source-text")).toBeVisible(); - await expect( - page.getByTestId("source-chunk-indexed-text-panel").getByTestId("highlighted-indexed-source-chunk"), - ).toBeVisible(); + await expect(page.locator("#source-text")).toHaveJSProperty("open", false); + await expect(page.getByTestId("inspect-indexed-text")).toBeVisible(); await expect(sectionTrigger).toBeVisible(); await revealPhoneHeaderControl(page, sectionTrigger); await sectionTrigger.click(); const sectionSheet = page.getByTestId("document-section-sheet"); - await expect(sectionSheet.getByRole("button", { name: /Pinned evidence/ })).toBeVisible(); + await expect(sectionSheet.getByRole("button", { name: /Cited excerpt/ })).toBeVisible(); await expect(sectionSheet.getByRole("button", { name: /PDF preview/ })).toBeVisible(); await expect(sectionSheet.getByRole("button", { name: /Indexed source text/ })).toBeVisible(); const mobileDensityToggle = sectionSheet.getByTestId("document-view-density-toggle"); @@ -4186,6 +4188,10 @@ test.describe("Clinical KB UI smoke coverage", () => { await expect(preview).toBeInViewport(); await openSection(/Indexed source text/); await expect(indexedTextHeading).toBeInViewport(); + await expect(page.locator("#source-text")).toHaveJSProperty("open", true); + await expect( + page.getByTestId("source-chunk-indexed-text-panel").getByTestId("highlighted-indexed-source-chunk"), + ).toBeVisible(); await openSection(/PDF preview/); await expect(preview).toBeInViewport();