From 015ffdc9ae90b8b947fc8cfeb00a4945fe32d329 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Aug 2026 06:20:54 +0000 Subject: [PATCH 1/5] docs: record PR #1649 ledger row and capture its cross-tool enforcement gap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two append-only docs items deliberately held back from PR #1649 so a second commit could not cancel that branch's in-flight CI. - `docs/branch-review-ledger.md`: the missing review record for the pr-handoff-stop hook. Recorded against the squash-merge commit 76169eb rather than the branch tip 2ad32de9b, which is unreachable now that the merged branch is deleted and would fail the ledger's head-resolution guard. - `docs/outstanding-issues.md` `#258`: the hook is registered in `.claude/settings.json`, so its deny list binds Claude Code only. Codex and Cursor sessions get the AGENTS.md prose and no gate — which is what was already in force, and already insufficient, before #1649. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Xg42EYSHXXyAXUJewzYxHA --- docs/branch-review-ledger.md | 1 + docs/outstanding-issues.md | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index fcf1ca90f3..028761025e 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -685,3 +685,4 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-08-06 | claude/settings-nav-freeze-desktop-tdzh7z (PR #1641) | c31827e63c20788c5dabb22b96ddbf239c65f893 | Run PR sweep: CI fix + threads + drift | before: mergeable/BLOCKED, 0 unresolved threads, 0 behind main; prior CI reds were infra (npm ECONNRESET; cancelled Set up job). after: product fix d4ecd2b5 (ResizeObserver pin-clamp distance reset) + ledger row; all review threads resolved; merge tree clean; hosted CI pending on GitHub Actions major_outage (no product failure) | local typecheck PASS; local lint (settings-dialog) PASS; no provider-backed checks run; hosted CI awaiting Actions recovery | | 2026-08-06 | cursor/mode-nav-pr-1647-a5eb | 4c8d70612f3be4a1267ed16b81e926a9f2e1ef50 | PR #1647 mode-nav | no high-confidence defects; medium: record pages lose mode destinations after Subnav removal (section-nav early return); addon-slot guard still coincidence-tested not runtime; includes() activeId fragile for future slugs | read mode-nav/*, page-secondary-navigation, mode-secondary-navigation, specifier/formulation record anchors + tests; catalog slug collision scan (0 hits); test:focused blocked (test paths changed) | | 2026-08-06 | claude/ds-truth-fixes | 7a9c41a971aa9111f050cd39d4429ada1b3f4409 | PR #1655 heavy review-and-fix | fixed DownloadLink tone DOM leak + stale ToggleSwitch/Links §9 docs; merge-tree clean; verify:cheap+verify:pr-local green; CI re-queued after push | bugbot+deep-review; vitest ui-primitives+ui-v2 60p; verify:cheap 5448p; verify:pr-local format+lint+typecheck+test+build+rag-fixtures; no provider gates | +| 2026-08-07 | claude/pr-handoff-stop-hook (PR #1649) | 76169ebcea48ca5efd9859b9a1f8c579dcc8b834 | PR #1649 pr-handoff-stop hook + AGENTS.md/handoff docs | shipped and squash-merged as 76169eb; hook denies post-handoff PR/CI polling (shell gh, GitHub MCP pull_request/workflow/check/job_log/update_branch, Monitor/ScheduleWakeup/CronCreate) while leaving commit, push, ledger:append and PR create/merge allowed; anchored exemption keeps create_pull_request_review denied; known limit: .claude/settings.json binds Claude Code only, Codex/Cursor get AGENTS.md prose with no enforcement (captured as an outstanding issue). Row recorded late and against the merged squash commit because the branch tip 2ad32de9b is unreachable after branch deletion | 6 repo gate scripts green; Prettier clean; bash -n clean; classifyPullRequestFiles all risk flags false; ~20 hook payloads exercised; verify:pr-local NOT run (no node_modules in that worktree, declared in the PR body) | diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index f73d9a53dc..e559fb7bf3 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -166,7 +166,7 @@ removed after current-main verification; it is not missing recommended work. | 113 | `#257` | Optional | High — formulation/specifiers flake | Standing until second reproduction | 15–30 min | Single unreproduced ui-formulation flake when run with ui-specifiers — record a second sighting only; do not quarantine until three on the same SHA. **Stop:** do not weaken assertions. | - + ## Open items > **Merged-main canary update (2026-07-23, run `30018289898`):** the new structured report correctly recorded evaluated tree `c24f2e8f2d30d0c59fc1eba025d3dcd63478137e`, run/attempt identity and `cross-region-runner` latency context. Golden retrieval remained 36/36 with document/content recall 1.0 and no failed cases. The 44-case answer gate had grounded-supported and unsupported-correct rates of 1.0, but failed because `neuroleptic-side-effect-escalation` again returned one citation where two are required (citation-failure rate 0.0227). `admission-discharge-comparison` again omitted the specific AKG admission document after `comparison_source_extractive_fallback`; `admission-discharge-coverage-paraphrase` was advisory-only at 24,870 ms. Answer cost was reported as `$0.234736`. Do not retry immediately: retain this as the first structured datapoint, compare it with the scheduled 2026-07-26 report, and keep retrieval/ranking unchanged. @@ -299,6 +299,7 @@ removed after current-main verification; it is not missing recommended work. | #255 | P2 | issue | Remote/Cloud containers cannot run any browser gate: Playwright lock drift plus a missing Chromium build | In the 2026-08-05 remote container, npm run verify:pr-local failed closed at its first precondition — 'check:installed-lock-parity ... playwright: installed 1.62.0 does not match locked 1.62.1' — and Chromium could not launch at all: 'Executable doesn't exist at /opt/pw-browsers/chromium_headless_shell-1234/...' while the image only ships chromium-1194 and chromium_headless_shell-1194. So verify:ui, verify:phone-chrome and verify:release are all unrunnable in a remote session regardless of the diff, and verify:pr-local cannot even reach its selected gates. This is not a one-off 'run npm ci': the installed Playwright disagrees with the lockfile, and installing the locked 1.62.1 would request a browser revision the image also does not carry, so the fix is an image/pin alignment rather than an install. Consequence worth writing down: PRs authored from remote sessions can only produce static and unit evidence locally and must delegate browser proof to CI's Production UI jobs, which is what #1620/#1621/#1622 did — #1621's Chromium evidence came entirely from CI (0 failed of 17, all three Production UI shards). Next action: align the container image's Playwright browser builds with the lockfile pin, or document the delegation explicitly in docs/testing.md so a remote session does not read a fail-closed parity error as a defect in its own change. Related but distinct from #149, which is about the parity checker only comparing seven top-level packages rather than about an actual blocking drift. Stop: do not work around it by setting PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD or pointing executablePath at the mismatched 1194 build to force a green browser run — a browser gate run against the wrong revision is not evidence. Renumbered from this PR's original #252 → #255 because main claimed #249–#251 via PR #1624. | session 2026-08-05 open-PR review sweep; verify:pr-local and verify:phone-chrome attempts on PRs #1620/#1621 | 2026-08-05 | | #256 | P2 | issue | Declared information-page section sets whose target ids nothing renders | A set in informationPageSectionDefinitions claims its route before the mode branch, then AvailableInformationPageNavigation drops every item whose targetIds are absent from the DOM and returns null, so the page shows no navigation at all. Proven live on PR #1647: specifierSections/formulationSections declared ten ids (specifier-overview, formulation-five-ps, and eight more) that no component rendered; invisible while those pages carried their own subnav, a dead end the moment it moved to the shared bar. Fixed for those two by wiring the anchors in. Suspected remaining: form-overview and the differential-presentation-* set returned no literal id= match. Next: verify each declared set at route level and either render the anchors or delete the set. Do NOT audit by grepping for id=: several live sections are rendered through a sectionId prop instead (source-evidence), so a source scan reports them dead and would have to be muzzled to pass — assert against the rendered DOM per route. | session 2026-08-06; PR #1647; Devin review finding | 2026-08-06 | | #257 | P3 | issue | Single unreproduced ui-formulation flake: keeps specifier and formulation route families clinically separate | Observed once on 2026-08-06 at PR #1647 head f5833acc, running tests/ui-formulation.spec.ts + tests/ui-specifiers.spec.ts together against local Chromium (1 failed, 11 passed). Did NOT reproduce: passed in isolation with --grep, and passed again on a full-file re-run (7/7). Recorded only so a second sighting is recognisable as a second rather than looking like a first. Per docs/testing.md this is one reproduction of three — do NOT quarantine, and do not weaken the assertion. Next: no action unless it recurs; if a second reproduction lands on the same SHA, note it here, and only on a third open a tests/flake-ledger.json entry with @quarantine and a <=30-day expiry. | session 2026-08-06; PR #1647 | 2026-08-06 | +| #258 | P2 | rec | The PR-handoff stop rule is enforced for Claude Code only; Codex and Cursor get prose with no gate | **Outcome:** a session that opens a PR stops following it in every agent this repo supports, not just Claude Code. **Detail:** PR #1649 added `.claude/hooks/pr-handoff-stop.sh` plus the AGENTS.md "Stop when the pull request is open" section. The hook is registered in `.claude/settings.json`, which only Claude Code reads, so the PostToolUse marker and the PreToolUse denials (shell `gh pr checks/status/view/run watch`, GitHub MCP tools named pull_request/workflow_run/workflow_job/check_run/check_suite/job_log/update_branch, and Monitor/ScheduleWakeup/CronCreate) simply do not exist for Codex or Cursor sessions. Those agents get the AGENTS.md prose and nothing else — and prose alone is exactly what was already in force, and already insufficient, before #1649. Cost is the same long tail of post-handoff CI polling the hook was built to cut, just relocated to whichever agent lacks the gate; a cloud Codex session is the worst case because nothing naturally ends it. **Next:** cheapest first — check whether Codex and Cursor expose any pre-tool interception this repo can register (Codex plugin hooks under `plugins/clinical-kb/`, Cursor rules under `.cursor/`); if neither offers a deny path, the fallback is a shared marker file plus a wrapper the agent is told to route `gh` through, which is weaker but still detectable. If no mechanism exists at all, record that explicitly here so the gap is a known limit rather than an open task. **Stop:** do not weaken the Claude Code hook to make the tools symmetric, and do not add a second copy of the deny list — one script, multiple registrations. | PR #1649; .claude/hooks/pr-handoff-stop.sh; .claude/settings.json; AGENTS.md "Stop when the pull request is open"; session 2026-08-07 | 2026-08-07 | ## Resolved / archive From dfe2946110e0ff93bd4acc571ae79c26b79a7a85 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 7 Aug 2026 09:52:12 +0000 Subject: [PATCH 2/5] docs(ledger): name exact #1649 verification gates in late review row CodeRabbit asked that the PR #1649 late ledger checks cell list the six named repo scripts and explicitly record incomplete handoff coverage (verify:pr-local / verify:ui not run; no provider-backed checks) instead of the opaque "6 repo gate scripts green" summary. Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 6643bd2eaf..7b47a50e32 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -685,7 +685,7 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-08-06 | claude/settings-nav-freeze-desktop-tdzh7z (PR #1641) | c31827e63c20788c5dabb22b96ddbf239c65f893 | Run PR sweep: CI fix + threads + drift | before: mergeable/BLOCKED, 0 unresolved threads, 0 behind main; prior CI reds were infra (npm ECONNRESET; cancelled Set up job). after: product fix d4ecd2b5 (ResizeObserver pin-clamp distance reset) + ledger row; all review threads resolved; merge tree clean; hosted CI pending on GitHub Actions major_outage (no product failure) | local typecheck PASS; local lint (settings-dialog) PASS; no provider-backed checks run; hosted CI awaiting Actions recovery | | 2026-08-06 | cursor/mode-nav-pr-1647-a5eb | 4c8d70612f3be4a1267ed16b81e926a9f2e1ef50 | PR #1647 mode-nav | no high-confidence defects; medium: record pages lose mode destinations after Subnav removal (section-nav early return); addon-slot guard still coincidence-tested not runtime; includes() activeId fragile for future slugs | read mode-nav/*, page-secondary-navigation, mode-secondary-navigation, specifier/formulation record anchors + tests; catalog slug collision scan (0 hits); test:focused blocked (test paths changed) | | 2026-08-06 | claude/ds-truth-fixes | 7a9c41a971aa9111f050cd39d4429ada1b3f4409 | PR #1655 heavy review-and-fix | fixed DownloadLink tone DOM leak + stale ToggleSwitch/Links §9 docs; merge-tree clean; verify:cheap+verify:pr-local green; CI re-queued after push | bugbot+deep-review; vitest ui-primitives+ui-v2 60p; verify:cheap 5448p; verify:pr-local format+lint+typecheck+test+build+rag-fixtures; no provider gates | -| 2026-08-07 | claude/pr-handoff-stop-hook (PR #1649) | 76169ebcea48ca5efd9859b9a1f8c579dcc8b834 | PR #1649 pr-handoff-stop hook + AGENTS.md/handoff docs | shipped and squash-merged as 76169eb; hook denies post-handoff PR/CI polling (shell gh, GitHub MCP pull_request/workflow/check/job_log/update_branch, Monitor/ScheduleWakeup/CronCreate) while leaving commit, push, ledger:append and PR create/merge allowed; anchored exemption keeps create_pull_request_review denied; known limit: .claude/settings.json binds Claude Code only, Codex/Cursor get AGENTS.md prose with no enforcement (captured as an outstanding issue). Row recorded late and against the merged squash commit because the branch tip 2ad32de9b is unreachable after branch deletion | 6 repo gate scripts green; Prettier clean; bash -n clean; classifyPullRequestFiles all risk flags false; ~20 hook payloads exercised; verify:pr-local NOT run (no node_modules in that worktree, declared in the PR body) | +| 2026-08-07 | claude/pr-handoff-stop-hook (PR #1649) | 76169ebcea48ca5efd9859b9a1f8c579dcc8b834 | PR #1649 pr-handoff-stop hook + AGENTS.md/handoff docs | shipped and squash-merged as 76169eb; hook denies post-handoff PR/CI polling (shell gh, GitHub MCP pull_request/workflow/check/job_log/update_branch, Monitor/ScheduleWakeup/CronCreate) while leaving commit, push, ledger:append and PR create/merge allowed; anchored exemption keeps create_pull_request_review denied; known limit: .claude/settings.json binds Claude Code only, Codex/Cursor get AGENTS.md prose with no enforcement (captured as an outstanding issue). Row recorded late and against the merged squash commit because the branch tip 2ad32de9b is unreachable after branch deletion | named #1649 gates: check-docs-links PASS (1629 refs); ci-change-scope --self-test PASS; check-gate-manifest PASS; check-codex-cloud-setup PASS (static); check-branch-review-ledger PASS; check-outstanding-issues PASS; Prettier clean on AGENTS.md/.claude/settings.json/handoff SKILL; bash -n hook clean; classifyPullRequestFiles all risk flags false; ~20 hook payloads exercised; incomplete vs full handoff: verify:pr-local NOT run (no node_modules); verify:ui NOT run (no UI delta); no provider-backed checks run | | 2026-08-07 | codex/docker-delivery-hardening (PR #1656) | fe339678ca23fc794238f458f0537a33f1b76c2c | prlanded | MERGED; tip tree empty vs squash fe339678; remote branch deleted | prlanded content verify; no provider-backed checks | | 2026-08-07 | claude/settings-nav-freeze-desktop-tdzh7z (PR #1641) | 7d3e62677ae178952aeca82048b492c4b84eaf05 | prlanded | MERGED; tip tree empty vs squash 7d3e6267; remote branch deleted | prlanded content verify; no provider-backed checks | | 2026-08-07 | cursor/viewer-phase1-shell-extract-1db8 (PR #1665) | bd46a39ac02e604eb45178b5ed38d39f06cc6830 | prlanded | MERGED; remote branch deleted; squash tip on main | prlanded; no provider-backed checks | From 7115bc00a89453dfec91c16b8b9473b0e151bc67 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Aug 2026 09:53:09 +0000 Subject: [PATCH 3/5] issues: capture the owed #1670 ledger row, the stranded Sentry commits, and a second lock-parity reproduction Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Xg42EYSHXXyAXUJewzYxHA --- docs/outstanding-issues.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index e559fb7bf3..2bfe57ac9f 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -166,7 +166,7 @@ removed after current-main verification; it is not missing recommended work. | 113 | `#257` | Optional | High — formulation/specifiers flake | Standing until second reproduction | 15–30 min | Single unreproduced ui-formulation flake when run with ui-specifiers — record a second sighting only; do not quarantine until three on the same SHA. **Stop:** do not weaken assertions. | - + ## Open items > **Merged-main canary update (2026-07-23, run `30018289898`):** the new structured report correctly recorded evaluated tree `c24f2e8f2d30d0c59fc1eba025d3dcd63478137e`, run/attempt identity and `cross-region-runner` latency context. Golden retrieval remained 36/36 with document/content recall 1.0 and no failed cases. The 44-case answer gate had grounded-supported and unsupported-correct rates of 1.0, but failed because `neuroleptic-side-effect-escalation` again returned one citation where two are required (citation-failure rate 0.0227). `admission-discharge-comparison` again omitted the specific AKG admission document after `comparison_source_extractive_fallback`; `admission-discharge-coverage-paraphrase` was advisory-only at 24,870 ms. Answer cost was reported as `$0.234736`. Do not retry immediately: retain this as the first structured datapoint, compare it with the scheduled 2026-07-26 report, and keep retrieval/ranking unchanged. @@ -296,10 +296,12 @@ removed after current-main verification; it is not missing recommended work. | #252 | P2 | issue | check:bundle-budget counts mockup chunks, contradicting #013's initial-bundle position | The budget's totalGzipBytes comes from measureChunkPaths(walkJsFiles(CHUNKS_DIR)) — EVERY built client chunk, including routes that 404 in production. The manifest-scoped initialDashboardChunks set is used only for the fixture-payload assertion, not the budget. So two repo positions disagree about mockups and nothing says so: #013 records that mockup chunks 'are not an initial production bundle' and must not be restructured without deploy-artifact evidence, while the gate charges them against a repo-wide ceiling. PR #1580 is the live cost — a mockups-only PR blocked on 'FAIL +10.1% vs baseline (tolerance 10%)' for chunks no user can load; it has sat red and unmerged since 2026-08-02. Docs now state the mechanism (AGENTS.md gate bullet, CLAUDE.md mockups bullet) so it stops being a surprise, but the metric decision is unmade. Next action: pick one and make the script say so — (a) exclude mockup-only chunks from totalGzipBytes so the number means production weight, which matches #013 but removes all back-pressure on mockup growth (59 routes on main today, 4 more in open PRs); or (b) keep counting them, rename the reported metric so it does not read as production bundle weight, and treat the tolerance as a deliberate hygiene ceiling. Option (b) additionally wants the mockup share reported separately, which is the measurement #013 asks for before any prune. Stop: do not raise the tolerance or run --update to clear #1580 — that discards the only back-pressure without deciding anything. Renumbered from this PR's original #249 → #252 because main claimed #249–#251 via PR #1624. | session 2026-08-05 open-PR review; PR #1580 Build log; scripts/check-bundle-budget.mjs; ledger #013 | 2026-08-05 | | #253 | P3 | task | #1606 needs a hand-merge against merged PR #1615, not a rebase | MobileResultFilterControl's native — its change is the iOS 16px anti-zoom rule — so #1606's blue-highlight fix does not exist on main today. #1606 is still open (verified 2026-08-05, not closed) and reports mergeable_state 'dirty' against main because both PRs rewrote the same function with different designs; resolving it needs a hand-merge of the two implementations, not a rebase and not a close-and-redo. Two things to re-verify on the current head before merging: (1) the P2 from Codex review — ArrowDown/ArrowUp previously focused the current placeholder option even when disabled (Services' 'current', Formulation's 'Current search'), stranding keyboard users since every option was tabIndex=-1; the branch is reported to have since fixed this, but confirm on the exact head being merged. (2) its prior lint error, react-hooks/set-state-in-effect at search-results-header-band.tsx:670 (setMenuBox(null) synchronously inside useLayoutEffect), which PR #1620's new pre-push guard now catches before push. Also re-check the 3 Playwright failures previously seen on ui-stress and ui-tools single-line badge assertions against the current implementation. Stop: do not close #1606 to route around the conflict — it is the only open PR carrying this accessibility fix. Renumbered from this PR's original #250 → #253 because main claimed #249–#251 via PR #1624. | session 2026-08-05 open-PR review sweep; PR #1606 (open, dirty, verified live); Codex review thread on search-results-header-band.tsx:696 | 2026-08-05 | | #254 | P2 | issue | PR #1617's Codex Cloud acceptance evidence is pinned to a head the branch has moved past | PR #1617 is the surviving side of the Codex Cloud MCP dispute (PR #1613 was closed against it on 2026-08-05), and its whole case rests on one first-hand measurement: a fresh Codex Cloud task reporting 'no Railway tool exposed', 'no Supabase tool exposed', 'no GitHub tool exposed' — zero provider tools from repo-written MCP config — while the hosted OAuth path passed in ChatGPT. That evidence is recorded in docs/audit/cloud-connection-acceptance-2026-08-05.md against task head 76dfe85fa93787b3845d0bd460aa18ff753ca2ca, and the PR body states 'The final branch head is 7d485f88db391cc7e8e73c57ddbde61f532375fc'. The branch head is now bf7684a77ef5fa5d53a1e7891cb7f446d4741e4a, so both pins are stale and the acceptance no longer describes what would merge. This matters more than usual because the acceptance IS the argument: #1613 was closed on the strength of it, and its own merge gate is the kind of exact-head check that a moved head invalidates. Next action: before #1617 leaves draft, re-run the fresh Codex Cloud acceptance at the exact current head and update both the report and the body pin, or state explicitly which parts of the evidence are head-independent (the connector-page absence of Railway/Supabase is a product-surface fact and does not depend on the head; the zero-tool discovery result does). Stop: do not treat the 76dfe85f/7d485f88 evidence as covering the current head, and do not reopen #1613 on the basis that #1617's evidence is stale — staleness is not refutation. Renumbered from this PR's original #251 → #254 because main claimed #249–#251 via PR #1624. | session 2026-08-05 open-PR review sweep; PR #1617 body and docs/audit/cloud-connection-acceptance-2026-08-05.md | 2026-08-05 | -| #255 | P2 | issue | Remote/Cloud containers cannot run any browser gate: Playwright lock drift plus a missing Chromium build | In the 2026-08-05 remote container, npm run verify:pr-local failed closed at its first precondition — 'check:installed-lock-parity ... playwright: installed 1.62.0 does not match locked 1.62.1' — and Chromium could not launch at all: 'Executable doesn't exist at /opt/pw-browsers/chromium_headless_shell-1234/...' while the image only ships chromium-1194 and chromium_headless_shell-1194. So verify:ui, verify:phone-chrome and verify:release are all unrunnable in a remote session regardless of the diff, and verify:pr-local cannot even reach its selected gates. This is not a one-off 'run npm ci': the installed Playwright disagrees with the lockfile, and installing the locked 1.62.1 would request a browser revision the image also does not carry, so the fix is an image/pin alignment rather than an install. Consequence worth writing down: PRs authored from remote sessions can only produce static and unit evidence locally and must delegate browser proof to CI's Production UI jobs, which is what #1620/#1621/#1622 did — #1621's Chromium evidence came entirely from CI (0 failed of 17, all three Production UI shards). Next action: align the container image's Playwright browser builds with the lockfile pin, or document the delegation explicitly in docs/testing.md so a remote session does not read a fail-closed parity error as a defect in its own change. Related but distinct from #149, which is about the parity checker only comparing seven top-level packages rather than about an actual blocking drift. Stop: do not work around it by setting PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD or pointing executablePath at the mismatched 1194 build to force a green browser run — a browser gate run against the wrong revision is not evidence. Renumbered from this PR's original #252 → #255 because main claimed #249–#251 via PR #1624. | session 2026-08-05 open-PR review sweep; verify:pr-local and verify:phone-chrome attempts on PRs #1620/#1621 | 2026-08-05 | +| #255 | P2 | issue | Remote/Cloud containers cannot run any browser gate: Playwright lock drift plus a missing Chromium build | In the 2026-08-05 remote container, npm run verify:pr-local failed closed at its first precondition — 'check:installed-lock-parity ... playwright: installed 1.62.0 does not match locked 1.62.1' — and Chromium could not launch at all: 'Executable doesn't exist at /opt/pw-browsers/chromium_headless_shell-1234/...' while the image only ships chromium-1194 and chromium_headless_shell-1194. So verify:ui, verify:phone-chrome and verify:release are all unrunnable in a remote session regardless of the diff, and verify:pr-local cannot even reach its selected gates. This is not a one-off 'run npm ci': the installed Playwright disagrees with the lockfile, and installing the locked 1.62.1 would request a browser revision the image also does not carry, so the fix is an image/pin alignment rather than an install. Consequence worth writing down: PRs authored from remote sessions can only produce static and unit evidence locally and must delegate browser proof to CI's Production UI jobs, which is what #1620/#1621/#1622 did — #1621's Chromium evidence came entirely from CI (0 failed of 17, all three Production UI shards). Next action: align the container image's Playwright browser builds with the lockfile pin, or document the delegation explicitly in docs/testing.md so a remote session does not read a fail-closed parity error as a defect in its own change. Related but distinct from #149, which is about the parity checker only comparing seven top-level packages rather than about an actual blocking drift. Stop: do not work around it by setting PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD or pointing executablePath at the mismatched 1194 build to force a green browser run — a browser gate run against the wrong revision is not evidence. Renumbered from this PR's original #252 → #255 because main claimed #249–#251 via PR #1624. **Second reproduction 2026-08-07 (remote container, PR #1670):** identical parity failure, same versions — 'playwright: installed 1.62.0 does not match locked 1.62.1' — two days later on a different image instance, so this is a standing property of the remote image rather than one bad container. New detail: the diff was docs-only (two append-only ledger files), and parity still halted `verify:pr-local` at step 2 of a 10-step plan that contained no browser gate at all. So the blast radius is wider than browser proof: a remote session cannot complete the PR-local gate for ANY scope, and has to re-run the selected steps by hand to get evidence (done there: the remaining eight all passed). That strengthens the documentation half of the next action — docs/testing.md should say a remote session may see this on a diff that never touches a browser. | session 2026-08-05 open-PR review sweep; verify:pr-local and verify:phone-chrome attempts on PRs #1620/#1621 | 2026-08-05 | | #256 | P2 | issue | Declared information-page section sets whose target ids nothing renders | A set in informationPageSectionDefinitions claims its route before the mode branch, then AvailableInformationPageNavigation drops every item whose targetIds are absent from the DOM and returns null, so the page shows no navigation at all. Proven live on PR #1647: specifierSections/formulationSections declared ten ids (specifier-overview, formulation-five-ps, and eight more) that no component rendered; invisible while those pages carried their own subnav, a dead end the moment it moved to the shared bar. Fixed for those two by wiring the anchors in. Suspected remaining: form-overview and the differential-presentation-* set returned no literal id= match. Next: verify each declared set at route level and either render the anchors or delete the set. Do NOT audit by grepping for id=: several live sections are rendered through a sectionId prop instead (source-evidence), so a source scan reports them dead and would have to be muzzled to pass — assert against the rendered DOM per route. | session 2026-08-06; PR #1647; Devin review finding | 2026-08-06 | | #257 | P3 | issue | Single unreproduced ui-formulation flake: keeps specifier and formulation route families clinically separate | Observed once on 2026-08-06 at PR #1647 head f5833acc, running tests/ui-formulation.spec.ts + tests/ui-specifiers.spec.ts together against local Chromium (1 failed, 11 passed). Did NOT reproduce: passed in isolation with --grep, and passed again on a full-file re-run (7/7). Recorded only so a second sighting is recognisable as a second rather than looking like a first. Per docs/testing.md this is one reproduction of three — do NOT quarantine, and do not weaken the assertion. Next: no action unless it recurs; if a second reproduction lands on the same SHA, note it here, and only on a third open a tests/flake-ledger.json entry with @quarantine and a <=30-day expiry. | session 2026-08-06; PR #1647 | 2026-08-06 | | #258 | P2 | rec | The PR-handoff stop rule is enforced for Claude Code only; Codex and Cursor get prose with no gate | **Outcome:** a session that opens a PR stops following it in every agent this repo supports, not just Claude Code. **Detail:** PR #1649 added `.claude/hooks/pr-handoff-stop.sh` plus the AGENTS.md "Stop when the pull request is open" section. The hook is registered in `.claude/settings.json`, which only Claude Code reads, so the PostToolUse marker and the PreToolUse denials (shell `gh pr checks/status/view/run watch`, GitHub MCP tools named pull_request/workflow_run/workflow_job/check_run/check_suite/job_log/update_branch, and Monitor/ScheduleWakeup/CronCreate) simply do not exist for Codex or Cursor sessions. Those agents get the AGENTS.md prose and nothing else — and prose alone is exactly what was already in force, and already insufficient, before #1649. Cost is the same long tail of post-handoff CI polling the hook was built to cut, just relocated to whichever agent lacks the gate; a cloud Codex session is the worst case because nothing naturally ends it. **Next:** cheapest first — check whether Codex and Cursor expose any pre-tool interception this repo can register (Codex plugin hooks under `plugins/clinical-kb/`, Cursor rules under `.cursor/`); if neither offers a deny path, the fallback is a shared marker file plus a wrapper the agent is told to route `gh` through, which is weaker but still detectable. If no mechanism exists at all, record that explicitly here so the gap is a known limit rather than an open task. **Stop:** do not weaken the Claude Code hook to make the tools symmetric, and do not add a second copy of the deny list — one script, multiple registrations. | PR #1649; .claude/hooks/pr-handoff-stop.sh; .claude/settings.json; AGENTS.md "Stop when the pull request is open"; session 2026-08-07 | 2026-08-07 | +| #259 | P3 | task | Ledger row for claude/pr-handoff-loop-prevention-54y5zr (PR #1670) is still owed | **Outcome:** every reviewed branch has a ledger row, including the ones whose row was deliberately delayed. **Detail:** PR #1670 (the ledger row for #1649 plus `#258`) was pushed and opened without its own review record, on purpose: appending one would have meant a second commit to that head, and `.github/workflows/ci.yml` sets `cancel-in-progress: true`, so it would have cancelled #1670's in-flight CI and restarted it — the exact waste AGENTS.md 'PR bundling' documents and the same reason #1649's row was owed in the first place. This is now the second consecutive occurrence of the same pattern, which is the point worth recording: a handoff whose deliverable *is* a ledger append cannot append its own row without paying for a CI restart, so the debt is structural rather than an oversight. **Next:** append the row for `claude/pr-handoff-loop-prevention-54y5zr` at the head that merges, riding a later low-risk PR (the bundling case AGENTS.md already recommends for queued ledger/outstanding-issues appends). If the pattern recurs a third time, consider whether the ledger row for a docs-only PR is better written after merge against the squash commit as a matter of routine — which is what #1649's row had to do anyway once its branch was deleted. **Stop:** do not push a tip whose sole delta is a ledger append; AGENTS.md forbids it because it marks every other open PR behind. | PR #1670; PR #1649; AGENTS.md 'PR bundling' and 'Babysit / Run PR ledger policy'; session 2026-08-07 | 2026-08-07 | +| #260 | P2 | task | Two unpushed Sentry commits are stranded on a Windows-only branch and will be lost with that machine | **Outcome:** the Sentry setup/logging-hardening work is either shipped or consciously discarded, not left sitting in one machine's reflog. **Detail:** `claude/cloud-pr-loop-prevention-bc052b` carries two commits — `c3c9d6a31` and `abbcdc8e9`, ~389 lines across `src/sentry.*.config.ts`, `src/lib/env.ts`, `src/lib/supabase/client.tsx`, `src/components/ui-primitives.tsx` — that were never pushed and are not the authoring session's own work. The branch does not exist on the remote, so the commits are unreachable from any cloud or remote container; a 2026-08-07 remote session could not inspect, verify, or ship them and could only record their existence. The same worktree (`.claude/worktrees/pensive-borg-6be2f0`) still holds the same four files uncommitted. Two Sentry branches DO exist on origin — `claude/sentry-nextjs-sdk-setup-2v24q5` and `cursor/sentry-nextjs-sdk-7cee` — but whether either already carries this change is unconfirmed: a three-dot diff against `origin/main` from the remote container returned empty for both, which is not trustworthy as proof either way and was not pursued further. Note this touches `src/lib/env.ts` and `src/lib/supabase/client.tsx`, so it is not a docs-class change and needs a real gate whenever it does ship. **Next:** from the Windows machine, diff those two commits against the two remote Sentry branches to decide whether the work is already represented. If it is, delete the branch; if it is not, push it and open a PR rather than leaving it local. **Stop:** do not discard the commits blind, and do not assume the remote Sentry branches supersede them without a content diff — nothing has yet compared them. | session 2026-08-07 remote container; handoff notes from the PR #1649 session; origin branches claude/sentry-nextjs-sdk-setup-2v24q5 and cursor/sentry-nextjs-sdk-7cee | 2026-08-07 | ## Resolved / archive From f6f6f51a00a64c81f9798f1d9f418d690b751894 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 7 Aug 2026 09:55:59 +0000 Subject: [PATCH 4/5] docs(ledger): record PR #1670 heavy review-and-fix Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 7b47a50e32..f67efb8a1e 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -697,3 +697,4 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-08-07 | cursor/document-cover-thumbnails-b74c (PR #1661) | 096ba3138fe4dda182d98cc2f9c4e0fe9a09132b | prlanded | MERGED; squash tip empty vs branch tip 04934971711f2f7d346bbe6e0b9c0c8cd9776e43; first-page PDF cover thumbnails on document search cards | content tree empty vs squash; no provider-backed checks run | | 2026-08-07 | cursor/grok-quick-wins-a2c0 (PR #1651) | 2563b4adf4f0c0580ca371acfee94f9ae1ca3d0f | prlanded | MERGED; squash tip empty vs branch tip 4655643770dba7ca4078f1586c0461fd91657bf9; Grok-safe quick wins (tokens, a11y, perf, gates) | content tree empty vs squash; no provider-backed checks run | | 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 | From a43be6fe083bc860b1d9ad8045771c2b77b86752 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Aug 2026 09:57:14 +0000 Subject: [PATCH 5/5] issues: close #259, its ledger row was appended by the PR #1670 review sweep Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Xg42EYSHXXyAXUJewzYxHA --- docs/outstanding-issues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index 2bfe57ac9f..970e19cbcb 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -300,7 +300,6 @@ removed after current-main verification; it is not missing recommended work. | #256 | P2 | issue | Declared information-page section sets whose target ids nothing renders | A set in informationPageSectionDefinitions claims its route before the mode branch, then AvailableInformationPageNavigation drops every item whose targetIds are absent from the DOM and returns null, so the page shows no navigation at all. Proven live on PR #1647: specifierSections/formulationSections declared ten ids (specifier-overview, formulation-five-ps, and eight more) that no component rendered; invisible while those pages carried their own subnav, a dead end the moment it moved to the shared bar. Fixed for those two by wiring the anchors in. Suspected remaining: form-overview and the differential-presentation-* set returned no literal id= match. Next: verify each declared set at route level and either render the anchors or delete the set. Do NOT audit by grepping for id=: several live sections are rendered through a sectionId prop instead (source-evidence), so a source scan reports them dead and would have to be muzzled to pass — assert against the rendered DOM per route. | session 2026-08-06; PR #1647; Devin review finding | 2026-08-06 | | #257 | P3 | issue | Single unreproduced ui-formulation flake: keeps specifier and formulation route families clinically separate | Observed once on 2026-08-06 at PR #1647 head f5833acc, running tests/ui-formulation.spec.ts + tests/ui-specifiers.spec.ts together against local Chromium (1 failed, 11 passed). Did NOT reproduce: passed in isolation with --grep, and passed again on a full-file re-run (7/7). Recorded only so a second sighting is recognisable as a second rather than looking like a first. Per docs/testing.md this is one reproduction of three — do NOT quarantine, and do not weaken the assertion. Next: no action unless it recurs; if a second reproduction lands on the same SHA, note it here, and only on a third open a tests/flake-ledger.json entry with @quarantine and a <=30-day expiry. | session 2026-08-06; PR #1647 | 2026-08-06 | | #258 | P2 | rec | The PR-handoff stop rule is enforced for Claude Code only; Codex and Cursor get prose with no gate | **Outcome:** a session that opens a PR stops following it in every agent this repo supports, not just Claude Code. **Detail:** PR #1649 added `.claude/hooks/pr-handoff-stop.sh` plus the AGENTS.md "Stop when the pull request is open" section. The hook is registered in `.claude/settings.json`, which only Claude Code reads, so the PostToolUse marker and the PreToolUse denials (shell `gh pr checks/status/view/run watch`, GitHub MCP tools named pull_request/workflow_run/workflow_job/check_run/check_suite/job_log/update_branch, and Monitor/ScheduleWakeup/CronCreate) simply do not exist for Codex or Cursor sessions. Those agents get the AGENTS.md prose and nothing else — and prose alone is exactly what was already in force, and already insufficient, before #1649. Cost is the same long tail of post-handoff CI polling the hook was built to cut, just relocated to whichever agent lacks the gate; a cloud Codex session is the worst case because nothing naturally ends it. **Next:** cheapest first — check whether Codex and Cursor expose any pre-tool interception this repo can register (Codex plugin hooks under `plugins/clinical-kb/`, Cursor rules under `.cursor/`); if neither offers a deny path, the fallback is a shared marker file plus a wrapper the agent is told to route `gh` through, which is weaker but still detectable. If no mechanism exists at all, record that explicitly here so the gap is a known limit rather than an open task. **Stop:** do not weaken the Claude Code hook to make the tools symmetric, and do not add a second copy of the deny list — one script, multiple registrations. | PR #1649; .claude/hooks/pr-handoff-stop.sh; .claude/settings.json; AGENTS.md "Stop when the pull request is open"; session 2026-08-07 | 2026-08-07 | -| #259 | P3 | task | Ledger row for claude/pr-handoff-loop-prevention-54y5zr (PR #1670) is still owed | **Outcome:** every reviewed branch has a ledger row, including the ones whose row was deliberately delayed. **Detail:** PR #1670 (the ledger row for #1649 plus `#258`) was pushed and opened without its own review record, on purpose: appending one would have meant a second commit to that head, and `.github/workflows/ci.yml` sets `cancel-in-progress: true`, so it would have cancelled #1670's in-flight CI and restarted it — the exact waste AGENTS.md 'PR bundling' documents and the same reason #1649's row was owed in the first place. This is now the second consecutive occurrence of the same pattern, which is the point worth recording: a handoff whose deliverable *is* a ledger append cannot append its own row without paying for a CI restart, so the debt is structural rather than an oversight. **Next:** append the row for `claude/pr-handoff-loop-prevention-54y5zr` at the head that merges, riding a later low-risk PR (the bundling case AGENTS.md already recommends for queued ledger/outstanding-issues appends). If the pattern recurs a third time, consider whether the ledger row for a docs-only PR is better written after merge against the squash commit as a matter of routine — which is what #1649's row had to do anyway once its branch was deleted. **Stop:** do not push a tip whose sole delta is a ledger append; AGENTS.md forbids it because it marks every other open PR behind. | PR #1670; PR #1649; AGENTS.md 'PR bundling' and 'Babysit / Run PR ledger policy'; session 2026-08-07 | 2026-08-07 | | #260 | P2 | task | Two unpushed Sentry commits are stranded on a Windows-only branch and will be lost with that machine | **Outcome:** the Sentry setup/logging-hardening work is either shipped or consciously discarded, not left sitting in one machine's reflog. **Detail:** `claude/cloud-pr-loop-prevention-bc052b` carries two commits — `c3c9d6a31` and `abbcdc8e9`, ~389 lines across `src/sentry.*.config.ts`, `src/lib/env.ts`, `src/lib/supabase/client.tsx`, `src/components/ui-primitives.tsx` — that were never pushed and are not the authoring session's own work. The branch does not exist on the remote, so the commits are unreachable from any cloud or remote container; a 2026-08-07 remote session could not inspect, verify, or ship them and could only record their existence. The same worktree (`.claude/worktrees/pensive-borg-6be2f0`) still holds the same four files uncommitted. Two Sentry branches DO exist on origin — `claude/sentry-nextjs-sdk-setup-2v24q5` and `cursor/sentry-nextjs-sdk-7cee` — but whether either already carries this change is unconfirmed: a three-dot diff against `origin/main` from the remote container returned empty for both, which is not trustworthy as proof either way and was not pursued further. Note this touches `src/lib/env.ts` and `src/lib/supabase/client.tsx`, so it is not a docs-class change and needs a real gate whenever it does ship. **Next:** from the Windows machine, diff those two commits against the two remote Sentry branches to decide whether the work is already represented. If it is, delete the branch; if it is not, push it and open a PR rather than leaving it local. **Stop:** do not discard the commits blind, and do not assume the remote Sentry branches supersede them without a content diff — nothing has yet compared them. | session 2026-08-07 remote container; handoff notes from the PR #1649 session; origin branches claude/sentry-nextjs-sdk-setup-2v24q5 and cursor/sentry-nextjs-sdk-7cee | 2026-08-07 | ## Resolved / archive @@ -456,5 +455,6 @@ Move resolved rows here with the resolution date and a one-line outcome. Keep th | #223 | issue | MatchExplanationChips is exported with zero call sites anywhere in the repo | Resolved 2026-08-06: MatchExplanationChips already absent from source on main; confirmed no call sites. | 2026-08-06 | | #225 | issue | Favourites renders up to three EmptyState live regions at once | Resolved 2026-08-06: FavouritesEmptyMatches already rendered once outside breakpoint containers on main. | 2026-08-06 | | #246 | issue | `refetching` claims a CSS dimming that globals.css does not define | Resolved 2026-08-06: false CSS dimming claim already removed from search-results-header-band; pulsing dot is the intentional refetch signal. | 2026-08-06 | +| #259 | task | Ledger row for claude/pr-handoff-loop-prevention-54y5zr (PR #1670) is still owed | Resolved 2026-08-07, and not by this session: a Cursor review-and-fix sweep on PR #1670 appended the row for claude/pr-handoff-loop-prevention-54y5zr at dfe2946110e0ff93bd4acc571ae79c26b79a7a85 (scope 'PR #1670 heavy review-and-fix'), and refined the #1649 row's checks cell to name the exact gates after a CodeRabbit finding. The structural observation this row was opened for still holds and is worth keeping: a handoff whose deliverable IS a ledger append cannot append its own row without a second commit to that head cancelling its in-flight CI. What resolved it here was a DIFFERENT session picking the row up in a sweep that was already touching the branch — which is the practical answer to the pattern, and cheaper than either paying for a CI restart or writing the row post-merge against the squash commit. | 2026-08-07 |