From 099273b25ab823ca502bfbfcf28a3da6dff33ac3 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 16:22:52 +0000 Subject: [PATCH 1/2] chore(issues): reconcile 19 inbox requests into the canonical ledger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Run from a fresh branch off the merged main (63526ee), which is the only place the canonical ledger may be edited. Applied 19 pending requests with 3 cancellation decisions; each request moves to docs/outstanding-issues-inbox/applied as an immutable audit record. Of the 19, three are mine from this session: #170 closed — filter contract rollout delivered; services, factsheets, therapy-compass and documents all converged on the shared sheet. Verified ARCHIVED. #309 updated — kept OPEN and corrected to partially delivered. PR F shipped the >20-or->3-groups tier; the 6-20 band that opened the row is still unimplemented. Verified still in the open table. new row — check:ledger-write-discipline reports a pass when the ledger edit is uncommitted, because it compares a committed range. The other 16 are other authors' queued requests; reconcile is a shared, deliberately serialized operation, not a per-session cleanup. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_011btGFwWKYFWDs5McQkqz9J --- .../02278dd9-cc4d-4114-8d5e-84414910b886.json | 0 .../155e96f9-c111-4d18-b4d8-5f83b3f79907.json | 0 .../2ab54188-4652-42e2-a6b0-ff2f673d3d10.json | 0 .../3f38592b-8346-48dc-87d9-acc585ffe5b8.json | 0 .../4944d2d8-69b6-479c-b0bd-a977494a74fb.json | 0 .../4bb5ba0d-ca46-454e-97f4-5a49f5c87f22.json | 0 .../66f364c0-5612-449b-9046-94b4dab0b152.json | 0 .../67f9ce93-caf5-4cd4-89e1-742e61ce882a.json | 0 .../7336de95-160e-4255-b2c0-abcfd7f4e093.json | 0 .../78fad74b-9478-4e58-9131-8a6bc5858c79.json | 0 .../976c4c7a-5c74-47e3-975b-046718c54859.json | 0 .../9a3ea3c5-31ed-4a89-a035-e6d364b906bf.json | 0 .../a3dca734-d253-4c42-8edc-dc690dd140dc.json | 0 .../a4bf50b9-95bf-458f-9a16-a80acf0830c0.json | 0 .../bbf8ba3d-cdfb-49ef-80f2-09964ac8483d.json | 0 .../dec50a21-f143-485d-a6b1-f9c3621013a6.json | 0 .../e48b440b-d5a0-436b-bea8-ca7670d1a6bb.json | 0 .../e760edf4-fdf7-41c2-aa97-6f4563c6152f.json | 0 .../f4b9c833-03f4-40bd-9537-90501c1287c4.json | 0 docs/outstanding-issues.md | 115 +++++++++--------- 20 files changed, 58 insertions(+), 57 deletions(-) rename docs/outstanding-issues-inbox/{ => applied}/02278dd9-cc4d-4114-8d5e-84414910b886.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/155e96f9-c111-4d18-b4d8-5f83b3f79907.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/2ab54188-4652-42e2-a6b0-ff2f673d3d10.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/3f38592b-8346-48dc-87d9-acc585ffe5b8.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/4944d2d8-69b6-479c-b0bd-a977494a74fb.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/4bb5ba0d-ca46-454e-97f4-5a49f5c87f22.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/66f364c0-5612-449b-9046-94b4dab0b152.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/67f9ce93-caf5-4cd4-89e1-742e61ce882a.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/7336de95-160e-4255-b2c0-abcfd7f4e093.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/78fad74b-9478-4e58-9131-8a6bc5858c79.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/976c4c7a-5c74-47e3-975b-046718c54859.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/9a3ea3c5-31ed-4a89-a035-e6d364b906bf.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/a3dca734-d253-4c42-8edc-dc690dd140dc.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/a4bf50b9-95bf-458f-9a16-a80acf0830c0.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/bbf8ba3d-cdfb-49ef-80f2-09964ac8483d.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/dec50a21-f143-485d-a6b1-f9c3621013a6.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/e48b440b-d5a0-436b-bea8-ca7670d1a6bb.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/e760edf4-fdf7-41c2-aa97-6f4563c6152f.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/f4b9c833-03f4-40bd-9537-90501c1287c4.json (100%) diff --git a/docs/outstanding-issues-inbox/02278dd9-cc4d-4114-8d5e-84414910b886.json b/docs/outstanding-issues-inbox/applied/02278dd9-cc4d-4114-8d5e-84414910b886.json similarity index 100% rename from docs/outstanding-issues-inbox/02278dd9-cc4d-4114-8d5e-84414910b886.json rename to docs/outstanding-issues-inbox/applied/02278dd9-cc4d-4114-8d5e-84414910b886.json diff --git a/docs/outstanding-issues-inbox/155e96f9-c111-4d18-b4d8-5f83b3f79907.json b/docs/outstanding-issues-inbox/applied/155e96f9-c111-4d18-b4d8-5f83b3f79907.json similarity index 100% rename from docs/outstanding-issues-inbox/155e96f9-c111-4d18-b4d8-5f83b3f79907.json rename to docs/outstanding-issues-inbox/applied/155e96f9-c111-4d18-b4d8-5f83b3f79907.json diff --git a/docs/outstanding-issues-inbox/2ab54188-4652-42e2-a6b0-ff2f673d3d10.json b/docs/outstanding-issues-inbox/applied/2ab54188-4652-42e2-a6b0-ff2f673d3d10.json similarity index 100% rename from docs/outstanding-issues-inbox/2ab54188-4652-42e2-a6b0-ff2f673d3d10.json rename to docs/outstanding-issues-inbox/applied/2ab54188-4652-42e2-a6b0-ff2f673d3d10.json diff --git a/docs/outstanding-issues-inbox/3f38592b-8346-48dc-87d9-acc585ffe5b8.json b/docs/outstanding-issues-inbox/applied/3f38592b-8346-48dc-87d9-acc585ffe5b8.json similarity index 100% rename from docs/outstanding-issues-inbox/3f38592b-8346-48dc-87d9-acc585ffe5b8.json rename to docs/outstanding-issues-inbox/applied/3f38592b-8346-48dc-87d9-acc585ffe5b8.json diff --git a/docs/outstanding-issues-inbox/4944d2d8-69b6-479c-b0bd-a977494a74fb.json b/docs/outstanding-issues-inbox/applied/4944d2d8-69b6-479c-b0bd-a977494a74fb.json similarity index 100% rename from docs/outstanding-issues-inbox/4944d2d8-69b6-479c-b0bd-a977494a74fb.json rename to docs/outstanding-issues-inbox/applied/4944d2d8-69b6-479c-b0bd-a977494a74fb.json diff --git a/docs/outstanding-issues-inbox/4bb5ba0d-ca46-454e-97f4-5a49f5c87f22.json b/docs/outstanding-issues-inbox/applied/4bb5ba0d-ca46-454e-97f4-5a49f5c87f22.json similarity index 100% rename from docs/outstanding-issues-inbox/4bb5ba0d-ca46-454e-97f4-5a49f5c87f22.json rename to docs/outstanding-issues-inbox/applied/4bb5ba0d-ca46-454e-97f4-5a49f5c87f22.json diff --git a/docs/outstanding-issues-inbox/66f364c0-5612-449b-9046-94b4dab0b152.json b/docs/outstanding-issues-inbox/applied/66f364c0-5612-449b-9046-94b4dab0b152.json similarity index 100% rename from docs/outstanding-issues-inbox/66f364c0-5612-449b-9046-94b4dab0b152.json rename to docs/outstanding-issues-inbox/applied/66f364c0-5612-449b-9046-94b4dab0b152.json diff --git a/docs/outstanding-issues-inbox/67f9ce93-caf5-4cd4-89e1-742e61ce882a.json b/docs/outstanding-issues-inbox/applied/67f9ce93-caf5-4cd4-89e1-742e61ce882a.json similarity index 100% rename from docs/outstanding-issues-inbox/67f9ce93-caf5-4cd4-89e1-742e61ce882a.json rename to docs/outstanding-issues-inbox/applied/67f9ce93-caf5-4cd4-89e1-742e61ce882a.json diff --git a/docs/outstanding-issues-inbox/7336de95-160e-4255-b2c0-abcfd7f4e093.json b/docs/outstanding-issues-inbox/applied/7336de95-160e-4255-b2c0-abcfd7f4e093.json similarity index 100% rename from docs/outstanding-issues-inbox/7336de95-160e-4255-b2c0-abcfd7f4e093.json rename to docs/outstanding-issues-inbox/applied/7336de95-160e-4255-b2c0-abcfd7f4e093.json diff --git a/docs/outstanding-issues-inbox/78fad74b-9478-4e58-9131-8a6bc5858c79.json b/docs/outstanding-issues-inbox/applied/78fad74b-9478-4e58-9131-8a6bc5858c79.json similarity index 100% rename from docs/outstanding-issues-inbox/78fad74b-9478-4e58-9131-8a6bc5858c79.json rename to docs/outstanding-issues-inbox/applied/78fad74b-9478-4e58-9131-8a6bc5858c79.json diff --git a/docs/outstanding-issues-inbox/976c4c7a-5c74-47e3-975b-046718c54859.json b/docs/outstanding-issues-inbox/applied/976c4c7a-5c74-47e3-975b-046718c54859.json similarity index 100% rename from docs/outstanding-issues-inbox/976c4c7a-5c74-47e3-975b-046718c54859.json rename to docs/outstanding-issues-inbox/applied/976c4c7a-5c74-47e3-975b-046718c54859.json diff --git a/docs/outstanding-issues-inbox/9a3ea3c5-31ed-4a89-a035-e6d364b906bf.json b/docs/outstanding-issues-inbox/applied/9a3ea3c5-31ed-4a89-a035-e6d364b906bf.json similarity index 100% rename from docs/outstanding-issues-inbox/9a3ea3c5-31ed-4a89-a035-e6d364b906bf.json rename to docs/outstanding-issues-inbox/applied/9a3ea3c5-31ed-4a89-a035-e6d364b906bf.json diff --git a/docs/outstanding-issues-inbox/a3dca734-d253-4c42-8edc-dc690dd140dc.json b/docs/outstanding-issues-inbox/applied/a3dca734-d253-4c42-8edc-dc690dd140dc.json similarity index 100% rename from docs/outstanding-issues-inbox/a3dca734-d253-4c42-8edc-dc690dd140dc.json rename to docs/outstanding-issues-inbox/applied/a3dca734-d253-4c42-8edc-dc690dd140dc.json diff --git a/docs/outstanding-issues-inbox/a4bf50b9-95bf-458f-9a16-a80acf0830c0.json b/docs/outstanding-issues-inbox/applied/a4bf50b9-95bf-458f-9a16-a80acf0830c0.json similarity index 100% rename from docs/outstanding-issues-inbox/a4bf50b9-95bf-458f-9a16-a80acf0830c0.json rename to docs/outstanding-issues-inbox/applied/a4bf50b9-95bf-458f-9a16-a80acf0830c0.json diff --git a/docs/outstanding-issues-inbox/bbf8ba3d-cdfb-49ef-80f2-09964ac8483d.json b/docs/outstanding-issues-inbox/applied/bbf8ba3d-cdfb-49ef-80f2-09964ac8483d.json similarity index 100% rename from docs/outstanding-issues-inbox/bbf8ba3d-cdfb-49ef-80f2-09964ac8483d.json rename to docs/outstanding-issues-inbox/applied/bbf8ba3d-cdfb-49ef-80f2-09964ac8483d.json diff --git a/docs/outstanding-issues-inbox/dec50a21-f143-485d-a6b1-f9c3621013a6.json b/docs/outstanding-issues-inbox/applied/dec50a21-f143-485d-a6b1-f9c3621013a6.json similarity index 100% rename from docs/outstanding-issues-inbox/dec50a21-f143-485d-a6b1-f9c3621013a6.json rename to docs/outstanding-issues-inbox/applied/dec50a21-f143-485d-a6b1-f9c3621013a6.json diff --git a/docs/outstanding-issues-inbox/e48b440b-d5a0-436b-bea8-ca7670d1a6bb.json b/docs/outstanding-issues-inbox/applied/e48b440b-d5a0-436b-bea8-ca7670d1a6bb.json similarity index 100% rename from docs/outstanding-issues-inbox/e48b440b-d5a0-436b-bea8-ca7670d1a6bb.json rename to docs/outstanding-issues-inbox/applied/e48b440b-d5a0-436b-bea8-ca7670d1a6bb.json diff --git a/docs/outstanding-issues-inbox/e760edf4-fdf7-41c2-aa97-6f4563c6152f.json b/docs/outstanding-issues-inbox/applied/e760edf4-fdf7-41c2-aa97-6f4563c6152f.json similarity index 100% rename from docs/outstanding-issues-inbox/e760edf4-fdf7-41c2-aa97-6f4563c6152f.json rename to docs/outstanding-issues-inbox/applied/e760edf4-fdf7-41c2-aa97-6f4563c6152f.json diff --git a/docs/outstanding-issues-inbox/f4b9c833-03f4-40bd-9537-90501c1287c4.json b/docs/outstanding-issues-inbox/applied/f4b9c833-03f4-40bd-9537-90501c1287c4.json similarity index 100% rename from docs/outstanding-issues-inbox/f4b9c833-03f4-40bd-9537-90501c1287c4.json rename to docs/outstanding-issues-inbox/applied/f4b9c833-03f4-40bd-9537-90501c1287c4.json diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index f8e82ff3f8..60f77cad92 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -88,56 +88,53 @@ removed after current-main verification; it is not missing recommended work. | 33 | `#165` | A2 | High — clinical UI | Next answer-home UX pass | 0.5–1 day | Adopt a consolidated answer-home notice block — the studies exist, nothing adopts them — the answer hero states its safety obligation, its scope, and its verification requirement as one block in one voice. | | 34 | `#168` | A3 | High — ledger architecture | With #156 / id-scheme redesign | design first | Sequential issue ids force every concurrent append to conflict — two sessions can append to this ledger at the same time without conflicting. | | 35 | `#169` | A3 | High — git hygiene | Next branch cleanup batch | 1–2 hours | Local branches carry work that exists on no remote — committed work is not lost when a machine or worktree is reclaimed. | -| 36 | `#170` | A2 | High — phone UI | Next documents/filter phone pass | 0.5–1 day | Documents and therapy already have page-owned phone filter sheets; remaining modes still use inline controls — shared-band Filter+Sheet adoption without regressing those two or sheetless Sort. | -| 37 | `#175` | A2 | Operator — clinical data + Standard | Next therapy catalogue curation window | 2–4 hours | Therapy modality is now null on all 205 records and needs curation or removal — the Therapy detail and recommend screens either show a curated modality or stop carrying the field at all. | -| 38 | `#178` | A3 | High — PR policy | Next pr-policy change | 1–2 hours | pr-policy does not flag operational risk bundled with clinical or UI risk — a PR that mixes operational-risk paths with clinical or UI risk is called out before it merges, because squash-merging that mix destroys per-it… | -| 39 | `#189` | A2 | Specialist — search/RAG budgets | After #098 route residual; before collapsing RPCs | 2–4 hours + canary if behaviour | Pin /api/search route-level round trips and disposition the x3 text RPC probes — a counting-proxy budget drives `POST` `/api/search` (auth/ratelimit/scope/enrichment/telemetry), and the retrieval-core finding that `matc… | -| 40 | `#036` | Optional | Specialist — privacy/schema | When visibility model is redesigned | design + migration | No explicit `is_public` visibility flag on documents — Public-corpus visibility is implicit: `owner_id IS NULL` on an `indexed` document (`resolveSearchScope`). The `metadata.public_corpus` marker is written by the prom… | -| 41 | `#101` | A3 | Specialist — RAG/retrieval | After #098 harness + canary approval | canary-gated | Canary-gated retrieval parallelisation candidates — metadata and memory hydration shipped in PR #1474; visual hydration, scope enumeration, typeahead caching, and universal-search coalescing remain, each behind the RAG flag and live-canary criteria. | -| 42 | `#156` | A3 | High — ledger architecture | With #168 id-scheme work | design first | Outstanding-issues ids are still allocated read-modify-write, and Update-branch corrupts the merge — two branches cannot silently claim the same outstanding-issues id, and no merge path can commit a file where they have. | -| 43 | `#177` | A3 | High — therapy catalogue build | Next therapy-index build change | 1–2 hours | Therapy catalogue aliases duplicate 2.53 MB of bytes instead of pointing at the hashed file — the unversioned catalogue aliases stop costing a second copy of every payload in the repo and the image. | -| 44 | `#180` | A3 | High — therapy catalogue build | With #177/#179 | 1–2 hours | build-therapies-index now overwrites its own source input — the therapy catalogue generator has a source it does not also destroy. | -| 45 | `#188` | A3 | Operator — DR/SRE | After any schema restore drill, or next DR review | checklist-owned | Document and track disaster-recovery re-creation checklist as ledger work — the five DR items that do not survive a schema restore are tracked with owners and verify steps, not only in `docs/operator-backlog.md`. | -| 46 | `#190` | A3 | Specialist — RAG structure | On explicit X3 go-ahead | 1 PR per extraction unit | X3: Finish rag.ts monolith decomposition — `src/lib/rag/rag.ts` is decomposed into focused modules per `docs/maturity-backlog-workorders.md` X3, with existing offline RAG contracts green. | -| 47 | `#191` | A3 | Operator — DB + Specialist | Approved live-DB window only | provider-gated | X5: ACL-migration consolidation (provider-gated) — ACL-related migrations are consolidated per maturity work-order X5 without weakening owner-scope/RLS. | -| 48 | `#192` | A3 | High — test coverage | Next coverage-floor pass | 0.5–1 day | X6: Raise clinical/retrieval/answer coverage floors — coverage floors for clinical, retrieval, and answer domains meet the maturity X6 targets with CI enforcing them. | -| 49 | `#193` | A3 | High — src/lib structure | After/with X3 non-protected clusters | 1 PR per cluster | X7: Complete the remaining src/lib domain-directory reorg — remaining `src/lib` clusters sit in their domain directories per X7 follow-on to X2. | -| 50 | `#194` | A3 | High — scripts/docs hygiene | Next scripts archive pass | 1–2 hours | L1: Archive retired backfill one-shots and dead ci-change-scope token — retired `backfill:*` one-shots and the dead `ci-change-scope` token are archived/removed with docs/script index updated. | -| 51 | `#195` | A3 | Operator — GitHub maintainer | Maintainer UI window | 30–60 min | M1: Repo-host hardening (branch protection and required checks) — GitHub branch-protection rulesets and required checks match audit §8 / maturity M1. | -| 52 | `#196` | A3 | Operator — DR/SRE | After schema restore drill | 1–2 hours | DR: Re-create pg_cron schedules after schema restore — ingestion/retention and related pg_cron schedules exist on the target DB after any schema restore. | -| 53 | `#197` | A3 | Operator — DR/SRE | After schema restore drill | 30–60 min | DR: Re-add Vault secrets including cron_ingestion_jwt — required Vault secrets (at least `cron_ingestion_jwt`) are present after schema restore. | -| 54 | `#198` | A3 | Operator — DR/SRE | After schema restore drill | 30–60 min | DR: Re-set custom database GUCs after schema restore — custom `app.*` GUCs required by the app/worker are set on the restored database. | -| 55 | `#199` | A3 | Operator — DR/SRE | After schema restore; Deno v2 available | 1–2 hours | DR: Redeploy Supabase edge functions (Deno v2.x) — required edge functions are deployed to the target project with Deno v2.x. | -| 56 | `#200` | A3 | Operator — DR/SRE | After schema restore drill | 1–2 hours | DR: Re-enter dashboard config after schema restore — auth providers/SSO redirect URLs, connection-pool caps, per-project keys, and `E2E_USER_*` are re-entered in the Supabase/Railway dashboards after restore. | -| 57 | `#183` | A2 | Operator — Sentry + Specialist | Next approved observability window with SENTRY_AUTH_TOKEN | 1–2 hours | Create Sentry metric alert for production DB span p95 > 500ms (`span.op:db`, environment production). **Stop:** no secret printing; blocked until token/env available. | -| 58 | `#206` | A2 | Specialist — answer UI contract | With AnswerState producer work (`#207`) | 2–4 hours | `partial_retrieval` has no app-facing producer — decide RAG contract vs UI-only mapping before AnswerCard. **Stop:** no retrieval behaviour change without RAG flag. | -| 59 | `#209` | A3 | High — design tokens / contrast | Next Gate 1 / warning-token pass | 1–2 hours | Add contrast pair for `--warning` used as body text (VerificationNotice / DoseLine). **Gate:** design-system contrast checks. **Stop:** do not invent a new status token without TOKENS.md. | -| 60 | `#211` | A3 | High — TypeScript strictness | Dedicated migration branch | multi-PR | Plan and start `noUncheckedIndexedAccess` migration (1266 errors); highest-risk files first. **Stop:** do not flip the flag on main without a staged plan. | -| 61 | `#212` | A3 | High — runtime validation | After highest-risk cast inventory | multi-PR | Replace `as unknown as` and unvalidated `JSON.parse` with Zod/guards at trust boundaries. **Stop:** RAG/provider boundaries need clinical/privacy care. | -| 62 | `#213` | A3 | High — error handling | Next fetch/stream hardening pass | 0.5–1 day | Stop swallowing fetch/stream errors with empty catches; check `response.ok`. **Stop:** do not change telemetry contracts silently. | -| 63 | `#215` | Optional | High — image perf | Next image/PWA pass | 2–4 hours | Image-optimization basics for lightbox, PWA lifecycle, demo PNGs. **Stop:** optional until measured need. | -| 64 | `#221` | A3 | High — design-system convergence | After `#218` cn() decision | 0.5–1 day | Converge remaining local EmptyState/LoadingState/Chip duplicates. **Stop:** not piecemeal before cn()/Chip decisions. | -| 65 | `#222` | A3 | High — headers / search chrome | During headers redesign decision | 2–4 hours | Decide whether mode-home-template / search-results-header-band are in PageHeader scope or permanently out. **Stop:** do not flatten phone composer ownership. | -| 66 | `#233` | A3 | High — design-system docs | Next COMPONENTS.md docs PR | 1–2 hours | Refresh section 0 maturity matrix and document FormField optionality-marker contract. **Gate:** docs checks. **Stop:** docs-only; no product behaviour change. | -| 67 | `#234` | A3 | High — design-system docs | With answer-surface docs | 30–60 min | Document `answer-copy-payload.ts` as the clipboard contract for three surfaces. **Stop:** do not add a second copy builder. | -| 68 | `#235` | A3 | High — design-system evidence | Next warmed local proof-shot pass | 1–2 hours | Capture missing ADOPTION.md §7 proof shots for adopted surfaces. **Stop:** not visual-baseline PNGs (`#118`); no Playwright snapshot commit. | -| 69 | `#236` | Optional | High — branch hygiene | Next cleanup batch with `#079` | 30–60 min | Dispose orphan DS V2 builder branches and leftover wave-5 dev servers. **Stop:** content-verify before delete; no force-clean. | -| 70 | `#237` | A3 | High — design-system a11y | Before freezing Linux visual baselines (#242) | 30–60 min | Eyeball low-confidence AccessibleTable densities at 320px; MissingValue phrases must remain readable. **Gate:** visual spot-check only. **Stop:** do not abbreviate MissingValue to a dash. | -| 71 | `#238` | A3 | High — overlays/UI | After Sheet portal default change (#1616) | 30–60 min | Visual pass for Sheet portal default on settings, sidebar, and answer overlays under OverlayRoot. **Stop:** do not revert portal default without evidence. | -| 72 | `#239` | Optional | High — phone chrome | When phone orientation QA is available | 15–30 min | Manual phone rotation check for ResizeObserver-only phone chrome reserve. **Gate:** `verify:phone-chrome` still owns automated coverage. **Stop:** do not widen reserve heuristics without reproduction. | -| 73 | `#240` | Optional | High — design tokens | Next design-owner review | 15–30 min | Confirm tooltip visual hard-clip asymmetry with design owner (sr-only keeps full text). **Stop:** no product change without that confirmation. | -| 74 | `#241` | A3 | High — therapy catalogue | Standing; with any therapy-home change | 15–30 min | Therapy home summary count/slugs remain build-time; keep `build-therapies-index --check` load-bearing. **Stop:** do not bypass the check. | -| 75 | `#242` | A2 | High — design-system baselines | After human review of Linux baselines | 1–2 hours | Commit approved Linux visual baselines and promote adoption not-committed → committed. **Stop:** never commit baselines from an unreviewed machine run. | -| 76 | `#244` | A3 | High — design tokens / forced-colors | With any ckb-v2 forced-colours edit | 15–30 min | Keep grouped dark selectors in the forced-colours media block so specificity matches dark rules. **Stop:** do not trim to a single `.ckb-v2.ckb-v2` selector. | -| 77 | `#245` | A3 | High — cross-mode links | Next CrossModeLinks / analytics pass | 30–60 min | responsive-compact CrossModeLinks keeps duplicate rails in the DOM; prefer one mount or accept test double-counts. **Stop:** do not break phone-only rail contract. | -| 78 | `#248` | A2 | Operator — Supabase + Specialist | After PR #1614 symptom repair; approved live/history window | 1–2 hours | Investigate why 20260705180000 search-health indexes were missing on live despite applied history; decide if drift checks should catch this class. **Stop:** no hosted mutation without approval. | -| 79 | `#250` | A2 | High — multi-agent execution | After Wave 0 queue repair on main (done in this capture); run remaining Wave 0/#202 process gates next on the engineering track | multi-wave | Execute the fastest-wins multi-wave plan (Waves 0–4 + operator track) with parallel agents and per-PR gates. Waves do not outrank A1 acuity. **Stop:** provider/RAG approvals still required where flagged. | -| 80 | `#253` | A2 | High — phone results UI | Next open-PR sweep | 15–30 min | Decide #1606's fate: the `MobileResultFilterControl` it rewrites was deleted by #247, so there is nothing left to hand-merge. Verify keyboard parity of the replacement sheet on a real device, then close #1606 as superseded. **Stop:** the decision is a human's; do not close #1606 automatically. | -| 81 | `#254` | A2 | Operator — Codex Cloud | Before #1617 leaves draft | 1–2 hours | Re-run Codex Cloud acceptance at the exact current head or mark head-independent evidence. **Stop:** do not treat stale pins as coverage. | -| 82 | `#257` | Optional | High — formulation/specifiers flake | Standing until second reproduction | 15–30 min | Single unreproduced ui-formulation flake when run with ui-specifiers — record a second sighting only; do not quarantine until three on the same SHA. **Stop:** do not weaken assertions. | +| 36 | `#175` | A2 | Operator — clinical data + Standard | Next therapy catalogue curation window | 2–4 hours | Therapy modality is now null on all 205 records and needs curation or removal — the Therapy detail and recommend screens either show a curated modality or stop carrying the field at all. | +| 37 | `#178` | A3 | High — PR policy | Next pr-policy change | 1–2 hours | pr-policy does not flag operational risk bundled with clinical or UI risk — a PR that mixes operational-risk paths with clinical or UI risk is called out before it merges, because squash-merging that mix destroys per-it… | +| 38 | `#189` | A2 | Specialist — search/RAG budgets | After #098 route residual; before collapsing RPCs | 2–4 hours + canary if behaviour | Pin /api/search route-level round trips and disposition the x3 text RPC probes — a counting-proxy budget drives `POST` `/api/search` (auth/ratelimit/scope/enrichment/telemetry), and the retrieval-core finding that `matc… | +| 39 | `#036` | Optional | Specialist — privacy/schema | When visibility model is redesigned | design + migration | No explicit `is_public` visibility flag on documents — Public-corpus visibility is implicit: `owner_id IS NULL` on an `indexed` document (`resolveSearchScope`). The `metadata.public_corpus` marker is written by the prom… | +| 40 | `#101` | A3 | Specialist — RAG/retrieval | After #098 harness + canary approval | canary-gated | Canary-gated retrieval parallelisation candidates — metadata and memory hydration shipped in PR #1474; visual hydration, scope enumeration, typeahead caching, and universal-search coalescing remain, each behind the RAG flag and live-canary criteria. | +| 41 | `#156` | A3 | High — ledger architecture | With #168 id-scheme work | design first | Outstanding-issues ids are still allocated read-modify-write, and Update-branch corrupts the merge — two branches cannot silently claim the same outstanding-issues id, and no merge path can commit a file where they have. | +| 42 | `#188` | A3 | Operator — DR/SRE | After any schema restore drill, or next DR review | checklist-owned | Document and track disaster-recovery re-creation checklist as ledger work — the five DR items that do not survive a schema restore are tracked with owners and verify steps, not only in `docs/operator-backlog.md`. | +| 43 | `#190` | A3 | Specialist — RAG structure | On explicit X3 go-ahead | 1 PR per extraction unit | X3: Finish rag.ts monolith decomposition — `src/lib/rag/rag.ts` is decomposed into focused modules per `docs/maturity-backlog-workorders.md` X3, with existing offline RAG contracts green. | +| 44 | `#191` | A3 | Operator — DB + Specialist | Approved live-DB window only | provider-gated | X5: ACL-migration consolidation (provider-gated) — ACL-related migrations are consolidated per maturity work-order X5 without weakening owner-scope/RLS. | +| 45 | `#192` | A3 | High — test coverage | Next coverage-floor pass | 0.5–1 day | X6: Raise clinical/retrieval/answer coverage floors — coverage floors for clinical, retrieval, and answer domains meet the maturity X6 targets with CI enforcing them. | +| 46 | `#193` | A3 | High — src/lib structure | After/with X3 non-protected clusters | 1 PR per cluster | X7: Complete the remaining src/lib domain-directory reorg — remaining `src/lib` clusters sit in their domain directories per X7 follow-on to X2. | +| 47 | `#194` | A3 | High — scripts/docs hygiene | Next scripts archive pass | 1–2 hours | L1: Archive retired backfill one-shots and dead ci-change-scope token — retired `backfill:*` one-shots and the dead `ci-change-scope` token are archived/removed with docs/script index updated. | +| 48 | `#195` | A3 | Operator — GitHub maintainer | Maintainer UI window | 30–60 min | M1: Repo-host hardening (branch protection and required checks) — GitHub branch-protection rulesets and required checks match audit §8 / maturity M1. | +| 49 | `#196` | A3 | Operator — DR/SRE | After schema restore drill | 1–2 hours | DR: Re-create pg_cron schedules after schema restore — ingestion/retention and related pg_cron schedules exist on the target DB after any schema restore. | +| 50 | `#197` | A3 | Operator — DR/SRE | After schema restore drill | 30–60 min | DR: Re-add Vault secrets including cron_ingestion_jwt — required Vault secrets (at least `cron_ingestion_jwt`) are present after schema restore. | +| 51 | `#198` | A3 | Operator — DR/SRE | After schema restore drill | 30–60 min | DR: Re-set custom database GUCs after schema restore — custom `app.*` GUCs required by the app/worker are set on the restored database. | +| 52 | `#199` | A3 | Operator — DR/SRE | After schema restore; Deno v2 available | 1–2 hours | DR: Redeploy Supabase edge functions (Deno v2.x) — required edge functions are deployed to the target project with Deno v2.x. | +| 53 | `#200` | A3 | Operator — DR/SRE | After schema restore drill | 1–2 hours | DR: Re-enter dashboard config after schema restore — auth providers/SSO redirect URLs, connection-pool caps, per-project keys, and `E2E_USER_*` are re-entered in the Supabase/Railway dashboards after restore. | +| 54 | `#183` | A2 | Operator — Sentry + Specialist | Next approved observability window with SENTRY_AUTH_TOKEN | 1–2 hours | Create Sentry metric alert for production DB span p95 > 500ms (`span.op:db`, environment production). **Stop:** no secret printing; blocked until token/env available. | +| 55 | `#206` | A2 | Specialist — answer UI contract | With AnswerState producer work (`#207`) | 2–4 hours | `partial_retrieval` has no app-facing producer — decide RAG contract vs UI-only mapping before AnswerCard. **Stop:** no retrieval behaviour change without RAG flag. | +| 56 | `#209` | A3 | High — design tokens / contrast | Next Gate 1 / warning-token pass | 1–2 hours | Add contrast pair for `--warning` used as body text (VerificationNotice / DoseLine). **Gate:** design-system contrast checks. **Stop:** do not invent a new status token without TOKENS.md. | +| 57 | `#211` | A3 | High — TypeScript strictness | Dedicated migration branch | multi-PR | Plan and start `noUncheckedIndexedAccess` migration (1266 errors); highest-risk files first. **Stop:** do not flip the flag on main without a staged plan. | +| 58 | `#212` | A3 | High — runtime validation | After highest-risk cast inventory | multi-PR | Replace `as unknown as` and unvalidated `JSON.parse` with Zod/guards at trust boundaries. **Stop:** RAG/provider boundaries need clinical/privacy care. | +| 59 | `#213` | A3 | High — error handling | Next fetch/stream hardening pass | 0.5–1 day | Stop swallowing fetch/stream errors with empty catches; check `response.ok`. **Stop:** do not change telemetry contracts silently. | +| 60 | `#215` | Optional | High — image perf | Next image/PWA pass | 2–4 hours | Image-optimization basics for lightbox, PWA lifecycle, demo PNGs. **Stop:** optional until measured need. | +| 61 | `#221` | A3 | High — design-system convergence | After `#218` cn() decision | 0.5–1 day | Converge remaining local EmptyState/LoadingState/Chip duplicates. **Stop:** not piecemeal before cn()/Chip decisions. | +| 62 | `#222` | A3 | High — headers / search chrome | During headers redesign decision | 2–4 hours | Decide whether mode-home-template / search-results-header-band are in PageHeader scope or permanently out. **Stop:** do not flatten phone composer ownership. | +| 63 | `#233` | A3 | High — design-system docs | Next COMPONENTS.md docs PR | 1–2 hours | Refresh section 0 maturity matrix and document FormField optionality-marker contract. **Gate:** docs checks. **Stop:** docs-only; no product behaviour change. | +| 64 | `#234` | A3 | High — design-system docs | With answer-surface docs | 30–60 min | Document `answer-copy-payload.ts` as the clipboard contract for three surfaces. **Stop:** do not add a second copy builder. | +| 65 | `#235` | A3 | High — design-system evidence | Next warmed local proof-shot pass | 1–2 hours | Capture missing ADOPTION.md §7 proof shots for adopted surfaces. **Stop:** not visual-baseline PNGs (`#118`); no Playwright snapshot commit. | +| 66 | `#236` | Optional | High — branch hygiene | Next cleanup batch with `#079` | 30–60 min | Dispose orphan DS V2 builder branches and leftover wave-5 dev servers. **Stop:** content-verify before delete; no force-clean. | +| 67 | `#237` | A3 | High — design-system a11y | Before freezing Linux visual baselines (#242) | 30–60 min | Eyeball low-confidence AccessibleTable densities at 320px; MissingValue phrases must remain readable. **Gate:** visual spot-check only. **Stop:** do not abbreviate MissingValue to a dash. | +| 68 | `#238` | A3 | High — overlays/UI | After Sheet portal default change (#1616) | 30–60 min | Visual pass for Sheet portal default on settings, sidebar, and answer overlays under OverlayRoot. **Stop:** do not revert portal default without evidence. | +| 69 | `#239` | Optional | High — phone chrome | When phone orientation QA is available | 15–30 min | Manual phone rotation check for ResizeObserver-only phone chrome reserve. **Gate:** `verify:phone-chrome` still owns automated coverage. **Stop:** do not widen reserve heuristics without reproduction. | +| 70 | `#240` | Optional | High — design tokens | Next design-owner review | 15–30 min | Confirm tooltip visual hard-clip asymmetry with design owner (sr-only keeps full text). **Stop:** no product change without that confirmation. | +| 71 | `#241` | A3 | High — therapy catalogue | Standing; with any therapy-home change | 15–30 min | Therapy home summary count/slugs remain build-time; keep `build-therapies-index --check` load-bearing. **Stop:** do not bypass the check. | +| 72 | `#242` | A2 | High — design-system baselines | After human review of Linux baselines | 1–2 hours | Commit approved Linux visual baselines and promote adoption not-committed → committed. **Stop:** never commit baselines from an unreviewed machine run. | +| 73 | `#244` | A3 | High — design tokens / forced-colors | With any ckb-v2 forced-colours edit | 15–30 min | Keep grouped dark selectors in the forced-colours media block so specificity matches dark rules. **Stop:** do not trim to a single `.ckb-v2.ckb-v2` selector. | +| 74 | `#245` | A3 | High — cross-mode links | Next CrossModeLinks / analytics pass | 30–60 min | responsive-compact CrossModeLinks keeps duplicate rails in the DOM; prefer one mount or accept test double-counts. **Stop:** do not break phone-only rail contract. | +| 75 | `#248` | A2 | Operator — Supabase + Specialist | After PR #1614 symptom repair; approved live/history window | 1–2 hours | Investigate why 20260705180000 search-health indexes were missing on live despite applied history; decide if drift checks should catch this class. **Stop:** no hosted mutation without approval. | +| 76 | `#250` | A2 | High — multi-agent execution | After Wave 0 queue repair on main (done in this capture); run remaining Wave 0/#202 process gates next on the engineering track | multi-wave | Execute the fastest-wins multi-wave plan (Waves 0–4 + operator track) with parallel agents and per-PR gates. Waves do not outrank A1 acuity. **Stop:** provider/RAG approvals still required where flagged. | +| 77 | `#253` | A2 | High — phone results UI | Next open-PR sweep | 15–30 min | Decide #1606's fate: the `MobileResultFilterControl` it rewrites was deleted by #247, so there is nothing left to hand-merge. Verify keyboard parity of the replacement sheet on a real device, then close #1606 as superseded. **Stop:** the decision is a human's; do not close #1606 automatically. | +| 78 | `#254` | A2 | Operator — Codex Cloud | Before #1617 leaves draft | 1–2 hours | Re-run Codex Cloud acceptance at the exact current head or mark head-independent evidence. **Stop:** do not treat stale pins as coverage. | +| 79 | `#257` | Optional | High — formulation/specifiers flake | Standing until second reproduction | 15–30 min | Single unreproduced ui-formulation flake when run with ui-specifiers — record a second sighting only; do not quarantine until three on the same SHA. **Stop:** do not weaken assertions. | - + ## Open items > **Merged-main canary update (2026-07-23, run `30018289898`):** the new structured report correctly recorded evaluated tree `c24f2e8f2d30d0c59fc1eba025d3dcd63478137e`, run/attempt identity and `cross-region-runner` latency context. Golden retrieval remained 36/36 with document/content recall 1.0 and no failed cases. The 44-case answer gate had grounded-supported and unsupported-correct rates of 1.0, but failed because `neuroleptic-side-effect-escalation` again returned one citation where two are required (citation-failure rate 0.0227). `admission-discharge-comparison` again omitted the specific AKG admission document after `comparison_source_extractive_fallback`; `admission-discharge-coverage-paraphrase` was advisory-only at 24,870 ms. Answer cost was reported as `$0.234736`. Do not retry immediately: retain this as the first structured datapoint, compare it with the scheduled 2026-07-26 report, and keep retrieval/ranking unchanged. @@ -162,7 +159,7 @@ removed after current-main verification; it is not missing recommended work. | #056 | P2 | task | Reconcile the existing staging migration history | `Clinical KB Staging` already exists as a healthy, empty Supabase/Railway tier with distinct secrets and no production clinical data, but it is 24 repository migrations behind (ten earlier history holes plus fourteen after `20260719055623`). In the next approved staging schema window, apply the exact missing migration chain, then re-run indexing, health, identity and data-boundary proof. Do not recreate the environment or copy production clinical documents. | current-main staging verification; `docs/staging-setup.md`; `docs/operator-backlog.md` | 2026-07-27 | | #057 | P2 | task | Complete staging soak and rollback rehearsal | After #056, run the documented soak and rollback against an exact candidate; retain latency/error/rollback evidence. Stop on unsafe data, identity mismatch, or an unowned rollback decision. | `docs/launch-operator-runbook.md`; `docs/audit/capacity-review.md` | 2026-07-24 | | #011 | P3 | task | Auth DB-connection allocation is operator-only | Supabase Auth (GoTrue) is capped at ~10 absolute DB connections (Supabase perf advisor). Switch to **percentage-based** allocation in the Supabase **dashboard** before the first compute scale-up — **not settable via SQL/MCP** (operator-owned). Verify via a staging soak + an approval-gated read-only advisor re-check. | `docs/auth-connection-cap-runbook.md`; `docs/process-hardening.md` (Known follow-up debts) | 2026-07-21 | -| #013 | P3 | rec | Route-chunk + mockup catalogue JSON weight | Keep this recommendation open and measurement-gated. `build:analyze` still finds route-scoped catalogue modules: `/specifiers` ships `specifiers-search-index.json` (~180 KB parsed), `/forms` ships `forms-catalog.json` (~132 KB), and `/formulation` ships `formulation-content.json` (~52 KB; client-side local search needs an index/full split or a search endpoint). The approved Lighthouse sample covered `/forms`, but `/specifiers` and `/formulation` remain unmeasured, so the precommitted `#017` rule does not permit archiving their payload work. **Next:** collect route-specific LCP/CLS and CrUX INP evidence for `/specifiers` and `/formulation`, then close only the routes that meet every `#017` threshold. **Mockup half settled 2026-08-09 (#252):** this row's position that development-only `*-mockups.tsx` chunks are not an initial production bundle is now enforced rather than merely asserted — `check:bundle-budget` weighs `/mockups/**`-exclusive chunks against a separate `mockups` baseline (273,873 gzip bytes, 25% tolerance) and production against its own (1,309,772, 10%), so mockup growth no longer consumes production headroom and the mockup share is reported on every run. That also supplies the separate-reporting measurement this row asked for before any prune: 267.5 KiB across 76 chunks over 66 mockup routes, 17.29% of the repo-wide total. The 'do not restructure them without deploy-artifact or cold-start evidence' guidance stands unchanged — the split removed the false pressure to prune, it did not authorise pruning. | session 2026-07-21 (`build:analyze`); PR #1470 review | 2026-07-21 | +| #013 | P3 | rec | Route catalogue weight remains measurement-gated; public field INP is unavailable | Keep the payload work open, but correct the measurement state: on 2026-08-13 the official Chrome UX Report current-record API returned 404 no-data for the psychiatry.tools origin and every reviewed URL (root, Therapy, Documents search, DSM, Forms, Services, Specifiers, and Formulation). Field INP is therefore unavailable because the site does not meet CrUX eligibility/coverage, not unverified and not a pass. The production app deliberately has no browser Sentry bundle, so adding RUM would expand the approved telemetry and privacy envelope. Next: continue lab LCP/TBT and interaction traces; request a separate privacy/operator decision before adding browser RUM, or recheck CrUX after traffic eligibility changes. Do not block route payload fixes waiting for a field dataset that does not exist, and do not infer an INP pass from absence. | session 2026-08-13 official CrUX current-record queries; https://cruxvis.withgoogle.com/ | 2026-07-21 | | #016 | P3 | rec | "Big but not easy" structural + motion perf | Deferred larger levers: (a) nonce-CSP forces every product route to `╞Æ Dynamic` (zero static generation) — evaluate Partial Prerendering / static shells for the static clinical catalogues (DSM/differentials/therapy/specifiers/formulation); (b) sidebar expand/collapse animates `grid-template-columns` (biggest smoothness cost, motion-gated — needs a transform-overlay rethink); (c) Therapy Compass fetches 692 KB / 2.5 MB JSON client-side (defer until interaction + confirm brotli); (d) settings/setup/admin dialogs static-imported into the home chunk (`next/dynamic` them); (e) **DONE 2026-08-01 in PR-T (ds-v2 therapy teardown):** deleted `therapy-compass.css` and removed its route-group layout import — no longer render-blocking on `/`, `/documents`, `/forms`, `/dsm` and every mode home; (f) `shared-search-app-shell.tsx:8` statically imports the `therapy-compass` barrel, pulling `workspace.tsx` + `bindings.tsx` + `nav.tsx` into every `(search-app)` route; (g) three client waterfalls (`use-app-preferences.ts:156-182`, `ClinicalDashboard.tsx:977-1069`, `signed-image.tsx:60-84` + `use-signed-image-url.ts:39`) and the paint offenders in `globals.css` beyond the sidebar grid — three stacked `backdrop-filter` passes on an always-mounted translating element (`:709-748`), `box-shadow` inside a `transition` list (`:677-684`), and `@keyframes shimmer` animating `background-position` on the shared `Skeleton` (`:2289-2296`). **CORRECTED 2026-07-29 on (c):** the Therapy Compass filenames are unversioned and Next serves `/public` with an ETag, so only the FIRST visit pays 690.6 KB / 2,470 KB — repeat visits pay ~4 revalidation round trips. The fix is content-hashed filenames + `immutable` (touching `scripts/build-therapies-index.mjs` and `check:therapy-data-index`), NOT a bare `Cache-Control` line. See `docs/audit/latency-audit-2026-07-28.md` L3-1/L3-2/L3-3/L3-6/L3-7. | session 2026-07-21 (build route table + design audit) | 2026-07-21 | | #018 | P2 | task | Split the lithium, ADHD and metabolic residuals by mechanism | Current evidence keeps the mechanisms separate. **Lithium — closed within this item:** the row/atom-aware subject guard, foreign-parameter rejection and query-specific range promotion returned `0.5–1.0 mmol/L` with correct targeting/citation; the full retrieval canary remained 36/36 with recall 1.0 and zero per-case RR regressions, and the full answer canary passed every blocking gate. **ADHD — open corpus debt:** `CG.MHSP.ADHD.pdf` is absent from the hosted corpus and the retrieved chart exposes `accessible_table_count=0`; repair corpus/fixture or ingestion evidence rather than weakening extractive budgets. **Metabolic — open structured-evidence debt:** the standalone plural classifier worsened the live answer and was reverted; obtain auditable schedule text/table evidence before another candidate. | targeted live lithium/ADHD/metabolic evidence 2026-07-27; `docs/evidence/rag-reliability-evidence-2026-07-27.md`; refuted approaches | 2026-07-21 | | #022 | P2 | task | Source-governance metadata refresh (operator) | The selected policy is now encoded locally as auditable `third_party_reference_attested` evidence with policy version, reviewer qualification, evidence references and append-only review history. It deliberately preserves `clinical_validation_status=unverified`; malformed, stale or non-BMJ evidence remains review debt. Migration `20260727010000_bmj_third_party_source_attestation.sql` is prepared but was **not applied**. The ten most visible local-document candidates are captured in `docs/evidence/rag-top-local-review-manifest-2026-07-26.json` with `attestation_applied=false`; qualified human review, deliberate hosted apply/attestation, and warning-rate remeasurement remain operator work. | governance worklist; local policy/migration tests; top-ten evidence manifest | 2026-07-21 | @@ -182,8 +179,8 @@ removed after current-main verification; it is not missing recommended work. | #100 | P2 | rec | Buffered answer generation has no incremental verified delivery | **Design complete; runtime work remains provider-gated.** [`verified-answer-incremental-delivery-design.md`](verified-answer-incremental-delivery-design.md) records the clinical-governance decision and staged contract: keep the `progress`/`final`/`error` allowlist; disclose bounded, owner-scoped evidence only after the canonical danger-level source-governance refusal permits it, then emit complete answer sections only after each reuses the full production verification boundary; reconcile every preview byte-for-byte with the authoritative `final`; discard all previews on error/cancel/retry; deploy behind separate parse/emission/render flags. Phase 0 contract proof and Phase 1 evidence preview can be developed offline, but visible rollout still needs clinical/browser proof. Phase 2 changes generation architecture and requires explicit approval for answer-quality evals plus a baseline/post live canary pair. **Naive token streaming remains REFUTED:** never re-land `token`, `revising`, provisional prose, or a weaker stream-only verifier. Cross-references #021. | `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 | | #101 | P3 | rec | Canary-gated retrieval parallelisation candidates | **Outcome:** remaining retrieval parallelisation candidates are explicit after PR #1474 shipped the metadata and memory parallelisation. **Remaining:** visual hydration triples (each migrated path still calls `attachPageVisualEvidence` after hydration — not yet parallelised, see `rag.ts:1442,1811,1857,1959,2194,2281`); nested `await`-in-loop scope enumeration (`search-scope.ts:202,328`); uncached typeahead results (`rag.ts:2698-2711`); and universal-search coalescing (`/api/search` has it; `/api/search/universal` does not). Each changes candidate assembly, truncation, or what the next keystroke returns, so each requires the #098 harness, the RAG flag, explicit live-canary approval, 36/36 retrieval, recall 1.0, and zero per-case reciprocal-rank regressions. Distinct from #001 (semantic rerank). **Completed:** PR #1474 parallelised metadata and memory (`hydrateCandidatesWithMetadataAndMemory`); do not propose that specific change again. **Stop:** no remaining candidate proceeds without its canary gate. | `docs/audit/latency-audit-2026-07-28.md` L2-1/L2-2/L2-8/L1-5; PR #1474 | 2026-07-29 | | #102 | P3 | task | Apply the additive `documents` index debt (operator) | **Outcome:** bare-column `ILIKE` and the paged status scan on `documents` are index-served on hosted. `documents_title_trgm_idx` indexes a CONCATENATED expression, so the bare-column predicates in `api/documents/route.ts:193` and `rag-candidate-sources.ts:477` (RAG path) cannot use it and fall back to scanning; `search-scope.ts:271-277` sorts per page against the single-column `documents_status_idx`. **Runbook prepared 2026-07-29 — NOT applied, item stays open:** three `CREATE INDEX CONCURRENTLY` statements authored and reviewed in `docs/operator-apply-performance-latency-remediation.md` — additive, though **the "recall is byte-identical" claim was RETRACTED on 2026-07-29 review**: `fetchDocumentTitleAliasRows` (`rag-candidate-sources.ts:482`) applies `.limit(12)` with no `ORDER BY`, so a new index can change which title-alias documents feed candidate assembly. Only the documents-list use stays ordering-safe; `(status,id)` is canary-gated too — see runbook, and making that `.limit(12)` deterministic first does **not** lift the gate — an unordered `LIMIT` has no stable selection to preserve, so imposing an order can pick a different twelve and is itself an ordering behaviour change on a retrieval surface, which AGENTS.md requires a canary pair for. Sequencing the ordering fix first is worthwhile (unordered `LIMIT` on a retrieval input is latent nondeterminism regardless) but yields two canary-gated changes, not one (PR #1377 review). **Deliberately NO migration file:** an additive-index migration without a synchronized `schema.sql` mirror and regenerated drift manifest is exactly what closed PR #1312, and the mirror cannot come first because `required_indexes` in `search_schema_health()` (`schema.sql:3178`) runs against live. **Next (operator):** **author the migration first** — `supabase/migrations/` is the source of truth and `schema.sql` only a mirror, so hand-run operator SQL never reaches staging, disaster-recovery replay, or a local `supabase db reset`, and a `required_indexes` registration would fail there (PR #1377 review); follow the `20260717170000_registry_projection_cleanup.sql` idempotent pattern. **That migration must also carry the health-function change** — `required_indexes` lives inside `search_schema_health()`, which is redefined by `create or replace function` in eleven migrations (copy `20260705180000_reconcile_search_health_indexes.sql:62`); editing `schema.sql:3177` alone moves only the mirror and leaves the indexes unmonitored on hosted (PR #1377 review). Then apply concurrently, confirm `indisvalid`, mirror both the index statements and the identical function body into `schema.sql`, run `npm run drift:manifest` (Docker), and deploy the migration LAST — in that order, in one change. Expect `check:drift` to report them as unexpected between steps 1 and 2. **Rollback is three deployed phases, not the reverse of one:** retract `required_indexes` via its own `create or replace function` migration and deploy → drop concurrently live → only then deploy the `schema.sql` removal plus an idempotent forward `drop index if exists` migration, because Supabase wraps migrations in a transaction and a plain `DROP INDEX` there takes the lock the concurrent procedure exists to avoid (PR #1377 review). | `docs/audit/latency-audit-2026-07-28.md` L2-3/L2-5; `docs/operator-apply-performance-latency-remediation.md` | 2026-07-29 | -| #117 | P2 | rec | Therapy Compass catalogue payload is the mobile LCP outlier | **Outcome:** `/therapy-compass` mobile LCP lands near the other mobile routes instead of double them. **Measured 2026-07-30** by the new pre-merge Lighthouse budget: mobile LCP 5229 ms, TBT 612 ms, CLS 0.142, against 2123-2460 ms on every other mobile route and 826 ms on desktop — so it is client-side work under mobile CPU/network throttling, not server latency. **Cause before this PR:** `useTherapyData` fetched `public/therapy-compass-data/therapies-index.json` (the thin browse index; 205 records) for the home/search/pathways screens, so the download plus JSON parse sat on the critical path before content painted. **Current split:** home now fetches `public/therapy-compass-data/therapies-home.211dab554c4ec62d.json` (136,288 bytes raw), pathways use the thin browse index, and search loads the full prose corpus (#1471). 90% of the index weight is long-form clinical prose — indications 159 KB (26%), contraindicationsOrCautions 139 KB (23%), bestUsedFor 73 KB (12%), clinicalSummary 67 KB (11%), patientPopulation 59 KB (10%), targetSymptoms 48 KB (8%) — while name, slug, category, tags and setting together are 54 KB (7%). **Remaining decision for search/pathways: rendered on the card, matched by search, or neither.** `therapy-card.tsx` references five of those prose fields and the same index feeds the search screen, so stripping fields could silently change clinical display or search recall. **Next:** settle that per-field question, then either pre-truncate prose that only feeds card display, or move search matching server-side / load prose on first keystroke. **Gate:** `check:therapy-data-index` plus the therapy Playwright journeys; re-measure with `npm run verify:lighthouse`. **Stop:** do not drop a field from the catalogue payload without confirming no card renders it and no search path matches on it. Same class as #013 (route-chunk / catalogue JSON weight), different route and now measured. | session 2026-07-30 Lighthouse budget first run; PR #1404; PR #1489 | 2026-07-30 | -| #118 | P2 | task | Adopt the visual and Lighthouse baselines so the two new gates actually gate | BROWSER DRIFT RE-MEASURED 2026-08-12 and it is WIDER than this row records. The row states the Lighthouse half 'still fails repo-wide for the Chromium 150-vs-151 baseline reason'. Two local runs today both refused with: 'Evidence incomplete. browser drift on 10 run(s): the baseline was measured by HeadlessChrome/**151**.0.0.0, this run used HeadlessChrome/**141**.0.0.0 — refresh it with the CI Refresh Lighthouse baseline dispatch (workflow_dispatch, refresh_lighthouse_baseline). Nothing is graded from this run.' So the gap is now 141-vs-151, and the practical consequence is concrete: the Lighthouse budget CANNOT grade anything from this container, which is where the cheap offline measurement lives. The measurements themselves are still valid observations — #147 was closed on two of them — they simply cannot be auto-graded against a baseline captured on a different browser. Next for this half: run the refresh_lighthouse_baseline dispatch so the committed baseline matches a browser the local harness can reproduce, OR record that local Lighthouse grading is permanently unavailable and treat CI as the only grader. The visual-baseline half of this row is unchanged and still governed by the 2026-08-09 owner decision above. | session 2026-07-30; PR #1404 | 2026-07-30 | +| #117 | P2 | rec | All live mobile routes breach LCP; shared render-blocking CSS and font are the current bottleneck | PR #1915 is merged and deployed at exact Railway SHA ca788d41e1e6b64dc6b9bc63609074d92470d7e7. Three-sample live medians on that SHA are Documents 3611 ms, DSM 3600 ms, Forms 3715 ms, root 3948 ms, Therapy 3543 ms, and Services 3791 ms; desktop LCP is 584-691 ms and CLS is within the mobile rule. Trace attribution shows LCP equals FCP, TTFB is only 267-316 ms, and the shared render-blocking stylesheet plus preloaded 28 KB Geist font dominate cold mobile paint. The follow-up branch removes mockup-only Tailwind vocabulary from production CSS (367,050 to 302,113 raw bytes; 55,140 to 46,152 gzip) while retaining a complete route-only mockup sheet. It keeps the display-swap body font preloaded because two hosted Lighthouse runs showed that removing it delayed text LCP, while the non-LCP mono font remains on-demand. Next: merge/deploy that exact SHA, rerun the complete live matrix, then attribute remaining shared JS/hydration cost if any route still exceeds 2500 ms. Therapy field safety review remains required for search/pathways. INP remains unverified because Lighthouse does not measure it and no usable CrUX result exists. Stop: do not strip clinical fields, weaken the Lighthouse budget, or claim an INP pass. | PR #1915; live Web Vitals runs 31704500966 and 31704504389; codex/performance-css-delivery | 2026-07-30 | +| #118 | P2 | task | Adopt the remaining visual baselines; Lighthouse now gates regressions | Lighthouse half resolved in PR #1915: authorized CI refresh run 31697669596 on current main produced all 10 route/strategy cells with one pinned HeadlessChrome/151 identity. The reviewed artifact was committed, lighthouse-budget.json enforce is true, the job no longer uses continue-on-error, merge_group coverage is restored, and pr-required now fails on a selected Lighthouse failure. The 2026-08-08 and 2026-08-13 complete baselines stayed within tolerance; the latter puts mobile LCP at 2357-2388 ms and Therapy is no longer an outlier. This relative local-production gate does not close #117 deployed-origin LCP work. Remaining #118 scope: adopt the CI-generated Linux visual snapshots and promote visual-baseline only after design-owner review and stable reruns. Stop: never use developer-machine snapshots or let a workflow update its own gate. | PR #1915; CI run 31697669596 artifact lighthouse-baseline-refresh-31697669596 | 2026-07-30 | | #150 | P2 | issue | CodeRabbit reviewed none of a full day's PRs; spending cap reached | IN FLIGHT 2026-08-12 in PR #1836 (finalize tooling follow-through notes). Checked against the open-PR list during the full ledger sweep. Do NOT start this row while that PR is open — duplicating a queued conversion is the exact failure #292 records, and it has happened twice. Re-verify this row against main after that PR merges, and close it there rather than here. **Outcome:** the repo's second automated reviewer is either funded or acknowledged as absent, rather than appearing to review while skipping. **Evidence 2026-07-30:** CodeRabbit posted "Review limit reached … Your organization has reached its usage spending cap" on **every** PR opened that day — #1404, #1430, #1444, #1445, #1479 — reviewing none of them. Each notice renders as an ordinary bot comment, so a skimming reader sees reviewer activity where there was no review. The Codex connector was the sole substantive reviewer across those PRs and found three real defects that had survived local gates and self-review: a proxy-variable inference in #1430, an `unset` vs `unspecified` git-attribute conflation in #1444, and an earlier P1 recursive-delete on an unvalidated `--dir`. **Next:** decide whether to raise the cap, switch to label-based opt-in so the budget lands on PRs that need it, or accept single-reviewer coverage explicitly. **ESCALATED 2026-07-30 — both reviewers are now capped, so this row's premise no longer holds.** The analysis above rests on the Codex connector being the surviving reviewer. On PR #1505 the Codex connector posted "You have reached your Codex usage limits for code reviews" while CodeRabbit posted its own spending-cap notice on the same PR. **That PR therefore received zero automated review**, and so will anything opened while both caps hold. This is not a second issue — it is the same one, with the fallback removed. **Why it is worth more than a status note:** on 2026-07-30 the single Codex finding on PR #1459 was correct and changed the outcome — it showed that a claimed `LoadingPanel` verification had matched `ModeHomePageSkeleton` instead, which caused `#105` to be closed on wrong evidence. Local gates did not catch it and neither did self-review; the review did. A window in which neither reviewer runs is a window in which that class of error lands. **Next:** the three options above now need deciding rather than deferring, because "accept single-reviewer coverage" is no longer one of them. Until then, treat any PR merged during a cap window as locally-gated only. **Stop:** do not read a CodeRabbit *or* Codex comment as a completed review without checking it is not a usage-limit notice — during this window both bots posted comments on every PR while reviewing none of them. | PRs #1404/#1430/#1444/#1445/#1479; `.coderabbit.yaml` | 2026-07-30 | | #152 | P2 | issue | Uncommitted work sits in worktrees whose branches are already merged | **Outcome:** work that exists in no branch and no PR is either committed or knowingly discarded, not lost to a disk reclaim. **Inventory 2026-07-30**, all from worktrees whose pre-snapshot branch tips were fully merged into `origin/main`, so the uncommitted changes existed nowhere else: `codex/reconcile-immediate-20260730` — 21 files, +395/-200 across 19 tracked, including `.github/workflows/ci.yml`, `package.json` and `docs/scripts-index.md`; `codex/document-results-mockup-20260730` — 8 files (+13/-3 tracked) plus an untracked `document-search-results/page.tsx` under `src/app/mockups/` (named without a full path here because it does not resolve in this repo); `codex/chat-ledger-triage-d344` — `docs/outstanding-issues.md` +59/-61; `claude/section-spy-browser-coverage` — `tests/ui-smoke.spec.ts` +51. A fifth (`claude/frosty-mayer-2c6167`) self-resolved to clean during the session. **Preserved 2026-07-30, not reviewed:** each was committed on its own branch as an unpushed `wip: preserve uncommitted work before worktree cleanup` snapshot, so the work now survives a worktree reclaim — `codex/reconcile-immediate-20260730` `748ef018f`, `codex/document-results-mockup-20260730` `5dbd9f965`, `codex/chat-ledger-triage-d344` `b7eae51a4`, `claude/section-spy-browser-coverage` `d949859c3`. All four worktrees are now clean. None is pushed and none is reviewed or verified; the snapshots exist to stop silent loss, not to endorse the content. First captured in PR #1490, which was closed unmerged; re-landed on `main` by PR #1508. **Next:** per snapshot, review and either promote it to a real branch/PR or `git reset --hard HEAD~1` to drop it. Do not bulk-delete worktrees without this check — it is why only one of the two "safe" candidates was removed in the 2026-07-30 cleanup. NOT VERIFIABLE FROM A CLOUD SESSION (checked 2026-08-12): this row describes machine-local git or process state. Claude Code on the web runs in a container cloned fresh at session start, so it reports 1 worktree, 0 upstream-less branches and no running dev servers no matter what is true on the machine that raised this. Do NOT close this row from a cloud session — that reading is an artifact of the container, not evidence. Verify from the originating machine. | session 2026-07-30 worktree cleanup; PR #1490 (closed); PR #1508 | 2026-07-30 | | #156 | P3 | issue | Outstanding-issues ids are still allocated read-modify-write, and Update-branch corrupts the merge | **Outcome:** two branches cannot silently claim the same outstanding-issues id, and no merge path can commit a file where they have. **Detail:** Residual of archived `#112` (gate landed; underlying read-modify-write race explicitly left open) plus post-`#133` evidence. `#133` fixed the two causes of *conflict frequency* — `#1444` removed `merge=union` and `#1479` excluded the ledger from Prettier so a maximum-width row stops re-padding the whole table. Neither touches **id allocation**, which is still read-modify-write against the `issues:next-id` marker, so two branches open at the same time still pick the same number. Measured on PR #1451 (2026-07-30): one P3 row was renumbered `#135` -> `#141` -> `#145` -> `#147` -> `#149` across four sync cycles, because `main` had taken each id in turn — every renumber was manual. This capture itself demonstrates the same hazard: first written as `#151` on PR #1506, then `#154` / `#155` after earlier main syncs, then `#156` here because `main` had already taken `#154` and `#155` for unrelated rows. Both renumbers happened *after* `#133` was closed, which is the point: the fixes in `#1444` and `#1479` were real, but they were fixes to conflict frequency, and allocation was never the same problem. The sharper finding is the resolution path: the GitHub **Update branch** button pushed a sync to that PR head (`df3f3aeed`) whose auto-merge produced **two rows numbered `#141` and two `next-id` markers**, leaving the marker at `142` — below `main`'s highest id, so the next allocation would have reused a live number. `git merge` reported success; only `npm run check:outstanding-issues` caught it. That guard runs in `verify:cheap` and `static-pr`, so such a head cannot merge — but the corruption is produced by a one-click path that runs no guard, and the cost lands on whoever notices. A second session on the same branch later dropped an entire appended evidence block while resolving this file, which the guard cannot detect at all: it validates ids and structure, not whether a merge kept both sides' prose. **Next:** cheapest first — document that Update branch must not be used on PRs touching this file (prefer `npm run sync:pr-branches:apply`, which the repo already prefers for other reasons), then consider allocating ids from a source that cannot collide (per-row files, or a date-plus-slug id) so concurrent branches never contend. **Stop:** do not reintroduce a merge driver here — `#133` settled that; this is about allocation and about merges that silently drop rows, not about the driver. Do not reopen archived `#112` — keep the gate outcome there and track the residual race here. | PR #1451 sync cycles; `df3f3aeed`; archived `#112`; session 2026-07-30; PR #1506 | 2026-07-31 | @@ -193,11 +190,8 @@ removed after current-main verification; it is not missing recommended work. | #165 | P2 | task | Adopt a consolidated answer-home notice block — the studies exist, nothing adopts them | **Outcome:** the answer hero states its safety obligation, its scope, and its verification requirement as one block in one voice. **Detail:** `/mockups/warning-consolidation` (PR #1437) diagnoses today's three stacked notices — the APP-5 privacy warning at 11px muted, a bare `/privacy` link, and an accent-blue `ShieldCheck` capability claim at 14px semibold — and shows the hierarchy is inverted: the least important line is the loudest, and two shields with opposite meanings sit ~40px apart. Three consolidations are drawn at 1440px and 390px. Recommended: **02 Safety card** on the hero (obligation on a warning-tinted top row, everything descriptive in one grey voice below) and **01 Assurance bar** on the docked composer — the same content model at two densities, so one component with a `density` prop covers both. **This is a governance change, not just a design one:** `PrivacyInputNotice` is the single site-wide APP-5 line and renders on the answer, documents and calculators composers, so all three move together; `tests/privacy-ui.test.ts`, `tests/ui-accessibility.spec.ts` and the phone-chrome reserve coverage all assert against the current markup and must change in the same commit; and the PR will need a full `## Clinical Governance Preflight` (the mockup PR correctly did not). **Third study (before/after):** `/mockups/answer-home-proposal` draws the concrete D-direction proposal as a full hero before/after rather than an isolated notice. **Second study (words only):** `/mockups/warning-line` answers a narrower brief — no icon, border, tint or background, one line where width allows. Six variants A-F; line counts measured from the rendered DOM, not asserted. Only B (middot clauses), D (obligation + verify) and F (compressed obligation) hold one line at desktop width, and **none fit one line on a 390px phone while the pinned APP-5 sentence stays verbatim** — 46 characters of obligation plus the 27-character link exceeds the ~60 available at 11px. Recommended there: **D**, the only compliant variant that is both one line and keeps weight-only hierarchy, reached by dropping the scope claim (a capability statement already visible on the answer itself). F fits best but rewrites the pinned obligation to \|No patient-identifiable information.\| and so needs the same privacy sign-off as `#166` plus a matching `tests/privacy-ui.test.ts` update. **Status:** PR #1437 was closed unmerged on 2026-07-30 as a deliberate pause during an owner-authorized ordered merge sweep, to be reopened at its queued place; branch `claude/warning-consolidation-mockups-09jyj7` is preserved and merged onto current `main`; these follow-up rows have been renumbered on each sync because `main` kept claiming the next ids while the PR was paused; the superseded numbers are deliberately not listed, since they now belong to unrelated rows. **Next:** decide block (02 + 01) versus line (D) direction, get wording sign-off for `#166`, then implement behind one component and run `verify:phone-chrome` before `verify:ui`. | session 2026-07-30; PR #1437; `/mockups/warning-consolidation`; `/mockups/warning-line` | 2026-07-30 | | #168 | P2 | rec | Sequential issue ids force every concurrent append to conflict | **Outcome:** two sessions can append to this ledger at the same time without conflicting. **Detail:** ids are allocated read-modify-write against the `issues:next-id` marker inside the file being edited, so two branches both read N and both write N. Because duplicate ids are unacceptable, a union merge driver is unsafe — .gitattributes says so explicitly — which is why this file deliberately has no driver and every overlapping append conflicts by hand. Manual resolution is where rows get dropped: PR #1490 was closed during one and took the only record of four snapshots with it (#152), and ids were renumbered under in-flight work three times in one session (#154, #155). The new writer (`scripts/outstanding-issues.mjs`) removes the mechanical errors but explicitly not this one. **Next:** replace the counter with a collision-free id (ULID, timestamp+suffix, or a content hash), keeping a short display number derived at render time if `#151` reads better than 01JQ…; then a union driver becomes safe to reinstate and concurrent appends stop conflicting at all. A larger variant is one row per file under `docs/issues` with the table generated, which the repo already does for `site-map.md`. **Stop:** do not reinstate `merge=union` while ids are sequential — that combination was tried in PR #1416 and removed for duplicating rows and the marker. Renumbered from this PR's original `#159` because `main` already used `#159` for the duplicated test-file-list finding. | session 2026-07-31; .gitattributes; #154/#155; PR #1524 sync | 2026-07-31 | | #169 | P2 | issue | Local branches carry work that exists on no remote | **Outcome:** committed work is not lost when a machine or worktree is reclaimed. **Detail 2026-07-31:** six `claude/*` branches in this checkout have commits and no `origin/` counterpart. Verified real for `claude/clinical-kb-design-system-333a69` — 57 files / +4069, tip `feat(design-system): v2 token layer, 26 components, browser-crash fix` dated 2026-07-31 17:40, whose added `.design-sync/previews/*.tsx` files are absent from main. Others unverified: `design-sync-db0a54`, `fable-implementation-fc937c`, `frosty-mayer-2c6167`, `issues-133-evidence`. **How to check, because the obvious measure lies:** `git rev-list --count origin/main..` and a three-dot diff both report landed work as unmerged, since this repo squash-merges and the original commits never become ancestors — my own merged branch reported 1 commit and +476 by that measure. Test instead whether files the branch adds exist on main (`git ls-tree origin/main `). **Next:** per branch, push it for review or confirm it is superseded and delete it; do not bulk-delete on the commit count. Sibling of #152, which covers uncommitted work in worktrees rather than unpushed commits on branches. NOT VERIFIABLE FROM A CLOUD SESSION (checked 2026-08-12): this row describes machine-local git or process state. Claude Code on the web runs in a container cloned fresh at session start, so it reports 1 worktree, 0 upstream-less branches and no running dev servers no matter what is true on the machine that raised this. Do NOT close this row from a cloud session — that reading is an artifact of the container, not evidence. Verify from the originating machine. | session 2026-07-31; local branch audit | 2026-07-31 | -| #170 | P2 | task | Phone filter sheets exist for documents and therapy; shared-band adoption remains | **Outcome:** phone filtering opens the repo's own bottom sheet instead of competing for width in the utilities rail, and both breakpoints agree on what each dimension means. **Current state (2026-08-12):** the shared contract landed (PR #1847, docs/filter-contract.md) discriminating ResultFilterGroup into lens (one-of-N) and facet (many-of-N); SegmentedControl gained an option hint slot (PR #1848); and the four lens modes now adopt it (PR #1857) — differentials, medication, applications and specifiers each replaced a bespoke aria-pressed desktop rail with SegmentedControl and build ONE option array shared by rail and sheet. That closes the defect where the same dimension claimed one-of-N in the sheet (role=radiogroup) and many-of-N on the rail (aria-pressed). Specifiers' footerNote also fixed: it reported results.length + catalogueMatches.length while its groups narrow only results. Formulation is the first real FACET adoption: domain list derived from the mechanisms that carry it (9 of 12 declared — Biological, Social and Cultural are carried by 0 of 12 and are gone), domain converted to resultFilterFacetGroup with union counts verified monotonic and non-additive, zero-yield options rendered as focusable dead ends, the desktop select retired in favour of the shared ResultFilterFacetChips (exported so facets get the same both-breakpoints primitive lens modes get from SegmentedControl), and the query-replacing pattern group evicted from the sheet to AnswerSuggestionChips below the band (all five presets, not the four the old .slice(0,4) exposed). **Remaining:** services (facet index + selection state + URL + the scope segment; substance_flags is an exact partition so it is a lens, not a facet, and ServiceRecord.tags is flattened with "general" colliding across three dimensions, so a typed facet carrier is needed first), factsheets (evict category presets to the composer), therapy-compass (converge onto the shared trigger/sheet; its onClearAll still wipes the query, which the contract forbids), documents last (port find-a-filter + collapse up as the >20 tier). **Stop:** the scope segment is deliberately NOT retrofitted to the adopted modes yet — filter-contract.md section 4 gates it on a catalogue meaningfully larger than the result set, and it lands with services where that is true. Do not ship a mode conversion without browser proof at 390px and 320px; these change rendered output, unlike #1847. | Round-7 design study; PR #1523 notes; `src/components/ui/sheet.tsx` | 2026-07-31 | -| #175 | P2 | task | Therapy modality is now null on all 205 records and needs curation or removal | VERIFIED CORRECT 2026-08-12 — re-checked against merged main during the full ledger sweep and left unchanged: public/therapy-compass-data/therapies-index.json holds 205 records and 0 carry a modality value, exactly as described. This stamp exists so a later reader can tell "checked and still true" from "never looked at"; the two were indistinguishable before. **Outcome:** the Therapy detail and recommend screens either show a curated modality or stop carrying the field at all. **Detail:** the source catalogue derived `modality` from each record's own tag list — all 205 records had one, every value was also present in that record's `tags`, and the whole catalogue collapsed to CBT/ACT/DBT. It mislabelled the treatments it could not describe: ECT and rTMS as "ACT", Psychoanalysis and Psychodynamic Psychotherapy as "CBT", MBT and TFP as "DBT". Pre-existing on main, surfaced by the PR #1489 review. The generator emits it only when the source curates a value that is not already a tag, which today means null for 205/205 on the index projections *and* the full catalogue the detail/recommend screens load (`catalogue: "full"`), so the two chips (`detail-screen.tsx:49`, `recommend-screen.tsx:115`) never render and `select.ts:117` contributes no same-modality point. Removal was provably search-neutral: `src/lib/therapies.ts` scores with boolean `haystack.includes(token)`, not term frequency, and every modality value was already contributed by `tags.join(" ")` in the same haystack. **Next:** one of two — curate real modality values in `public/therapy-compass-data/therapies.json` (clinical work, needs the psychiatrist), or drop the field from `types.ts`, `src/lib/therapies.ts`, the two chips and `select.ts`. **Stop:** do not reinstate the tag-derived value to make the chips reappear; a guess rendered as curated fact is the defect. `tests/therapy-compass-pathways.test.ts` pins the echo invariant on both the index and the full catalogue asset. Renumbered from this PR's original `#169` because `main` claimed `#169`–`#174` while the branch was open. | PR #1489 review remediation; PR #1532; session 2026-07-31 | 2026-07-31 | -| #177 | P3 | rec | Therapy catalogue aliases duplicate 2.53 MB of bytes instead of pointing at the hashed file | VERIFIED CORRECT 2026-08-12 — re-checked against merged main during the full ledger sweep and left unchanged: public/therapy-compass-data/ holds THREE copies of the catalogue (therapies.json plus two hashed assets), which is the grace-generation case this row predicted. This stamp exists so a later reader can tell "checked and still true" from "never looked at"; the two were indistinguishable before. **Outcome:** the unversioned catalogue aliases stop costing a second copy of every payload in the repo and the image. **Detail:** `public/therapy-compass-data/` ships each catalogue twice — `therapies.json` (2,529,429 bytes) alongside `therapies..json` with identical content, and the same for the index and home projections. Git stores one blob for each identical pair, so history is unaffected, but the working tree and every Docker image carry both. PR #1489's remediation adds a one-deploy grace generation, so a stem whose content changed now holds three copies until the next regeneration. **Next:** serve the alias as a Next rewrite to the current hashed filename instead of writing a duplicate file — the alias would keep its `max-age=0, must-revalidate` header (the hashed target keeps `immutable`, so the rewrite must not inherit it) and the generator would stop copying bytes. Check that a rewrite still satisfies the alias-equality assertions in `check:therapy-data-index` and `tests/therapy-compass-mode-wiring.test.ts`, which currently compare file bytes. **Stop:** do not drop the aliases outright — they are the fallback `useTherapyData` uses when a pre-deploy bundle names a hash that no longer exists. Renumbered from this PR's original `#171`. | PR #1489 review remediation; PR #1532; session 2026-07-31 | 2026-07-31 | +| #175 | P2 | task | Therapy modality is now null on all 205 records and needs curation or removal | VERIFIED CORRECT 2026-08-12 — re-checked against merged main during the full ledger sweep and left unchanged: src/data/therapies-source.json holds 205 records and 0 carry a modality value, exactly as described. This stamp exists so a later reader can tell "checked and still true" from "never looked at"; the two were indistinguishable before. **Outcome:** the Therapy detail and recommend screens either show a curated modality or stop carrying the field at all. **Detail:** the source catalogue derived `modality` from each record's own tag list — all 205 records had one, every value was also present in that record's `tags`, and the whole catalogue collapsed to CBT/ACT/DBT. It mislabelled the treatments it could not describe: ECT and rTMS as "ACT", Psychoanalysis and Psychodynamic Psychotherapy as "CBT", MBT and TFP as "DBT". Pre-existing on main, surfaced by the PR #1489 review. The generator emits it only when the source curates a value that is not already a tag, which today means null for 205/205 on the index projections *and* the full catalogue the detail/recommend screens load (`catalogue: "full"`), so the two chips (`detail-screen.tsx:49`, `recommend-screen.tsx:115`) never render and `select.ts:117` contributes no same-modality point. Removal was provably search-neutral: `src/lib/therapies.ts` scores with boolean `haystack.includes(token)`, not term frequency, and every modality value was already contributed by `tags.join(" ")` in the same haystack. **Next:** one of two — curate real modality values in `src/data/therapies-source.json` (clinical work, needs the psychiatrist), or drop the field from `types.ts`, `src/lib/therapies.ts`, the two chips and `select.ts`. **Stop:** do not reinstate the tag-derived value to make the chips reappear; a guess rendered as curated fact is the defect. `tests/therapy-compass-pathways.test.ts` pins the echo invariant on both the index and the full catalogue asset. Renumbered from this PR's original `#169` because `main` claimed `#169`–`#174` while the branch was open. | PR #1489 review remediation; PR #1532; session 2026-07-31 | 2026-07-31 | | #178 | P2 | rec | pr-policy does not flag operational risk bundled with clinical or UI risk | IN FLIGHT 2026-08-12 in PR #1837 (harden verification & PR policy guards). Checked against the open-PR list during the full ledger sweep. Do NOT start this row while that PR is open — duplicating a queued conversion is the exact failure #292 records, and it has happened twice. Re-verify this row against main after that PR merges, and close it there rather than here. **Outcome:** a PR that mixes operational-risk paths with clinical or UI risk is called out before it merges, because squash-merging that mix destroys per-item revert. **Detail:** `classifyPullRequestFiles` already computes `operationalRisk`, `clinicalRisk`, `ragRanking` and `ui` independently, but nothing reacts to the combination. AGENTS.md's "PR bundling" section forbids bundling anything once `operationalRisk` is true; the classifier is where that could be enforced. PR #1489 is the worked example: 33 files spanning `.github/workflows/ci.yml`, both Dockerfiles, a rewrite of the bundle-budget gate, a phone-chrome scroll change and a therapy data restructure, merged as one squash (945148251). Reverting any single item now means hand-reverting hunks out of the squash commit, because the branch commits are unreachable. The remediation PR for that review repeats the pattern on a smaller scale (clinical data plus a one-line ci.yml timeout), which is why this is a recommendation rather than a hard gate — the right severity is probably a warning that names the mixed classes, not a merge block. **Next:** emit an advisory line from `evaluatePullRequestPolicy` when `operationalRisk` coincides with `clinicalRisk` or `ui`, listing which paths drove each; decide separately whether it ever blocks. Cover it in the `--self-test` block. **Stop:** do not make it a hard failure in the same change that introduces it — land the signal first and see how often it fires. Renumbered from this PR's original `#172`. | PR #1489 review remediation; PR #1532; session 2026-07-31 | 2026-07-31 | -| #180 | P3 | issue | build-therapies-index now overwrites its own source input | RE-CONFIRMED STILL LIVE 2026-08-12 on merged main, with the exact lines. scripts/build-therapies-index.mjs takes public/therapy-compass-data/therapies.json as its SOURCE (:24-28, with the missing-source error naming it at :31) and also writes that same path as `legacyFullTarget` (:269). Source and target are one file, which is the defect this row names. Note the sibling that is now closed: the pretty-print regression (#179) is fixed and gated at :240-244, so do not conflate the two — the compactness guard does not address the read-write overlap. Next unchanged: give the generator a source that it never writes (or a target it never reads), so a re-run cannot consume its own output. | PR #1532; session 2026-07-31 | 2026-07-31 | | #183 | P2 | task | Create Sentry metric alert for production DB span p95 > 500ms | Still blocked 2026-08-01 closeout: SUPABASE_ACCESS_TOKEN and SENTRY_AUTH_TOKEN missing from session env; Sentry MCP OAuth can list/get alerts but has no create tool; browser hits login wall; no metric rules exist yet on clinibase-xz. Create Metric Alert: p95(span.duration), filter span.op:db, environment production, threshold >500ms, notify Active Members. Provide SENTRY_AUTH_TOKEN in session to finish via sentry alert metrics create. | session 2026-07-31 db-query-perf follow-up | 2026-07-31 | | #188 | P3 | task | Document and track disaster-recovery re-creation checklist as ledger work | **Outcome:** the five DR items that do not survive a schema restore are tracked with owners and verify steps. **Split into concrete tasks 2026-07-31:** `#196` pg_cron; `#197` Vault secrets; `#198` custom GUCs; `#199` edge functions; `#200` dashboard config. Keep this umbrella as the index; execute the child tasks. Distinct from `#056` (staging schema) and resolved `#054` (secrets reconciliation). **Stop:** do not treat a schema restore as complete until children are green. | docs/operator-backlog.md Disaster-recovery re-creation; session 2026-07-31 | 2026-07-31 | | #189 | P2 | task | Pin /api/search route-level round trips and disposition the x3 text RPC probes | **Outcome:** a counting-proxy budget drives `POST` `/api/search` (auth/ratelimit/scope/enrichment/telemetry), and the retrieval-core finding that `match_document_chunks_text_v2` and `match_document_table_facts_text_v2` each issue three times per search is either documented as intentional or collapsed under the RAG canary gate. **Source:** residual next actions on `#098` after answer-path and retrieval-core budgets landed. **Next:** (a) route-level budget following `tests/answer-route-preamble.test.ts`; (b) decide probe vs collapse — behaviour change needs RAG flag + canary. **Stop:** do not change retrieval assembly without approval. | session 2026-07-31; #098 residual; tests/search-round-trip-budget.test.ts | 2026-07-31 | @@ -234,7 +228,7 @@ removed after current-main verification; it is not missing recommended work. | #242 | P2 | task | Commit approved Linux visual baselines and promote adoption not-committed → committed | VERIFIED CORRECT 2026-08-12 — re-checked against merged main during the full ledger sweep and left unchanged: Six linux/ PNGs are committed, but the adoption manifest still carries 68 `not-committed` entries — the surfaces flip is the remaining work, as stated. This stamp exists so a later reader can tell "checked and still true" from "never looked at"; the two were indistinguishable before. Baselines and provenance are DONE as of PR #1729 (branch claude/ds-adopt-visual-baselines): all six linux/ PNGs committed from ubuntu artifact visual-baseline-31251091603 (main @ bc33d414e), AWAITING_BASELINE emptied, and tests/__screenshots__/linux/provenance.json written with per-candidate SHA-256 + dimensions and an approved human review. Proven by that PR's own run: visual-junit tests=9 failures=0 skipped=0, and no visual-candidates/ directory, i.e. all six compared rather than skipped. REMAINING: only the surfaces flip to baseline.status committed. Blocked on ordering, measured 2026-08-08: validateLinuxVisualBaselineSet short-circuits on declaredPaths.length===0, so declaring files activates its rule that no non-allowlisted path may change since candidateSourceHead — and PR #1729 necessarily changed tests/design-system-adoption.test.ts, whose initialiseCandidateRepository seeded fixtures from the LIVE spec and so failed the moment AWAITING_BASELINE emptied. The two cannot land together. Next: after #1729 merges, re-capture candidates from a main run that already contains that fixture fix, then flip the surfaces against that head. Note this does not affect whether pixels compare — Playwright compares because the goldens exist on disk. | PR #1616 review findings; session 2026-08-05 | 2026-08-05 | | #244 | P3 | rec | Forced-colours v2 mapping depends on grouped dark selectors staying in the media block | IN FLIGHT 2026-08-12 in PR #1841 (forced-colours selector specificity). Checked against the open-PR list during the full ledger sweep. Do NOT start this row while that PR is open — duplicating a queued conversion is the exact failure #292 records, and it has happened twice. Re-verify this row against main after that PR merges, and close it there rather than here. ckb-v2-tokens.css forced-colours block lists .ckb-v2.ckb-v2, .dark .ckb-v2.ckb-v2, and .ckb-v2.dark.ckb-v2 so specificity matches dark rules. Trimming to a single .ckb-v2.ckb-v2 silently drops dark HCM. Next: keep the contract test pin; never trim that selector group. | PR #1616 review findings; session 2026-08-05 | 2026-08-05 | | #245 | P3 | rec | responsive-compact CrossModeLinks keeps duplicate rails in the DOM | IN FLIGHT 2026-08-12 in PR #1842 (CrossModeLinks rail behaviour). Checked against the open-PR list during the full ledger sweep. Do NOT start this row while that PR is open — duplicating a queued conversion is the exact failure #292 records, and it has happened twice. Re-verify this row against main after that PR merges, and close it there rather than here. Phone chip rail and md+ card rail both mount; display:none removes the inactive from the a11y tree. Tests/analytics counting role=link see doubles; cross-mode-links-rail is phone-only. Next: prefer the variant test ids; do not collapse to one rail with JS breakpoints (hydration risk). | PR #1616 review findings; session 2026-08-05 | 2026-08-05 | -| #248 | P2 | issue | Investigate why 20260705180000 search-health indexes were missing on live despite applied history | PR #1614 repairs the symptom with a mark-applied guard only. Confirm out-of-band whether the earlier reconcile migration partially applied, indexes were manually dropped, or schema_migrations was repaired — and whether check:drift should have caught this class before runtime search_schema_health. Renumbered from this PR's original #237 → #246 because main already used #237–#247 (PR #1616 findings plus the results-bar rows from PR #1615). | PR #1614 review / session 2026-08-05 (renumbered on main merge) | 2026-08-05 | +| #248 | P2 | issue | Investigate why 20260705180000 search-health indexes were missing on live despite applied history | APPEND 2026-08-13: the prior closure is withdrawn. Repository and live-drift evidence establishes that 20260705180000_reconcile_search_health_indexes.sql is recorded as applied while documents_title_trgm_idx and document_chunks_content_trgm_idx are missing on live. Supabase transaction semantics exclude a persisted partial migration, but the present record does not distinguish skipped DDL/history repair from indexes created and later dropped. In an approved read-only window, query supabase_migrations.schema_migrations for the 20260705180000 statements fingerprint and inspect the relevant audit/history evidence; retain both hypotheses until that evidence establishes the cause. Separately, scheduled check:drift did detect the missing indexes, but red runs were not routed. | PR #1614 review / session 2026-08-05 (renumbered on main merge) | 2026-08-05 | | #250 | P2 | task | Execute the fastest-wins multi-wave plan (Wave 0–4) | SUPERSEDED IN LARGE PART 2026-08-12 — this row is a wave plan whose contents have been overtaken, and as written it now misdirects. Rows it names as live A1 work are CLOSED: #207 and #226 were archived on main, and #166 (its sibling in that cluster) archived in the 2026-08-12 sweep. Of its engineering waves: 1B's gate-integrity set is done (#149, #210 re-scoped, #204/#167 covered by in-flight PR #1837); 1C's hygiene set is largely done or in flight (#232, #151, #154, #187, #142, #156, #186 all now carry IN FLIGHT notes against PR #1835/#1836); Wave 0/#202 is in flight in PR #1840. What genuinely remains of the plan is Wave 1A (#147 phone CLS, still open and still reproducible offline at zero provider cost), Wave 2 (#117 then #118), and Wave 3 (#098 then #189). The A1 track is now just #059, #053 and #231 — two operator rows and one live investigation. Next: either re-cut this row against that much smaller remainder, or close it and let #147/#117/#118/#098/#189 stand on their own, which is probably the honest move now that the multi-agent framing has served its purpose. Stop unchanged: no RAG behaviour change without flag plus canary, no provider gates without approval, and do not mix operationalRisk with clinical or UI in one squash. | session 2026-08-05 fastest-wins plan | 2026-08-05 | | #253 | P3 | task | #1606 needs a hand-merge against merged PR #1615, not a rebase | SUPERSEDED IN PART 2026-08-07: the component both PRs rewrite no longer exists. `MobileResultFilterControl` — the native `