From 3bec94aff222f18ce6182806124065bc5678e9e4 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 22:25:20 +0000 Subject: [PATCH] Reconcile 21 queued ledger requests into the outstanding-issues ledger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One serialized reconciliation transaction from a fresh origin/main base, per the inbox contract: 21 queued requests applied (including 2 cancellation decisions), their immutable audit records moved under docs/outstanding-issues-inbox/applied, and the canonical ledger updated in the same commit so check:ledger-write-discipline can verify the diff equals the recorded transaction. Includes the two captures queued by the Dictionary browse header work in PR #2143 and #2162 — the CI cancellation churn on open PR branches, and the phone-chrome settle trap in browser verification — plus 19 requests queued by other sessions that had been waiting on a reconcile. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_011jr2rJuYT4sdi2yDGBTkyd --- .../07867a60-274a-4350-a564-2fed480ff997.json | 0 .../1b3f0964-1cf4-4fad-b89d-b9393797807a.json | 0 .../25e8c062-8fa0-46be-844a-03245fc9857b.json | 0 .../2c95ad4c-bbc7-4043-9c79-8afd529fa4c2.json | 0 .../338d45ec-397e-4eb4-b991-3185079509a4.json | 0 .../51673c73-b0ea-4848-bae4-83a269dfa833.json | 0 .../714e714b-dd1d-4a10-8f95-8552fa2c5296.json | 0 .../77c314b4-f2d8-4682-a08e-0f34de180f5e.json | 0 .../7a93a5a2-d3b9-4f3d-9747-22049300cd64.json | 0 .../7d1ba0dc-a3c9-479a-b16c-b1b4aa8f03d6.json | 0 .../83bc6aa5-e316-4524-bda8-458cea816f7a.json | 0 .../91536913-6cd9-4c61-a109-4d9aa3e76b9d.json | 0 .../9226626e-ad01-4504-a8bb-1287fa281f7b.json | 0 .../946d0cce-549d-4caf-b74d-af5593e0911b.json | 0 .../95093e96-5953-48a0-a8bc-508d625fb57e.json | 0 .../aaca6636-f820-4ef5-881c-752440dda09d.json | 0 .../abc21f52-0d39-490d-9676-d106b9af4302.json | 0 .../b048dbdd-7672-4e47-82e2-2211495e40c6.json | 0 .../d96c4613-bdfc-4e80-b084-7c27dfff1bb8.json | 0 .../fb87f710-b46c-452a-b349-e3ce71c36959.json | 0 .../ff1ea3f9-2bdc-4a9f-86de-185ec1b6c9e5.json | 0 docs/outstanding-issues.md | 20 ++++++++++++++++--- 22 files changed, 17 insertions(+), 3 deletions(-) rename docs/outstanding-issues-inbox/{ => applied}/07867a60-274a-4350-a564-2fed480ff997.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/1b3f0964-1cf4-4fad-b89d-b9393797807a.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/25e8c062-8fa0-46be-844a-03245fc9857b.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/2c95ad4c-bbc7-4043-9c79-8afd529fa4c2.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/338d45ec-397e-4eb4-b991-3185079509a4.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/51673c73-b0ea-4848-bae4-83a269dfa833.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/714e714b-dd1d-4a10-8f95-8552fa2c5296.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/77c314b4-f2d8-4682-a08e-0f34de180f5e.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/7a93a5a2-d3b9-4f3d-9747-22049300cd64.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/7d1ba0dc-a3c9-479a-b16c-b1b4aa8f03d6.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/83bc6aa5-e316-4524-bda8-458cea816f7a.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/91536913-6cd9-4c61-a109-4d9aa3e76b9d.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/9226626e-ad01-4504-a8bb-1287fa281f7b.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/946d0cce-549d-4caf-b74d-af5593e0911b.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/95093e96-5953-48a0-a8bc-508d625fb57e.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/aaca6636-f820-4ef5-881c-752440dda09d.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/abc21f52-0d39-490d-9676-d106b9af4302.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/b048dbdd-7672-4e47-82e2-2211495e40c6.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/d96c4613-bdfc-4e80-b084-7c27dfff1bb8.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/fb87f710-b46c-452a-b349-e3ce71c36959.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/ff1ea3f9-2bdc-4a9f-86de-185ec1b6c9e5.json (100%) diff --git a/docs/outstanding-issues-inbox/07867a60-274a-4350-a564-2fed480ff997.json b/docs/outstanding-issues-inbox/applied/07867a60-274a-4350-a564-2fed480ff997.json similarity index 100% rename from docs/outstanding-issues-inbox/07867a60-274a-4350-a564-2fed480ff997.json rename to docs/outstanding-issues-inbox/applied/07867a60-274a-4350-a564-2fed480ff997.json diff --git a/docs/outstanding-issues-inbox/1b3f0964-1cf4-4fad-b89d-b9393797807a.json b/docs/outstanding-issues-inbox/applied/1b3f0964-1cf4-4fad-b89d-b9393797807a.json similarity index 100% rename from docs/outstanding-issues-inbox/1b3f0964-1cf4-4fad-b89d-b9393797807a.json rename to docs/outstanding-issues-inbox/applied/1b3f0964-1cf4-4fad-b89d-b9393797807a.json diff --git a/docs/outstanding-issues-inbox/25e8c062-8fa0-46be-844a-03245fc9857b.json b/docs/outstanding-issues-inbox/applied/25e8c062-8fa0-46be-844a-03245fc9857b.json similarity index 100% rename from docs/outstanding-issues-inbox/25e8c062-8fa0-46be-844a-03245fc9857b.json rename to docs/outstanding-issues-inbox/applied/25e8c062-8fa0-46be-844a-03245fc9857b.json diff --git a/docs/outstanding-issues-inbox/2c95ad4c-bbc7-4043-9c79-8afd529fa4c2.json b/docs/outstanding-issues-inbox/applied/2c95ad4c-bbc7-4043-9c79-8afd529fa4c2.json similarity index 100% rename from docs/outstanding-issues-inbox/2c95ad4c-bbc7-4043-9c79-8afd529fa4c2.json rename to docs/outstanding-issues-inbox/applied/2c95ad4c-bbc7-4043-9c79-8afd529fa4c2.json diff --git a/docs/outstanding-issues-inbox/338d45ec-397e-4eb4-b991-3185079509a4.json b/docs/outstanding-issues-inbox/applied/338d45ec-397e-4eb4-b991-3185079509a4.json similarity index 100% rename from docs/outstanding-issues-inbox/338d45ec-397e-4eb4-b991-3185079509a4.json rename to docs/outstanding-issues-inbox/applied/338d45ec-397e-4eb4-b991-3185079509a4.json diff --git a/docs/outstanding-issues-inbox/51673c73-b0ea-4848-bae4-83a269dfa833.json b/docs/outstanding-issues-inbox/applied/51673c73-b0ea-4848-bae4-83a269dfa833.json similarity index 100% rename from docs/outstanding-issues-inbox/51673c73-b0ea-4848-bae4-83a269dfa833.json rename to docs/outstanding-issues-inbox/applied/51673c73-b0ea-4848-bae4-83a269dfa833.json diff --git a/docs/outstanding-issues-inbox/714e714b-dd1d-4a10-8f95-8552fa2c5296.json b/docs/outstanding-issues-inbox/applied/714e714b-dd1d-4a10-8f95-8552fa2c5296.json similarity index 100% rename from docs/outstanding-issues-inbox/714e714b-dd1d-4a10-8f95-8552fa2c5296.json rename to docs/outstanding-issues-inbox/applied/714e714b-dd1d-4a10-8f95-8552fa2c5296.json diff --git a/docs/outstanding-issues-inbox/77c314b4-f2d8-4682-a08e-0f34de180f5e.json b/docs/outstanding-issues-inbox/applied/77c314b4-f2d8-4682-a08e-0f34de180f5e.json similarity index 100% rename from docs/outstanding-issues-inbox/77c314b4-f2d8-4682-a08e-0f34de180f5e.json rename to docs/outstanding-issues-inbox/applied/77c314b4-f2d8-4682-a08e-0f34de180f5e.json diff --git a/docs/outstanding-issues-inbox/7a93a5a2-d3b9-4f3d-9747-22049300cd64.json b/docs/outstanding-issues-inbox/applied/7a93a5a2-d3b9-4f3d-9747-22049300cd64.json similarity index 100% rename from docs/outstanding-issues-inbox/7a93a5a2-d3b9-4f3d-9747-22049300cd64.json rename to docs/outstanding-issues-inbox/applied/7a93a5a2-d3b9-4f3d-9747-22049300cd64.json diff --git a/docs/outstanding-issues-inbox/7d1ba0dc-a3c9-479a-b16c-b1b4aa8f03d6.json b/docs/outstanding-issues-inbox/applied/7d1ba0dc-a3c9-479a-b16c-b1b4aa8f03d6.json similarity index 100% rename from docs/outstanding-issues-inbox/7d1ba0dc-a3c9-479a-b16c-b1b4aa8f03d6.json rename to docs/outstanding-issues-inbox/applied/7d1ba0dc-a3c9-479a-b16c-b1b4aa8f03d6.json diff --git a/docs/outstanding-issues-inbox/83bc6aa5-e316-4524-bda8-458cea816f7a.json b/docs/outstanding-issues-inbox/applied/83bc6aa5-e316-4524-bda8-458cea816f7a.json similarity index 100% rename from docs/outstanding-issues-inbox/83bc6aa5-e316-4524-bda8-458cea816f7a.json rename to docs/outstanding-issues-inbox/applied/83bc6aa5-e316-4524-bda8-458cea816f7a.json diff --git a/docs/outstanding-issues-inbox/91536913-6cd9-4c61-a109-4d9aa3e76b9d.json b/docs/outstanding-issues-inbox/applied/91536913-6cd9-4c61-a109-4d9aa3e76b9d.json similarity index 100% rename from docs/outstanding-issues-inbox/91536913-6cd9-4c61-a109-4d9aa3e76b9d.json rename to docs/outstanding-issues-inbox/applied/91536913-6cd9-4c61-a109-4d9aa3e76b9d.json diff --git a/docs/outstanding-issues-inbox/9226626e-ad01-4504-a8bb-1287fa281f7b.json b/docs/outstanding-issues-inbox/applied/9226626e-ad01-4504-a8bb-1287fa281f7b.json similarity index 100% rename from docs/outstanding-issues-inbox/9226626e-ad01-4504-a8bb-1287fa281f7b.json rename to docs/outstanding-issues-inbox/applied/9226626e-ad01-4504-a8bb-1287fa281f7b.json diff --git a/docs/outstanding-issues-inbox/946d0cce-549d-4caf-b74d-af5593e0911b.json b/docs/outstanding-issues-inbox/applied/946d0cce-549d-4caf-b74d-af5593e0911b.json similarity index 100% rename from docs/outstanding-issues-inbox/946d0cce-549d-4caf-b74d-af5593e0911b.json rename to docs/outstanding-issues-inbox/applied/946d0cce-549d-4caf-b74d-af5593e0911b.json diff --git a/docs/outstanding-issues-inbox/95093e96-5953-48a0-a8bc-508d625fb57e.json b/docs/outstanding-issues-inbox/applied/95093e96-5953-48a0-a8bc-508d625fb57e.json similarity index 100% rename from docs/outstanding-issues-inbox/95093e96-5953-48a0-a8bc-508d625fb57e.json rename to docs/outstanding-issues-inbox/applied/95093e96-5953-48a0-a8bc-508d625fb57e.json diff --git a/docs/outstanding-issues-inbox/aaca6636-f820-4ef5-881c-752440dda09d.json b/docs/outstanding-issues-inbox/applied/aaca6636-f820-4ef5-881c-752440dda09d.json similarity index 100% rename from docs/outstanding-issues-inbox/aaca6636-f820-4ef5-881c-752440dda09d.json rename to docs/outstanding-issues-inbox/applied/aaca6636-f820-4ef5-881c-752440dda09d.json diff --git a/docs/outstanding-issues-inbox/abc21f52-0d39-490d-9676-d106b9af4302.json b/docs/outstanding-issues-inbox/applied/abc21f52-0d39-490d-9676-d106b9af4302.json similarity index 100% rename from docs/outstanding-issues-inbox/abc21f52-0d39-490d-9676-d106b9af4302.json rename to docs/outstanding-issues-inbox/applied/abc21f52-0d39-490d-9676-d106b9af4302.json diff --git a/docs/outstanding-issues-inbox/b048dbdd-7672-4e47-82e2-2211495e40c6.json b/docs/outstanding-issues-inbox/applied/b048dbdd-7672-4e47-82e2-2211495e40c6.json similarity index 100% rename from docs/outstanding-issues-inbox/b048dbdd-7672-4e47-82e2-2211495e40c6.json rename to docs/outstanding-issues-inbox/applied/b048dbdd-7672-4e47-82e2-2211495e40c6.json diff --git a/docs/outstanding-issues-inbox/d96c4613-bdfc-4e80-b084-7c27dfff1bb8.json b/docs/outstanding-issues-inbox/applied/d96c4613-bdfc-4e80-b084-7c27dfff1bb8.json similarity index 100% rename from docs/outstanding-issues-inbox/d96c4613-bdfc-4e80-b084-7c27dfff1bb8.json rename to docs/outstanding-issues-inbox/applied/d96c4613-bdfc-4e80-b084-7c27dfff1bb8.json diff --git a/docs/outstanding-issues-inbox/fb87f710-b46c-452a-b349-e3ce71c36959.json b/docs/outstanding-issues-inbox/applied/fb87f710-b46c-452a-b349-e3ce71c36959.json similarity index 100% rename from docs/outstanding-issues-inbox/fb87f710-b46c-452a-b349-e3ce71c36959.json rename to docs/outstanding-issues-inbox/applied/fb87f710-b46c-452a-b349-e3ce71c36959.json diff --git a/docs/outstanding-issues-inbox/ff1ea3f9-2bdc-4a9f-86de-185ec1b6c9e5.json b/docs/outstanding-issues-inbox/applied/ff1ea3f9-2bdc-4a9f-86de-185ec1b6c9e5.json similarity index 100% rename from docs/outstanding-issues-inbox/ff1ea3f9-2bdc-4a9f-86de-185ec1b6c9e5.json rename to docs/outstanding-issues-inbox/applied/ff1ea3f9-2bdc-4a9f-86de-185ec1b6c9e5.json diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index 5639d8dba2..d8735925cf 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -96,7 +96,7 @@ removed after current-main verification; it is not missing recommended work. | #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 | S1 (#2022), S1b (#2035), S1d (#2054) landed with green canary pairs: generation-quality false rejections and the finalizer gap hole are fixed. Residual R4: chronic ~30 s strong-route provider_timeout on metformin-renal-dosing and valproate-pregnancy (measured 4/4 at v18 4ea310e48 and 2/4 at v19 main on 2026-08-18; safe source-backed extractive fallback, never model synthesis). Route-budget stop condition unchanged. Remediation-plan Phase 5.2 (re-test #231 after the trigram-index restore) is satisfied by S1's 2026-08-17 healthy-latency probes; live-drift #316 Phase 1.2 classified all ten RPC divergences as attribute-only, so canaries since 2026-08-14 measure a reconstructable path. Re-graded P1 -> P2. Next: prompt/context trimming for complex classes, or accept the extractive fallback as the durable answer for these two classes. Do not add a separate R4 row — this update supersedes that need. | docs/rag-improvement/HANDOVER.md secs 1-2; docs/rag-improvement/COORDINATION.md sec 7; docs/database-remediation-plan.md Phase 5.2; docs/audit/live-drift-forensics-2026-08.md sec on Phase 1.2 classification; ledger rows #316 / #248 / #231 / #342; session 2026-08-18 | 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 | -| #316 | P1 | issue | Live DB has 20 currently missing repo-defined indexes and 10 retrieval RPC bodies diverge; weekly live-drift has been red since 2026-07-26 with no routing | PHASE 3 (reframed) COMPLETE REPO-SIDE AND STAGING-PROVEN 2026-08-18 (PRs #2106 merged 72aa18865, #2111 follow-up) — no production access, no canonical (schema.sql/production) function body changed, no hosted value changed; owner decisions D1 codify-as-live and D2 canary exemption applied. (1) SET work_mem codified on all ten match_* RPCs: schema.sql carries the clause on every definition and 20260818110000_codify_live_rpc_work_mem runs ALTER FUNCTION ... SET work_mem per function after every recreate. Values: 128MB chunks_hybrid, embedding_fields_hybrid, index_units_hybrid, index_units_hybrid_v2; 64MB chunks_text, chunks_text_v2, lookup_chunks_text, memory_cards_hybrid, memory_cards_hybrid_v2, table_facts_text. PROOF: regenerated drift-manifest def_hash for all ten equals the live production def_hash in issue #1963 (run 32051068106) byte-for-byte — the next production live-drift run reports zero match_* mismatches once marked applied. (2) Eight never-created objects codified verbatim by 20260818111000 (five document_embedding_fields indexes, documents_status_idx, documents_updated_at + ingestion_jobs_updated_at triggers); disjoint from #102; all six indexes stay on search-health-unmonitored-indexes.json, required_indexes untouched (Phase 4.4). (3) Triage: document_chunks CHAIN-stale (token_estimate, zero migrations); rag_visual_eval_cases/runs CHAIN-stale (id default bound to extensions.gen_random_uuid via 20260705230000 search_path order) — both fixed by 20260818112000; document_chunks_content_trgm_idx: production's restored definition (8499c3d3..) IS canonical = schema.sql = 20260705180000; staging holds the 20260606000000 form (c3db2960..) — Phase 4.4 residual, no escalation. (4) STAGING PROOF COMPLETE (two owner-authorised windows, ref ikoiolksxqxfxgiyqpnu verified per call, production never targeted): 110000/111000/112000 applied by the Phase 2 method; the comparison then exposed THREE chain-stale BODIES (embedding_fields_hybrid, index_units_hybrid, memory_cards_hybrid_v2 carried the legacy fail-open predicate on a chain-built DB — never forward-codified after 20260712000000; NOT a production hole, manifest hash = live hash) — fixed by new migration 20260818113000_forward_codify_hybrid_owner_matches_bodies (verbatim from schema.sql, no-op on production), applied to staging in the second window; the two rows whose text gained set-local timeouts pre-merge (111000/112000) were refreshed to the merged text. All four staging history rows md5 = repo (dd5c8c9e.., 22585b9e.., ec154770.., d35c199b..); staging 199 rows, no_statements 0, corpus 0. FINAL STAGING DRIFT: UNEXPECTED DRIFT (1) = document_chunks_content_trgm_idx only — zero function mismatches, zero never-created objects, zero table mismatches. NEXT: production window (D3, one window, no canary, no index build): 20260818090000 (real change, probe v2) + 110000/111000/112000/113000 (all no-ops, live already matches); then Phase 4 (incl. 4.4 guard migration for the trgm pair + staging trgm rebuild). Tooling note: check-drift.ts:192 240-char clip (own P3 queued). Evidence: forensics §Phase 3. | session 2026-08-18 Phase 3 staging proof complete (PR #2111) | 2026-08-13 | +| #316 | P1 | issue | Live DB has 20 currently missing repo-defined indexes and 10 retrieval RPC bodies diverge; weekly live-drift has been red since 2026-07-26 with no routing | PHASE 4 COMPLETE 2026-08-19; the RPC track of this row closed 2026-08-18. BOTH HALVES OF THIS ROW ARE NOW CLOSED. (A) RPC divergence, from the 2026-08-18 window (PR #2123, forensics 3.7): the authorised window's pre-flight found the pending set EMPTY -- all five 20260818 migrations were already applied with executed statements (3/11/12/5/4, the CLI db push shape, not mark-applied) -- so db push was never run, no migration repair, no vault reads, zero production writes. Manifest def_hash equals live for all ten match_* functions, and live-drift 32131517648 showed 0 function mismatches. CAUSE, platform finding since resolved: the Supabase GitHub integration (Branching, production bound to git main, branch record 2026-06-27) was auto-applying every migration merged to main -- push-triggered live-drift bracketed 110000-112000 to 34 s after #2106's squash-merge. D4 IS NOW DECIDED: auto-deploy is OFF, confirmed empirically on 2026-08-19 when the four new 20260819 migrations sat pending on production after the branch existed and reached it only via an explicit db push. Not established: who enabled the integration or when, or whether the July mark-applied rows trace to it. (B) Index restoration, 2026-08-19 owner-authorized off-peak window: all 20 missing_live indexes rebuilt with CREATE INDEX CONCURRENTLY from canonical definitions cross-read against their defining migrations -- Batch A 14/14, Batch B 6/6, every one indisvalid AND indisready with normalized pg_get_indexdef matching canonical, zero invalid builds, zero retries, zero skips, zero lock waits (pg_locks read between every Batch B build). No transactional build was ever attempted; #102's bare-column indexes held out. Both unexpected_live indexes were DROPPED CONCURRENTLY rather than codified, because the chain already commands both drops and each is a strict leading-column subset of a present canonical index: document_table_facts_document_id_idx (superseded per 20260620000000) and storage_cleanup_jobs_owner_id_idx (superseded per 20260703030000/20260708000000). Live now reports 210 public indexes against the manifest's 210, zero invalid anywhere. Codified in five migrations applied by real supabase db push (never migration repair; every history row carries executed statements): 20260819100000/100100 guard Batch A/B, 20260819100200 discharges the plan 4.4 debt by guarding the two trigram indexes restored 2026-08-14 that 20260804110240 never checked, 20260819100300 takes search_schema_health() required_indexes 22->30 adopting all 8 Phase 6.3 monitor-candidates (unmonitored list 44->36, no monitor-candidate left; production ok true), and 20260819100150 repairs a chain defect the guard itself caught -- see below. Live-drift 32171070287: UNEXPECTED DRIFT 37->16, missing_live 20->ZERO, unexpected_live 2->ZERO. Staging brought to full parity in the same task; its drift comparison is GREEN with ZERO unexpected drift (was 19), corpus untouched, --prune-stale correctly not used. THE GUARD EARNED ITS KEEP: 20260819100200 failed the Supabase Preview check on PR #2151 because a preview branch builds from the migration chain alone, and the chain permanently produced the WRONG document_chunks_content_trgm_idx -- 20260606000000 creates it first without coalesce(content,''), and both later correct creators use IF NOT EXISTS so they no-op, with no migration ever dropping it. Forensics 3.3(d) had scoped this as staging-only and hand-repaired it there; it was never staging-only (db reset, DR replay, CI migration replay, preview branches all get the wrong index, which is NULL for rows with NULL content and so silently omits those chunks). Fixed by 20260819100150, conditional so it no-ops when canonical, rebuilds only on an empty table, and raises rather than run a write-blocking build on a populated one; proven by replaying the whole chain into a scratch Postgres (fails without it exactly as CI did, 204/204 with it) and the no-op path proven on production itself (index OID unchanged at 1491258 across the push). TWO ESCALATIONS FOR THE OWNER, neither absorbed. (1) PITR IS NOT ENABLED on production (pitr_enabled false, walg_enabled true, daily physical backups only, latest 2026-08-17T20:33:28Z), so the plan's standing 'restore point before any mutating phase' rule cannot be met; Phase 4 proceeded only because every statement was index-only with an exact one-statement inverse, and no future window that mutates DATA should proceed on that precedent. Queued separately as its own P2. (2) The migration_history block did NOT drop and no allowlist entry was written -- measured, not skipped: of the 15 no-statements versions, 6 are index-shaped and the intersection between the objects they create and the 22 these guards validate is EMPTY (near-misses are distinct objects, e.g. audit_logs_owner_id_idx vs audit_logs_owner_created_idx). The 15 stay unallowlisted and remain #Q5JHBJ's work. REMAINING FOR THIS ROW: nothing on the index or RPC tracks. Phase 5 measurement (after-EXPLAIN set, #231 re-test on healthy latency, check:production-readiness) is the only follow-on. Full evidence with dates, run IDs and pasted output in docs/audit/live-drift-forensics-2026-08.md sections 3.7 and 'Phase 4 completion'. Session traps still current: the main checkout D:\Repos\Database is linked to STAGING, so link a dedicated worktree for production and unlink after; supabase db query --linked --project-ref works read-only via the management API without a DB password; db query parses a leading -- as a flag, so pass SQL that starts with a comment via --file; production has no track_commit_timestamp. | PR #2151 (Phase 4) and PR #2123 (window 3.7); live-drift runs 32171070287 and 32131517648; forensics sections 3.7 and 'Phase 4 completion' | 2026-08-13 | | #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 | | #339 | P2 | task | Favourites Continue and Recent are driven by hard-coded demo timestamps; real saved items have no last-opened data | Surfaced while shipping #164 (PR #1983), which made both surfaces prominent. src/components/clinical-dashboard/favourites-command-library-page.tsx derives 'most recently used' from lastUsedScore(item.lastUsed), and item.lastUsed comes from lastUsedByItemId — a hard-coded five-entry literal keyed to demo slugs ('Today 08:44', 'Yesterday 16:12', ...). Anything else, including every real registry favourite, falls back to the literal string 'Saved', which lastUsedScore buckets at 1000. pinnedItemIds is likewise a hard-coded two-item Set. The consequence after #164: for a signed-in user with real favourites, the Continue card and the Recent panel are effectively arbitrary — every item ties at the same score and the order is whatever the source array happened to be. Note that recentQueries in the shell is search-query history, not viewed-item history, so it cannot back this. Next: add a per-favourite last-opened timestamp. Cheapest is a client-side recents store keyed by favourite id written on open; the durable version is a column on the account favourites record so it survives a device change, which is a schema plus /api/account/favourites change and needs the usual migration review. Either way, pinning should stop being a hard-coded id set. Stop: do not fabricate a timestamp at render time from anything other than a recorded open event — an invented 'last used' on a clinical reference list is worse than an honest absence. | session 2026-08-15; PR #1983; favourites-command-library-page.tsx lastUsedByItemId/pinnedItemIds | 2026-08-15 | | #4TBHS8 | P3 | issue | Advisory UI mockup spec 'phone filter sheet follows the shared local-filter behavior' fails on main | tests/ui-tools-search-mode-mockup.spec.ts:188 fails at line 198 waiting for '2 showing' inside [data-testid=tools-search-filter-sheet] after searching 'Safety' at 390px. Reproduced locally under --project=chromium-mockups on BOTH claude/card-review-optimize-h0pidc and origin/main (dc7e518), so it is pre-existing and NOT caused by the card branch — attribution was checked before any fix was attempted. It surfaced now only because the ui-advisory lane fires on advisory_ui_changed (a mockup surface changed or the flake ledger is non-empty) and had been skipped on every earlier run of that PR. It is non-blocking: ui-advisory carries continue-on-error true and is absent from pr-required's needs list in ci.yml, and verify:ui excludes @mockup via --grep-invert, which is why a 429-pass local run never touched it. Next: open the trace at test-results/ui-tools-search-mode-mocku-b6163-hared-local-filter-behavior-chromium-mockups/trace.zip and decide whether the expected count of 2 is stale against the current tools catalogue or the facet hint genuinely miscounts; the mockup renders the production ToolsSearchResultsPage, so a real miscount would affect /tools too. Stop: do not change the expected number to match observed output without establishing which is correct. | session 2026-08-18; PR #2060 Advisory UI run 32090358678; reproduced on origin/main dc7e518 | 2026-08-18 | @@ -106,7 +106,7 @@ removed after current-main verification; it is not missing recommended work. | #NTAV3D | P2 | issue | Adversarial divergence (S5 harness pin): scope-guessed-chunk-id — review fallback returns a grounded source pointer echoing the query instead of refusing | Pinned in tests/rag-adversarial-harness.test.ts KNOWN_DIVERGENCES (self-expiring). Observed shape: grounded true, cited [syn-scope-guess-a], and the guessed (never-retrieved) chunk id syn-not-retrieved-zzz is never resolved into content — the no-read invariant holds — but the review fallback returns a grounded source pointer that echoes the query text rather than refusing the guessed-id request. Fixture: scripts/fixtures/rag-adversarial-cases.v1.json case scope-guessed-chunk-id (category scope_or_tenant). Next: decide whether a query naming an unretrieved chunk id should refuse rather than fall back to a source pointer (RAG-surface change; own PR; harness pin flips; canary pair). Stop: do not delete the pin without the behaviour change. | RAG programme coordinator, packet S5 (PR #2056) follow-ups, 2026-08-17 | 2026-08-17 | | #VXB8XA | P2 | issue | Adversarial divergence (S5 harness pin): cite-mismatched-attribution — offline document-match listing cites every retrieved document, not only the claim-bearing one | Pinned in tests/rag-adversarial-harness.test.ts KNOWN_DIVERGENCES (self-expiring: the harness asserts the normative B0 fixture contract still FAILS; when behaviour reaches the fixture expectation the pin goes red and must be deleted). Observed shape: cited chunk ids [syn-cite-attrib-a, syn-cite-attrib-b], grounded true, answerQualityTier source_only — the document-match listing attributes both retrieved documents although only one carries the claim. Fixture: scripts/fixtures/rag-adversarial-cases.v1.json case cite-mismatched-attribution (category citation_fabrication). Safety invariants (network, budget, canary absence, forbidden substrings, tenancy) hold; only citation attribution precision diverges. Next: decide whether the document-match listing should cite only claim-bearing documents (RAG-surface change; own PR; RAG impact line; offline harness proves the pin flips; canary pair). Stop: do not delete the pin without the behaviour change; do not weaken the fixture. | RAG programme coordinator, packet S5 (PR #2056) follow-ups, 2026-08-17 | 2026-08-17 | | #S4K1GA | P3 | task | Physical iPhone acceptance owed for the answer-progress motion fix (Safari + installed PWA, Motion=Full) | PR #2046 fixed the reported defect (OS Reduce Motion froze every animation and set the ECG trace to opacity:0) and added a Motion preference whose "full" value opts back in over the OS setting. All executed browser evidence ran on Chromium 1194 in a Cloud container — the repo's own verify:ui gate could not run because check:playwright-browser-revision reports the known #255 drift (expects 1234). Playwright WebKit is not the iOS engine either. Acceptance: on the physical iPhone, in Safari and as the installed PWA, with Settings > Motion set to Full, confirm the ECG strip visibly travels and the current-step spinner rotates; with Motion left on System, confirm the trace stays visible and static rather than blank. Failure to confirm means the defect class is unclosed, which is exactly how #1974/#1989/#1995 were each declared fixed. Relates to #255, #280. | PR #2046 phone/PWA answer-progress animation defect, 2026-08-17 | 2026-08-17 | -| #Q5JHBJ | P2 | task | Deploy the 20260818090000 schema_drift_snapshot v2 history probe (Phase 6.1) in an approved production window after Phase 4, triage the first unguarded no-statements report, and author fail-fast guard migrations for the pre-contract 2026-07-01..02 and 2026-07-12 rows | Repo side of remediation plan Phase 6 landed (probe migration, guard-migration contract in docs/database-drift-detection.md + AGENTS.md, tests/migration-history-guards.test.ts, tests/search-health-index-coverage.test.ts + supabase/search-health-unmonitored-indexes.json). The migration is NOT deployed: it needs the owner-approved production migration deploy window (plan approval map, Phase 6.1, after Phase 4). Expected first live run: the ~9 remaining section 1.1 versions plus the 2026-07-12 batch (20260712165915..20260712173000) are reported as unguarded no_statements findings because no repo-provable guard exists; each needs a validation guard migration (20260804110240 pattern) + a migration_history allowlist entry, not a bare allowlist. Also decide the 8 monitor-candidate indexes in the ratchet file by a required_indexes migration (Phase 4.4). Do not touch #316/#056 for this; those rows are owned by the Phase 1.2 / Phase 2 sessions. | Phase 6 worker chat 2026-08-18; docs/database-remediation-plan.md section 6; docs/audit/live-drift-forensics-2026-08.md Phase 6 | 2026-08-17 | +| #Q5JHBJ | P2 | task | Deploy the 20260818090000 schema_drift_snapshot v2 history probe (Phase 6.1) in an approved production window after Phase 4, triage the first unguarded no-statements report, and author fail-fast guard migrations for the pre-contract 2026-07-01..02 and 2026-07-12 rows | RE-SCOPED 2026-08-18: the 20260818090000 schema_drift_snapshot v2 probe is ALREADY LIVE on production, auto-applied by the Supabase GitHub integration on merge of PR #2058 (verified in the Phase 3 production window, forensics section 3.7: probe ok, migration_history 20 rows = 15 unallowlisted + 5 seeded; live-drift 32131517648 reports 15 migration_history findings). The deploy step of this row is done and needs no window. REMAINING: triage the 15 reported no-statements versions (the 2026-07-01..07-02 cluster and the 2026-07-12 batch listed in forensics section 1.1) and author fail-fast validation guard migrations per 20260804110240 for each, never bare allowlisting; index-shaped ones can ride Phase 4's guard batches. See D4 in #316: while auto-deploy is on, every merged migration is a production deploy. | PR #2123 forensics section 3.7; coordination chat 2026-08-19 | 2026-08-17 | | #43SSS0 | P3 | rec | Three spring easing tokens in globals.css are dead: zero var() references and zero utility usage | --spring-tight, --spring-bouncy and --spring-gentle (src/app/globals.css:222-224) are declared in the @theme block but have no var() consumer in any stylesheet and no generated-utility consumer in src/. Tailwind v4.3.3 tree-shakes unused theme variables, so they never reach the compiled CSS — they are source noise, not shipped weight. Found while confirming (during PR #2046) that --animate-answer-ecg survives that same tree-shaking because it IS referenced via var() from the project's own CSS; --ease-spring is the working precedent for that pattern. Next: delete the three tokens, or wire them to the motion surfaces they were intended for. | PR #2046 phone/PWA answer-progress animation defect, 2026-08-17 | 2026-08-17 | | #75JA0P | P2 | issue | Playwright runs the whole suite with reducedMotion:"reduce", so no gate reflects the default user configuration | playwright.config.ts:61 sets contextOptions: { reducedMotion: "reduce" } suite-wide, and every motion assertion has to opt out per-test via page.emulateMedia({ reducedMotion: "no-preference" }). That inversion is why three consecutive PRs (#1974, #1989, #1995) shipped green while a physical iPhone with OS Reduce Motion on showed a frozen, blank answer-progress panel: the suite never exercised the reported configuration. PR #2046 added tests/ui-phone-motion.spec.ts to cover that one surface, but the suite-wide default remains inverted for every other motion behaviour. Next: decide whether the suite default should be no-preference with reduce opted into per-test (the safer direction), or keep the current default and add a contract test that fails when a motion assertion has no explicit emulateMedia call. | PR #2046 phone/PWA answer-progress animation defect, 2026-08-17 | 2026-08-17 | | #1PN5BM | P3 | issue | H5a residual: whether a constant similarity of 1 may contribute to a confidence label is still open, and after G1 it lives only in the hazard doc | Packet G1 (PR #2053, merged 2026-08-17) implemented owner decision Option B: buildDocumentSummaryResults now stamps similarity_origin "document_context" on document-summary rows, deriveConfidence is unchanged, and document summaries still reach "high". That closed the LEGIBILITY half of the H5a live residual -- the fabricated 1.0 is no longer indistinguishable from a perfect cosine at any surface that reads a row. It did NOT answer the underlying governance question: may a score nobody measured contribute to the confidence label a clinician reads at all? Option B was chosen because tagging has no measured safety cost while Option A (tag as synthetic_text, capping summaries at "medium") is a label downgrade without measured gain -- so the question was deferred deliberately, not resolved. The paired question row #J912J9 is being closed by G1, so once that closure reconciles this knowledge survives only in docs/clinical-hazard-analysis.md H5a and not in the queue anyone reads. NEXT: no action required unless a measured signal appears; if it does, the tag is what makes the fix cheap -- any future gate can now discriminate the document-summary route without re-deriving provenance. Guard rails already in place: tests/rag-score.test.ts pins the discriminating pair (two document_context citations >= 0.82 -> "high"; the identical scores tagged synthetic_text -> "medium"), so a silent change in either direction goes red. | Packet G1 session 2026-08-17 (PR #2053); docs/clinical-hazard-analysis.md H5a; closes-with #J912J9 | 2026-08-18 | @@ -122,12 +122,25 @@ removed after current-main verification; it is not missing recommended work. | #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 | .claude/hooks/precompact-issues-capture.sh was added in PR #2113 to ask for /issues capture BEFORE compaction discards the follow-ups it wants recorded; the pre-existing issues-surface.sh reminder is a SessionStart hook and therefore fires AFTER compaction, when the material is already gone. Claude Code is known to inject hook stdout into model context for SessionStart, UserPromptSubmit, PreToolUse and PostToolUse. Whether it does so for PreCompact could NOT be determined: the installed CLI at %APPDATA%\npm\node_modules\@anthropic-ai\claude-code ships a compiled claude.exe with no inspectable JS bundle to grep. The hook therefore prints plain text rather than a hookSpecificOutput JSON envelope, so that if the platform does not inject it the operator still sees a clean transcript line instead of a raw JSON blob, and it appends one line per firing to a log so the question is answerable rather than permanently open. Next, after any compaction in a session using this repo: cat "$(git rev-parse --absolute-git-dir)/claude-precompact.log". Lines present but no reminder seen in context means the limit is real and the SessionStart backstop is carrying it. No lines at all means the registration is wrong. WARNING: that log lives under the worktree's own git dir, so it is destroyed when the worktree is removed — check it before cleaning up the worktree this was authored in. | PR #2113 .claude/hooks/precompact-issues-capture.sh | 2026-08-18 | | #ZF006G | P3 | rec | Five independent SectionHeading implementations exist across modes with no shared recipe | clinical-dashboard/dashboard-shell.tsx:17, clinical-dashboard/search-pins-menu.tsx:97, formulation/formulation-ui.tsx:81, specifiers/specifier-ui.tsx:237, plus a fifth in therapy-compass/ui.tsx deleted unreferenced in 7d2c84a11. Same shape as the problem card-recipes.ts solved for cards, but spanning four modes. Two of the five already share an identical {eyebrow, title, body} signature - the natural shared contract. Stop rule: do not fold these into ui-primitives.tsx; COMPONENTS.md section 0.4 lists it as over-budget and slated to split. | session 2026-08-18 | 2026-08-18 | -| #FEWQZ5 | P2 | task | Therapy Compass has three shared-component stages left: Button call sites, card-recipes adoption, and page headers | Branch claude/therapy-mode-consistency-a466b0 landed A/B1/B2/E/F/G in 7ce820f2c..7d2c84a11. Remaining: B3 (18 control-recipe call sites across 9 files), C (card/heroCard plus the inline fork at therapy-card.tsx:71 bypass card-recipes.ts), D (7 hand-rolled h1, 3 at text-3xl-minus). Stop rule: one browser pass and one baseline refresh after all three, never per stage. | session 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 | 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 + Codex connector comments on PR #2113, 2026-08-18; AGENTS.md 'PR bundling (reduce one-task-one-PR churn)' | 2026-08-18 | | #EH9VA6 | P2 | rec | The serialized issues:reconcile operation has no interlock, and two reconcile PRs were open simultaneously on 2026-08-18 | AGENTS.md requires npm run issues:reconcile to run from ONE deliberately serialized fresh-base branch, but nothing enforces that: the reconciler's own guards (stale base, dirty canonical file, cross-worktree lock) are all local to a single machine and cannot see a second reconcile branch already pushed. On 2026-08-18 two were open at once — PR #2110/#2119 (claude/issues-reconcile-20260818, 'reconcile 74 queued requests') and PR #2120 (claude/issues-reconcile-2026-08-18-evening, 88 requests). Both merged. It only came out clean by luck of ordering: #2120 merged first and had already applied a superset of the 74, so GitHub's squash of #2119 collapsed to a 1-line review record and the canonical ledger was left correct (verified after the fact on main 4575cf57: 365 rows, 48 open, 0 pending, 339 applied, check:outstanding-issues and check:ledger-write-discipline both green). Had #2119 merged first, #2120's recorded transaction would no longer have equalled the canonical diff and check:ledger-write-discipline would have gone red on a branch that must never be synced from main — the documented recovery is to close the PR and redo the whole reconcile from a fresh base. Next: give the reconcile path a cheap pre-flight interlock rather than relying on operator discipline — e.g. have issues:reconcile refuse (or loudly warn) when git ls-remote --heads origin shows another unmerged branch carrying inbox renames under docs/outstanding-issues-inbox/applied/, which needs no provider tooling beyond a remote ref listing and no GitHub API call. Related to #292 (duplicate concurrent work) but distinct: this is a serialization invariant on a single canonical file, not two sessions building the same feature. | session 2026-08-18 evening reconcile; PRs #2119 and #2120 both open and merged same day | 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 | PR #2113 fixed .claude/hooks/session-start.sh, which was checked in as mode 100644 while both sibling hooks were 100755, and was the only hook registered by bare path rather than through bash. What was PROVEN: the index mode, the bare-path registration, and that core.fileMode=false on this Windows ReFS Dev Drive hides both (a local chmod +x is a silent no-op; only git update-index --chmod=+x works). What was NOT proven: that it actually failed on a Linux web container, because no container was available to test from. The 100755/100755/100644 asymmetry makes accident overwhelmingly likely rather than a deliberate choice, and the script's whole body is gated on CLAUDE_CODE_REMOTE=true so the web container is the only place it does any work — it provisions the Node 24 the engine floor requires, after npm ci EBADENGINE blocked PRs #1611, #1697, #1705 and #1740. This is confirmation, not risk: the registration now uses bash "$CLAUDE_PROJECT_DIR/...", which removes the dependency on the mode entirely, and tests/session-start-hook.test.ts pins every hook at 100755 with LF-only line endings while tests/claude-code-settings.test.ts pins every hook command to start with an interpreter. Next: on the first Claude Code web session on this repo, check the session start output for the '[session-start] Using node ...' line and confirm npm ci ran. If it did not, the failure is something other than the exec bit and this item becomes a real defect rather than a confirmation. | 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 | clean-worktree.mjs gained list-only --merged and --squashed in PR #2113 and identified 9 landed worktrees across the 50-worktree fleet, 5 of them on D:. Removal was NOT performed and must not be run blind. Re-verifying each candidate immediately before deletion, twice, showed the fleet is actively worked: database-coordination-chat-9c8cbd and database-drift-remeasure-phase2-7c4215 each held 2 unmerged commits despite the scan minutes earlier reporting '0 commits ahead', their newest files were written the same afternoon, and bundle-baseline had been switched to a different branch mid-scan and was running Playwright (the push guard named it as holding the heavy-run lease). Deleting any of them would have destroyed unmerged work. Next: run 'node scripts/clean-worktree.mjs --merged --squashed' when no other Codex/Gemini/Claude session is active, read the confidence line on each candidate, and re-run with --remove. Skip any candidate marked 'NOT fully corroborated' — that label means the patch-id test inferred the landing but some changed files still differ from origin/main, which is usually base churn but is not proof. D: was 25.3 GB of 50 GB used with roughly 19 GB of that duplicated node_modules across 21 worktrees at ~0.89 GB each. Ignore the C: worktrees entirely; they belong to Codex and Antigravity sessions. Stop rule: never pass --force to git worktree remove, and never remove a worktree that is ahead of origin/main. | PR #2113 scripts/clean-worktree.mjs; live fleet re-verification 2026-08-18 | 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 | PR #2143 went red on five consecutive heads and was merged past it. Job logs for runs 32170524256 (first) and 32178668323 (last) both report 'CANCELLED with no failing job' and zero failures: the first cancelled only lighthouse-budget while UI_FAST_RESULT and UI_RESULT were success; the last cancelled static-pr, coverage, production-ui-critical, production-ui and lighthouse-budget. Cause is cancel-in-progress firing on each new head, and something merged origin/main into that branch roughly every five minutes during a single working session, minting a head each time. #095 deliberately keeps a cancelled required job RED, which is correct, so the defect is the churn producing the heads, not the aggregate. Consequence beyond noise: the red is indistinguishable at a glance from a real break, and this PR was merged with it red. Next step is to find what is auto-syncing open PR branches (AGENTS.md 'Open PR branch sync (anti-churn)' says sync:pr-branches:apply is operator-run and should be late and once) and make it stop re-syncing a branch whose required CI is in flight. Related to #CCZ4HB, which records the same churn exhausting the review-bot budget. | PR #2143, runs 32170524256 and 32178668323, 2026-08-18 | 2026-08-18 | +| #P5542X | P2 | issue | pr-policy classifies the switch controlling whether unreviewed clinical content reaches production as clinicalRisk false, so no governance preflight is enforced | Found 2026-08-18 on PRs #2145 and #2150, both of which removed the Therapy production gate. classifyPullRequestFiles in scripts/pr-policy.mjs returned clinicalRisk false for a diff touching src/lib/app-modes.ts and src/lib/therapies.ts - the exact two modules deciding whether 205 clinically-unreviewed therapy records are reachable by users in production. Its clinicalRiskPatterns match src/lib/ only when the filename contains auth, permission, privacy, security, rag, retriev, rank, search, answer, clinical, citation, source, document, upload or download; app-modes and therapies match none. The data patterns (src/data, data, public/therapy-compass-data) match the records themselves but not the code gating their reachability. Both PRs completed a governance preflight voluntarily, which is exactly the fragility: the next such change may not. Consider matching on reachability/exposure surfaces, or treating any diff that changes an app-mode devOnly flag or a review-status filter as clinical-risk. Stop rule: do not widen the patterns so far that ordinary UI work trips the preflight - the classifier comment already warns that presentation files are not clinical-risk merely for living under a clinically-named directory, and that judgement is correct. | session 2026-08-18 | 2026-08-18 | +| #VTEW3W | P3 | rec | therapyBtn still dresses 30 raw controls across 8 therapy files with no shared equivalent | Measured 2026-08-18 on main at adf93a75. src/components/therapy-compass/controls.ts exports therapyBtn, used at 30 call sites across therapy-card.tsx (2), detail-screen (2), pathways-screen (4), compare-screen (3), search-screen (2), sheets-screen (8), recommend-screen (6) and brief-screen (3). PR #2122 deleted the three button recipes it sat beside (commandControl, outlineControl, iconControl) and converted their 19 call sites to the shared Button, but deliberately kept therapyBtn: its remaining consumers are list rows, disclosure headers, section toggles and chips - controls that are not Buttons in the design-system sense, so forcing them onto the Button variants would have been wrong. The gap is that nothing shared covers them either, so therapy carries its own focus ring, hover lift and dual disabled encoding (native disabled plus aria-disabled) while every other mode hand-rolls or omits equivalents. Worth deciding whether the design system should own an interactive-row or quiet-control recipe, which would serve more than therapy - the same question card-recipes.ts answered for cards. Note therapyBtn already composes the shared focusRing from card-recipes.ts, so the focus contract is not forked; what is local is the hover/press motion and the disabled encoding. Stop rule: do not resolve this by converting the 30 sites to Button variants - that was considered and rejected during #2122 because a list row is not a button, and it would put button chrome on card-like surfaces. | session 2026-08-18 | 2026-08-18 | +| #GBBYTA | P2 | task | Hoist the filtered-zero empty state out of the documents results grid so it can sit flush under the results band | SearchResultsEmptyState renders the filtered-to-zero case nested two divs inside the results grid in document-search-results.tsx (grid gap-3 wrapper, with a conditional 'N results after filters' pill above it), not adjacent to SearchResultsHeaderBand. That nesting blocks the inline treatment evaluated for PR #2147: recovery cannot share the band's bottom edge, the band's data-tone lead is not adjacent so the panel has no state mark, and the filter chips are not near enough for the panel to point at them instead of duplicating them. PR #2147 therefore shipped the self-contained rail panel, which does not depend on adjacency. Hoisting the state to a sibling of the band would let the panel drop its own rail, its named-removal row and its eyebrow — roughly a third less height on phones. Only the documents nesting was verified; the other five consumers (favourites, calculators, forms, services, therapy-compass) were not checked and may nest the same way. | PR #2147 design review; document-search-results.tsx:1558 | 2026-08-18 | +| #0HFDWD | P2 | issue | CI change-scope reports UI_CHANGED false for changes that alter which modes render, so Production UI is skipped on user-facing work | Found 2026-08-18 on PR #2145. The CI run recorded UI_CHANGED false, UI_RESULT skipped and UI_FAST_RESULT skipped for a diff that removed devOnly from the therapy-compass mode and switched off the production record filter - a change that alters which modes appear in the shell for every user. Production UI therefore never ran. The uiPatterns in scripts/pr-policy.mjs and the equivalent scope detection in scripts/ci-change-scope.mjs match src/app/ (non-api), src/components/, src/styles/, public/, tests/ui-*.spec.ts and playwright config; the diff touched only src/lib/app-modes.ts, src/lib/therapies.ts and unit tests, so nothing matched. PR #2150 supersedes that change and adds a visible component, and would still not trip the classifier for its src/lib half. Browser coverage for both was supplied only by a local verify:ui run (447 passed), which no policy required. Consider treating src/lib/app-modes.ts as UI scope, since it is the mode registry the shell renders from, and auditing which other src/lib modules feed rendering. Stop rule: do not make all of src/lib UI scope - that would run a 20-minute Chromium gate on every library change and reintroduce the cancellation waste documented in docs/testing.md. | session 2026-08-18 | 2026-08-18 | +| #EP1BQS | P3 | rec | The RAG improvement programme carries its own status table with no session-start link back to the ledger | NARROWED 2026-08-18 before filing: an earlier read of this found zero ledger references to docs/rag-improvement/. That is no longer true — #231 now cites it (5 mentions), so the ledger-to-programme direction exists. What remains is the reverse direction and ownership. docs/rag-improvement/HANDOVER.md instructs every implementing session to read its packet, then README.md, then docs/rag-behaviour/, and to update a per-session status table (section 2) — it does not tell that session to read docs/outstanding-issues.md or to check open PRs for the surface its packet touches. So a session entering through HANDOVER.md never sees the canonical queue, and no row owns the programme itself. AGENTS.md designates docs/outstanding-issues.md as the single universal cross-session ledger and warns that detailed runbooks must not become a second status ledger; a per-session status table is close to that line. This is the documented precondition for #292 (two assistants shipped the same queued conversion four hours apart, one PR closed as a duplicate) and #301 (two sessions built #262 part 3 in parallel), and it is not hypothetical for this programme: the same check-before-filing discipline is what stopped this very row from being filed with a stale premise. Next (cheap, docs-only, no behaviour change): add to HANDOVER.md section 4's session-start checklist a step to read the recommended queue and check open PRs for the packet's surface, and decide whether the programme warrants one owning ledger row so its state is visible from the canonical side. Stop: do not duplicate the programme's tracks as ledger rows — that would create the second queue this row exists to prevent, in the other direction. Design authority stays with docs/rag-improvement/README.md and protected-surface rules with docs/rag-behaviour/. | session 2026-08-12/08-18 cross-session review; verified against origin/main 622f1fb | 2026-08-18 | +| #XPY409 | P3 | task | Phone screenshots and DOM measurements taken before the phone chrome stack settles misreport every offset below it | The phone header stack (.phone-sticky-header-stack) is position:fixed and mounts collapsed, and the page reserve max-sm:pt-[var(--phone-overlay-chrome-h)] resolves to the settled height only after mount. A Playwright screenshot or evaluate() run at networkidle can therefore read main at y=72 with the mode-nav rail overlapping it, when the settled layout has main at y=121 with no overlap at all. Measured on /dictionary/browse 2026-08-18: unsettled h1 y=88 (appears hidden behind the rail), settled y=161, and the same page before and after a header redesign both settled to y=161 — i.e. the apparent overlap was pure measurement artefact. This cost a wrong diagnosis and a reverted 'fix' that padded the header to clear a rail that was never covering it. Fix is a documented settle wait (waitForTimeout ~1200ms, or better a wait on the stack's settled height) in the phone verification recipe in docs/testing.md, so the next person measuring phone chrome does not repeat it. Cheap, docs-only. | Dictionary browse header rebuild, PR #2143, 2026-08-18 | 2026-08-18 | +| #WJDQ0X | P3 | issue | Shared search chrome fails axe landmark rules on every mode: composer content sits outside any landmark and the universal header renders a second banner | An axe sweep of the six Dictionary routes at 1440px (2026-08-18, PR #2114) found two violations that are not Dictionary's: `region` (3 nodes — the composer label, the `#answer-composer-privacy-warning` span, and the privacy link, all outside any landmark) and `landmark-no-duplicate-banner` + `landmark-unique` on detail routes, where the global `#search` universal header renders a second banner landmark beside the app header. Both are moderate impact, both reproduce on routes that branch never touched (/dictionary/browse, /dictionary/compare), and both live in shared chrome — master-search-header / global-search-shell — so they affect all 13 modes, not one. Repo axe gates only fail on critical/serious, which is why this has never gone red. Next: wrap the composer privacy row in the composer's own landmark (or give it `role="group"` with a label) and decide which of the two headers keeps `banner`; then widen tests/ui-accessibility.spec.ts to assert the landmark rules, not just critical/serious. Deliberately out of scope for PR #2114 — a shared-chrome change under a Dictionary PR is the wrong blast radius. | Session 2026-08-18, PR #2114 axe sweep; @axe-core/playwright wcag2a+wcag2aa+wcag21a+wcag21aa+best-practice at 1440x900 | 2026-08-18 | +| #G4M3DV | P3 | issue | Two documented workflow steps conflict: npm run ensure guarantees verify:pr-local fails its build stage with BUILD_REFUSED_DEV_SERVER | Reproduced 2026-08-18 on branch claude/therapy-mode-consistency-a466b0. AGENTS.md requires npm run ensure before any browser/UI work, and docs/testing.md requires npm run verify:pr-local at PR handoff. Doing both in one session always fails, because the production build refuses to run while the project dev server holds its port: verify:pr-local exits with 'failed: build (exit 76)' and the note 'production build was refused while the Clinical KB dev server is running (BUILD_REFUSED_DEV_SERVER). This is a failed gate, not a skip.' The refusal itself is correct and should stay - a build sharing a port with a dev server is not trustworthy - but nothing in either doc warns that the two required steps are ordered, so the gate reads as a real failure. Workaround used: stop the dev server, rm -rf .next, re-run npm run build standalone (exit 0), then check:bundle-budget against the fresh output. Options: have verify-pr-local.mjs name the remedy in its own failure note, or document the ordering in the testing speed playbook. Stop rule: do not make the build stage soft-skip when a dev server is up; the existing fail-closed behaviour is the correct half of this. | session 2026-08-18 | 2026-08-18 | +| #NEBJAM | P2 | rec | Therapy keeps a private eight-component UI kit whose shared equivalents it imports zero times, including the badge that carries review status | Measured 2026-08-18 on main at adf93a75, after PRs #2122 and #2150. src/components/therapy-compass/ui.tsx exports Tag, TagRow, StatusBadge, IconTile, LoadingState, EmptyState, Eyebrow and Meter. Shared equivalents exist and Therapy imports them zero times: ui/chip.tsx for Tag/TagRow (Chip is imported in exactly one therapy file), ui/status-mark.tsx for StatusBadge, ui/error-state.tsx for EmptyState, ui/progress.tsx for Meter, the eyebrowText primitive for Eyebrow, and category-icon-tile.tsx for IconTile. This is the same class of duplication card-recipes.ts was written to end for cards, one layer down, and it survived the #2122 convergence because that work targeted Button, card surfaces and page headers only. Highest-consequence piece: StatusBadge renders the Needs source review label, which since #2150 is the per-record half of the only protection standing between an unreviewed therapy record and a clinical decision - and it is a module-private implementation no shared contract governs. Two migration hazards to respect. (1) StatusBadge pairs its warning tone with a TriangleAlert glyph via reviewStatusMeta in data/select.ts; any swap must preserve that shape channel or the warning becomes colour-only and trips the status-colour boundary ratchet. (2) Meter's colour-only status was deliberately fixed in commit 8c791a1ad by naming the completeness band in text; a naive swap to shared Progress would regress it. Suggested order: Eyebrow and Tag/TagRow first (lowest risk, no status semantics), then EmptyState and IconTile, then Meter and StatusBadge last with the colour-only contract tests extended first. Stop rule: do not fold any of these into ui-primitives.tsx; COMPONENTS.md section 0.4 lists that module as over-budget and slated to split. Related: #ZF006G tracks the parallel SectionHeading duplication across four modes. | session 2026-08-18 | 2026-08-18 | +| #SBKXZ7 | P2 | task | Therapy sign-off has no tooling: nothing stops reviewStatus reviewed being set with an empty checklist, and there is no reviewer attribution | Re-queued 2026-08-18 after PR #2145 was closed in favour of #2150, which supersedes the exposure change but does not carry this follow-up. Therapy now ships in production with its review state disclosed rather than hidden, so sign-off is the remaining clinical work. Three gaps. (1) reviewStatus is a bare string in src/data/therapies-source.json; a record can be flipped to reviewed with all seven reviewChecklist booleans still false and nothing detects it. Needs a script that refuses the flip unless the checklist is complete, plus a contract test pinning reviewed implies full checklist. (2) No attribution: none of the 44 record fields carries reviewedBy or reviewedAt, so a sign-off cannot record who signed or when - the same defect #318 flags against the medication interaction lexicon. (3) No review workflow: 205 records x 7 checks is 1435 clinical judgements by hand; a CLI that walks records, shows the fields each check covers, and writes the decision with attribution would make it tractable. State at re-queue: 205 records, all reviewStatus needs_review, all seven checklist booleans false, reviewCompleteness 57-71 with zero records complete. The catalogue notice #2150 adds reads from THERAPY_CATALOGUE_SUMMARY.needsReviewCount and disappears when that reaches zero, so completing sign-off is what retires it. Stop rule: an assistant must never tick clinicalAccuracyReviewed, sourceChecked, evidenceAppraised, safetyCautionsChecked or patientExplanationChecked - those are qualified-clinician attestations. proofread and australianEnglishChecked are non-clinical and may be done with attribution. | session 2026-08-18 | 2026-08-18 | +| #9DGA6R | P2 | task | Build packet B4: Docling worker shadow mode (WORKER_DOCUMENT_EXTRACTOR_MODE=legacy\|shadow) — authorised by the Gate B PASS of 2026-08-18 | Gate B passed on 2026-08-18 (evidence run 32176604314 at 8a92378; record docs/rag-improvement/gate-b-decision-record-2026-08-18.md): design-only authorisation for README section B4 per HANDOVER S7+. Scope: typed WORKER_DOCUMENT_EXTRACTOR_MODE env defaulting to legacy, shadow runs after legacy success on a 1-5 percent cohort selected by src/lib/index-quality.ts signals, aggregate metadata only, no chunk/embedding/index writes, kill switch, one-step rollback to legacy; ingestion-worker-reviewer reviews the PR. Two caveats travel from the decision record: the table-heavy leg passed at parity-on-ceiling (fixtures.v2 hardness corpus precedes any table-quality promotion argument), and docling runs eager at roughly 9-19 s/doc on 2 CPUs vs legacy's 1 s — cohort sizing must budget for it. | packet S6b Gate B decision record, 2026-08-18 | 2026-08-18 | +| #BSBE9B | P3 | task | Docling lab fixtures.v2 table-hardness corpus: add unruled, merged-cell, and rotated-header tables so the Gate B table-heavy improvement leg has measurable headroom | The v1 corpus's table strata are cleanly ruled grids on which the legacy extractor already scores cell F1 1.0 (S6 smoke run and the S6b Gate B run), so the pre-agreed table-heavy improvement target was set to 0 pp (parity at ceiling) by owner decision on 2026-08-18. Before any table-heavy delta is treated as decisive for a Docling promotion beyond B4 shadow design, add a docling-lab-fixtures.v2 stratum set where the legacy find_tables path is expected to degrade: unruled tables, merged cells, rotated headers. Fixture-hardness change only — eval/docling/ manifest + generator, no worker or extractor edit. See eval/docling/README.md 'Known limitation (v1 corpus)' and docs/rag-improvement/gate-b-decision-record-2026-08-18.md. | packet S6b (Gate B run), owner threshold decision 2026-08-18 | 2026-08-18 | ## Resolved / archive @@ -476,3 +489,4 @@ Move resolved rows here with the resolution date and a one-line outcome. Keep th | #315 | rec | If the ui-smoke scroll-hide flake (archived #290) recurs, start from the reporter-stranding mechanism — and treat the old regression window as unconfirmed | Documented UI smoke test reporter stranding analysis in operator runbook | 2026-08-18 | | #326 | task | Keep post-restore environment recovery controls visible in the universal ledger | Documented post-restore environment recovery controls in launch runbook | 2026-08-18 | | #183 | task | Create Sentry metric alert for production DB span p95 > 500ms | Configured Sentry alert routing on critical worker spans | 2026-08-18 | +| #FEWQZ5 | task | Therapy Compass has three shared-component stages left: Button call sites, card-recipes adoption, and page headers | Done — all three stages landed in PR #2122 (squash 092633eb). B3: 19 control-recipe call sites converted to the shared Button and commandControl/outlineControl/iconControl deleted. C: all 16 therapy card surfaces converged on card-recipes cardSurface, with the hero accent edge kept on the left as heroAccentEdge reading the shared --cat-accent. D: six page headers adopted the shared PageHeader; three h1 remain and are tracked separately as #D6G8TC. Verified by verify:cheap (673 files, 7276 tests), verify:ui (447 passed) and check:design-system-contract; ratchets fell (legacy shadow aliases 104 to 89, padding 53 to 52, gaps 30 to 28) and none rose. | 2026-08-18 |