From 1f3f6a8b88f70abce7f1df2471462fc5a2eeeb11 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Aug 2026 06:00:31 +0000 Subject: [PATCH 1/2] docs(issues): reconcile 21 queued ledger requests into the canonical ledger The inbox write path had been stuck since 2026-08-17. Four reconcile branches on origin (claude/issues-reconcile-2026-08-19, claude/ledger-reconcile-2026-08-21, claude/ledger-reconcile-issues-c1trbj, claude/rag-r0-reconcile-inbox) each started this transaction and none landed, and the guard against concurrent reconciliation correctly refused to start a fifth while they existed. None has an open pull request, so nothing was in flight to wait for. Verified before overriding the guard, rather than assuming: every request UUID on all four branches is already present on main, so no queued note would be lost. Their only content absent from main is two branch-review records (added in the next commit) and five source files that main deleted deliberately in #2211. Applies 21 requests with 4 cancellation decisions. One cancellation, 76390200 against 0a0ab127, arrived after its target had already been applied by an earlier reconciliation; the tool recorded it and changed nothing, and correcting that row needs a fresh update request. Six new rows, including the hydration-gap guard filed today as #2TAQDC; #231 re-scoped, #6GW95D demoted P2 -> P3, and the three CLS rows carry the root cause. Pending inbox is now zero. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UksCoUHQMp72XUdp7RjchE --- .../05a7854d-49d3-40b1-8973-fbbf191883da.json | 0 .../0c9671c5-34ed-4c53-b896-3a61108816ba.json | 0 .../1f2a0d03-e7be-4e77-9e41-d3e5e854ab9f.json | 0 .../416afc07-6d2a-479d-a10d-c323bf037767.json | 0 .../47f8e3bf-7d16-489d-b01d-962cb3b0e9df.json | 0 .../50eff4b6-19e0-4992-9596-95501f63226b.json | 0 .../5481ae21-8980-4056-9c0f-64baee28bd73.json | 0 .../5c3b582c-9dc6-4462-ba5c-563279d9ebc5.json | 0 .../61ed61bb-e3f1-406d-9842-5068bf30dfcc.json | 0 .../6895c60a-309d-4ecc-ae2e-5d34163761b2.json | 0 .../7099cbfb-c105-45c3-9ad3-4b1ada4d4a3e.json | 0 .../76390200-e6e0-4280-86de-adb758ee98a9.json | 0 .../79122ddb-a365-495b-a591-b21fa621aecb.json | 0 .../7c0f035c-2b63-47c2-b5a6-6df0118fdb9f.json | 0 .../a01b19c4-f58a-4d57-915a-f9e62849ca6c.json | 0 .../cab0fdc2-b88c-45bb-98ac-3e4acffcddd2.json | 0 .../cb98537c-0571-4bd2-bf65-1993e31565fb.json | 0 .../d1763a59-f5ba-436f-808a-189bde920e7c.json | 0 .../d83ca697-3bb1-431d-b740-9691b5a20b39.json | 0 .../f048c9c7-fc23-4361-8cb8-38bee74e3405.json | 0 .../fe5acdef-dd9d-4aad-be88-edf6d531985a.json | 0 docs/outstanding-issues.md | 18 ++++++++++++------ 22 files changed, 12 insertions(+), 6 deletions(-) rename docs/outstanding-issues-inbox/{ => applied}/05a7854d-49d3-40b1-8973-fbbf191883da.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/0c9671c5-34ed-4c53-b896-3a61108816ba.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/1f2a0d03-e7be-4e77-9e41-d3e5e854ab9f.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/416afc07-6d2a-479d-a10d-c323bf037767.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/47f8e3bf-7d16-489d-b01d-962cb3b0e9df.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/50eff4b6-19e0-4992-9596-95501f63226b.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/5481ae21-8980-4056-9c0f-64baee28bd73.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/5c3b582c-9dc6-4462-ba5c-563279d9ebc5.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/61ed61bb-e3f1-406d-9842-5068bf30dfcc.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/6895c60a-309d-4ecc-ae2e-5d34163761b2.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/7099cbfb-c105-45c3-9ad3-4b1ada4d4a3e.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/76390200-e6e0-4280-86de-adb758ee98a9.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/79122ddb-a365-495b-a591-b21fa621aecb.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/7c0f035c-2b63-47c2-b5a6-6df0118fdb9f.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/a01b19c4-f58a-4d57-915a-f9e62849ca6c.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/cab0fdc2-b88c-45bb-98ac-3e4acffcddd2.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/cb98537c-0571-4bd2-bf65-1993e31565fb.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/d1763a59-f5ba-436f-808a-189bde920e7c.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/d83ca697-3bb1-431d-b740-9691b5a20b39.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/f048c9c7-fc23-4361-8cb8-38bee74e3405.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/fe5acdef-dd9d-4aad-be88-edf6d531985a.json (100%) diff --git a/docs/outstanding-issues-inbox/05a7854d-49d3-40b1-8973-fbbf191883da.json b/docs/outstanding-issues-inbox/applied/05a7854d-49d3-40b1-8973-fbbf191883da.json similarity index 100% rename from docs/outstanding-issues-inbox/05a7854d-49d3-40b1-8973-fbbf191883da.json rename to docs/outstanding-issues-inbox/applied/05a7854d-49d3-40b1-8973-fbbf191883da.json diff --git a/docs/outstanding-issues-inbox/0c9671c5-34ed-4c53-b896-3a61108816ba.json b/docs/outstanding-issues-inbox/applied/0c9671c5-34ed-4c53-b896-3a61108816ba.json similarity index 100% rename from docs/outstanding-issues-inbox/0c9671c5-34ed-4c53-b896-3a61108816ba.json rename to docs/outstanding-issues-inbox/applied/0c9671c5-34ed-4c53-b896-3a61108816ba.json diff --git a/docs/outstanding-issues-inbox/1f2a0d03-e7be-4e77-9e41-d3e5e854ab9f.json b/docs/outstanding-issues-inbox/applied/1f2a0d03-e7be-4e77-9e41-d3e5e854ab9f.json similarity index 100% rename from docs/outstanding-issues-inbox/1f2a0d03-e7be-4e77-9e41-d3e5e854ab9f.json rename to docs/outstanding-issues-inbox/applied/1f2a0d03-e7be-4e77-9e41-d3e5e854ab9f.json diff --git a/docs/outstanding-issues-inbox/416afc07-6d2a-479d-a10d-c323bf037767.json b/docs/outstanding-issues-inbox/applied/416afc07-6d2a-479d-a10d-c323bf037767.json similarity index 100% rename from docs/outstanding-issues-inbox/416afc07-6d2a-479d-a10d-c323bf037767.json rename to docs/outstanding-issues-inbox/applied/416afc07-6d2a-479d-a10d-c323bf037767.json diff --git a/docs/outstanding-issues-inbox/47f8e3bf-7d16-489d-b01d-962cb3b0e9df.json b/docs/outstanding-issues-inbox/applied/47f8e3bf-7d16-489d-b01d-962cb3b0e9df.json similarity index 100% rename from docs/outstanding-issues-inbox/47f8e3bf-7d16-489d-b01d-962cb3b0e9df.json rename to docs/outstanding-issues-inbox/applied/47f8e3bf-7d16-489d-b01d-962cb3b0e9df.json diff --git a/docs/outstanding-issues-inbox/50eff4b6-19e0-4992-9596-95501f63226b.json b/docs/outstanding-issues-inbox/applied/50eff4b6-19e0-4992-9596-95501f63226b.json similarity index 100% rename from docs/outstanding-issues-inbox/50eff4b6-19e0-4992-9596-95501f63226b.json rename to docs/outstanding-issues-inbox/applied/50eff4b6-19e0-4992-9596-95501f63226b.json diff --git a/docs/outstanding-issues-inbox/5481ae21-8980-4056-9c0f-64baee28bd73.json b/docs/outstanding-issues-inbox/applied/5481ae21-8980-4056-9c0f-64baee28bd73.json similarity index 100% rename from docs/outstanding-issues-inbox/5481ae21-8980-4056-9c0f-64baee28bd73.json rename to docs/outstanding-issues-inbox/applied/5481ae21-8980-4056-9c0f-64baee28bd73.json diff --git a/docs/outstanding-issues-inbox/5c3b582c-9dc6-4462-ba5c-563279d9ebc5.json b/docs/outstanding-issues-inbox/applied/5c3b582c-9dc6-4462-ba5c-563279d9ebc5.json similarity index 100% rename from docs/outstanding-issues-inbox/5c3b582c-9dc6-4462-ba5c-563279d9ebc5.json rename to docs/outstanding-issues-inbox/applied/5c3b582c-9dc6-4462-ba5c-563279d9ebc5.json diff --git a/docs/outstanding-issues-inbox/61ed61bb-e3f1-406d-9842-5068bf30dfcc.json b/docs/outstanding-issues-inbox/applied/61ed61bb-e3f1-406d-9842-5068bf30dfcc.json similarity index 100% rename from docs/outstanding-issues-inbox/61ed61bb-e3f1-406d-9842-5068bf30dfcc.json rename to docs/outstanding-issues-inbox/applied/61ed61bb-e3f1-406d-9842-5068bf30dfcc.json diff --git a/docs/outstanding-issues-inbox/6895c60a-309d-4ecc-ae2e-5d34163761b2.json b/docs/outstanding-issues-inbox/applied/6895c60a-309d-4ecc-ae2e-5d34163761b2.json similarity index 100% rename from docs/outstanding-issues-inbox/6895c60a-309d-4ecc-ae2e-5d34163761b2.json rename to docs/outstanding-issues-inbox/applied/6895c60a-309d-4ecc-ae2e-5d34163761b2.json diff --git a/docs/outstanding-issues-inbox/7099cbfb-c105-45c3-9ad3-4b1ada4d4a3e.json b/docs/outstanding-issues-inbox/applied/7099cbfb-c105-45c3-9ad3-4b1ada4d4a3e.json similarity index 100% rename from docs/outstanding-issues-inbox/7099cbfb-c105-45c3-9ad3-4b1ada4d4a3e.json rename to docs/outstanding-issues-inbox/applied/7099cbfb-c105-45c3-9ad3-4b1ada4d4a3e.json diff --git a/docs/outstanding-issues-inbox/76390200-e6e0-4280-86de-adb758ee98a9.json b/docs/outstanding-issues-inbox/applied/76390200-e6e0-4280-86de-adb758ee98a9.json similarity index 100% rename from docs/outstanding-issues-inbox/76390200-e6e0-4280-86de-adb758ee98a9.json rename to docs/outstanding-issues-inbox/applied/76390200-e6e0-4280-86de-adb758ee98a9.json diff --git a/docs/outstanding-issues-inbox/79122ddb-a365-495b-a591-b21fa621aecb.json b/docs/outstanding-issues-inbox/applied/79122ddb-a365-495b-a591-b21fa621aecb.json similarity index 100% rename from docs/outstanding-issues-inbox/79122ddb-a365-495b-a591-b21fa621aecb.json rename to docs/outstanding-issues-inbox/applied/79122ddb-a365-495b-a591-b21fa621aecb.json diff --git a/docs/outstanding-issues-inbox/7c0f035c-2b63-47c2-b5a6-6df0118fdb9f.json b/docs/outstanding-issues-inbox/applied/7c0f035c-2b63-47c2-b5a6-6df0118fdb9f.json similarity index 100% rename from docs/outstanding-issues-inbox/7c0f035c-2b63-47c2-b5a6-6df0118fdb9f.json rename to docs/outstanding-issues-inbox/applied/7c0f035c-2b63-47c2-b5a6-6df0118fdb9f.json diff --git a/docs/outstanding-issues-inbox/a01b19c4-f58a-4d57-915a-f9e62849ca6c.json b/docs/outstanding-issues-inbox/applied/a01b19c4-f58a-4d57-915a-f9e62849ca6c.json similarity index 100% rename from docs/outstanding-issues-inbox/a01b19c4-f58a-4d57-915a-f9e62849ca6c.json rename to docs/outstanding-issues-inbox/applied/a01b19c4-f58a-4d57-915a-f9e62849ca6c.json diff --git a/docs/outstanding-issues-inbox/cab0fdc2-b88c-45bb-98ac-3e4acffcddd2.json b/docs/outstanding-issues-inbox/applied/cab0fdc2-b88c-45bb-98ac-3e4acffcddd2.json similarity index 100% rename from docs/outstanding-issues-inbox/cab0fdc2-b88c-45bb-98ac-3e4acffcddd2.json rename to docs/outstanding-issues-inbox/applied/cab0fdc2-b88c-45bb-98ac-3e4acffcddd2.json diff --git a/docs/outstanding-issues-inbox/cb98537c-0571-4bd2-bf65-1993e31565fb.json b/docs/outstanding-issues-inbox/applied/cb98537c-0571-4bd2-bf65-1993e31565fb.json similarity index 100% rename from docs/outstanding-issues-inbox/cb98537c-0571-4bd2-bf65-1993e31565fb.json rename to docs/outstanding-issues-inbox/applied/cb98537c-0571-4bd2-bf65-1993e31565fb.json diff --git a/docs/outstanding-issues-inbox/d1763a59-f5ba-436f-808a-189bde920e7c.json b/docs/outstanding-issues-inbox/applied/d1763a59-f5ba-436f-808a-189bde920e7c.json similarity index 100% rename from docs/outstanding-issues-inbox/d1763a59-f5ba-436f-808a-189bde920e7c.json rename to docs/outstanding-issues-inbox/applied/d1763a59-f5ba-436f-808a-189bde920e7c.json diff --git a/docs/outstanding-issues-inbox/d83ca697-3bb1-431d-b740-9691b5a20b39.json b/docs/outstanding-issues-inbox/applied/d83ca697-3bb1-431d-b740-9691b5a20b39.json similarity index 100% rename from docs/outstanding-issues-inbox/d83ca697-3bb1-431d-b740-9691b5a20b39.json rename to docs/outstanding-issues-inbox/applied/d83ca697-3bb1-431d-b740-9691b5a20b39.json diff --git a/docs/outstanding-issues-inbox/f048c9c7-fc23-4361-8cb8-38bee74e3405.json b/docs/outstanding-issues-inbox/applied/f048c9c7-fc23-4361-8cb8-38bee74e3405.json similarity index 100% rename from docs/outstanding-issues-inbox/f048c9c7-fc23-4361-8cb8-38bee74e3405.json rename to docs/outstanding-issues-inbox/applied/f048c9c7-fc23-4361-8cb8-38bee74e3405.json diff --git a/docs/outstanding-issues-inbox/fe5acdef-dd9d-4aad-be88-edf6d531985a.json b/docs/outstanding-issues-inbox/applied/fe5acdef-dd9d-4aad-be88-edf6d531985a.json similarity index 100% rename from docs/outstanding-issues-inbox/fe5acdef-dd9d-4aad-be88-edf6d531985a.json rename to docs/outstanding-issues-inbox/applied/fe5acdef-dd9d-4aad-be88-edf6d531985a.json diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index 4790d34165..ee37e2f66f 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -94,7 +94,7 @@ removed after current-main verification; it is not missing recommended work. | #100 | P2 | rec | Buffered answer generation has no incremental verified delivery | UPDATE 2026-08-21 (repo read on main at 1cc0d2987, no provider access): the client-side half has landed. NEXT_PUBLIC_RAG_INCREMENTAL_EVIDENCE_PREVIEW_RENDER is present in .env.example (commented, default false) and is consumed by src/lib/client-env.ts, so the Phase 1 client parsing/rendering flag exists alongside the server-side RAG_INCREMENTAL_EVIDENCE_PREVIEW=false. Remaining scope is therefore narrower than recorded: verify:ui proof of the client render path, then the design's provider-backed acceptance gates before production enablement. Phase 2 stays provider-gated. Not verified: whether the render path is actually exercised by a UI journey. | `docs/verified-answer-incremental-delivery-design.md`; `docs/audit/latency-audit-2026-07-28.md` L0-1; `src/lib/answer-stream-contract.ts:18-21` | 2026-07-30 | | #102 | P3 | task | Apply the additive `documents` index debt (operator) | UPDATE 2026-08-21 (read-only Supabase MCP get_advisors performance lint against production ref sjrfecxgysukkwxsowpy): documents_title_trgm_idx exists on public.documents and is reported by the unused_index lint as never used. TREAT THAT AS WEAK EVIDENCE, NOT CONFIRMATION: 20260819100200_restore_search_health_trigram_indexes was applied two days earlier and recreating an index resets its usage statistics, so a zero-use reading is expected regardless of whether the bare-column ILIKE predicates can reach it. The same lint currently reports 31 unused indexes, several of them freshly restored in the 20260819100000-100300 batch, which is consistent with a stats reset rather than dead indexing. The row's actual claim - that the index covers a CONCATENATED expression and so cannot serve the bare-column predicates in the documents API route and rag-candidate-sources - was NOT tested, because that needs EXPLAIN or a pg_indexes read and SQL execution was blocked in this session. Re-measure with EXPLAIN in the operator window before applying the prepared runbook. | `docs/audit/latency-audit-2026-07-28.md` L2-3/L2-5; `docs/operator-apply-performance-latency-remediation.md` | 2026-07-29 | | #191 | P3 | task | X5: ACL-migration consolidation (provider-gated) | **Outcome:** ACL-related migrations are consolidated per maturity work-order X5 without weakening owner-scope/RLS. **Next:** DB-owner approved window only; live-DB provider confirmation required before apply. **Stop:** no hosted apply from an agent session without explicit approval. | docs/maturity-backlog-workorders.md X5; #086 | 2026-07-31 | -| #231 | P2 | issue | Generation fallbacks no longer stick in answer cache; lithium generation quality still falls back safely | PHASE 5.2 CONFIRMED SATISFIED with fresh data 2026-08-22 Perth (2026-08-21 UTC), not reopened. This row already recorded that remediation-plan Phase 5.2 is satisfied by S1's 2026-08-17 healthy-latency probes; the Phase 5 close-out re-measured production end to end and confirms it. Retrieval now costs 955 ms on the text fast path and 6,720 ms on hybrid (from 31,610 ms and 21,757 ms at the incident), against answerRouteBudgetMs.fast of 25,000 ms -- so retrieval consumes 4-27% of the fast budget and is no longer capable of binding it. The 2026-08-14 verdict that pre-generation latency WAS the binding cause stands for that window and is now closed out. Residual R4 (chronic ~30 s strong-route provider_timeout on metformin-renal-dosing and valproate-pregnancy, with a safe source-backed extractive fallback) is generation-side and unchanged; no separate R4 row was created, per this row's own instruction. INCONSISTENCY TO FIX AT RECONCILE: the recommended-execution-queue row for #231 still presents it as A1 / 'immediate approved live investigation' with the old framing ('live answers degrade to source-only when answerRouteBudgetMs.fast binds while retrieval is healthy'), which contradicts the P1 -> P2 re-grade recorded in this detail row. The queue entry should be re-graded to match P2 and re-scoped to the R4 generation-side residual, so the queue stops advertising a retrieval investigation that the measurements have closed. | docs/audit/live-drift-forensics-2026-08.md Phase 5 close-out 5.1(a) and 5.2; production probes 2026-08-22 Perth (2026-08-21 UTC) | 2026-08-04 | +| #231 | P2 | issue | Re-scoped: separate initial provider timeouts from quality-retry exhaustion before changing the RAG path | PHASE 5.2 CONFIRMED SATISFIED with fresh data 2026-08-22 Perth (2026-08-21 UTC), not reopened. This row already recorded that remediation-plan Phase 5.2 is satisfied by S1's 2026-08-17 healthy-latency probes; the Phase 5 close-out re-measured production end to end and confirms it. Retrieval now costs 955 ms on the text fast path and 6,720 ms on hybrid (from 31,610 ms and 21,757 ms at the incident), against answerRouteBudgetMs.fast of 25,000 ms -- so retrieval consumes 4-27% of the fast budget and is no longer capable of binding it. The 2026-08-14 verdict that pre-generation latency WAS the binding cause stands for that window and is now closed out. Residual R4 (chronic ~30 s strong-route provider_timeout on metformin-renal-dosing and valproate-pregnancy, with a safe source-backed extractive fallback) is generation-side and unchanged; no separate R4 row was created, per this row's own instruction. INCONSISTENCY TO FIX AT RECONCILE: the recommended-execution-queue row for #231 still presents it as A1 / 'immediate approved live investigation' with the old framing ('live answers degrade to source-only when answerRouteBudgetMs.fast binds while retrieval is healthy'), which contradicts the P1 -> P2 re-grade recorded in this detail row. The queue entry should be re-graded to match P2 and re-scoped to the R4 generation-side residual, so the queue stops advertising a retrieval investigation that the measurements have closed. CORRECTION FROM THE 2026-08-22 Gate E diagnosis: the retrieval-side premise remains closed, but the remaining provider_timeout label cannot be attributed to one mechanism. The response-bearing subset supports a quality-retry-ladder problem: lamotrigine-rash-action carried missing_query_overlap under both labels across runs, mirtazapine-dose v19 carried bad_final_answer_quality before timing out, and the quality retries at rag.ts:3623 and rag.ts:3709 have no deadline-admission check. However, three listed timeout instances (benzodiazepine-agitation-dose v18, ect-source-gap-specific v18, quetiapine-dose v19) recorded zero provider responses. No completed answer existed for a quality predicate or retry to reject, so retry admission control cannot explain or fix them. NEXT: preserve these as two mechanisms. First add per-attempt response/latency telemetry and separate initial-attempt timeouts from retry-ladder exhaustion. Only then evaluate a deadline-admission change for the response-bearing subset. Do not raise answerRouteBudgetMs or weaken quality gates. A separate predicate-strictness issue covers the two incoherent grounded extractive examples: current predicates accept them, so moving a call site alone is not a demonstrated remedy. | docs/audit/live-drift-forensics-2026-08.md Phase 5 close-out 5.1(a) and 5.2; production probes 2026-08-22 Perth (2026-08-21 UTC); docs/rag-improvement/231-diagnosis-2026-08-22.md (corrected after PR #2264 review) | 2026-08-04 | | #308 | P3 | issue | Desktop /documents/search CLS is 0.119, above threshold and stable across runs and baselines | Measured 2026-08-12 during the #147 close-out, twice, on the offline Lighthouse harness (Chromium 141): desktop /documents/search CLS **0.119**, against a committed baseline that also reads **0.119**. So this is long-standing and deterministic, not a regression — and it is above the 0.1 threshold. It sits outside #147's scope, which was mobile only, and it contradicts that row's claim that 'desktop passes everywhere: 0.016-0.097' — that range is stale. Companion desktop values from the same runs, all passing: /dsm 0.014, /forms 0.059-0.064, / 0.006, /therapy-compass 0.000. Desktop attribution completed 2026-08-14: a Playwright + PerformanceObserver(layout-shift) harness against an offline production build at 1350x940 DPR 1 recorded **0.118** CLS. This is a separate attribution measurement, not a replacement for the canonical 0.119 Lighthouse value. One first-paint+~0.3-0.5s event contributed ~99.98% of that harness total: MasterSearchHeader's composer-adoption effect portals the search composer into GlobalSearchShell's desktop slot, while the header shrinks 184px and the slot grows 0 -> 184px. This is shared desktop search-chrome timing, not page-local. Next: reserve the settled height at the adoption boundary under the one-composer/hidden-means-zero-reserve contracts, then re-measure with the same harness. Stop: do not raise the CLS budget; do not read local LCP or TBT from the loopback harness; and do not use a blanket min-height that hides the shift without matching the header reserve. | Local offline verify:lighthouse runs 2026-08-12 (two runs, identical CLS); #147 close-out; lighthouse-budget.json. Attribution: session 2026-08-14, PR branch codex/visual-layout-polish; desktop CLS script adapted from scripts/measure-cls-attribution.mjs (offline, not committed). | 2026-08-12 | | #321 | P3 | task | Four follow-up groups cover nine controls after #291 | PARTIAL 18 August 2026. Of the four follow-up groups: (1) the filmstrip 'Page unknown' control is FIXED — document-image-filmstrip.tsx converted its data-driven disabled state from native disabled to aria-disabled=true + ignoreUnavailableActivation + an sr-only reason, per docs/wiring-conventions.md's stated-reason pattern (settles this one control from #291's follow-up list); tests/document-image-filmstrip.dom.test.tsx gained a focused case (aria-disabled, not natively disabled, accessible description, click is a no-op), vitest run: 3 passed. The other three groups are unchanged and still not single-PR-sized: the six differential comparison page controls remain coupled to its own planned rewrite and pinned density test; DocumentViewer's persistent-access-reason/transient-loading split is a classification design decision, not yet made; the pin-limit control remains a capacity-state judgement call. Stays open for those three. | PR #1778 body; verified against main 2d27039 | 2026-08-14 | | #2AB2NJ | P3 | task | Owner decision: enable RAG_TELEMETRY_EXTENDED (verification_latency_ms projection) in production once a dashboard consumer exists | Packet S5 (PR #2056, merge 093f9340c) landed the B1 telemetry gap assessment: the one proven gap is verification_latency_ms, now persisted behind RAG_TELEMETRY_EXTENDED (typed, default false) via the allow-listed projection module with canary-absence tests. Enabling it in production is an owner decision gated on a dashboard consumer existing (no consumer today), and is a Railway env change (provider-backed, explicit approval; rollback = set false). Next: when a dashboard question needs verification latency, set RAG_TELEMETRY_EXTENDED=true on the Database service after confirming the canary-absence tests are still green on main. Stop: do not enable speculatively; do not add unproven fields. | RAG programme coordinator, packet S5 (PR #2056) follow-ups, 2026-08-17 | 2026-08-17 | @@ -109,9 +109,9 @@ removed after current-main verification; it is not missing recommended work. | #6SMMB4 | P3 | task | Confirm D:\.npm-cache is a registered Dev Drive trusted cache, or Defender is scanning every npm ci | The repo lives on a Windows Dev Drive (D:, ReFS, 50 GB) and npm config get cache resolves to D:\.npm-cache, which is correctly on the same volume. Whether that path is registered as a Dev Drive TRUSTED cache is unverified: 'fsutil devdrv query D:' returns 'Failed to open the volume. Error 5: Access is denied' without elevation, and the non-elevated registry fallback (HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem, FilterAttachModeOnDevDrive and DevDriveTrustSetting) reads empty. If it is not registered, Microsoft Defender real-time scanning runs over every npm ci — and this machine performs a lot of them: 21 D: worktrees each carry their own ~0.89 GB / 51,735-file node_modules, because npm extracts fresh copies rather than hardlinking from cache (ReFS does support hardlinks here, probed directly, but npm does not use them). Next: from an ELEVATED prompt run 'fsutil devdrv query D:' and, if the cache is not listed as trusted, 'fsutil devdrv trust D:\.npm-cache'. Cheap, one-off, no code change. Not blocking anything. | session 2026-08-18; fsutil Error 5 without elevation | 2026-08-18 | | #GKFK9V | P3 | rec | Where the RAG improvement programme board lives, and to check it before starting RAG-surface work | The RAG improvement programme board lives at docs/rag-improvement/README.md (design), HANDOVER.md (packet status table + prompts) and COORDINATION.md (coordinator manual). Before starting any RAG-surface item, check that status table AND the open PR list (#292); packets S1-S3, G1, S4-S6 landed 2026-08-17/18; Track A complete. | docs/rag-improvement/README.md, HANDOVER.md, COORDINATION.md; session 2026-08-18 | 2026-08-18 | | #RZQQBT | P3 | task | Confirm from its own log whether the PreCompact hook's output actually reaches model context | UPDATE 2026-08-21 (measured on this machine): still unanswered, and now with a concrete reason. The hook's own log has never been written - neither the main repository's .git/claude-precompact.log nor the per-worktree claude-precompact.log under the .git/worktrees admin directory exists. So no compaction has fired the hook in an inspectable session yet, and the question stays open rather than answered-negative. Next: after the next compaction in a session using this checkout, read that path; an entry proves the hook ran, and its absence in model context would then be the separate question. | PR #2113 .claude/hooks/precompact-issues-capture.sh | 2026-08-18 | -| #CCZ4HB | P1 | rec | PR churn has exhausted the review-bot budget, so PRs are now landing with no automated review at all | UPDATE 2026-08-21 (second session, three PRs later): STILL HAPPENING, and now measured on consecutive PRs rather than one. CodeRabbit skipped review on PR #2252, #2255 and #2256 — every PR opened in that session — reporting: 'Your 88 included PR review attempts over the past 7 days set your current allowance at 1 review per hour' and 'Your organization has reached its usage spending cap.' Countdown observed shrinking across the session (48 minutes -> 32 -> 27), i.e. the hourly allowance was being consumed as fast as it refilled. Two refinements to the row below. (1) The 2026-08-18 reading was 101 reviews/7 days; this one is 88 attempts/7 days, so the trailing window is falling but the spending cap is the binding constraint now, not the rate limit — raising the cap is therefore the lever, and it is an account/billing decision the repo cannot gate its way out of. (2) Draft PRs are skipped outright ('Review skipped. Draft detected'), so the repo's own default of opening PRs as drafts guarantees no bot review until someone undrafts — and undrafting mid-CI also cancels the in-flight run and reds the required aggregate (observed on #2255 and #2256). That interaction is worth stating in AGENTS.md: the draft default, the review-bot budget, and the cancel-in-progress churn are the same problem seen from three sides. Next unchanged: decide between a churn gate, raising the cap, or accepting unreviewed merges deliberately. PRIOR RECORD: CodeRabbit on PR #2113: '101 included PR reviews in the past 7 days; at that activity level, included reviews refill at 1 review per hour. Your organization has reached its usage spending cap.' The Codex connector reported its own usage limit on the same PR. Net effect: #2113 received ZERO automated review, and so will subsequent PRs until the cap resets or credits are added. AGENTS.md 'PR bundling' already measured the CI half of this cost on 2026-07-30 (437 PR-triggered runs over ~3 days, ~40% cancelled mid-run, ~12 Production-UI-hours burned on runs that never completed). This is the second bill for the same behaviour and the more dangerous one, because CI waste is money while missing review is undetected defects — and the PRs most likely to need review are the ones landing during a churn spike. The bundling rule exists as prose in AGENTS.md and is evidently not binding; the newtask skill also asks the question in prose. Decide whether it gets a gate. Note the repo has already learned this lesson once in a different area: .claude/hooks/pr-handoff-stop.sh states in its own header that 'prose rules in AGENTS.md have not held, a denied tool call does.' Next: decide between (a) a push/PR-creation gate that refuses a new branch when an open PR of the same scope exists, (b) raising the bot spending cap, or (c) accepting unreviewed merges deliberately rather than by accident. Stop rule: do not weaken any required check to compensate for missing bot review. | CodeRabbit comments on PRs #2252, #2255, #2256 (2026-08-21); prior evidence PR #2113 (2026-08-18) | 2026-08-18 | +| #CCZ4HB | P1 | rec | PR churn has exhausted the review-bot budget, so PRs are now landing with no automated review at all | UPDATE 2026-08-21 (second session, three PRs later): STILL HAPPENING, and now measured on consecutive PRs rather than one. CodeRabbit skipped review on PR #2252, #2255 and #2256 — every PR opened in that session — reporting: 'Your 88 included PR review attempts over the past 7 days set your current allowance at 1 review per hour' and 'Your organization has reached its usage spending cap.' Countdown observed shrinking across the session (48 minutes -> 32 -> 27), i.e. the hourly allowance was being consumed as fast as it refilled. Two refinements to the row below. (1) The 2026-08-18 reading was 101 reviews/7 days; this one is 88 attempts/7 days, so the trailing window is falling but the spending cap is the binding constraint now, not the rate limit — raising the cap is therefore the lever, and it is an account/billing decision the repo cannot gate its way out of. (2) Draft PRs are skipped outright ('Review skipped. Draft detected'), so the repo's own default of opening PRs as drafts guarantees no bot review until someone undrafts — and undrafting mid-CI also cancels the in-flight run and reds the required aggregate (observed on #2255 and #2256). That interaction is worth stating in AGENTS.md: the draft default, the review-bot budget, and the cancel-in-progress churn are the same problem seen from three sides. Next unchanged: decide between a churn gate, raising the cap, or accepting unreviewed merges deliberately. PRIOR RECORD: CodeRabbit on PR #2113: '101 included PR reviews in the past 7 days; at that activity level, included reviews refill at 1 review per hour. Your organization has reached its usage spending cap.' The Codex connector reported its own usage limit on the same PR. Net effect: #2113 received ZERO automated review, and so will subsequent PRs until the cap resets or credits are added. AGENTS.md 'PR bundling' already measured the CI half of this cost on 2026-07-30 (437 PR-triggered runs over ~3 days, ~40% cancelled mid-run, ~12 Production-UI-hours burned on runs that never completed). This is the second bill for the same behaviour and the more dangerous one, because CI waste is money while missing review is undetected defects — and the PRs most likely to need review are the ones landing during a churn spike. The bundling rule exists as prose in AGENTS.md and is evidently not binding; the newtask skill also asks the question in prose. Decide whether it gets a gate. Note the repo has already learned this lesson once in a different area: .claude/hooks/pr-handoff-stop.sh states in its own header that 'prose rules in AGENTS.md have not held, a denied tool call does.' Next: decide between (a) a push/PR-creation gate that refuses a new branch when an open PR of the same scope exists, (b) raising the bot spending cap, or (c) accepting unreviewed merges deliberately rather than by accident. Stop rule: do not weaken any required check to compensate for missing bot review. UPDATE 2026-08-22: the problem remains confirmed on PR #2263: CodeRabbit reported a temporary review limit, a one-review-per-hour allowance, and an exhausted organisation spending cap. This is an owner billing/process decision, not a check failure that can be bypassed. The current decision remains: raise the cap to restore automated review coverage, or explicitly document an alternative review policy. Do not weaken required checks to compensate. | CodeRabbit comments on PRs #2252, #2255, #2256 (2026-08-21); prior evidence PR #2113 (2026-08-18); CodeRabbit review-limit notice on PR #2263 (2026-08-22) | 2026-08-18 | | #164Z0H | P3 | task | Confirm on a real Claude Code web session that the session-start hook now runs, after the exec-bit fix | UPDATE 2026-08-21 (repo read on main at 1cc0d2987): the repo-side half is now proven green. git ls-files -s .claude/hooks/ reports mode 100755 for all five hooks, session-start.sh included, so the 100644 asymmetry recorded here is fixed in the index rather than only on disk. What remains is exactly what this row was filed for and cannot be proven from Windows: one real Claude Code web/container session confirming the hook actually runs and provisions Node 24. | PR #2113; AGENTS.md 'Claude Code hook scripts' | 2026-08-18 | -| #6GW95D | P2 | task | Nine landed worktrees are still on disk holding ~4.5 GB on a 51%-full Dev Drive; removal was deferred because the fleet was live | UPDATE 2026-08-21 (later the same day): the scope is wider than .claude/worktrees, and the previous update's 69 is itself a floor. An accidental `npx vitest run` issued from C:/Users/joshs instead of the repo root enumerated 253 copies of just TWO test files across the home directory, i.e. 253 separate checkouts of this repository, spread over at least six distinct roots: .codex/worktrees, .gemini/antigravity/worktrees, .copilot/repos/copilot-worktrees, .local/share/opencode/worktree, Documents/Codex///work, and an AppData/Local/pnpm store projects directory (which also contained its own .worktrees). Temp copies under AppData/Local/Temp/guard-push-format-* were still present too. So every AI tool in the fleet keeps its own worktree root and none of them is covered by a sweep scoped to .claude/worktrees. Most had no node_modules (the run failed them with 'Cannot find package'), so raw disk is likely below 253 x 0.89 GB, but the count is the point: any recursive tool run from the home directory now walks all of them. Re-scope this row from 'nine landed worktrees' to a fleet-wide inventory across all six roots before pruning, and keep the #XCAX01 safety preconditions — the 2026-08-21 data-loss incident is why this is not a one-line delete. PRIOR RECORD: UPDATE 2026-08-21 (measured on this machine): the figure recorded here is badly understated and moving the wrong way. git worktree list reported 69 registered worktrees under .claude/worktrees, not nine. At the ~0.89 GB / 51,735-file node_modules per worktree measured in #6SMMB4, even partial installs put this well beyond the 4.5 GB recorded, on a Dev Drive noted as 51% full. SAFETY, learned the hard way on 2026-08-21: a sweep removed an in-use worktree during this very session and destroyed its uncommitted work - see the separate row filed for that. Re-measure actual on-disk size, then prune, but confirm each worktree is landed, holds no uncommitted or unpushed work, AND is not currently checked out by a live agent session before removing it. | Accidental home-directory vitest run, 2026-08-21: 253 test-file copies across six worktree roots | 2026-08-18 | +| #6GW95D | P3 | task | Fleet-wide worktree inventory and safe orphan cleanup remain, but the Dev Drive capacity emergency is resolved | UPDATE 2026-08-21 (later the same day): the scope is wider than .claude/worktrees, and the previous update's 69 is itself a floor. An accidental `npx vitest run` issued from C:/Users/joshs instead of the repo root enumerated 253 copies of just TWO test files across the home directory, i.e. 253 separate checkouts of this repository, spread over at least six distinct roots: .codex/worktrees, .gemini/antigravity/worktrees, .copilot/repos/copilot-worktrees, .local/share/opencode/worktree, Documents/Codex///work, and an AppData/Local/pnpm store projects directory (which also contained its own .worktrees). Temp copies under AppData/Local/Temp/guard-push-format-* were still present too. So every AI tool in the fleet keeps its own worktree root and none of them is covered by a sweep scoped to .claude/worktrees. Most had no node_modules (the run failed them with 'Cannot find package'), so raw disk is likely below 253 x 0.89 GB, but the count is the point: any recursive tool run from the home directory now walks all of them. Re-scope this row from 'nine landed worktrees' to a fleet-wide inventory across all six roots before pruning, and keep the #XCAX01 safety preconditions — the 2026-08-21 data-loss incident is why this is not a one-line delete. PRIOR RECORD: UPDATE 2026-08-21 (measured on this machine): the figure recorded here is badly understated and moving the wrong way. git worktree list reported 69 registered worktrees under .claude/worktrees, not nine. At the ~0.89 GB / 51,735-file node_modules per worktree measured in #6SMMB4, even partial installs put this well beyond the 4.5 GB recorded, on a Dev Drive noted as 51% full. SAFETY, learned the hard way on 2026-08-21: a sweep removed an in-use worktree during this very session and destroyed its uncommitted work - see the separate row filed for that. Re-measure actual on-disk size, then prune, but confirm each worktree is landed, holds no uncommitted or unpushed work, AND is not currently checked out by a live agent session before removing it. UPDATE 2026-08-22: the capacity premise is now false. D: has been expanded to 80 GB total, with 22 GB used and 59 GB available (27% used), so this is no longer a space emergency. The worktree safety/inventory evidence remains relevant: 253 checkouts across at least six roots, and the #XCAX01 preconditions remain mandatory. A same-day .claude/worktrees check found 30 directories: 12 registered to live sessions and 18 unregistered orphans. Sixteen orphans were empty and two held one entry; one empty unregistered directory could not be removed because a live process held it. NEXT: keep this as a low-priority fleet-inventory and safe-orphan-cleanup task. Any future cleanup must cover every root, default to report-only, skip a directory when liveness or cleanliness cannot be proved, and never delete a registered worktree. | Accidental home-directory vitest run, 2026-08-21: 253 test-file copies across six worktree roots; Dev Drive / worktree inventory re-measured 2026-08-22 | 2026-08-18 | | #VKH7N1 | P3 | rec | eval-canary neuroleptic-side-effect-escalation exceeded its 20 s latency SLO once | Run 32111839806 (canary pair 32100681177 -> 32111839806, otherwise green): strong generation took 20.2 s on neuroleptic-side-effect-escalation, flagged as a non-blocking latency advisory. Answer was still grounded via the source-backed extractive fallback. Watch on subsequent canaries; escalate only if it repeats or worsens. | docs/rag-improvement/HANDOVER.md packet table row S2, canary pair 32100681177 -> 32111839806 | 2026-08-18 | | #1K6T35 | P2 | issue | Point-in-time recovery is OFF on the live Supabase project, so the remediation plan's 'restore point before any mutating phase' rule cannot be met | Measured 2026-08-19 during the Phase 4 production window (forensics section 'Phase 4 completion', step 1). `supabase backups list --project-ref sjrfecxgysukkwxsowpy` returns pitr_enabled false, walg_enabled true, and seven retained daily physical backups, most recent COMPLETED 2026-08-17T20:33:28Z -- roughly 38 hours before that window opened. CONSEQUENCE: the recovery-point objective for the live clinical corpus (2851 documents, 70120 chunks) is up to ~24 hours, and the database-remediation plan's standing rule 'PITR/backup restore point captured before any mutating phase' CANNOT be satisfied on this project as currently configured. Phase 4 proceeded anyway and said so: every statement in it was index-only with an exact one-statement inverse (CREATE INDEX CONCURRENTLY <-> DROP INDEX CONCURRENTLY) and no data-loss surface, the same reasoning the 2026-08-14 incident window recorded. That reasoning does NOT generalise. Any future window that mutates DATA rather than indexes -- #022's BMJ attestation hosted apply, #036's public_corpus marker migration, #191's ACL consolidation, #057's restore/soak drill, or any reindex or backfill -- must not proceed on this precedent while the only restore point is a day-old physical backup. DECISION REQUIRED FROM THE OWNER, and it is dashboard plus billing work no agent can do: either enable PITR on the Supabase project (Database settings -> Add-ons -> Point in Time Recovery; it is a paid add-on, so this is a cost decision as well as a safety one), or deliberately accept the ~24h RPO and amend the plan's standing rule to say so, so that future sessions stop treating an unsatisfiable checklist item as if it had been met. Whichever is chosen, record it, because the current state is that the rule reads as satisfied by default when nobody checks. Consider re-grading this row to P1 if a ~24-hour worst-case data loss on the clinical corpus is judged unacceptable; it is filed P2 because the exposure is a deliberate platform configuration rather than a regression, and no data loss has occurred. Related: #057 (restore/rollback drill -- its value is limited while PITR is off), #188 and #196-#200 (DR codification). | Phase 4 production window 2026-08-19; docs/audit/live-drift-forensics-2026-08.md 'Phase 4 completion' step 1; PR #2151 | 2026-08-18 | | #TF6TPJ | P2 | issue | Repeated main-merges on open PR branches cancel required CI, so 'PR required' reads red with zero failing jobs | UPDATE 2026-08-21 (repo read on main at 1cc0d2987): the guard that closes the shared root cause has landed. scripts/guard-push.mjs now carries an explicit Guard 2 in-flight CI push guard block naming #HSSHRG, with inFlightCiVerdict() and findInFlightCiRuns(), covering merge-main syncs made outside sync:pr-branches. #HSSHRG was closed on that evidence. This row should be re-checked against that guard and closed too if the false-red symptom is gone; it was NOT verified against a live PR from here, which needs GitHub access. | PR #2143, runs 32170524256 and 32178668323, 2026-08-18 | 2026-08-18 | @@ -137,18 +137,18 @@ removed after current-main verification; it is not missing recommended work. | #S19JRT | P2 | task | Add the DB-side structural constraint backing the source_metadata pin, or document why the data-backed pin is sufficient | Re-files #343, closed 2026-08-18 with outcome 'Made retrieval row contract source_metadata schema structural and nullish' -- that outcome is false. Verified 2026-08-21: PR #2107 loosened the source_metadata pin in src/lib/rag/rag-row-contracts.ts to .nullish(); PR #2121 restored the strict .nullable()-required-key pin (git log: ce702ba68 then 4575cf57a). The comment at rag-row-contracts.ts:44-49 explicitly reads 'PR #2107 loosened it to .nullish() and this PR restores it. See docs/outstanding-issues.md #343 for the constraint-backing follow-up.' The DB-side structural constraint (check (jsonb_typeof(metadata) = 'object')) was never added: grep of supabase/schema.sql and supabase/migrations/ finds only 'metadata jsonb not null default {}::jsonb' with no jsonb_typeof check anywhere. The cancelled duplicate #ND10QT record itself states '#343, which is still open', confirming the two closures landed inconsistently. Actionable follow-up: add the check (jsonb_typeof(metadata) = 'object') constraint on documents.metadata with a fail-fast validation guard migration per AGENTS.md's guard-migration contract, or record in this row why the Zod-level pin in rag-row-contracts.ts is sufficient without a DB constraint. | session 2026-08-21 ledger reconciliation and docs-truth pass | 2026-08-20 | | #3514B7 | P2 | issue | Live production carries migration 20260820120000_migration_history_versions_rpc, which exists nowhere in the repository | Found 2026-08-21 by a read-only Supabase MCP list_migrations against production ref sjrfecxgysukkwxsowpy (Clinical KB Database), compared against main at 1cc0d2987. Production's newest recorded version is 20260820120000 migration_history_versions_rpc. supabase/migrations/ holds 210 files and its newest is 20260819110500_validate_history_function_bodies.sql; there is no 20260820 file, and the string migration_history_versions has zero occurrences anywhere under supabase/, scripts/ or src/. The live database is therefore one migration ahead of the repository, and that migration's SQL is not under version control here. LIKELY RELATED, and the reason this matters rather than being cosmetic: pending request cc60253d (2026-08-19) records that live-drift's Align migration history step fails on PGRST106 because supabase_migrations is not exposed to PostgREST. An RPC named migration_history_versions is exactly the shape of a fix for that, applied live on 2026-08-20 with the repo side still unmerged. NOT VERIFIED from this session: whether an open PR carries the file, which needs GitHub access. NEXT: identify the PR or session that applied it; if none exists, capture the live function definition and land it as a forward migration so schema.sql, the drift manifest and the migration chain agree. STOP: do not re-apply, repair, or mark-apply anything on production to resolve this - it is a repo-side reconciliation, and any live mutation needs its own approved window under the guard-migration contract. | Read-only Supabase MCP list_migrations on sjrfecxgysukkwxsowpy, 2026-08-21; compared to supabase/migrations on main 1cc0d2987; relates to pending request cc60253d | 2026-08-20 | | #3SG2H9 | P3 | task | Ledger inbox update path is never driven with a modern Crockford display id, and ledger-inbox.mjs --self-test uses only legacy #001 | Residual of #DREDWA, surfaced by a Codex review finding on PR #2217 and then measured rather than accepted. WHAT IS ALREADY COVERED, so this is a narrow gap and not a reopening: tests/repo-hygiene.test.ts 'fingerprints and closes ULID-display-id rows minted by reconcile' drives applyRequest with action done against the ULID display id #6BG9X2, asserts the row is archived, and asserts the stale-fingerprint path throws; scripts/outstanding-issues.mjs self-tests drive addIssue, resolveIssue and updateIssue against the all-digit Crockford id #041061 ('done archives all-digit Crockford #041061'); and scripts/check-outstanding-issues.mjs self-tests resolve issueRowFingerprint for #041061 with an explicit failure message. The original bug and its stated failure mode are therefore guarded. WHAT IS STILL UNCOVERED: (1) the inbox update action is only ever applied against legacy #001 - the ULID test covers done alone; (2) scripts/ledger-inbox.mjs --self-test builds its done, update, cancel and reconcile fixtures entirely from #001, so the self-test that ships with the writer would not catch an id-scheme regression on its own. NEXT: add a ULID-display-id update case beside the existing done case in tests/repo-hygiene.test.ts, and give ledger-inbox.mjs --self-test one modern-id row (ideally the all-digit #041061 shape) driven through done, update and reconcile. Small and offline-testable; npm run test:focused -- --files tests/repo-hygiene.test.ts plus npm run check:outstanding-issues covers it. | Codex review comment 3829158494 on PR #2217, verified against tests/repo-hygiene.test.ts, scripts/outstanding-issues.mjs, scripts/check-outstanding-issues.mjs and scripts/ledger-inbox.mjs on 2026-08-21 | 2026-08-21 | -| #50QRCF | P2 | issue | Lighthouse budget mobile-root CLS is intermittent: 0.223 vs 0.016 baseline on one run, ~0.000 on the next, same code | MEASURED ON CI, NOT INFERRED. PR #2204 ran the same Lighthouse budget job on two consecutive heads whose only difference was deleting one JSON file under docs/outstanding-issues-inbox/ -- no source, asset, route or style change, nothing that can affect layout. Head c8b7bcdd PASSED. Head 09ff450c FAILED with exactly one metric out of tolerance: 'mobile-root cls +0.207 vs baseline (max +0.02)', measuring 0.223 against a 0.016 baseline (job 96780922258, run 32485431566). Every other cell was comfortably inside tolerance on the failing run: mobile-root LCP 2261 vs 2253, TBT 312 vs 296; mobile-documents-search LCP 2274 vs 2272, TBT 394 vs 348; desktop-documents-search LCP 804 vs 866; desktop-root LCP 815 vs 822. TWO SEPARATE PROBLEMS. (1) GATE RELIABILITY: a required check in pr-required flips pass/fail on a diff that cannot influence it, so it can block any PR at random. Note the grader already re-confirms an out-of-budget cell twice and takes the majority, so this survived that mechanism -- the instability is wider than a single spike. (2) PROBABLE REAL DEFECT ON THE MOBILE HOME ROUTE: 0.223 is not a marginal overshoot of a 0.02 tolerance, it is a large layout shift that sometimes occurs during load on '/' at mobile viewport and sometimes does not. Likely candidates are a webfont swap, an image or media element without reserved dimensions, or late-hydrating chrome (the phone composer/header reserve is a known-sensitive area per docs/search-chrome-behaviour.md). A user on a phone would feel this when it happens. Worth reproducing directly rather than only through the budget gate. DO NOT respond by widening the CLS tolerance or refreshing the baseline to absorb 0.223 -- that would encode an intermittent user-visible shift as acceptable. Diagnose which element shifts first. The Lighthouse artifact for the failing run is retained (artifact 9447841108) and contains the per-run reports, which identify the shifting elements. RELATED CAUTION FOR TRIAGE: main's CI runs do not exercise this job (path-scoped, perf scope only), so a green main run is not evidence the gate passes there -- the ci-triage bot correctly classified it 'not baselined'. | job 96780922258 (run 32485431566) vs the passing run on head c8b7bcdd; lighthouse-budget.json; scripts/check-lighthouse-budget.mjs | 2026-08-21 | +| #50QRCF | P2 | issue | Lighthouse budget mobile-root CLS is intermittent: 0.223 vs 0.016 baseline on one run, ~0.000 on the next, same code | CAUSE FOUND AND FIXED — landed on main 2026-08-22 in PR #2253 (merge 66594dd, fix commit 0cf0493). Full evidence is recorded on #TYZK23; this row is the gate-reliability half of the same defect. Summary: the shifting element is the PWA install card (div.pwa-notice-stack), which can mount during a window in which #main-content is briefly absent from the DOM while Next 16 streams and hydrates the route. Its phone geometry is selected by body:has(#main-content[data-phone-footer-owner="hero"]) …, so a card mounting in that gap paints tall (h=401) and is restyled compact (h=161) when the shell returns — one discrete 0.2230 shift. That also explains observation (1) on this row, the part that looked impossible: the gate flips pass/fail on diffs that cannot influence layout because what varies between runs is TIMING (network speed, and whether beforeinstallprompt fires early enough to land inside the gap), not the diff. Deleting one JSON file changes nothing about the page and everything about which side of that race the run lands on — so head c8b7bcdd passing and head 09ff450c failing was never a contradiction. FIXED in src/components/pwa-lifecycle.tsx: the notice stack is held unmounted until the app shell is present. Reproduced locally at exactly 0.2230 before the fix and 0.000 after, using a synthetic beforeinstallprompt at ~120ms plus network throttling. CI on 0cf0493 (run 32531103787): Lighthouse budget SUCCESS. ALSO LANDED, and worth keeping even after this row closes: scripts/run-lighthouse-budget.mjs now prints layout-shift attribution (selector, snippet, score, raw worst item) when grading fails, before the report directory is deleted. This row previously required downloading a CI artifact that this environment cannot reach; that is no longer necessary, and the next occurrence of any layout-shift breach will name its own element. Stop rules honoured: tolerance not widened, baseline not refreshed. NOT CLOSED HERE — close with #TYZK23 and #KFRC3H once further Lighthouse budget runs confirm. | CI run 32531103787 on head 0cf0493; local reproduction 2026-08-22; merged in PR #2253; full trace on #TYZK23 | 2026-08-21 | | #45V4Y7 | P3 | task | Dead exports on protected surfaces: answerQuestion (rag.ts), embedText (openai.ts), clinicalRankScore (clinical-search.ts) | The 2026-08-20 repo-cleanup sweep removed 60+ verified-dead exported symbols but deliberately left three untouched because they sit on RAG-protected surfaces that AGENTS.md requires flagging before any edit, deletion included. All three are exported, imported by nothing, and referenced nowhere else in the tree: src/lib/rag/rag.ts answerQuestion (a thin wrapper superseded by answerQuestionWithScope, which is what /api/answer actually calls), src/lib/openai.ts embedText, and src/lib/clinical-search.ts clinicalRankScore. Removing them cannot change retrieval behaviour because nothing calls them, but the removal still travels through docs/rag-behaviour and the RAG impact declaration on the PR. Next step: confirm with the owner, then delete in a single RAG-scoped PR carrying 'RAG impact: no retrieval behaviour change -- dead exports with zero callers'. | src/lib/rag/rag.ts, src/lib/openai.ts, src/lib/clinical-search.ts | 2026-08-20 | | #72G3XZ | P3 | task | Watch runner usage now that every main push gets its own CI concurrency group | PR #2209 (merged af2075a) changed ci.yml so base-branch pushes key concurrency on github.run_id. The defect it fixed was real: cancel-in-progress: false only prevents supersession, while GitHub separately keeps at most ONE pending run per concurrency group, so during a merge burst each newly queued main run cancelled the one already waiting. Observed 2026-08-20: a1c2ced, d745d15, 97f6142 and 1cc0d29 all cancelled while a ~70-minute release-browser-matrix held CI-refs/heads/main, and a mobile-/ CLS regression rode through that gap. The accepted cost is a real one and nobody has measured it yet: a burst of N merges now produces N concurrent runs instead of one plus a survivor, each carrying release-browser-matrix. The in-file comment already accepts this ('concurrent main runs, one per merge, each already scoped by the changes job'), but that was written as a prediction, not an observation. Next: after a few days of normal traffic, compare Actions minutes on main-branch pushes against the week before af2075a, and confirm no queueing/limit pressure appeared. If the cost is worse than the defect, the alternative is a bounded group (for example keyed on run_id only while a long job is in the workflow) rather than reverting to the shared group, which would restore the eviction hole. Pinned by the 'never cancels an in-flight run for a base-branch push' case in tests/ci-cache-safety.test.ts, which now also asserts the per-run key. | PR #2209, merged af2075a; ci.yml concurrency block | 2026-08-21 | | #V15EAS | P3 | issue | therapies-home and therapies-index generated assets are byte-identical, so the home projection saves nothing | public/therapy-compass-data/therapies-home.211dab554c4ec62d.json and therapies-index.211dab554c4ec62d.json have identical content hashes. The home asset exists so the Therapy landing page can paint the catalogue count and default artifact destinations without downloading and parsing a full record projection first, but it is currently a byte-for-byte copy of the index, so the landing page pays the full 136 KB it was meant to avoid. This is a projection bug in scripts/build-therapies-index.mjs rather than stray duplication. Next step: either narrow the home projection to the fields THERAPY_CATALOGUE_SUMMARY actually needs, or drop the asset and point the home path at the index. Note the immutable Cache-Control in next.config.ts is keyed on the content hash, so a narrowed projection lands under a new URL and needs the one-generation retention already implemented in generated-assets.ts. Found by the 2026-08-20 repo-cleanup audit. | scripts/build-therapies-index.mjs, src/components/therapy-compass/data/generated-assets.ts, public/therapy-compass-data/ | 2026-08-20 | -| #TYZK23 | P2 | issue | mobile-/ Lighthouse CLS is bistable at 0.016 or 0.223 and reproduces only in CI, so the budget gate randomly reddens UI PRs and each looks like its own regression | BISTABLE, NOT A REGRESSION. Five CI measurements across PR #2199 heads: 175c641 FAIL (2/3 samples, cls 0.223), 74f39f0 FAIL (3/3, 0.223), 8506db3 PASS (no breach detected at all — 4 artifact files, no confirmation samples collected), c56d12d FAIL (2/3, 0.223), 4eabf0b FAIL. The decisive pair is 8506db3 -> c56d12d: the ONLY delta between those heads is 26 ledger JSON files under docs/outstanding-issues-inbox/, zero source code. The value recurring to three decimals is one discrete layout shift firing or not, rather than accumulating noise. NOT PR #2199's: it changes only DocumentViewer.tsx and document-viewer/document-overview-landing.tsx, and DocumentViewer is imported solely by src/app/(search-app)/documents/[id]/{page,loading}.tsx, so neither module is in the client bundle for /. TWO NEGATIVE LOCAL RESULTS, 2026-08-21, both in the Claude web container on Chromium 141: (1) scripts/measure-cls-attribution.mjs against / reported 'CLS=0.000 shifts=0' with the reserve timeline showing a single unset write at 436ms; (2) a full local npm run verify:lighthouse -- --keep, which applies Lighthouse's own mobile emulation and throttling, reported mobile-root cls 0.000 against the 0.016 baseline (ungraded: 'evidence incomplete — browser drift', HeadlessChrome/141 vs the baseline's /151). So the shift does not fire under either local harness and depends on something CI-specific — runner CPU contention, or Chromium 151 behaviour. NOT #JVYQEM, or at least not confidently: that row's phone remedy has already landed (--spacing-mode-home-composer-phone is 10.125rem, not the 6.625rem it records) and its scale (~0.035) is an order of magnitude below 0.223. NEXT STEP THAT DOES NOT NEED A REPRO: every failing run uploads a lighthouse-budget- artifact containing the full Lighthouse report; download lighthouse-budget-32463920997 (or any failing run) and read the mobile-root cumulative-layout-shift audit's debugdata, which names the shifting node directly. Only if that is empty is a CI-side attribution dispatch needed. Stop rules: do not raise the cls tolerance in lighthouse-budget.json to clear it; do not re-adopt the Lighthouse baseline while the metric is bistable, because the refresh would bake in whichever state that run happened to land on; and do not attribute it to a component without evidence from a run where it actually fired. | CI runs 32412788947, 32415624534, 32459391430, 32460303619, 32463920997 on PR #2199; local attribution + Lighthouse runs 2026-08-21 | 2026-08-21 | +| #TYZK23 | P2 | issue | mobile-/ Lighthouse CLS is bistable at 0.016 or 0.223 and reproduces only in CI, so the budget gate randomly reddens UI PRs and each looks like its own regression | ROOT CAUSE FOUND, REPRODUCED LOCALLY, AND FIXED — landed on main 2026-08-22 in PR #2253 (merge 66594dd), commits bc23075 (diagnostic) and 0cf0493 (fix). This row asked for the shifting node from a run where the shift actually fired; that evidence now exists. (1) ATTRIBUTION. The Lighthouse artifact could not be downloaded (Azure Blob egress blocked by this network policy), so the attribution was moved into the runner instead: scripts/run-lighthouse-budget.mjs now parses layout-shifts / layout-shift-elements / cumulative-layout-shift out of each per-cell report and prints selector, snippet, score and the raw worst item BEFORE the report directory is deleted, but only when grading already failed. CI then printed: "mobile-root cls=0.2230 / 0.2230 body.min-h-full > div.pwa-notice-stack", boundingRect {top:654, bottom:815, width:396, height:161}, nodeLabel "Install Clinical KB … Install app / Not now" — the COMPACT install card. (2) MECHANISM. #main-content briefly stops existing while Next 16 streams the route in and React hydrates it. The phone install-card geometry is chosen by body:has(#main-content[data-phone-footer-owner="hero"]) …, so a card mounting inside that gap is styled by a selector that is false: it paints tall (h=401, bottom gap 92px), then is restyled compact (h=161, bottom gap 8px) when the shell returns. One discrete restyle, which is why the value recurs to three decimals instead of drifting. (3) LOCAL REPRODUCTION — the first one anyone has achieved, and the answer to this row note that it "reproduces only in CI". Four earlier attempts failed because beforeinstallprompt never fires in this container. Dispatching it synthetically from an init script at ~120ms WITH network throttling reproduced 0.2230 exactly, with the trace: t=4726ms #main-content present (owner=hero) -> t=7855ms #main-content GONE -> t=9083ms pwa-notice-stack mounts top=330 bottom=731 h=401 -> t=9930ms #main-content returns, stack top=654 bottom=815 h=161 -> t=9963ms SHIFT value=0.2230 div.pwa-notice-stack. So it was never CI-specific runner contention or Chromium 151 behaviour; it needed a slow network plus an early install prompt, which CI has and a fast local container does not. (4) FIX. src/components/pwa-lifecycle.tsx holds the notice stack unmounted until the app shell is present, via useSyncExternalStore over a MutationObserver on documentElement. The readyState==="complete" escape hatch releases the gate ONLY while the shell has never been seen (appShellHasEverMounted===false); an earlier version without that qualifier was refuted by CI returning the identical 0.2230, because load fires ~4s and the gap is at ~9s. That refuted commit was reverted rather than left in place with a message claiming a fix. Same local reproduction after the fix: CLS 0.000. CI on 0cf0493 (run 32531103787): Lighthouse budget SUCCESS, pr-required SUCCESS. (5) STOP RULES HONOURED: the cls tolerance was not widened, the baseline was not refreshed, and nothing was attributed without a run where the shift fired. NOT CLOSED HERE: one green CI run on a bistable metric is weak on its own — the deterministic local before/after is the stronger half. Close this row together with #50QRCF and #KFRC3H after the next Lighthouse budget runs on main-scoped PRs come back green. | CI run 32531103787 (Lighthouse budget success) on head 0cf0493; CI attribution output on the failing head; local reproduction 2026-08-22 in the Claude web container; merged in PR #2253 | 2026-08-21 | | #8A00R7 | P2 | issue | AGENTS.md loads ~6k tokens/turn of Codex/Cursor-only sections, but three gates pin them in place | AGENTS.md is 1213 lines (~19.6k tokens) loaded every turn, plus CLAUDE.md (~2.4k). 372 of those lines (30 percent, ~6k tokens/turn) are Codex-only or Cursor-only and can never fire in a Claude Code session: Dependency shortcut, Codex review throttling, Codex Desktop worktree setup, Codex productivity defaults, Codex GitHub review behavior, Codex Cloud environment, Cursor Cloud instructions. The obvious fix (move them to docs/ and leave pointers) is BLOCKED: scripts/check-codex-cloud-setup.mjs line 1122 requires exactly one '## Codex Cloud environment' heading in AGENTS.md; scripts/check-codex-autofix-workflow.mjs requires the scoped resolve command, the 'one automatic repair pass per pull request lifetime' phrase and the disposition marker in AGENTS.md; tests/setup-codex-worktree.test.ts line 106 requires 'Never configure Windows Desktop worktrees' and the dry-run command in AGENTS.md. Any restructure must move the gate assertions to the new file paths in the same change. Measured 2026-08-21 on main a341832af. | Session applying the writing-for-agents skill to AGENTS.md, 2026-08-21 | 2026-08-20 | | #2DQXD8 | P3 | issue | Two contract tests pin unreachable components: VerificationWorkspace and TherapyListItem | The 2026-08-20 cleanup sweep found VerificationWorkspace (with its only caller RenderModelSourceList) in src/components/clinical-dashboard/evidence-panels.tsx and TherapyListItem in src/components/therapy-compass/therapy-card.tsx are exported, imported by nothing, and rendered by no route. Removing them was reverted because two committed contract tests assert on the source text of those files: tests/rendered-text-formatting.test.ts requires the literal compactSourceSnippet(source.snippet ?? "", { dropTitle: source.title }) to appear in the dashboard surfaces, and that string exists only inside RenderModelSourceList; tests/therapy-review-regressions.test.ts requires therapy-card.tsx to surface reviewStatus, which only TherapyListItem does. Both guards are therefore currently satisfied by code no user can reach, so they are not protecting the live render path they name. Next step: identify the live source-card and therapy-record render paths, repoint both assertions at them, then delete the unreachable components. Do not simply delete the assertions -- they guard clinical output formatting and the per-record review badge. | tests/rendered-text-formatting.test.ts, tests/therapy-review-regressions.test.ts, src/components/clinical-dashboard/evidence-panels.tsx, src/components/therapy-compass/therapy-card.tsx | 2026-08-20 | | #CHW9N3 | P3 | rec | mode-home loading contract enumerates its thirteen routes by hand, so the next standalone home can ship with no loading coverage | MODE_HOME_LOADING_ROUTES in tests/mode-home-loading-contract.test.ts is a hand-written thirteen-entry array. It currently matches standaloneModeHomePaths (src/lib/search-route-ownership.ts) exactly - the mismatch that let medications, calculators and dictionary ship without loading.tsx was ledger #6K9YGQ and is now fixed - but nothing keeps the two lists in step, so the next standalone mode home added will silently have no loading coverage and CI will not notice. Fix: derive the test's route list from standaloneModeHomePaths instead of hand-writing it. | session 2026-08-21, raised while closing #6K9YGQ | 2026-08-20 | | #61TZJA | P3 | task | Re-adopt the document-viewer Linux visual baseline after PR #2199 lands | PR #2199 makes document search on demand, which moves the document-viewer golden two ways: the overview action reads 'Search document' instead of 'Add to scope', and the closed composer releases the desktop sm:pb-40 clearance that was previously always reserved. The committed tests/__screenshots__/linux/document-viewer.png therefore drifts the moment that PR merges. This is ordinary pixel drift, which scripts/classify-visual-baseline-outcome.mjs scores advisory rather than red, and the visual-baseline job runs only on pushes to main — so the refresh point is post-land, from that run's artifact, via npm run design-system:baselines:adopt. Blocked until #2199 merges: adopting earlier would commit a golden for a state main does not have. Related: the same PR removed .document-viewer-composer from that target's mask (it is no longer rendered in the default state, and assertMaskSelectors fails loudly on a mask matching zero nodes), so the closed composer's resting layout is now inside the compared region rather than painted over. Stop rule: adopt from the CI artifact only, never from a developer machine or this container — font hinting alone would make every later run red, which is what the suite's own header warns about. | PR #2199 (tests/ui-visual-baseline.spec.ts, commit e6d52b8); ci.yml visual-baseline job | 2026-08-21 | | #800E5M | P2 | rec | Reachability scans must not treat in-flight programme scaffolding as dead code (Ward Flow, Caring Contacts) | The 2026-08-20 cleanup sweep initially removed symbols from src/components/ward-management/ and src/components/caring-contacts/ because no file imported them. That test is wrong for a programme still under construction. wallClockNow() is a specified export of ward-clock.ts in docs/superpowers/plans/2026-08-18-ward-flow-phase-1-model.md, and movementsByStage(stage) is a specified export of ward-movements.ts in the Phase 2 coordinator plan, whose 55 tasks are all still unchecked -- the consumers (Tasks 5, 7, 8) have not been built yet. Ward Flow itself landed only on 2026-08-19 in PR #2140, and Caring Contacts is an active design programme with no production route. All of those removals were reverted on the owner's instruction and the areas are byte-identical to their pre-sweep state. Recommendation for any future dead-code sweep: before removing a symbol, check whether it is named as a module contract in docs/superpowers/plans/ or docs/superpowers/specs/, and treat any plan with unchecked tasks as in-flight and out of scope. A second trap in the same sweep: this container clones shallow (105 commits spanning only 2026-08-19..2026-08-20), so git cannot date file creation and 'is this recent?' is unanswerable locally -- deepen the clone before relying on file age. | docs/superpowers/plans/2026-08-18-ward-flow-phase-1-model.md, docs/superpowers/plans/2026-08-18-ward-flow-phase-2-coordinator-screen.md, src/components/ward-management/, src/components/caring-contacts/ | 2026-08-20 | | #4XBMMR | P2 | issue | public/mockups/** is publicly served and indexable in production; mockups/README.md claims otherwise | src/proxy.ts 404s /mockups/* routes when NODE_ENV=production, but its matcher explicitly excludes .svg/.png/.jpg/.jpeg/.gif/.webp/.ico, so the 19 MB of design comps under public/mockups/ are served unauthenticated at psychiatry.tools/mockups/... . src/lib/crawler-policy.ts serves robots.txt as allow:/ by design so crawlers can read per-page noindex metadata, and a raw PNG carries no such metadata; the X-Robots-Tag: noindex header in next.config.ts is scoped to /offline.html alone. mockups/README.md 'Production behavior' asserts that robots.txt disallows indexing, which is false. Content is UI design comps, not clinical or patient data, so this is weight and a broken documented guarantee rather than a privacy incident. Next step: add an X-Robots-Tag: noindex header for /mockups/:path* in next.config.ts, decide whether the comps should ship in the deploy image at all, and correct the README either way. Found by the 2026-08-20 repo-cleanup audit. | src/proxy.ts, src/lib/crawler-policy.ts, next.config.ts, mockups/README.md, public/mockups/ | 2026-08-20 | -| #KFRC3H | P2 | issue | mobile-/ Lighthouse CLS bistable flake: PR #2234 fixes 8 more racy double-:has() install-card selectors PR #2219 missed; unconfirmed pending CI | PR #2219 (fix/pwa-notice-cls-shift) fixed ONE of nine identical-shaped selectors in the phone-hero install-card @media block in src/app/globals.css: each chained a redundant second body:has(.pwa-install-native-sheet) on top of the hero-composer-ownership :has(), and PR #2219 removed that second :has() only from .pwa-notice-stack's own bottom-gap rule. The other 8 rules (hiding tagline/copy/support/benefits, compacting header/body/actions/compact-copy) kept the identical two-:has() shape, predating PR #2219 entirely (present on main before that branch existed), with zero test coverage. Confirmed via CI job log that mobile-root CLS was STILL exactly 0.223 on a run (32477570217, head 851f0d693b656aba8fd492cd627f726917c24410) after BOTH of PR #2219's fixes had landed on that branch -- proving a mechanism survived both. PR #2234 (fix/lighthouse-cls-flake-mobile-root, based on main, does NOT touch PR #2219's branch) replaces the second :has() in all 8 remaining rules with a plain descendant combinator off .pwa-install-native-sheet, adds a static-contract test guarding all 9 selectors, and passes npm run verify:pr-local in full (lint/typecheck/696 test files/build/RAG fixtures all green). NOT YET CONFIRMED: could not download the CI Lighthouse artifact (Azure Blob Storage egress blocked by this environment's network policy) to see the exact layout-shifts debugdata/node for the post-both-fixes failing run, so this fix is static-analysis-based, not trace-confirmed. A 20-run local repro loop (real Lighthouse 12.8.2, offline/demo build, Chromium rev 1194 vs CI's pinned chromium-1234) produced 0/20 breaches before and after -- consistent with this ledger's own prior finding that local repro never catches this regardless of harness or Chromium revision, so non-repro is not disconfirming. Confirming evidence must come from N consecutive green CI Lighthouse budget runs on PR #2234 itself. Do not close this row or the original bistable entry until that CI evidence lands; if PR #2234 lands and CI is STILL bistable afterward, a fourth mechanism remains and the original bistable entry should stay open with this PR noted as ruled out. | Investigation continuing docs/outstanding-issues-inbox/5f472de7-797f-49ba-8ba5-b5b6d4a7ad7a.json; PR https://github.com/BigSimmo/Database/pull/2234; CI run https://github.com/BigSimmo/Database/actions/runs/32477570217 | 2026-08-21 | +| #KFRC3H | P2 | issue | mobile-/ Lighthouse CLS bistable flake: PR #2234 fixes 8 more racy double-:has() install-card selectors PR #2219 missed; unconfirmed pending CI | THE MECHANISM IS NOT THE SELECTOR SHAPE — resolved 2026-08-22, landed on main in PR #2253 (merge 66594dd, fix commit 0cf0493). This row predicted "if PR #2234 lands and CI is STILL bistable afterward, a fourth mechanism remains". A fourth mechanism did remain; it has now been traced, reproduced locally and fixed, and it is orthogonal to the double-:has() cleanup. WHAT ACTUALLY FIRES: #main-content briefly stops existing while Next 16 streams the route in and React hydrates it (measured gap roughly t=7.9s to t=9.9s under network throttling). Every phone install-card rule — the one PR #2219 fixed and the eight PR #2234 rewrites alike — is keyed on body:has(#main-content[data-phone-footer-owner="hero"]), so during that gap the OWNERSHIP :has() is false whether or not a second redundant :has() is chained onto it. A card mounting inside the gap paints tall (h=401, bottom gap 92px) and is restyled compact (h=161, bottom gap 8px) when the shell returns: one discrete shift, 0.2230. Removing the redundant second :has() therefore cannot close this, which is exactly consistent with 0.223 surviving BOTH of PR #2219 fixes on run 32477570217 — the observation this row correctly refused to explain away. THE FIX: hold the notice stack unmounted until the app shell is present (src/components/pwa-lifecycle.tsx, useSyncExternalStore over a MutationObserver, with the readyState escape hatch qualified to fire only when the shell has NEVER been seen; an unqualified version was refuted by CI returning the identical 0.2230 and was reverted). CONFIRMED BOTH WAYS: the first successful local reproduction (synthetic beforeinstallprompt at ~120ms plus network throttling) gave exactly 0.2230 before and 0.000 after; CI run 32531103787 on head 0cf0493 reports Lighthouse budget SUCCESS. PR #2234 IS STILL WORTH LANDING — the redundant :has() chains are genuinely racy and its static-contract test guards all nine selectors — but it should be recorded as hardening, not as the cause, and its confirmation criterion of "N consecutive green CI Lighthouse runs" can no longer distinguish it from this fix now that both would be on main. Also landed: scripts/run-lighthouse-budget.mjs prints layout-shift attribution on a failing grade, which removes this row blocker of being unable to download the CI artifact. Close with #TYZK23 and #50QRCF once further Lighthouse budget runs confirm. | CI run 32531103787 on head 0cf0493; local reproduction 2026-08-22; merged in PR #2253; contrast with run 32477570217 | 2026-08-21 | | #8VAY97 | P2 | task | The document_index_units retrieval path has no EXPLAIN baseline, and Phase 5 has no query-specific plan-flip evidence | Two Phase 5.1 deliverables are explicitly OPEN, not discharged. Re-graded P3 -> P2 versus the withdrawn request 2040d1fb, because that request understated the gap by claiming substitute coverage that does not exist. (A) NO EXPLAIN BASELINE FOR THE INDEX-UNITS PATH. public.explain_retrieval_rpc accepts exactly four names -- match_documents_for_query, match_document_chunks_text, match_document_lookup_chunks_text, match_document_table_facts_text -- and raises 22023 Unsupported retrieval RPC for anything else, proven against production for both match_document_chunks_text_v2 and match_document_index_units_hybrid_v2. For the first of those the v1 sibling match_document_chunks_text shares the owning table document_chunks and is a usable stand-in. For the second there is none: match_document_index_units_hybrid_v2 delegates to match_document_index_units_hybrid_scoped over document_index_units (supabase/schema.sql:8033-8054), and no supported RPC touches that table. document_index_units is one of the two section 1.2 outliers, so the outlier that most needed a baseline is the one that has none. (B) NO QUERY-SPECIFIC PLAN-FLIP EVIDENCE. explain_retrieval_rpc EXPLAINs `select * from public.(...)`, so a PL/pgSQL body's inner plan is never exposed and every sample reports a single Function Scan with no index names. Plan section 5.1's 'record plan flips (seq scan -> index scan)' is therefore unanswerable through this instrument. The pg_stat_user_indexes read captured in Phase 5.1(c) is a WEAKER and DIFFERENT signal, not a substitute: idx_scan is cumulative across every workload touching the table and no before/after delta was captured around the samples, so it can prove an index is never chosen by anything but cannot prove that a given query changed plan. NEXT: one migration extending the explain_retrieval_rpc p_rpc branch list to the _v2 family (at minimum match_document_index_units_hybrid_v2 and match_document_chunks_text_v2), shipped in an approved window -- with D4 ON, merging it to main deploys it, so it needs the window and a green post-merge live-drift run. Then re-run npm run profile:retrieval --analyze to capture the missing baseline. For (B), consider whether an auto_explain-style capture is a better fit than widening the RPC. STOP: do not record the cumulative index-usage read as plan-flip evidence; that conflation is exactly what this row exists to prevent. | Codex review of PR #2250 (P2, comment 3833062803 and 3833062807); docs/audit/live-drift-forensics-2026-08.md Phase 5 close-out 5.1(b); supabase/schema.sql:8033-8054 | 2026-08-21 | | #47M1XD | P2 | issue | Restored trigram indexes have never been scanned, so the 2026-08-14 incident fix is probably mis-attributed to them rather than to ANALYZE | Measured on production 2026-08-22 Perth (2026-08-21 UTC), read-only. pg_stat_database.stats_reset is NULL, so no database-wide reset has been recorded -- note this does NOT prove the counters were never reset, because pg_stat_reset_single_table_counters(oid) resets one table or index without touching that database-level timestamp; 163 of 210 public indexes show scans, max(idx_scan) 262,335,345, total 344,747,815 -- the counters are demonstrably live. Against that, ALL 22 indexes restored or repaired by the incident response report idx_scan = 0: the 20 Phase 4 rebuilds of 2026-08-19, and both trigram indexes restored 2026-08-14 -- document_chunks_content_trgm_idx (68 MB, OID 1491258) and documents_title_trgm_idx (648 kB, OID 1491257). The OID matters: 1491258 is the same value Phase 4 Step 4 recorded as UNCHANGED across the 2026-08-19 push, so the index was never dropped and recreated and its counter has accumulated for the full seven days since restoration. What actually serves retrieval on those tables is document_chunks_search_idx (GIN tsvector, 37,717 scans), document_chunks_embedding_hnsw_idx (HNSW, 4,906) and documents_title_search_idx (37,299). Note that a DIFFERENT trigram index on a sibling table, document_table_facts_title_row_param_trgm_idx, is used 2,372 times, so this is not a blanket property of the planner or of pg_trgm. CONSEQUENCE: forensics Phase 5 attributes the 31,610 -> 1,535 ms recovery to 'restore + ANALYZE', treating the trigram restore as the fix. Zero scans mean those indexes are very unlikely to be carrying that recovery -- unlikely rather than impossible, because a per-relation counter reset would be invisible to the read above; the OID continuity across the 2026-08-19 push is what makes the zero credible, and ANALYZE was an unseparated co-intervention in the same operation -- 20260626020000 itself ends by running analyze over documents, document_chunks, document_table_facts, document_embedding_fields and document_index_units. Stale planner statistics is the better-supported explanation for both the 31-second regression and its recovery -- better-supported, not established. THIS DOES NOT RETRACT THE REPAIR: the two indexes are repo-defined, the migration chain commands them, and drift is correctly closed by their presence. It retracts the ATTRIBUTION, and it changes triage order -- the next 30-second retrieval incident should check planner statistics BEFORE hunting missing indexes. Secondary and non-urgent: roughly 69 MB of currently unscanned index. NEXT: (a) in an approved window, first rule out a per-index counter reset -- on PostgreSQL 18+ read pg_stat_all_indexes.stats_reset for these two OIDs, and on earlier versions fall back to whatever reset or audit record exists (the production server version was not read in this window); (b) separate the two interventions by measuring retrieval latency before and after a fresh ANALYZE; and (c) only then decide whether the two trigram indexes earn their storage. STOP: do NOT drop them -- they are repo-defined and dropping them reopens live drift. | production read-only window 2026-08-22 Perth (2026-08-21 UTC); docs/audit/live-drift-forensics-2026-08.md Phase 5 close-out 5.1(c) | 2026-08-21 | | #K9XD5N | P3 | issue | DegradedNotice banner shifts the hero section by ~0.16 CLS on / when it appears/disappears (separate from the .pwa-notice-stack fix in PR #2219) | scripts/measure-cls-attribution.mjs (offline-only trigger, no install prompt) shows main#main-content > ... > section (the answer-mode hero, sourced from ClinicalDashboard's DegradedNotice) shifting from y=88,h=623 to y=165,h=623 — 0.159 CLS, 2 entries — purely from the in-flow DegradedNotice banner mounting/unmounting above it when isOnline flips. This is distinct from the .pwa-notice-stack root cause fixed in PR #2219 (fix/pwa-notice-cls-shift): that fix collapses .pwa-notice-stack's contribution to 0 under the same harness, but this hero-section shift is untouched by it and remained present in the fixed-code re-run (0.159 total, single source). The real Lighthouse CI trace that motivated #2219 (mobile-root, PRs #2199/#2204, 2026-08-21) named only .pwa-notice-stack as the shift source at score 0.223, so this hero-section mechanism apparently did not fire (or was below threshold) in that specific CI run — but it is a real, reproducible shift on the same route and could surface as a future budget flake. Likely fix direction: reserve the DegradedNotice banner's height (or use the same unmounted-frame-swap pattern) so its mount/unmount does not resize the hero section while already visible. | Investigation for PR #2219 (fix/pwa-notice-cls-shift), 2026-08-21 | 2026-08-21 | @@ -156,6 +156,12 @@ removed after current-main verification; it is not missing recommended work. | #BJ80DB | P2 | issue | Windows-only test-budget failures: 3 tests fixed (2 of the original 5, plus 1 newly found in the same file), 2 unreproducible, 1 (hosted-migration-role-guard) still unexplained | Supersedes cancelled request 67bf71cf-25cb-40d9-a8a2-bbf993bf6b29, whose diagnosis was partly wrong. ORIGINAL REPORT: a full npm run test on a clean Windows workstation (D:\Repos\Database, main at cdfcbaccd, empty tree) exited 1 with 4 files / 5 tests failed, 680 files / 7423 passed, 601.68s. CORRECTED FINDING: main was never broken. A full GATE_RECEIPTS=off npm run test on Linux at the content-identical HEAD is green (696 files, 7770 passed, 4 skipped, exit 0, 196s), and git diff cdfcbaccd..HEAD shows none of the four test files nor their subjects changed. THE ROOT CAUSE FOR THE TWO NOW FIXED IS A TIME BUDGET, NOT A DEFECT IN BEHAVIOUR. (1) codex-cloud-setup 'writes managed shell policy behaviorally and preserves unrelated Codex config' measured 24.96s on that workstation running its file ALONE, against the 30s testTimeout in vitest.config.mts - 83% of budget with nothing competing; a full run puts maxWorkers:4 on the machine and it tips over. It spawns bash scripts/setup-codex-cloud.sh twice, cheap on Linux and expensive on Windows where every spawn goes through Git Bash. Its immediate sibling already carried an explicit 120_000 budget for the same reason (measured 112.9s); this one had been missed. FIXED in PR #2255 by giving it the same budget. (2) http-readiness 'honours the overall deadline when every response remains open' and its neighbour 'destroys stalled requests and resumes polling' both work to millisecond budgets (requestTimeoutMs 40, timeoutMs 180/1000) that Windows cannot hold. The neighbour fails IN ISOLATION ON AN IDLE MACHINE ('expected 4 to be 3'), which is stronger than a load flake: the 40ms request budget bounds not only the two deliberately-stalled responses but also the healthy third one, so a merely-late good response is destroyed as though stalled and the loop polls a fourth time. FIXED in PR #2256 (requestTimeoutMs 40->250 with the deadline 1s->5s; the other case's deadline 180ms->600ms), with a negative proof that breaking the stall-destroy path still fails both. (3) The two document-viewer page-virtualization cases PASS on re-run on that workstation (whole file 3566ms). PR #2252 made their shared failure point legible: flushIdle() could only report 'expected 0 to be greater than 0', which reads as a slow machine; it now names the canvas-budget cause and prints slot/rendered/observer counts. STILL OPEN AND THE REASON THIS ROW EXISTS: hosted-migration-role-guard 'accepts the current repository state' passed on re-run at 1550ms, nowhere near its 30s budget, so the timeout explanation that resolved the other two does NOT fit it and nothing has been changed for it. Its original cause is unexplained. Leading hypothesis, untested: validateRepository() calls repositoryEntries(), which runs git ls-files --cached --others --exclude-standard, so it grades UNTRACKED non-ignored files too - any stray file under scripts/, supabase/migrations/, .github/workflows/ or the guarded exact paths whose name or content matches the reserved role turns it red while git status still looks clean enough to call the tree empty, as would a nested git worktree directory (readFileSync on a directory entry throws EISDIR). It also SHA-256s the on-disk bytes of the pinned immutable migration, so CRLF would break it, though .gitattributes '* text=auto eol=lf' should prevent that and the bytes hash correctly on Linux. NEXT: if it recurs, capture git status --porcelain --untracked-files=all and git ls-files --others --exclude-standard at the moment of failure - the assertion alone will not identify which entry did it. FLAKE POLICY DOES NOT APPLY TO ANY OF THESE: tests/flake-ledger.json is explicitly Playwright-only (exact spec+title, @quarantine tag); all five are Vitest and none are eligible for quarantine. Also note the previously known local-Windows failures (tests/session-start-hook.test.ts, worker-observability) PASSED in the reported run, so any note calling those the known environmental set is stale. STANDING LESSON: on this workstation, a test's assertions being deterministic does NOT make its runtime deterministic. Subprocess-spawning and wall-clock-bounded tests need budgets sized for Windows, and 83%-of-budget in isolation is a latent failure, not a pass. | PRs #2252, #2255, #2256 (2026-08-21); Windows workstation runs at cdfcbaccd and after | 2026-08-21 | | #HDYCQP | P2 | issue | PR mergeability re-fires on merged/closed PRs and always fails: the mergeability job has no PR-state filter, and a merged PR's mergeable_state is permanently 'unknown' | MECHANISM (confirmed end to end on PR #2229, 2026-08-21). .github/workflows/pr-mergeability.yml listens on pull_request_target with types [opened, synchronize, reopened, ready_for_review, edited]. GitHub fires 'edited' on CLOSED and MERGED pull requests, and this repo has a bot that reliably edits PR descriptions (the PR #2229 body carries a '## Summary by CodeRabbit' section). The 'mergeability' job is gated only by `if: github.event_name == 'pull_request_target'` — there is NO filter on pull_request.state, pull_request.merged, or head-ref existence. Its 'Signal real merge conflicts' step calls github.rest.pulls.get and feeds the result to classifyMergeability() in scripts/pr-mergeability.mjs. For a merged PR the API returns mergeable: null and mergeable_state: 'unknown' PERMANENTLY, which is exactly the classifier's 'retry' branch, so all 5 attempts retry and the step ends in core.setFailed('GitHub did not finish computing mergeability for PR # after 5 attempts.'). The failure is therefore deterministic, not flaky: any pull_request_target event on a merged or closed PR fails this required check. EVIDENCE. PR #2229 merged 2026-08-21T13:52:54Z (merge commit 68bcb38d031cb9c355d81d809c535a2ea3e2f9c6), head branch claude/ledger-reconcile-0821b deleted by the merge. At 13:54:31Z — 97 seconds after the merge — workflow run 32489311892 (.github/workflows/pr-mergeability.yml, event pull_request_target, triggering_actor coderabbitai[bot]) started; job 96792973908 'Refresh PR mergeability after base push' correctly skipped, and job 96792972554 'PR mergeability' failed at 13:54:51.99Z with exactly that setFailed message. A live pulls.get on #2229 today still returns state 'closed', merged true, mergeable_state 'unknown' — so the check would fail identically on every future edit event. MISSING FILTER, AND THE PROOF IT WAS AN OVERSIGHT. The sibling job in the same file, 'refresh-after-base-push', already scopes its work with github.rest.pulls.list({ state: 'open' }). The state filter was applied to the push path and omitted from the pull_request_target path. SMALLEST FIX (not applied; .github/workflows/** is operational-risk scope). Add a closed/merged branch to classifyMergeability() in scripts/pr-mergeability.mjs, ahead of the existing draft branch, returning { ok: true, action: 'skip', reason: 'merged' } when merged === true or state === 'closed', and pass state: latestPr.state / merged: latestPr.merged from both call sites in pr-mergeability.yml. This mirrors the existing draft skip path exactly, keeps the check reporting rather than silently skipping the job, and is provable offline with 'node scripts/pr-mergeability.mjs --self-test' plus a new assertion there. Do NOT fix this by dropping the 'edited' trigger type: 'edited' is what catches a base-branch change on an OPEN PR, which genuinely alters mergeability. Any change to this file must respect the deliberate cancel-in-progress asymmetry pinned by tests/ci-cache-safety.test.ts, which this fix does not touch. CORRECTION TO THE ORIGINAL REPORT — the co-reported symptoms were NOT part of this defect. The clarifying comment on #2229 (issuecomment-5371138259) attributed failing 'PR required' and 'Static PR checks' to the merge as well. That is wrong. ci.yml's pull_request types do not include 'edited', and the run list confirms it: every ci.yml run on claude/ledger-reconcile-0821b is pre-merge, and the last one (32489042816, 13:51:34-13:52:52Z, head 754cbfcb) was SUCCESS three seconds before the merge. The six earlier failures were genuine, diff-attributable failures on earlier heads — e.g. run 32488292727 at 13:44Z, where 'Static PR checks' exited 1 on its own step and 'PR required' logged 'static-pr result was failure' — and were fixed before merge. What made them look post-merge is that ci-triage.yml edits ONE comment in place: comment 5369760686 was created 12:23:46Z and last updated 13:44:36Z, both pre-merge, but still sat on the page after the merge naming those two jobs. So the ci-triage comment is stale-by-design, not re-fired; only 'PR mergeability' actually re-fired post-merge. Worth knowing before diagnosing the next one. RELATED, NOT DUPLICATE. #TF6TPJ is the cancellation-driven false red on OPEN PR branches ('PR required' red with zero failing jobs after repeated main-merges); different trigger, different job, different failure text. #HSSHRG (closed 2026-08-21) is the in-flight CI push guard in scripts/guard-push.mjs. Neither covers a required check re-firing against an already-merged PR. pr-policy.yml carries the same pull_request_target 'edited' exposure but is not defective — its post-merge run 32489311920 at 13:54:33Z passed. SCOPE NOT MEASURED. GitHub reads for this investigation were authorised only for PR #2229, so how often this fires repo-wide is unquantified. It should recur on any PR whose description a bot edits after merge, which is the normal CodeRabbit pattern here. | PR #2229; run 32489311892 job 96792972554 (2026-08-21T13:54:51Z); .github/workflows/pr-mergeability.yml; scripts/pr-mergeability.mjs; https://github.com/BigSimmo/Database/pull/2229#issuecomment-5371138259 | 2026-08-21 | | #9X40BT | P2 | rec | Supabase preview-branch compute is an uncapped cost sitting outside the organisation Spend Cap | Dashboard read 2026-08-21 (the same read that settled D4) shows Automatic branching ON with limit 3 and "Supabase changes only" enabled, and the same screen warns that Branching Compute is NOT covered by the organisation's Spend Cap. Preview databases are therefore an uncapped cost that scales with the number of open PRs touching supabase/**, on a project that already runs auto-deploy to production. CONTEXT FOR ANY DECISION TO REDUCE OR DISABLE THEM: CI's Migration replay job (db-reset-verify, which runs `supabase migration up --local`) independently replays the entire migration chain on every database-touching PR, so preview branches are a SECOND net rather than the only one -- turning them down would not leave the chain unproven. Preview branches did earn their keep once (the 20260819100200 guard failure on PR #2151 was caught by a preview branch building from the chain alone), so this is a cost/benefit decision, not a cleanup. NEXT: decide whether to keep branching at limit 3, lower the limit, or disable it, and record the decision with its date. STOP: do not change Supabase project settings without explicit owner approval; this row is a decision request, not a licence to reconfigure the project. | Supabase dashboard read 2026-08-21; docs/audit/live-drift-forensics-2026-08.md D4 section; AGENTS.md Supabase project safety | 2026-08-21 | +| #S4R2W3 | P3 | issue | Two clinical questions are answered with a bare list of document titles instead of an answer, and no gate fires | Found 2026-08-22 while diagnosing #231 against the 60 Gate E answers (docs/rag-improvement/231-diagnosis-2026-08-22.md section 3.1). Two of the eight identical plain source_only cases are clinical questions that receive the document-inventory answer shape reserved for source-lookup questions. 'What is the duress procedure pathway?' (case quality-duress-pathway, classified query_class document_lookup, intent pathway_referral) and 'When is IM medication used in the agitation pathway?' (case quality-agitation-im-route, query_class medication_dose_risk, intent pathway_referral, routed high_confidence_extractive_retrieval) both return: I found 5 indexed documents that support this query: followed by five titles. Neither question asked which documents exist. The routing chain for both ends at the first token -- no fallback reason, no gate reason, no retry, provider_attempted false -- so nothing flags it. CONTRAST, and why this is a real defect rather than a design choice: the other four cases in the same group (lithium-monitoring-documents, long-acting-injectable-documents, patient-safety-plan-documents, nocc-document-support) literally ask which documents or sources support X, and for those the same document list IS the correct answer. So the shape is right for four and wrong for two, which points at route selection or query classification rather than at the extractive answer builder. NEXT: diagnose separately -- establish whether the fault is hasSourceSupportLookupIntent / the query classifier assigning document_lookup to a pathway question, or shouldUseExtractiveMedicationLookup admitting a pathway question into the high-confidence extractive route. Do NOT fold this into the #231 retry-ladder work or the grounded-extractive gate work; it is a different surface with a different fix. STOP: protected RAG surface (src/lib/rag/rag-routing.ts, clinical-search), flag before editing, behaviour change needs a live eval-canary pair. Lower priority than the other two because the answer is at least honest and cites real documents, rather than being wrong or incoherent. | docs/rag-improvement/231-diagnosis-2026-08-22.md section 3.1; Gate E dumps output/gate-e/dump-v18.json and dump-v19.json of 2026-08-21; session 2026-08-22 | 2026-08-21 | +| #M6JNR8 | P2 | issue | Recommended-execution-queue rows cannot be re-graded by any ledger writer operation | FILED 2026-08-21 from PR #2260 review (Codex P2, verified against the repo). The queue row for an issue and its Open-items row can disagree indefinitely, because no writer can change the queue row's text. Concrete instance: queue row 1 still presents `#231` as A1 / 'Immediate approved live investigation' into retrieval binding `answerRouteBudgetMs.fast`, while the #231 Open-items row -- updated by PR #2260 -- records the P1 -> P2 re-grade and the measurement that closed that cause (retrieval now consumes 4-27% of the 25 s fast budget). The row itself asks for the queue entry to be re-graded 'AT RECONCILE'. That could not be honoured, and the reason is structural, not an oversight: (1) scripts/outstanding-issues.mjs updateIssue edits only Open-items columns (editable = { pri: 1, summary: 3, detail: 4, source: 5 }) and the only queue-touching function is pruneResolvedIdFromQueue, which runs on 'done' and merely deletes the row; (2) a hand edit of docs/outstanding-issues.md is refused by check:ledger-write-discipline, whose own self-test asserts 'manual canonical edit accepted' is a failure; (3) creating a request on the reconcile branch and applying it in the same transaction is refused -- verifyIssueReconciliation returns 'was introduced without moving the identical pending request from the base', since the applied batch must equal the base inbox exactly. So the queue can only be pruned, never corrected. RISK: the queue owns recommended order, acuity, timing, and approvals, so a stale A1 entry can send an operator into provider-backed live work on a cause the ledger elsewhere declares closed. NEXT (choose one, owner's call): add a queue-row edit action to scripts/outstanding-issues.mjs plus an inbox request action that carries it, so re-grades travel through the serial transaction like every other mutation; or accept a narrow, explicitly-audited hand-edit path for the queue section only. STOP: do not hand-edit docs/outstanding-issues.md to clear the #231 instance -- that is the exact write the guard exists to refuse. | PR #2260 review thread discussion_r3833600137; docs/outstanding-issues.md queue row 1 and row #231; scripts/outstanding-issues.mjs:255-279; scripts/check-ledger-write-discipline.mjs:130-175 | 2026-08-21 | +| #NPQJKP | P2 | issue | Answer-quality predicates accept two incoherent grounded extractive answers; diagnose predicate strictness before changing gate reachability | Found while correcting PR #2264's #231 diagnosis. The code-path fact remains: a grounded first-choice extractive answer bypasses generatedAnswerQualityFailureReason because the call at rag.ts:3113 sits inside the !finalizedAnswer.grounded branch. But the two cited incoherent grounded outputs do not demonstrate that moving the call fixes them. The focused predicate evaluation described in the PR review returned null for both using their actual query classes: 'The guidance for metabolic is that compliance, monitoring and evaluation.' with medication_dose_risk, and 'The guidance is that aim > To effectively identify admission and discharge processes to facilitate ease of access to > 2.' with document_lookup. Therefore the present predicates accept these outputs, and a guard-only reachability edit would not catch either. NEXT: add focused tests, determine the smallest clinically defensible predicate/threshold gap, and measure eval impact. Consider a separate reachability change only after a grounded extractive candidate is shown to fail an existing predicate while bypassing the call site. STOP: protected RAG surface; do not weaken grounding, citations, numeric verification, ranking, selection or generation safeguards; a behaviour change needs a live eval-canary pair with owner approval. | PR #2264 review of docs/rag-improvement/231-diagnosis-2026-08-22.md; src/lib/rag/rag-extractive-answer.ts generatedAnswerQualityFailureReason | 2026-08-22 | +| #2TAQDC | P3 | rec | Nothing stops a future :has(#main-content…) rule inheriting the app-shell hydration gap that caused the mobile-/ CLS flake | FILED 2026-08-22 from the fix for #TYZK23 / #50QRCF / #KFRC3H (PR #2253, commit 0cf0493). Established by measurement, not inference: on a throttled phone load, #main-content is absent from the DOM for roughly two seconds while Next 16 streams the route in and React hydrates it (trace: present at t=4726ms, GONE at t=7855ms, back at t=9930ms). Any CSS keyed on body:has(#main-content…) therefore evaluates FALSE for a window during load, and any element mounting inside that window paints with the wrong geometry and is restyled when the shell returns. That is exactly the 0.2230 layout shift three ledger rows chased for a week across four candidate mechanisms. NO LIVE DEFECT TODAY: all eleven current consumers are the PWA install card (src/app/globals.css lines 3018-3047), and 0cf0493 defends that one consumer by holding the notice stack unmounted until the shell is present. What is missing is anything that stops the next one. There is no lint rule, no contract test, and no line in docs/search-chrome-behaviour.md warning that a shell-presence :has() is only sound for an element that cannot mount during hydration — so a twelfth consumer added anywhere would silently reintroduce a bistable CLS that took three ledger rows and several days to attribute. OPTIONS, cheapest first: (a) a paragraph in docs/search-chrome-behaviour.md beside the existing phone-chrome reserve rules, stating the gap and the condition; (b) a static contract test asserting every :has(#main-content…) rule targets a descendant of .pwa-notice-stack, which fails loudly the moment a consumer is added elsewhere and costs nothing to run; (c) a lint rule, almost certainly not worth it for eleven selectors. RECOMMEND (a) plus (b). STOP RULE: do not respond by deleting the :has() selectors. They are the correct way to express phone-composer ownership, and the gap is a property of streaming hydration, not a selector bug. | commit 0cf0493 and the local reproduction trace, 2026-08-22; src/app/globals.css lines 3018-3047; related rows #TYZK23, #50QRCF, #KFRC3H | 2026-08-22 | +| #JZM7RM | P2 | rec | Two independent auto-fixers can answer the same PR review comment, and only one of them is governed or documented | On PR #2249 a single Codex review comment on .claude/hooks/push-format-guard.sh drew more than one responder: this repo's .github/workflows/codex-autofix-review-comments.yml, and an app-level 'Autofix pull requests' watcher that also instructed a live session to do the identical work. Two complete fixes were built independently and one was discarded. Evidence in local git history: commit 687b166d (author 'Claude', 2026-08-21T19:41:56Z) and c9d990aa (author 'BigSimmo', +0800) both edit the same hook on the same branch claude/push-guard-hookspath-relative; b9414fa0 and 94b6c291 are two separate 'merge main into claude/push-guard-hookspath-relative' commits made minutes apart by those two different identities, and 828106a5 merges the branch into itself to reconcile the divergence. The repo-side Codex workflow carries real safeguards: trusted-bot login gating, a per-PR dedup marker, one automatic repair pass per PR lifetime, and a skip-codex-review opt-out. The app-level watcher has none of these, and the string 'Autofix pull requests' appears nowhere in .github/ or docs/ - it is in neither the repository nor the tooling map, while docs/agents-guide.md line 44 assigns 'Primary PR code-review + automatic resolve' to Codex alone. Next: decide which responder owns automatic resolve, then either document and throttle the app-level watcher in docs/agents-guide.md or disable it, so one comment cannot spawn two competing fixes. Sibling of the ledger item recording the same failure between two human-driven sessions. | PR #2259 review; verified against local git history at origin/main 226bd32c (687b166d, c9d990aa, b9414fa0, 94b6c291, 828106a5); replaces a mis-filed payload that duplicated inbox request 67bf71cf-25cb-40d9-a8a2-bbf993bf6b29 | 2026-08-21 | +| #VV83VA | P3 | rec | Local process-spawn starvation silently makes every git push take minutes; the one-command diagnostic is not written down anywhere | MEASURED TWICE, THREE DAYS APART. On 2026-08-20 a session recorded `gh --version` at 1m37s and concluded gh itself was broken, which made every git push take 5-10 minutes because .githooks/pre-push -> guard-push.mjs invokes gh several times per push. On 2026-08-22 the same binary measured 0.201s, 0.262s and 0.278s on three consecutive runs. IT WAS NEVER GH. The decisive comparison is a binary with nothing to do with gh: `node --version` was 17s on 2026-08-20 and 0.080s on 2026-08-22, a ~200x change. `git --version` measures 0.069s. gh is unchanged between the two readings -- same 2.97.0, same C:\Program Files\GitHub CLI\gh.exe on local disk (not a network path, not a shim), same 41,775,416 bytes, and ZERO extensions installed, so the usual suspects (startup update check, extension probing, slow path) are all excluded. Windows Defender real-time protection was ON in BOTH readings, so Defender alone is not sufficient to cause it either; exclusions could not be read without administrator rights. WHAT ACTUALLY CHANGED: the machine had been rebooted about an hour before the fast reading. The condition is machine-wide process-spawn starvation that accumulates with long uptime and many concurrent agent sessions, and a reboot clears it. The precise mechanism cannot be pinned down retroactively once the state is gone, and that is stated rather than guessed at. IT IS ALREADY REBUILDING: one hour after the reboot the machine showed 49 node processes, 31 claude processes, 548 processes total, and 6.9 GB free of 32 GB. So this recurs rather than being a one-off, and the next session to hit it will again mistake it for a broken tool. NEXT (cheap, docs-only): write the diagnostic down where a session will find it -- `time node --version` is the whole test. Sub-second means the machine is healthy and a slow push is something else; multiple seconds means spawn starvation, and the fix is to close sessions or reboot, NOT to debug gh, git, or the pre-push guard. Natural home is the traps section of AGENTS.md or docs/testing.md. STOP: do not change Windows security settings or add a Defender exclusion on this evidence -- the two readings show Defender is not the discriminating variable, and a security-settings change needs the owner regardless. | measured 2026-08-20 and 2026-08-22 on the Windows workstation; session 2026-08-22 Phase 5 close-out | 2026-08-21 | ## Resolved / archive From 71cbf03e072ff715906321a115213e7a6f52e559 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Aug 2026 06:00:56 +0000 Subject: [PATCH 2/2] docs(ledger): rescue two branch-review records orphaned on dead reconcile branches These are the only content on the four abandoned reconcile branches that exists nowhere on main. Both record real 2026-08-17 reconciliation reviews whose transactions were later redone and landed by other branches, so the reviews happened but their audit records never did. Recovered verbatim from claude/ledger-reconcile-issues-c1trbj and claude/rag-r0-reconcile-inbox rather than rewritten, since the records are content-addressed and immutable by contract. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UksCoUHQMp72XUdp7RjchE --- ...f73474461a37aeb350869157a151008e52d8e808b1f58a33faa.record.md | 1 + ...7c8cb344d1587c1f8e617108bc2de777d41e308fa989bb725c3.record.md | 1 + 2 files changed, 2 insertions(+) create mode 100644 docs/branch-review-records/42c9767e935f4f73474461a37aeb350869157a151008e52d8e808b1f58a33faa.record.md create mode 100644 docs/branch-review-records/65d188be1a43a7c8cb344d1587c1f8e617108bc2de777d41e308fa989bb725c3.record.md diff --git a/docs/branch-review-records/42c9767e935f4f73474461a37aeb350869157a151008e52d8e808b1f58a33faa.record.md b/docs/branch-review-records/42c9767e935f4f73474461a37aeb350869157a151008e52d8e808b1f58a33faa.record.md new file mode 100644 index 0000000000..5d16cee749 --- /dev/null +++ b/docs/branch-review-records/42c9767e935f4f73474461a37aeb350869157a151008e52d8e808b1f58a33faa.record.md @@ -0,0 +1 @@ +| 2026-08-17 | claude/rag-r0-reconcile-inbox | b849065dd292279515cbad87a9eb08ba0d6a9fee | issues:reconcile after PRs #2023/#2024/#2035/#2036/#2037 (28 requests, 3 cancellations, #212 closed) + HANDOVER S4/T4 rows | single fresh-base reconcile; supersedes PR #2032 partial-base attempt | check:outstanding-issues (0 pending, 217 applied); check:ledger-write-discipline passed f5b0932914eb..HEAD; verify:pr-local docs scope | diff --git a/docs/branch-review-records/65d188be1a43a7c8cb344d1587c1f8e617108bc2de777d41e308fa989bb725c3.record.md b/docs/branch-review-records/65d188be1a43a7c8cb344d1587c1f8e617108bc2de777d41e308fa989bb725c3.record.md new file mode 100644 index 0000000000..fec12e22a1 --- /dev/null +++ b/docs/branch-review-records/65d188be1a43a7c8cb344d1587c1f8e617108bc2de777d41e308fa989bb725c3.record.md @@ -0,0 +1 @@ +| 2026-08-17 | claude/ledger-reconcile-issues-c1trbj | 527fd43796dfbaf76905671452aaa27f994b6871 | issues:reconcile after #2023/#2024 | 22 requests applied (8 requested: #212 correction, #J912J9 governance question P1, #DP6M3G R1 P1, #6BG9X2 R2+R3 P2, #BTVMVK Sentry search error P2, #ND10QT source_metadata pin P3, #TYJ0XP canary protocol note P3, #0MSNT8 G1 P3; plus 14 other queued requests: #056, #098, #265, #314, #316, #318, #322, #237, #330, #331, #192, #162, #238, #324) | check:outstanding-issues pass; check:ledger-write-discipline pass |