From c4cbdf8ad6457d333eea45f955da758f3c257703 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 07:56:04 +0000 Subject: [PATCH 1/3] issues: archive five completed rows, delete none Triage pass over all 58 open items found five rows whose work is finished and whose next action is empty. Each moves from Open items to Resolved / archive with its fix evidence and the 2026-07-30 date: - #095 the pr-required aggregate now routes a cancelled result through a shared cancelled_error helper; guarded by seven cases that execute the extracted script, three mutation-proven. The red is deliberately retained, since GitHub counts a skipped required check as passing. - #096 every PR #1316 sub-item is dispositioned: the adoption-gate root-path gap closed on PR #1394, four findings were fixed independently, and the Therapy Compass retry-waiter finding was corrected to not-a-live-defect. - #104 a correction row with no next action - the worker's triple image read is an accepted peak-memory trade-off documented at worker/main.ts:866-869, not debt. Archived so a fourth audit does not re-file it. - #109 the branch sweep refuses on a shallow clone, an indeterminate result is its own failure, and the guard moved into the exported collector so the evidence-pack path fails closed too. - #115 the band adoption gate was redesigned to walk a real reachability graph rather than asking whether a file mentions the band. Nothing is deleted. The ledger contract is archive-only (SKILL.md:44 "Archive, never delete"; this file's own conventions: "rows are archived, not deleted, so the history stays auditable"), so no row qualifies for deletion. Row total is unchanged at 120: 58 -> 53 open, 62 -> 67 archived. Prettier widened the archive Outcome column to fit the new evidence, which repads the other archive rows; git diff --ignore-all-space is 7 insertions / 7 deletions, i.e. the five moved rows plus both separators. Verified: node scripts/check-outstanding-issues.mjs --self-test && node scripts/check-outstanding-issues.mjs -> "Outstanding-issues guard passed: 120 rows (53 open, 67 archived), unique ids, next-id=126 above the highest". npx prettier --check . -> "All matched files use Prettier code style!" Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_011YdPS2KhKqz2buzsUgmX3c --- docs/outstanding-issues.md | 248 ++++++++++++++++++------------------- 1 file changed, 124 insertions(+), 124 deletions(-) diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index 60ae9f7e56..cffd662fb7 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -95,132 +95,132 @@ removed after current-main verification; it is not missing recommended work. > > **Exact-head release confirmation (2026-07-28):** after final review narrowed expanded chunk selection to the measured clozapine blood-count action shape, `output/rag-retrieval-post-exact-head.json` repeated all 36 cases with document/content recall 1.0, MRR 0.8921, content MRR 0.9406, nDCG 0.9308, irrelevant-at-10 0.0917, zero failures and zero per-case document/content reciprocal-rank regressions versus `rag-retrieval-post-final.json`. Median latency rose 13,563 -> 19,729 ms while p90 improved 56,765 -> 55,660 ms; neither run had a latency-failed case, so no ranking or latency gate changed. Cache-bypassed exact-head answer probes for both admission/discharge cases and the clozapine threshold case were substantive, grounded, expected-source-backed and free of citation/numeric/route failures; all used zero provider requests and $0 generation cost. This was the protected behavior-change merge gate, not a rerun for #023. -| ID | Pri | Type | Summary | Detail / next action | Source | Added | -| ---- | --- | ----- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | ---------- | -| #059 | P1 | task | Verify containment of every credential reported exposed in chat | **Outcome:** every reported exposed credential is rejected or retired. **Next:** in approved security windows, verify and revoke or rotate the GitHub token, OpenAI key, Supabase service-role JWT, database password, and E2E credential; create replacements only when required and update only intended secret stores. **Success:** provider evidence confirms the old credentials cannot authenticate, replacements are distinct and minimally scoped, presence/readiness checks pass, and secret scans remain clean. **Stop:** no provider or secret-store action without approval; never print or paste values into Git, logs, issues, or chat. | session 2026-07-24 security reconciliation; AI Agent Target Manifest | 2026-07-24 | -| #065 | P2 | task | Complete the paused compact document source-text accordion | **Outcome:** the document viewer uses compact nested disclosures while retaining complete text, citation/search navigation, print behavior, and composer clearance. **Next:** only when the user explicitly resumes, reconcile `codex/chat-document-text-accordion-7cb4` with current `main` and complete the focused 320/390/1280 px tests. **Success:** default disclosures are closed; deep links and search open only the active passage; printing expands/restores state; no overflow. **Verify:** focused document-viewer Playwright, `verify:cheap`, `verify:ui`, and static production-readiness. **Stop:** remain paused until explicit user return; no provider calls. | paused document-viewer task; `codex/chat-document-text-accordion-7cb4` | 2026-07-24 | -| #001 | P2 | task | Semantic reranking still gated off | `RAG_SEMANTIC_RERANK_ENABLED=false` from PR #901. Do not enable until the provider-backed 36/36 retrieval-quality gate **and** an ambiguity-focused canary are explicitly approved and recorded. | `docs/process-hardening.md` (Semantic reranking rollout debt); PR #901 | 2026-07-21 | -| #053 | P1 | task | Execute cross-border privacy/legal package | Execute OpenAI and Railway DPAs; decide ZDR and Australian data residency; obtain prompt-cache behavior in writing; review subprocessors; obtain APP 8 and APP 5/1 counsel sign-off. Do not represent the release as privacy-approved or alter final public privacy wording before sign-off. | `docs/openai-cross-border-basis.md`; `docs/privacy-impact-assessment.md` | 2026-07-24 | -| #055 | P2 | task | Run one exact-SHA full release and PR gate | Before the next full-confidence release/handoff, record the candidate/PR SHA and run the local/provider release gates, Firefox/WebKit, required hosted CI, and actionable GitHub review-thread closure once. Stop at the first actionable failure and rerun only the repaired smallest gate. | `docs/launch-operator-runbook.md`; `docs/codex-review-protocol.md` | 2026-07-24 | -| #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 23 repository migrations behind. 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/capacity-review.md` | 2026-07-24 | -| #005 | P3 | rec | `finalScore` saturates at clamp ceiling | Base + ~40 stacked boosts routinely exceed 1.0, so strong matches tie at 1.0 and order by an arbitrary `document_id` tiebreak. If ranking is ever revisited, break ties by the **pre-clamp** score rather than raising the `[0,1]` ceiling (downstream gates assume `[0,1]`). Ordering already sorts by the unbounded pre-clamp `rankScore` (`clinical-search.ts:1735,1927,1950-1955`), so the clamp confines only the reported confidence value, not result order. Not a defect on the current golden set; any change here is a protected RAG surface (canary required). | `docs/rag-hybrid-findings-and-todo.md` P1 item 4; `src/lib/clinical-search.ts:1735` | 2026-07-21 | -| #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 | `build:analyze`: `/specifiers` ships `specifiers-search-index.json` (~180 KB parsed), `/forms` ships `forms-catalog.json` (~132 KB), `/formulation` ships `formulation-content.json` (~52 KB, client-side local search — needs index/full split or a search endpoint, architectural). All route-scoped (not initial bundle). Also `*-mockups.tsx` (~100 KB across chunks) build though `/mockups` 404s in prod — exclude from the prod artifact. | session 2026-07-21 (build:analyze) | 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) `src/app/(search-app)/layout.tsx:4` imports 71.6 KB of Therapy-Compass-only CSS in the ROUTE-GROUP layout, making it 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 | -| #017 | P3 | task | Field Web-Vitals baseline via live Lighthouse | In-sandbox runtime vitals were blocked (prod server hard-requires Supabase secrets; dev-mode CLS measured excellent at 0.00–0.04, content-first pages 0.000). Run Lighthouse against `psychiatry.tools` for real LCP/INP/CLS to prioritize #012–#016 by measured impact rather than reasoning. **Instrument landed (PR #1385):** `live-web-vitals.yml` is dispatch-only, takes no secrets and runs nothing until someone dispatches it — which is itself a live production action needing explicit approval. `scripts/summarise-web-vitals.mjs` holds the decision rule, committed before any numbers were read: mobile LCP < 2500 ms AND CLS < 0.1 on every route (plus INP < 200 ms from CrUX) closes this row and makes only the explicitly measured **payload** findings gated by #017 WONTFIX — e.g. the route-chunk/catalogue weight in #013. It does **not** close #016 wholesale: #016's motion, CSS, client-waterfall, caching, and dynamic-import items stay open unless they have separate evidence, because Lighthouse LCP/CLS do not validate those. Any breach makes only that route's findings actionable. It fails closed — a missing run, a null metric, a route-slug collision, and a measurement that landed on a different URL than requested (redirect, dropped or reordered query) are all breaches. **Sampled since PR for #114:** each route/strategy cell is measured `SAMPLES` times (default 3), the MEDIAN is graded, and a cell whose samples land on both sides of a threshold is reported as incomplete evidence rather than resolved either way — so the "evidence is too noisy" stop rule can finally fire. A mid-run Chrome change also disqualifies the verdict. **Read the first dispatch as a measurement to be sanity-checked, not as an oracle:** the grading logic took eight rounds of review corrections to reach this shape, each fix locally right and globally incomplete, so cross-check the emitted table against the raw Lighthouse JSON artifact before recording a verdict — most of all a PASS, which would close #017 and the explicitly measured payload findings. Lighthouse cannot measure INP in lab conditions, so that clause of the rule is confirmed from CrUX field data and any pass is provisional on it. | session 2026-07-21 (measurement pass) | 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 | -| #021 | P3 | rec | E-3d H2 residual: strong/comparison generation discards | approx. 6 generation attempts per full 44-case run still fail the final quality gate and fall to extractive on strong-route comparison/complex shapes (the designed-conservative outcome). PARKED: weakest cost/benefit on the queue — a wave (approx. $2-4 pair + reviewer cycle) to shave seconds off a few hard cases. Revisit only if latency/waste complaints or a cheaper lever appears. | E-3c design record; runs #59-#61 diagnostics | 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 | -| #023 | P2 | task | Complete scheduled browser and labeling disposition | The 2026-07-26 retrieval and answer artifacts are read and compared under resolved #051. Scheduled CI run `30216361999` failed its existing production dependency audit before Firefox/WebKit, while production Chromium passed. After that audit is green, capture one scheduled/manual browser-matrix datapoint; separately record the human decision for the stable irrelevant-at-10 set. #084 now makes each top-10 grade and matched signal reproducible, but it does not substitute for the human disposition. Do not rerun or spend on RAG for this item. | runs `30216191889`/`30216361999`; per-rank diagnostics #084; session 2026-07-27 | 2026-07-21 | -| #024 | P2 | issue | WebKit e2e `_rsc` prefetch access-control errors | PR #1205 narrowed catch-all interception and duplicate navigation, but Next 16.2.11 still raises `_rsc` access-control `pageerror`s after document-source fallbacks: `/documents/source?id=&page=2&chunk=safety%20plan` → `/documents/?page=2&chunk=safety+plan`; `/documents/source/evidence?id=not-a-uuid&page=2` → `/documents/search`. The invalid-id failure survived removing every Playwright route; Chromium passed both. **Next:** on a provider-free macOS host, run both URLs in stable Safari and Safari Technology Preview without interception, capture console text plus `_rsc` status/access-control headers, and compare Playwright WebKit with routing on/off. Treat as an app defect only if native Safari reproduces; otherwise return to the harness. Never suppress `pageerror` or change CORS without native evidence. | PRs #1179/#1205; current-main local WebKit evidence; session 2026-07-28 | 2026-07-28 | -| #025 | P2 | task | Activate the three webhooks (operator secrets) | Merged (#968/#1100) + deployed but inert — verified live: `POST /api/webhooks/railway` returns `503 webhook_not_configured`; the Supabase document-change trigger exists but lacks both activation inputs. To turn on: (1) Railway ΓåÆ set `RAILWAY_WEBHOOK_SECRET` + add the `?token=…` webhook URL; (2) set `SLACK_WEBHOOK_URL`/`DISCORD_WEBHOOK_URL` in BOTH the Railway **app/server env** and **GitHub repo secrets**; (3) set one matching document-change secret in the Railway app env as `SUPABASE_INGESTION_WEBHOOK_SECRET` and in Supabase Vault as `ingestion_webhook_secret`, then set the per-environment database GUC `app.ingestion_webhook_base_url` to the deployed app origin. Each path fails closed until fully configured, so this is pure ops. See `docs/webhooks.md` for verification and rotation. | sessions 2026-07-22/24; PRs #968/#1100; docs/webhooks.md | 2026-07-22 | -| #027 | P3 | rec | External uptime monitor independent of GitHub/Railway | `live-domain-monitor.yml` runs on GitHub's cron, so it won't run in exactly the outage it should catch (Actions or the deploy itself down). Add an off-platform synthetic monitor (UptimeRobot / Better Stack / Checkly) hitting `/api/health` with a webhook alert. Provider setup, not code. | session 2026-07-22 webhook review | 2026-07-22 | -| #028 | P3 | rec | Runtime error tracking (Sentry or similar) | No error tracking in the repo — production exceptions on `psychiatry.tools`, including how often `RAG_PROVIDER_MODE=auto` silently degrades to source-only, are invisible. Weigh adding `@sentry/nextjs` (dependency + DSN secret + instrumentation) vs cost; alert ΓåÆ chat/issue. Provider-backed; needs explicit sign-off before adding the dependency. | session 2026-07-22 webhook review | 2026-07-22 | -| #033 | P3 | rec | Source governance metadata absent from the LLM prompt | `buildRagSourceBlock` omits `document_status`, `clinical_validation_status`, and `extraction_quality`, so the model cannot self-caveat during generation and governance is enforced only post-hoc. Generation-surface change: needs `eval:rag` plus `eval:quality --rag-only` (grounded-supported must not drop, citation-failure 0) and explicit approval. Carries the same "unknown Γëá bad" hazard as #032 — on a partially-enriched corpus the model would likely over-caveat correct sources, so design the prompt wording before spending an eval. | `src/lib/rag/rag-source-block.ts:126-198`; PR #1051 audit item 8 | 2026-07-22 | -| #035 | P3 | rec | Threshold-conflict detection covers only 3 params | `detectThresholdDisagreements` checks only ANC, WBC, and platelets paired with withholding verbs, so cross-source conflicts on medication doses, lithium/thyroid levels, or vital signs go undetected. Deliberately narrow (see the comment at `:469-474`). Broadening changes when an answer is classified `conflicting` and adds warnings — real false-positive risk. Needs new fixtures plus a behaviour review before any change. | `src/lib/evidence.ts:469-574`; PR #1051 audit item 7 | 2026-07-22 | -| #036 | P3 | rec | 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 promotion migrations but never used as a retrieval filter. Promotion is unconditional on `clinical_validation_status`, so unverified documents are publicly searchable — compensated by keeping `unverified_source` in the frontend-visible warning set. A hard schema flag touches RLS and the clinical-risk-gated retrieval RPCs; weigh against the existing compensating control before acting. | `supabase/schema.sql:61-108`; `src/lib/search-scope.ts:181-236`; PR #1051 audit item 3 | 2026-07-22 | -| #037 | P3 | rec | D5 trust-cap-all-claims flag parked OFF | `NEXT_PUBLIC_RAG_TRUST_CAP_ALL_CLAIMS` extends authority gating from high-risk claims to **all** supported claims (`deriveTrust`). Ships OFF by design; flipping it caps trust to `medium` for routine claims across the board — a product/clinical-UX decision, not a defect. Both states are test-pinned. Next action: product decision, then flip and re-baseline the UI expectations. | `src/lib/answer-render-policy.ts:159-177`; PR #1051 audit item 11 | 2026-07-22 | -| #038 | P3 | rec | Consolidate shared comparison behavior | Several clinical modes expose comparison workflows with similar selection, empty-state and mobile-dock needs. Define one shared behavioral contract before another comparison surface is added; keep mode-specific clinical content separate. This is a design-system recommendation, not a current defect. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | -| #039 | P3 | rec | Consolidate catalogue toolbar patterns | Catalogue/search pages have independently evolved filter, sort, result-count and mobile toolbar behavior. Inventory the existing implementations and converge only the repeated interaction contract; do not flatten mode-specific search semantics. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | -| #040 | P3 | rec | Add targeted visual-regression baselines | Keep a small approved baseline set for high-value desktop/mobile surfaces and accessibility modes instead of screenshotting every route. Start with account/settings, document viewer, mode homes and bottom-composer interactions; define an intentional-update workflow before enabling blocking comparisons. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | -| #079 | P3 | task | Disposition retained worktrees in bounded cleanup batches | **Outcome:** the retained reconciliation tail is gradually classified without another disruptive all-worktree sweep. **Next:** process no more than ten worktrees per explicitly scheduled pass using current owner/process metadata, open-PR state, exact review-ledger coverage, ancestry, and cherry-pick-aware content proof. **Success:** remove only clean, inactive, bundled worktrees whose content is merged or explicitly rejected; record every disposition and retain recovery evidence. **Stop:** preserve dirty, active, secret-bearing, post-freeze, paused, or ambiguous work and never use reset, force deletion, broad clean, or process killing. | final reconciliation inventory retained 104 independent worktrees; session 2026-07-24 | 2026-07-24 | -| #085 | P3 | rec | Upload-limit client/server sync is unguarded | `NEXT_PUBLIC_MAX_UPLOAD_MB` (client, build-time inlined) and `MAX_UPLOAD_MB` (server, runtime) default 150/150 but nothing keeps them in sync — no zod link, gate, or test. Lower server-only → the 413-after-full-transfer UX FV-04 (#1064/#1069) was built to prevent; lower client-only → false pre-check rejection of files the server would accept (breaks #1064's client-is-a-strict-superset invariant). Both documented in `.env.example`/`docs/deployment-architecture.md` but unenforced; the client value is also frozen at `next build`, so changing the Railway service var without an image rebuild silently no-ops. Cheapest guard: a `check:*` script (or CI assertion) that fails when the two configured values disagree. | session 2026-07-28 (FV-04 adversarial workflow, PR #1069); ID #085 after #084 claimed on main | 2026-07-27 | -| #086 | P3 | task | Repository maturity backlog — remaining structural work | **Outcome:** the deferred repository-maturity backlog ships as verified draft PRs, one per structural change. **Canonical runbook:** [`docs/maturity-backlog-workorders.md`](maturity-backlog-workorders.md). **Remaining:** X3 `rag.ts` decomposition; X7 finish the `src/lib` domain reorg; X6 clinical/retrieval/answer coverage floors; X5 ACL-migration consolidation (provider-gated); L1 archive the retired `backfill:*` one-shots + the dead `ci-change-scope` token; L4 ledger rotation; M1 repo-host hardening (maintainer, audit §8). **Next:** X3 on user go-ahead. **Stop:** RAG/retrieval items need the flag + go-ahead; X5 is live-DB provider-gated. | `docs/maturity-backlog-workorders.md`; audit §8/§10; session 2026-07-28 | 2026-07-28 | -| #088 | P3 | task | Watch for union-driver duplication as open PRs merge the repaired ledger | **Outcome:** the 2026-07-28 ledger repair does not resurface as duplicated rows. **Next:** on the first few open PRs that merge `origin/main` after the repair, run `npm run check:branch-review-ledger` on the merged head. Ordinary 3-way merges take main's repaired lines cleanly; only a same-hunk conflict would let `merge=union` keep both the corrupted and repaired copies. **Success:** three consecutive post-repair merges pass the guard. **Stop:** if duplication appears, remove exact duplicates only (the ledger contract allows that) and never rewrite surrounding records. | branch-review-ledger hygiene pass rewrote 226 historical lines; session 2026-07-28 | 2026-07-28 | -| #090 | P2 | task | Upgrade the eslint ecosystem to clear remaining dev-scoped high advisories | **Outcome:** full `npm audit` reports zero high advisories from the eslint toolchain. **Next:** in a dedicated dependency pass, upgrade eslint and its plugin/config set together (npm offers `eslint@10.8.0`, `isSemVerMajor`); residual highs (`@eslint/config-array`, `@eslint/eslintrc`, `eslint`, `eslint-config-next`, `eslint-plugin-import`, `eslint-plugin-jsx-a11y`, plus the advisory's numeric `<=5.0.7` hit on the unused `brace-expansion@1.1.16` / `2.1.2` maintenance lines that still ship an unpatched `main`) cascade from this toolchain. **Success:** `npm run lint` clean, `verify:cheap` green, full-audit highs cleared, no rule-config regressions. **Stop:** an eslint major previously broke `eslint-plugin-react` here — do not bundle into an unrelated PR, and do not force with `npm audit fix --force`. Production `npm audit --omit=dev` is already clean after the exceljs `archiver@8` / `unzipper@0.12.5` overrides on PR #1314; this item is eslint-dev cleanup only. | session 2026-07-28 brace-expansion triage (PR #1314) | 2026-07-28 | -| #091 | P2 | issue | Results band cannot express a partial-source failure | **Outcome:** a favourites/results count is never asserted while some of its sources failed to load. **Detail:** `SearchResultsBandStatus` is a single flat status, so a page fed by several registries has no way to say "3 loaded, 1 failed". `saved-registry-favourites-status.ts:29` works around this with `itemCount > 0 && folded !== "ready" ? "ready" : folded`, and `favourites-command-library-page.tsx` applies the same mask a second time — so a partial failure renders a confident count with no fault indication, which is the exact defect class PR #1316 removed everywhere else. Neither favourites page consumes the true `registryStatus` the fold already returns. **Next:** decide between (a) a `partial` arm on the status union carrying a "some sources unavailable" note beside an honest count, or (b) surfacing `registryStatus` as a separate inline notice and dropping the mask. **Stop:** do not widen the mask to more surfaces before the shape is decided. | PR #1316 review thread PRRT_kwDOSh5Fis6UUf-k; session 2026-07-28 | 2026-07-28 | -| #092 | P3 | task | Refetch pulse deferred on auth-backed registries (privacy invariant) | **Outcome:** a background refresh keeps the prior count visible instead of a skeleton, without weakening identity clearing. **Detail:** the `refetching` status is built in the band and adopted only on `formulation-home-page.tsx`, where the lag is `useDeferredValue` over static data. It is deliberately NOT adopted on `use-registry-records.ts:85`, `use-medication-catalog.ts:76` or `use-differential-catalog.ts:133`, which all clear data on entering loading. `use-differential-catalog.ts:122` states why: "Auth must clear prior identity's matches immediately", and `:164` that "a later retype of any prior query cannot resurrect authorized matches." **Next:** if adopted, guard preservation on identity AND query equality, and pin with a test that an identity change still clears immediately. **Stop:** never hold records across an auth transition. | PR #1316 plan phase 6; session 2026-07-28 | 2026-07-28 | -| #093 | P2 | issue | Next streaming `S:` clone causes Playwright strict-mode violations under CI load | **Outcome:** duplicate-element strict-mode failures stop appearing on loaded CI runs. **Detail:** under full-suite CI load Next.js leaves a hidden duplicate page root in the stream, so a `getByTestId` that is unique locally resolves to 2 elements in CI (seen as `differentials-search-results` on PR #1316, and previously noted on PR #1294 against main). It does not reproduce in isolation, on a single spec, or locally. The documented workaround is to scope the locator to the visible root. **Reproduced locally 2026-07-28** (isolated _production_ build via `run-playwright.mjs`, full `verify:ui`): `ui-tools.spec.ts:563` duplicated `forms-home` and `ui-smoke.spec.ts:3001` duplicated `favourite-row-lithium-monitoring-guideline`; in both, copy 1 is nested under `mobile-composer-reserve-pad`. Both pass when run alone, so it is load/order-dependent, not build-mode dependent — this also corrects an earlier note that CI uses `next dev`; it does not. **Strongest evidence (CI run `30345484316`, 2026-07-28): `ui-overlap.spec.ts:199` on `/` asserted `toHaveCount(1)` successfully and then the same `header#search` locator resolved to 2 a statement later, one of them hidden.** A duplicate that appears _after_ a passing count assertion is a stream/hydration artifact by construction, not a static double mount and not something a CSS or component change can cause. That makes four distinct testids across four specs with the identical shape. **Mitigated, not fixed, on `main` (2026-07-28):** `3a8edb93` rewrapped `gotoHome` in `tests/ui-overlap.spec.ts` to retry count-and-visibility together via `toPass`, so a transient second header no longer trips strict mode there — its own note says "checking count then immediately calling waitFor races that flicker into a strict-mode violation". That hardens one helper; the duplicate root itself is unchanged and other specs remain exposed. **Confirmed pre-existing:** at `631d90d2`, the commit before PR #1316's first commit, that spec already documented "two `header#search` nodes" and "a second transient `header#search` can exist briefly" — so this predates that branch. **Next:** with a full-suite repro now available, bisect the preceding specs to find the state that triggers the second mount, then either scope the shared helpers to the visible root once or fix the mount. **Stop:** do not paper over new occurrences with `.first()` before the duplicate itself is explained. | PR #1316 CI runs; PR #1294 note on main; session 2026-07-28 | 2026-07-28 | -| #094 | P2 | rec | Design-system gates assert structure, not rendered effect | **Outcome:** a style contract cannot pass while the style is inert. **Detail:** PR #1316's accent rail shipped inert because `.search-band` sat in `@layer components`, which loses to Tailwind's utilities layer regardless of specificity — and the test asserted `toHaveClass("search-band")`, i.e. class presence, not effect. Computed style showed `1px rgb(229,231,235)` where `2px rgb(11,111,134)` was intended. The same shape of gap let a rail-colour assertion compare a colour against a width and pass unconditionally. **Next:** for contracts where the visual IS the requirement (rails, forced-colors thickness, tap targets), assert `getComputedStyle` in a Playwright case rather than class names in a DOM test, and add the unlayered-component convention to the design-system contract check. **Stop:** do not convert existing passing DOM tests wholesale; add computed-style proof only where the effect carries the meaning. | PR #1316 Codex P2 finding; session 2026-07-28 | 2026-07-28 | -| #095 | P3 | issue | `PR required` reports failure for concurrency-cancelled jobs | **Outcome:** a red `PR required` means a real failure. **Detail:** the aggregate calls `require_success` on `coverage`/`production-ui`, so any push that supersedes an in-flight run reports `coverage result was cancelled` → exit 1, indistinguishable at a glance from a genuine failure. Eleven such reds were produced on PR #1316 in one session, and `Production UI` never once ran to completion. **FIXED 2026-07-30:** the aggregate now distinguishes the two. `require_success` / `require_skipped_or_success` route a `cancelled` result through a shared `cancelled_error` helper instead of reporting it as a plain failure. **First attempt was invalid and hosted CI was the only thing that caught it:** it passed the workflow-level cancelled status function through an `env:` value, but GitHub allows those functions (success/failure/cancelled/always) ONLY in `if:` conditions, so `ci.yml` failed to parse — the run was named `.github/workflows/ci.yml` rather than `CI` and created **zero jobs**. Valid YAML and invalid Actions schema, so prettier, lint, typecheck, `check:github-actions` and the full unit suite all passed the broken file. Reading each job's own `cancelled` result needs no such expression and loses nothing, since a supersession cancels the upstream jobs anyway. A new guard now fails locally on any status-check function outside an `if:` across `.github/workflows/**` — and it immediately caught a second instance in the explanatory comment written to warn about the first, because expressions are interpolated inside `run:` blocks too. **Second review finding, also real:** the first working version exited on the first non-success, so a run that was cancelled AND broken — `safety` cancelled while `build` had already failed — announced "not a real failure" and hid the break entirely, which is worse than the ambiguity the change set out to remove. It also asserted supersession as fact, wrong for a hand-cancelled run. Now two-pass: every requirement is recorded before anything is reported, **genuine failures win and are all listed**, a concurrent cancellation is demoted to a `::warning::` context line, and the cancelled-only headline states both possible causes instead of asserting one. Mutation-proven: four cases fail against the first-exit version. The message is actionable rather than merely accurate: it names the supersession, points at the newest run for the current head, and tells a reader who finds NO newer run that the run was hand-cancelled, verified nothing, and must be re-run rather than merged past. Measured cost of the ambiguity before the fix: one 2026-07-30 session spent four separate investigations on `::error::changes result was cancelled` / `static-pr result was cancelled`, while PR #1401 merged straight through an unrelated red the repo had learned to ignore. **The tempting fix was rejected as unsafe.** Treating `cancelled` as neutral, or skipping the aggregate via `if: !cancelled()`, would make the red disappear — but GitHub counts a SKIPPED required check as PASSING, so a hand-cancelled run on the current head would become mergeable with nothing verified. The result therefore stays RED; only the diagnosis cost was removed. Guarded by seven cases in `tests/ci-cache-safety.test.ts` that EXECUTE the extracted aggregate script under synthetic job results rather than grepping the YAML (the defect was behavioural, and a structural assertion passed against it — see `#094`), including one that pins `if: always()` plus the wiring, and one that asserts no cancelled required job can ever exit 0. Mutation-proven: three of them fail against the pre-fix aggregate. **Stop:** do not relax `require_success` for genuine failures, and do not make this aggregate skippable — a skipped required check reads as green. | PR #1316 runs 30340972329 / 30341225585; session 2026-07-28 | 2026-07-28 | -| #096 | P2 | task | PR #1316 review follow-ups — adoption-gate coverage closed | **Outcome:** the two remaining PR #1316 review findings are fixed on `main` with tests. **Do not chase the commits.** The seven Codex follow-up SHAs (`ff5b682`, `77cfe12`, `9840ed9`, `81ffb86`, `a5d6561`, `967e16c`, `e544d0d`) are **unreachable** — `git fetch origin ` fails for all seven, no open PR or branch carries them, and none was in the squash merge `4bcfeb90`. They were authored in a sandbox on a branch named `work` and never pushed, so the "follow-up PR metadata" each reported does not exist. **Durable source:** the [PR #1316 review threads](https://github.com/BigSimmo/Database/pull/1316/files) persist and describe every fix with file and line detail; re-derive from those, not from the hashes. **Was live on `main` through 2026-07-28:** the band adoption gate skipped query-backed root modes — `modeHrefToPagePath` returned null for `pathOnly === "/"`, so `/?mode=prescribing` and Documents never entered the route inventory and the root dashboard page was unchecked. Closed on PR #1394 (see Adoption-gate gap closed below). **Already fixed independently, no action:** favourites hub counts (`libraryCountsTrusted`), the document-search status derivation, the 401 session-expiry path, and the record-path duplicate notice. **Corrected 2026-07-28 — the Therapy Compass retry-waiter finding is NOT a live defect.** `use-therapy-data.ts:68` `retryWaitersRef` is genuinely unscoped, so a newer request can settle an older retry's promise, but no caller observes it: `useTherapyData` lives in the long-lived `TcProvider` (`bindings.tsx:206`) and `requestKey` derives only from `screen`, so it cannot change without the screen changing; the sole awaiting caller is the band's `AsyncButton` inside `search-screen.tsx:33`, which unmounts on that transition, and `workspace.tsx:36` uses `onClick={b.retryData}` which discards the promise. An earlier note here claimed a visible "Retry stops being busy" symptom — that was wrong and is retained only as the correction. It becomes real if a future caller ever awaits `retry()` from a control that survives a `requestKey` change. **Adoption-gate gap closed 2026-07-29.** Root-path and href-less modes now resolve to `src/app/(search-app)/page.tsx`. Closing it surfaced two further defects in the same gate that the original finding did not name: the hand-rolled walk was capped at two import hops while the root route's real chain is four (`layout -> shared-search-app-shell -> global-search-shell -> ClinicalDashboard -> document-search-results`), and it followed neither `layout.tsx` — which is where that route's band actually comes from, since the page renders only a pass-through — nor `dynamic(() => import(...))`, which is how the dashboard code-splits its mode workspaces. All three are fixed together with a bounded BFS; each was verified load-bearing by reverting it and watching the gate fail. **Stop:** not user-facing; do not let it block a release, and do not add waiter keying without a reproducer showing a still-mounted control whose busy state clears early. | PR #1316 review sweep; session 2026-07-28 | 2026-07-28 | -| #097 | P3 | issue | Gitleaks reports a false red when the PR head moves mid-run | **Outcome:** a red `Gitleaks` means a secret was found, not that someone pushed. **Detail:** on 2026-07-28 the job triggered for head `9bace1d1` checked out that merge ref, then queried the API and built its range against head `40278453` — pushed seconds later and absent from the checkout. Git rejected the range (`fatal: Invalid revision range`), so it scanned `~0 bytes`, logged `no leaks found in partial scan`, and exited 1. The scan did not run at all, which is worse than a normal failure because the natural reading is "noise, ignore it". It cleared on its own once the head stopped moving (`23 commits scanned`, `~198 KB`, `no leaks found`). Both range endpoints resolve in any complete checkout — verified locally against the branch and the PR merge ref — so this is not a `fetch-depth` problem. **Next:** pin the scan to a range the job controls (`base.sha`..the checked-out head) instead of re-querying the API mid-run, so a concurrent push cannot invalidate it. **Related:** same push-churn family as #095. **Stop:** do not weaken the gate to a soft-pass; the fix is a stable range, not a tolerated failure. | PR #1316 runs 30344938800 / 30346797225; session 2026-07-28 | 2026-07-28 | -| #098 | P2 | task | Offline round-trip budget harness for the hot routes | **Outcome:** per-scenario Supabase round-trip counts are pinned by a test, so an extra round trip on a hot path is a red gate rather than an inference. **Done 2026-07-29:** the measurement gap is closed — `Server-Timing` now covers `auth`/`ratelimit`/`scope` on `/api/answer`, `auth`/`ratelimit`/`search`/`total` on `/api/search`, and `auth`/`ratelimit` on `/api/answer/stream` (previously the route the UI actually calls emitted no header at all). Headers flush before the first SSE frame, so in-stream stages cannot reach a header and must NOT be routed through the governed `progress`/`final` contract. `tests/answer-route-preamble.test.ts` pins admission-before-scope (no scope call while the limiter is pending or after a deny) and the client-disconnect abort signal. **Next:** generalise it — wrap the Supabase client in a counting proxy and assert per-scenario query budgets over the existing offline suites — `scripts/eval-rag-offline.mjs`, `scripts/test-rag-offline.mjs`, `scripts/rag-offline-contract.mjs` and the contract fixture `scripts/fixtures/rag-offline-contract-tests.json`. **An earlier version of this row named `test-cache-path.mjs` and `check-rag-fixtures.mjs`** (corrected 2026-07-29, PR #1377 review, matching the audit's own retraction): neither exercises a RAG request — the first computes Vitest/TypeScript cache paths, the second only validates fixture manifests — so building the harness on them would have counted nothing. Sequence before #099 and #101: it is the enabler and the standing guard. No providers, no DB. | `docs/audit/latency-audit-2026-07-28.md` measurement plan; `src/lib/server-timing.ts`; `src/lib/answer-stream-contract.ts:18-21` | 2026-07-29 | -| #099 | P2 | task | Remove the remaining fixed per-request round trips | **Outcome:** the answer path stops paying avoidable per-request Supabase round trips. **Done 2026-07-29:** shared-cache-hit promotion deferred off the response path with its mid-request staleness guard intact and documented (`rag.ts:3234`, `rag-cache.ts`); scope resolution overlapped with the rate-limit RPC, signal threaded so a client disconnect finally cancels its paginated queries (`answer/route.ts`). **REFUTED on PR #1377 review — do not retry:** the same pass also overlapped scope with the rate-limit RPC and aborted it on deny, claiming the limiter could "deny for free". It cannot. With caller-supplied `filters` or explicit ids, scope passes its zero-query early returns (`search-scope.ts:242,253`) into the paginated `documents` loop at `:269`, and an `AbortSignal` cancels the client request without un-executing a statement Postgres already began — so throttled traffic kept burning database capacity while collecting 429s, against `capacity-review.md:106-113`'s first-soft-failure warning. Scope is behind admission again, pinned by `tests/answer-route-preamble.test.ts`. Re-attempting the overlap requires a non-database admission gate ahead of the durable limiter first. **Remaining:** (a) the 8 `setCachedSearch` awaits — deferring changes `throwIfAborted` semantics and widens a real mutation window because the clone happens after an `await`, so each branch needs discharging individually; (b) batch the anonymous subject+global rate-limit pair, which needs a NEW atomic RPC modelled on `consume_summary_rate_limits_atomic` and cannot be called until the operator applies it — `Promise.all` is the WRONG fix because it consumes the global bucket even when the subject bucket already denied; (c) stop the proxy and route handler resolving identity twice per authenticated request — no in-process memo can do this (different `Request` objects), so the proxy must forward unspoofable verified claims via a header it controls. Cross-references #011: halving auth resolutions eases the ~10-connection Auth cap that `capacity-review.md:106-113` calls the first hard failure. | `docs/audit/latency-audit-2026-07-28.md` L1-1/L1-3/L1-4; `src/lib/api-rate-limit.ts:276-282`; `src/proxy.ts:125` | 2026-07-29 | -| #100 | P2 | rec | Buffered answer generation has no incremental verified delivery | **Outcome:** a clinician sees verified answer content before the whole generation completes. Highest-leverage latency finding in the 2026-07-28 audit: generation is buffered (`openai.ts:465`) and delivered in ONE `final` SSE frame, so time-to-first-content equals total latency — a strong answer inside its 25 s SLO still shows a blank panel for 25 s. The 15 s `sse-heartbeat` exists because that silence routinely exceeds 15 s; it instruments the defect rather than fixing it. **Naive token streaming is REFUTED, not merely unbuilt:** `answer-stream-contract.ts:18-21` removed `token`/`revising` deliberately because a rolling deployment would "re-expose unvalidated clinical prose", and raw tokens bypass the numeric-faithfulness gate the 2026-07-01 audit filed as H1. **Only admissible shape:** progressive disclosure of already-verified units (evidence/sources at retrieval-complete, then per-section after that section clears verification) over the existing whitelisted `progress` event. Needs a clinical-governance decision plus a canary pair. Also add the refutation to `docs/rag-behaviour/refuted-approaches.md`. Cross-references #021. **Stop:** do not re-land `token` streaming. | `docs/audit/latency-audit-2026-07-28.md` L0-1; `src/lib/answer-stream-contract.ts:18-21`; `src/lib/sse-heartbeat.ts` | 2026-07-29 | -| #101 | P3 | rec | Canary-gated retrieval parallelisation candidates | **Outcome:** independent retrieval stages stop running serially, proven by a live canary pair. Candidates: metadata/memory/visual hydration triples repeated on four branches (`rag.ts:2460,2493,2521` and three more) while `rag.ts:2751-2804` already parallelises three RPCs in one `Promise.all`, so the omission is inconsistency rather than intent; the nested `await`-in-loop scope enumeration (`search-scope.ts:202,328`); typeahead results never cached (`rag.ts:2698-2711`); 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 needs 36/36 retrieval plus recall 1.0 and zero per-case rr regressions. Distinct from #001 (semantic rerank). Resolved #075 and #083 are the precedents for why these are gated rather than free. **Stop:** needs the #098 harness and explicit canary approval first. | `docs/audit/latency-audit-2026-07-28.md` L2-1/L2-2/L2-8/L1-5 | 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. The documents-list and `(status,id)` uses stay ordering-safe; the RAG-path index is canary-gated, 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 | -| #103 | P2 | issue | Wide table-facts trigram index missing from `schema.sql` | **Outcome:** the migration chain and `schema.sql` agree on `document_table_facts` trigram indexes. `supabase/migrations/20260714190000_document_table_facts_trgm_idx.sql` creates a wide 5-column trigram index that is **absent from `supabase/schema.sql`**, so local replay and the live database can diverge. Distinct from #102: different owner and verification path. **Next:** confirm whether the wide index exists live, then take one of exactly two routes — **retained:** mirror `document_table_facts_text_trgm_idx` into `supabase/schema.sql` beside the narrow one and regenerate `drift-manifest.json`; **redundant:** drop it through a new forward migration, never by deleting `20260714190000`. **`drift-allowlist.json` is NOT a third option** (PR #1377 review): its own header scopes it to _"Known live-vs-`schema.sql` divergence"_, so it can silence a live drift finding but cannot reconcile the migration chain with the mirror — a fresh `supabase db reset` still runs `20260714190000` and creates the index while `schema.sql` still omits it, leaving this row's stated outcome unmet. **No offline gate catches this today:** the migration↔`schema.sql` parity test (`tests/drift-detection.test.ts:59-68`) only asserts one migration's `schema_drift_snapshot` function definition, not an index inventory — which is why this sits open rather than red in CI, and why a replay-to-schema inventory comparison is the check that would have caught it. Note the narrow `document_table_facts_title_row_param_trgm_idx` (`schema.sql:6425`) is the one the effective RPC expression (`:6726`) actually matches, so the wide index may be genuinely redundant — do not drop it without live scan evidence, per the monitored-not-auto-fixed index policy. | `docs/audit/latency-audit-2026-07-28.md` limitations; `npm run check:drift` | 2026-07-29 | -| #104 | P3 | rec | CORRECTION — the worker's triple image read is deliberate, not debt | **Outcome:** a future audit does not re-file this a third time. The 2026-07-28 latency audit listed L4-2 (`worker/main.ts` `readFile`s each extracted image up to 3x per document — hash, caption on cache miss, upload) as "CONFIRMED with no fix evidence", carried forward from the 2026-07-01 audit's `L11`. **That was wrong.** The 2026-07-01 disposition table already recorded it as a deliberate peak-memory trade-off, and the rationale is documented in place at `worker/main.ts:866-869`: holding every extracted image Buffer for a document with hundreds of multi-MB page images would multiply the worker's peak memory, and disk I/O is the cheaper resource for a background pipeline. The three reads (`:872`, `:1034`, `:1129`) are real but accepted. **Next:** none — revisit only if ingestion throughput becomes a measured complaint AND a bounded-buffer design is proposed. **Stop:** do not "fix" this by caching buffers; that trades a decided memory ceiling for disk I/O nobody has measured as a problem. | `docs/audit/repo-audit-2026-07-01.md` L11 + disposition table; `docs/audit/latency-audit-2026-07-28.md` L4-2 retraction | 2026-07-29 | -| #105 | P3 | task | Verify the `#017`-exempt client latency wins in a browser | **Outcome:** the two zero-payload client fixes are confirmed in a real browser. `#017` gates _payload_ decisions (#012/#013/#016 are all byte-count items); a `loading` fallback ships zero bytes and a resource hint ships ~60, so neither can be justified or refuted by a Lighthouse number — that is why these were not held behind #017. **Implementation shipped 2026-07-29; browser verification still PENDING:** 10 of 11 `ssr:false` dashboard surfaces had NO `loading` fallback and rendered nothing between HTML arrival and chunk execution — all now use the shared `LoadingPanel` (`role="status"` + accessible label); Supabase `preconnect`/`dns-prefetch` added, since `AuthProvider` awaits a cross-origin `getUser()` on mount that every auth-gated fetch queues behind and there were no resource hints anywhere in `src/`. Shipped with `verify:cheap` + `verify:pr-local` only. **Next:** run `npm run verify:ui` once the heavy-run lock is free, and confirm the preconnect appears in `` on a live page. **Stop:** the two sidebar dialogs are intentionally excluded — they mount on open, so a fallback would render into a closed dialog. | `docs/audit/latency-audit-2026-07-28.md` L3-4/L3-5; `src/components/clinical-dashboard/clinical-dashboard-lazy.tsx` | 2026-07-29 | -| #106 | P2 | rec | Ingestion worker and indexing agent are verified by grepping their own source | **Outcome:** the ingestion worker and indexing agent are verified by executing code, not by asserting on their own source text. **Detail:** measured 2026-07-29 via `npm run test:coverage` — `worker/main.ts` (2,015 lines) and `supabase/functions/indexing-v3-agent/index.ts` (1,966 lines) each report **0% executed lines**; no test imports either module. Both are covered only by `readFileSync` + `toContain` assertions in `worker-safe-logging.test.ts`, `worker-visual-capture.test.ts` and `document-metadata-merge.test.ts`, which pass whenever a string is present and break on harmless refactors; `document-metadata-merge.test.ts` additionally reimplements the SQL deep-merge in TypeScript and tests the reimplementation rather than the worker. Area totals: `worker/` 18.6% lines, `supabase/functions/` 4.5%. **Next:** continue the extraction pattern that already works here — `indexing-v3-agent/behavior.ts` (167 lines, 96%) and `ingestion-worker/auth.ts` (30 lines, 90%) — pulling the highest-risk decision points out of `worker/main.ts` (job claim/retry, generation commit, failure classification) into importable modules with executing tests, retiring the matching source-text assertion as each lands. Roughly cost-neutral: each extracted test replaces a grep assertion. **Stop:** do not try to make the 2,000-line entrypoint importable in one pass; extract incrementally and keep each step green. | session 2026-07-29 test-coverage analysis | 2026-07-29 | -| #107 | P2 | rec | Component state matrices are the largest untested surface | **Outcome:** loading / empty / error / disabled states on interactive components are covered by executing tests, not only by E2E happy paths. **Detail:** measured 2026-07-29 — production components (excluding mockups) sit at **38.2% lines / 22.8% branch** across 12,602 lines, with **83 of 208 files at zero executed lines**; there are 51 `.dom.test.tsx` files against 195 components. Playwright does visit these routes, so they are smoke-covered, but branch coverage is where the state matrix lives and smoke journeys rarely reach it. Worst by uncovered lines: `global-search-shell.tsx` (7%), `mode-action-popup.tsx` (21%), `answer-content.tsx` (27%), `document-search-results.tsx` (32%), `universal-search-command-surface.tsx` (39%), `master-search-header.tsx` (43%). A concrete first target with clinical meaning: `calculator-ui.tsx` now covers all exported scoring logic, but `seedCheckboxDefaults`, `toggleCheckboxAnswer` and `selectOptionAnswer` stay uncovered because they are module-private and only reachable through React event handlers — `seedCheckboxDefaults` is what makes an all-negative CAGE / SAD PERSONS screen read as a valid 0 rather than incomplete, so a regression there is a false-negative risk. **Next:** treat as a per-PR convention rather than a backfill push — `docs/testing.md` already prescribes the state matrix, so the gap is enforcement. Start with `global-search-shell.tsx`, which `docs/search-chrome-behaviour.md` treats as a contract surface. Keep additions in the jsdom tier (measured ~0.54s per file) instead of new Playwright journeys (~231 production journeys already run serially at `workers: 1` against a 45-minute CI budget). **Stop:** do not chase the coverage percentage by backfilling low-risk components; the re-ratcheted broad floor in `vitest.config.mts` holds the line. | session 2026-07-29 test-coverage analysis | 2026-07-29 | -| #108 | P3 | task | Five verified-landed remote branches await deletion (blocked in-session) | **Outcome:** the five branches whose content is fully on `main` are gone. **Detail:** a full-history branch-cleanup review on 2026-07-29 verified these introduce an empty diff against `main` and back no open PR: `claude/clinical-kb-pwa-review-asi3wb` @ `df29f311b60cadf8e43bf51283a9d6f496b295e3`, `claude/dazzling-blackwell-f348d0` @ `c9bec8f9dce38cb647de9aa64ebf08bf7823a524`, `codex/document-reader-condensed-view` @ `b5cdbf301d517239ffe9ed941b9ebe809aea0bfd`, `cursor/page-anchored-search-composer-30ee` @ `7ff134ca7f614db527b8d142676640305533669d`, `cursor/pr-1379-babysit-ledger-9365` @ `be2de03f855cb7fdfccea4bb74d05eb4c9bf6c61`. **The HEADs are recorded because they are unrecoverable once the refs are deleted:** `hasCompletedCleanupReview` (`scripts/sweep-branch-ledger.mjs:83-93`) matches a completed row on branch name AND HEAD together, so without them no later operator could ever append the required `branch-cleanup` rows. Each candidate now also has its own `branch-cleanup-deletion-pending` ledger row keyed to its own HEAD. Deletion could not be performed: the session git proxy rejects ref deletion with **HTTP 403**, and the GitHub MCP toolset exposes no delete-branch capability. The remaining 87 were deliberately NOT cleared — their touched files still differ from `main`, which is the conservative direction. **Next — ORDER MATTERS:** append the completed `branch-cleanup` row for each branch FIRST, from a checkout that still has the objects, and only then delete the refs. `resolveHead` (`scripts/branch-review-ledger.mjs:155-167`) runs `git rev-parse --verify ^{commit}` and refuses to append a HEAD that is not a commit in the repository, so the reverse order is unexecutable once the refs are gone and their objects are pruned. The `n/a - ` escape hatch does not help here: `hasCompletedCleanupReview` only matches a 7-40 char hex HEAD, so an `n/a` row would leave the branch resurfacing in every future sweep. **Progress 2026-07-30 — the prerequisite is DONE; only the deletion is left, and it needs a caller who can delete refs.** Re-verified against the live remote first, which mattered: `claude/clinical-kb-pwa-review-asi3wb` and `claude/dazzling-blackwell-f348d0` are **already gone** (surfaced by `git remote prune`, deleted by someone with the permission this session lacks), so the list is three, not five. The remaining three are still at exactly the recorded HEADs, and the proof was re-run and strengthened: for each, `tree(tip) == tree(merge-base)` byte-for-byte, so the branch nets zero content change from where it forked and nothing on it is absent from `main`. That is stronger than either check the guide names — `--cherry-pick` still reports 13/6/4 patch-unique commits on them, which is the squash-merge false positive, and none is an ancestor of `main`, so `--merged` would also miss them. A completed `branch-cleanup` row keyed to its own HEAD is now appended for each of the three (`npm run check:branch-review-ledger` passes at 1272 records), so the ORDER MATTERS constraint above is satisfied and the refs can now be deleted safely at any time. Deletion re-attempted and still blocked: `git push origin --delete` exits 1 with **HTTP 403** from the session git proxy (`recentRelayFailures` empty, so it is credential scope, not a relay fault), and a tool search confirms the GitHub MCP set exposes `create_branch` but no delete-branch capability. **Next:** delete these three refs from the GitHub UI or any session whose credentials permit ref deletion — `codex/document-reader-condensed-view`, `cursor/page-anchored-search-composer-30ee`, `cursor/pr-1379-babysit-ledger-9365`. Nothing else is required first. **Stop:** do not widen to the other 87 without per-branch content proof. | session 2026-07-29 branch cleanup; ledger `branch-cleanup-deletion-pending` @ 855aa291 | 2026-07-29 | -| #109 | P2 | issue | Remote sessions clone shallow, silently invalidating all branch/merge analysis | **Outcome:** no session draws branch conclusions from a truncated history. **Detail:** on 2026-07-29 this repo's remote session had `git rev-parse --is-shallow-repository` = **true** with only **74** commits of `origin/main` (full history is 2829). Every merge-base, `--cherry-pick`, and ahead/behind number computed in that state was wrong: local `main` reported `ahead 52` and `refusing to merge unrelated histories` (it is actually 0 ahead with a shared base), and an all-branch sweep wrongly showed **90 of 91** branches as carrying unmerged work. Acting on that would have meant either deleting live branches or abandoning cleanup entirely. `git fetch --unshallow` corrected both. **FIXED 2026-07-29:** `scripts/sweep-branch-ledger.mjs` now refuses outright on a shallow clone via the exported `shallowCloneRefusal`, printing no inventory and exiting 1 in both text and `--json` mode, before the fetch and before any branch maths. `docs/branch-cleanup-guide.md` §Safety Rules gains the `is-shallow-repository` precondition ahead of its numbered steps, because the raw `git` commands it documents have no such guard. Proven in a real `--depth 1` clone: unguarded the sweep exited **0** and named the live checked-out branch a deletion candidate with "no unique patch content"; guarded it exits 1 with the `--unshallow` remedy. Five cases in `tests/repo-hygiene.test.ts` cover both directions, including that the string `"false"` (truthy) must NOT be read as shallow — the way this guard could fail dangerously in reverse. **Hardened in review:** an indeterminate `is-shallow-repository` result (empty output from a swallowed `git` failure) is now refused as its own failure rather than treated as complete, and the same refusal was extended to `scripts/reconciliation-preflight.mjs`, which reports its own merge-base-derived ahead/behind. That guard then had to move OUT of the preflight CLI and INTO the exported `collectReconciliationState`, because `buildReconciliationEvidencePack` calls the collector directly and stamps `status: "complete"`: in a `--depth 1` clone the guarded CLI exited 1 while the evidence-pack command exited 0 and persisted shallow ahead/behind as completed evidence. The collector now throws `UnverifiedHistoryError` (`code: "history-not-verified"`), so every current and future caller fails closed by default instead of by remembering to ask; the CLI catches it only to keep the `--json` envelope. Regression cases live with each entry point (`tests/reconciliation-preflight.test.ts`, `tests/reconciliation-evidence-pack.test.ts`) and build a real `--depth 1` clone, asserting `is-shallow-repository` is `true` first so a git behaviour change cannot make them pass vacuously. **Second failure mode, found in review after the first fix landed: complete history is not complete branch coverage.** `git clone --depth 1` implies `--single-branch`, pinning `remote.origin.fetch` to the one cloned branch; `git fetch --unshallow` converts the history so `--is-shallow-repository` reads `false` and the shallow guard passes, but it does not widen the refspec, and an ordinary `git fetch origin` respects the narrow one. Measured in a `main`+`feature` fixture: after unshallowing, `git ls-remote --heads origin` listed both while `refs/remotes/origin` held only `origin/main`, and the sweep exited **0** reporting `"branches": []` — and an empty inventory is not a safe failure, since it reads as "nothing to clean up" and a missing `origin/main` makes every `rev-list` fail into `0/0`, i.e. every branch a deletion candidate. Fixed both ways: the sweep's fetch now passes an explicit `+refs/heads/*:refs/remotes/origin/*` (repairing coverage without rewriting the operator's config), and `branchCoverageRefusal` refuses when neither the configured refspec nor a completed wildcard fetch establishes coverage — `--no-fetch`, offline, or a failed fetch. Its remedy is deliberately `git remote set-branches origin '*'`, not `--unshallow`, which fixes history and does nothing here. **Two further routes to the same empty-inventory answer, both found in review, both from checking only half of the refspec.** (1) The DESTINATION matters as much as the source, because the sweep enumerates `refs/remotes/origin` and nothing else: with `+refs/heads/*:refs/remotes/upstream/*`, `refs/remotes/upstream` held `upstream/main` and `upstream/feature` while `refs/remotes/origin` stayed empty and the sweep exited **0** with `"branches": []`. (2) Git substitutes the matched suffix into ``, so a `refs/*` source nests one level deeper: `+refs/*:refs/remotes/origin/*` writes `refs/remotes/origin/heads/main`, `origin/main` then does not resolve at all, every comparison fails into `0/0`, and the sweep exited **0** naming both `heads/feature` and **`heads/main`** as deletion candidates — a green run recommending the deletion of `main`. Coverage from config therefore requires exactly `refs/heads/*` to `refs/remotes/origin/*`; a completed wildcard fetch still establishes coverage by itself, since the sweep passes that destination explicitly. **Stop:** never delete a branch, or report a branch as unmerged, from a shallow clone, a single-branch refspec, or a refspec whose destination is not `refs/remotes/origin/*`. | session 2026-07-29; `docs/branch-cleanup-guide.md`; `scripts/sweep-branch-ledger.mjs` | 2026-07-29 | -| #110 | P3 | task | Design-system project token manifest lags its stylesheet | **Outcome:** the claude.ai/design token panel matches the shipped stylesheet. **Detail:** PR #1375 pushed a recompiled `_ds_bundle.css` (Clinical Sky, `--e0`–`--e4`, 4px radius grid, `--tracking-eyebrow`/`--leading-display`/`--leading-prose`) plus the four changed guideline docs to project `08d6f126`, but `_ds_manifest.json` is converter-generated and still advertises `--text-4xs: 0.5rem`, the old `--radius-lg/xl/2xl` values, and `--tw-leading`/`--tw-tracking` entries scoped to the retired `.leading-[…]` / `.tracking-[0.08em]` utilities. Rendering is correct; only the token inventory lags. Hand-editing was rejected — `kind`/`scope`/`annotation` are converter heuristics and a wrong panel is worse than a stale one. **Next:** in a session with the `/design-sync` skill, `npm ci`, then `npm install --prefix .ds-sync --no-save --package-lock=false esbuild ts-morph @types/react @tailwindcss/cli geist`, read `.design-sync/NOTES.md`, and run `resync.mjs --remote` so bundle and manifest regenerate together. **Stop:** do not hand-author `_ds_manifest.json`; the converter is not a published npm package and ships with the skill. | PR #1375; `.design-sync/NOTES.md`; project `08d6f126` (`_ds_needs_recompile` marker present) | 2026-07-29 | -| #115 | P3 | rec | Band adoption gate treats a discovered import as rendered | **Closed 2026-07-30.** `tests/search-results-band-adoption.test.ts` no longer asks "does this file mention the band?" but "does anything the route actually mounts reach it?". It parses each module with `@babel/parser` into a small graph — exported name to local declaration, local to the identifiers its body references, and which locals render the band — then walks from the route's default export, carrying at each hop the set of exports the importer mounts. So a static `import { X }` is followed only when `X` is reachable from a mounted declaration; `dynamic(() => import("…").then((m) => m.Named))` follows only that binding, which is how the code-split dashboard workspaces are written; a bare `import "…"` is not followed at all; `export { X } from "…"` is followed only when the importer wants `X`; and `export * from "…"` never supplies a default, so a page whose importer wants only the default gets no hop from it. **Why the redesign rather than more patches:** six false greens were reported in one day (unrendered import, `export { X }`, `export { X } from`, `export *`, bare side-effect import, JSX in an unmounted helper, and a lazy import reaching every sibling export), all one defect — presence is not reach. Two of the six were introduced by an earlier patch to the same walker. **Verified:** all five production search routes still reach the band; gutting `(search-app)/services/page.tsx` and `tools/page.tsx` to `
` each reports an orphan; fourteen temp-dir fixtures cover both directions, and the two guarding the new mechanisms were confirmed to fail against the prior behaviour by targeted mutation (presence-based band check, and following bare imports). Residual: reachability is per module, so a mounted declaration referencing an identifier anywhere in its body counts, and control flow inside it is not modelled. | PR #1400; session 2026-07-30 | 2026-07-30 | -| #116 | P2 | issue | An unmergeable PR runs no CI at all, with no signal that it stopped | **Outcome:** a PR that has silently stopped being tested says so. **Detail:** on 2026-07-30 PR #1400 ran **no** `CI`, `Gitleaks` or `Semgrep` workflow across three consecutive pushes, and nothing anywhere said why. Cause: the PR had a real content conflict with `main`, so GitHub could not build `refs/pull/1400/merge`, and every `pull_request`-triggered workflow is skipped in that state. `pull_request_target` ones (`PR Policy`) still ran, and CircleCI posted failure ~3s after each push - faster than a checkout, so no step executed. The symptom reads as "CI is broken" or "my tests fail", and about 40 minutes went into replicating CI steps locally (all green) before the cause was found. **Diagnostic:** compare check-run counts with another open PR (3 vs 16-19), list workflow runs and note only `pull_request_target` fired, then confirm with `git merge-tree --write-tree origin/main HEAD`. **Next:** make staleness visible instead of silent - e.g. a `pull_request_target` job that fails when `mergeable_state` is `dirty`, so a conflicted PR shows one red check naming the conflict rather than an empty check list. `npm run sync:pr-branches` covers the behind-but-clean case only. **Stop:** never conclude "CI is failing" from a missing check - the absence of a check is not a failing check. | PR #1400; session 2026-07-30 | 2026-07-30 | -| #120 | P2 | issue | `verify:phone-chrome` exits 0 while reporting failed browser tests | **Outcome:** the phone-chrome gate cannot report success when no test executed. **Evidence 2026-07-30 (PR #1396):** `npm run verify:phone-chrome` finished with **exit code 0** while its own output ended `13 failed`. Every one of the 13 failed at browser launch (`browserType.launch: Executable doesn't exist ... chrome-headless-shell`), so zero assertions ran, yet the gate returned success. This is the green-when-broken case `AGENTS.md` warns about ("Exit code 0 alone is not proof") realised in a gate that is supposed to be the proof. **Next:** make the runner propagate the Playwright exit status, and fail loudly on a launch error rather than treating a zero-test run as a pass. Stop: do not paper over it by grepping output in the caller — the runner owns the status. | `scripts/verify-phone-chrome.mjs`; `scripts/run-playwright.mjs` | 2026-07-30 | -| #121 | P3 | issue | Container Playwright browser build lags the pinned client | **Outcome:** browser gates run in remote sessions without hand-patching. **Evidence 2026-07-30:** the repo's Playwright client resolves headless-shell build `1234`; the container image provides `1194` at `/opt/pw-browsers`, so every browser test fails at launch. Worked around in-session by symlinking `chromium_headless_shell-1234/chrome-headless-shell-linux64/chrome-headless-shell` to the `1194` `headless_shell` binary plus its sibling resources — container-local, nothing committed, and it disappears with the session. `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1` means the mismatch cannot self-heal. **Next:** decide whether the image pins the browser build or the repo pins a client matching the image; until then any remote session claiming browser proof must state which it used. | `docs/testing.md`; container `/opt/pw-browsers` | 2026-07-30 | -| #122 | P2 | issue | `ci/circleci: verify` fails on every branch and its log needs operator access | **Outcome:** the CircleCI status is trustworthy signal again, or it stops reporting. **Evidence 2026-07-30:** `ci/circleci: verify` was `failure` on every open PR sampled — #1396, #1407, #1405, and #1400, which is a **docs-only** `AGENTS.md` change — plus #1403's head. It is sharply bounded in time: #1393's head **passed** at build 638 (03:57), and builds 645 (04:09) onward all failed. The job's entire contents were mirrored locally on PR #1396's exact tip and every part is green — `format:check` clean, `lint` exit 0, `typecheck` exit 0, `npm run test` `432 passed (432)` / `4473 passed \| 4 skipped`, and the PyMuPDF-gated `tests/pdf-extractor.test.ts` (the repo's only `process.env.CI`-gated tests) `6 passed (6)` under a locally built `PyMuPDF==1.28.0` venv with `PYTHON_BIN` set exactly as `.circleci/config.yml` does. So the failure is in the job's **environment**, not repo code. Around 40 builds fired in ~40 minutes across 8 open PRs in that window, so credit/quota exhaustion is the leading hypothesis — **explicitly unverified**: the CircleCI project is private and no CircleCI token is available to any agent session, and `api/v1.1/project/gh/BigSimmo/Database/` returns `Build not found` unauthenticated. **Next:** an operator opens one failing build and reads the failing step; if it is quota, either raise it or remove the CircleCI status so it stops masking real reds. **Stop:** do not chase this from a PR branch — it is not branch-specific, and no agent can read the log. Do not go looking for a CircleCI token. | `.circleci/config.yml`; PR #1396 session 2026-07-30 | 2026-07-30 | -| #125 | P3 | issue | `ui-therapy-nav-scroll.spec.ts` cites a spec file that does not exist | **Outcome:** a reader following the comment finds the coverage it names, or the comment stops naming it. **Detail:** the spec's comment points at `mode-nav-bar-anchoring.spec.ts`, left behind when PR #1390 moved `ModeNav` into the universal header; no such file exists anywhere in the repo. Harmless at runtime, but it sends the next person looking for anchoring coverage to a file that is not there, and it is the kind of stale pointer that makes a reader distrust the surrounding comments. Either repoint it at the coverage that actually exists (`ui-mode-nav-density.spec.ts`, landed in #1405) or delete the reference. | Noticed and explicitly deferred in PR #1405's body to keep that diff scoped to `#113` | 2026-07-30 | +| ID | Pri | Type | Summary | Detail / next action | Source | Added | +| ---- | --- | ----- | -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ---------- | +| #059 | P1 | task | Verify containment of every credential reported exposed in chat | **Outcome:** every reported exposed credential is rejected or retired. **Next:** in approved security windows, verify and revoke or rotate the GitHub token, OpenAI key, Supabase service-role JWT, database password, and E2E credential; create replacements only when required and update only intended secret stores. **Success:** provider evidence confirms the old credentials cannot authenticate, replacements are distinct and minimally scoped, presence/readiness checks pass, and secret scans remain clean. **Stop:** no provider or secret-store action without approval; never print or paste values into Git, logs, issues, or chat. | session 2026-07-24 security reconciliation; AI Agent Target Manifest | 2026-07-24 | +| #065 | P2 | task | Complete the paused compact document source-text accordion | **Outcome:** the document viewer uses compact nested disclosures while retaining complete text, citation/search navigation, print behavior, and composer clearance. **Next:** only when the user explicitly resumes, reconcile `codex/chat-document-text-accordion-7cb4` with current `main` and complete the focused 320/390/1280 px tests. **Success:** default disclosures are closed; deep links and search open only the active passage; printing expands/restores state; no overflow. **Verify:** focused document-viewer Playwright, `verify:cheap`, `verify:ui`, and static production-readiness. **Stop:** remain paused until explicit user return; no provider calls. | paused document-viewer task; `codex/chat-document-text-accordion-7cb4` | 2026-07-24 | +| #001 | P2 | task | Semantic reranking still gated off | `RAG_SEMANTIC_RERANK_ENABLED=false` from PR #901. Do not enable until the provider-backed 36/36 retrieval-quality gate **and** an ambiguity-focused canary are explicitly approved and recorded. | `docs/process-hardening.md` (Semantic reranking rollout debt); PR #901 | 2026-07-21 | +| #053 | P1 | task | Execute cross-border privacy/legal package | Execute OpenAI and Railway DPAs; decide ZDR and Australian data residency; obtain prompt-cache behavior in writing; review subprocessors; obtain APP 8 and APP 5/1 counsel sign-off. Do not represent the release as privacy-approved or alter final public privacy wording before sign-off. | `docs/openai-cross-border-basis.md`; `docs/privacy-impact-assessment.md` | 2026-07-24 | +| #055 | P2 | task | Run one exact-SHA full release and PR gate | Before the next full-confidence release/handoff, record the candidate/PR SHA and run the local/provider release gates, Firefox/WebKit, required hosted CI, and actionable GitHub review-thread closure once. Stop at the first actionable failure and rerun only the repaired smallest gate. | `docs/launch-operator-runbook.md`; `docs/codex-review-protocol.md` | 2026-07-24 | +| #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 23 repository migrations behind. 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/capacity-review.md` | 2026-07-24 | +| #005 | P3 | rec | `finalScore` saturates at clamp ceiling | Base + ~40 stacked boosts routinely exceed 1.0, so strong matches tie at 1.0 and order by an arbitrary `document_id` tiebreak. If ranking is ever revisited, break ties by the **pre-clamp** score rather than raising the `[0,1]` ceiling (downstream gates assume `[0,1]`). Ordering already sorts by the unbounded pre-clamp `rankScore` (`clinical-search.ts:1735,1927,1950-1955`), so the clamp confines only the reported confidence value, not result order. Not a defect on the current golden set; any change here is a protected RAG surface (canary required). | `docs/rag-hybrid-findings-and-todo.md` P1 item 4; `src/lib/clinical-search.ts:1735` | 2026-07-21 | +| #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 | `build:analyze`: `/specifiers` ships `specifiers-search-index.json` (~180 KB parsed), `/forms` ships `forms-catalog.json` (~132 KB), `/formulation` ships `formulation-content.json` (~52 KB, client-side local search — needs index/full split or a search endpoint, architectural). All route-scoped (not initial bundle). Also `*-mockups.tsx` (~100 KB across chunks) build though `/mockups` 404s in prod — exclude from the prod artifact. | session 2026-07-21 (build:analyze) | 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) `src/app/(search-app)/layout.tsx:4` imports 71.6 KB of Therapy-Compass-only CSS in the ROUTE-GROUP layout, making it 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 | +| #017 | P3 | task | Field Web-Vitals baseline via live Lighthouse | In-sandbox runtime vitals were blocked (prod server hard-requires Supabase secrets; dev-mode CLS measured excellent at 0.00–0.04, content-first pages 0.000). Run Lighthouse against `psychiatry.tools` for real LCP/INP/CLS to prioritize #012–#016 by measured impact rather than reasoning. **Instrument landed (PR #1385):** `live-web-vitals.yml` is dispatch-only, takes no secrets and runs nothing until someone dispatches it — which is itself a live production action needing explicit approval. `scripts/summarise-web-vitals.mjs` holds the decision rule, committed before any numbers were read: mobile LCP < 2500 ms AND CLS < 0.1 on every route (plus INP < 200 ms from CrUX) closes this row and makes only the explicitly measured **payload** findings gated by #017 WONTFIX — e.g. the route-chunk/catalogue weight in #013. It does **not** close #016 wholesale: #016's motion, CSS, client-waterfall, caching, and dynamic-import items stay open unless they have separate evidence, because Lighthouse LCP/CLS do not validate those. Any breach makes only that route's findings actionable. It fails closed — a missing run, a null metric, a route-slug collision, and a measurement that landed on a different URL than requested (redirect, dropped or reordered query) are all breaches. **Sampled since PR for #114:** each route/strategy cell is measured `SAMPLES` times (default 3), the MEDIAN is graded, and a cell whose samples land on both sides of a threshold is reported as incomplete evidence rather than resolved either way — so the "evidence is too noisy" stop rule can finally fire. A mid-run Chrome change also disqualifies the verdict. **Read the first dispatch as a measurement to be sanity-checked, not as an oracle:** the grading logic took eight rounds of review corrections to reach this shape, each fix locally right and globally incomplete, so cross-check the emitted table against the raw Lighthouse JSON artifact before recording a verdict — most of all a PASS, which would close #017 and the explicitly measured payload findings. Lighthouse cannot measure INP in lab conditions, so that clause of the rule is confirmed from CrUX field data and any pass is provisional on it. | session 2026-07-21 (measurement pass) | 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 | +| #021 | P3 | rec | E-3d H2 residual: strong/comparison generation discards | approx. 6 generation attempts per full 44-case run still fail the final quality gate and fall to extractive on strong-route comparison/complex shapes (the designed-conservative outcome). PARKED: weakest cost/benefit on the queue — a wave (approx. $2-4 pair + reviewer cycle) to shave seconds off a few hard cases. Revisit only if latency/waste complaints or a cheaper lever appears. | E-3c design record; runs #59-#61 diagnostics | 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 | +| #023 | P2 | task | Complete scheduled browser and labeling disposition | The 2026-07-26 retrieval and answer artifacts are read and compared under resolved #051. Scheduled CI run `30216361999` failed its existing production dependency audit before Firefox/WebKit, while production Chromium passed. After that audit is green, capture one scheduled/manual browser-matrix datapoint; separately record the human decision for the stable irrelevant-at-10 set. #084 now makes each top-10 grade and matched signal reproducible, but it does not substitute for the human disposition. Do not rerun or spend on RAG for this item. | runs `30216191889`/`30216361999`; per-rank diagnostics #084; session 2026-07-27 | 2026-07-21 | +| #024 | P2 | issue | WebKit e2e `_rsc` prefetch access-control errors | PR #1205 narrowed catch-all interception and duplicate navigation, but Next 16.2.11 still raises `_rsc` access-control `pageerror`s after document-source fallbacks: `/documents/source?id=&page=2&chunk=safety%20plan` → `/documents/?page=2&chunk=safety+plan`; `/documents/source/evidence?id=not-a-uuid&page=2` → `/documents/search`. The invalid-id failure survived removing every Playwright route; Chromium passed both. **Next:** on a provider-free macOS host, run both URLs in stable Safari and Safari Technology Preview without interception, capture console text plus `_rsc` status/access-control headers, and compare Playwright WebKit with routing on/off. Treat as an app defect only if native Safari reproduces; otherwise return to the harness. Never suppress `pageerror` or change CORS without native evidence. | PRs #1179/#1205; current-main local WebKit evidence; session 2026-07-28 | 2026-07-28 | +| #025 | P2 | task | Activate the three webhooks (operator secrets) | Merged (#968/#1100) + deployed but inert — verified live: `POST /api/webhooks/railway` returns `503 webhook_not_configured`; the Supabase document-change trigger exists but lacks both activation inputs. To turn on: (1) Railway ΓåÆ set `RAILWAY_WEBHOOK_SECRET` + add the `?token=…` webhook URL; (2) set `SLACK_WEBHOOK_URL`/`DISCORD_WEBHOOK_URL` in BOTH the Railway **app/server env** and **GitHub repo secrets**; (3) set one matching document-change secret in the Railway app env as `SUPABASE_INGESTION_WEBHOOK_SECRET` and in Supabase Vault as `ingestion_webhook_secret`, then set the per-environment database GUC `app.ingestion_webhook_base_url` to the deployed app origin. Each path fails closed until fully configured, so this is pure ops. See `docs/webhooks.md` for verification and rotation. | sessions 2026-07-22/24; PRs #968/#1100; docs/webhooks.md | 2026-07-22 | +| #027 | P3 | rec | External uptime monitor independent of GitHub/Railway | `live-domain-monitor.yml` runs on GitHub's cron, so it won't run in exactly the outage it should catch (Actions or the deploy itself down). Add an off-platform synthetic monitor (UptimeRobot / Better Stack / Checkly) hitting `/api/health` with a webhook alert. Provider setup, not code. | session 2026-07-22 webhook review | 2026-07-22 | +| #028 | P3 | rec | Runtime error tracking (Sentry or similar) | No error tracking in the repo — production exceptions on `psychiatry.tools`, including how often `RAG_PROVIDER_MODE=auto` silently degrades to source-only, are invisible. Weigh adding `@sentry/nextjs` (dependency + DSN secret + instrumentation) vs cost; alert ΓåÆ chat/issue. Provider-backed; needs explicit sign-off before adding the dependency. | session 2026-07-22 webhook review | 2026-07-22 | +| #033 | P3 | rec | Source governance metadata absent from the LLM prompt | `buildRagSourceBlock` omits `document_status`, `clinical_validation_status`, and `extraction_quality`, so the model cannot self-caveat during generation and governance is enforced only post-hoc. Generation-surface change: needs `eval:rag` plus `eval:quality --rag-only` (grounded-supported must not drop, citation-failure 0) and explicit approval. Carries the same "unknown Γëá bad" hazard as #032 — on a partially-enriched corpus the model would likely over-caveat correct sources, so design the prompt wording before spending an eval. | `src/lib/rag/rag-source-block.ts:126-198`; PR #1051 audit item 8 | 2026-07-22 | +| #035 | P3 | rec | Threshold-conflict detection covers only 3 params | `detectThresholdDisagreements` checks only ANC, WBC, and platelets paired with withholding verbs, so cross-source conflicts on medication doses, lithium/thyroid levels, or vital signs go undetected. Deliberately narrow (see the comment at `:469-474`). Broadening changes when an answer is classified `conflicting` and adds warnings — real false-positive risk. Needs new fixtures plus a behaviour review before any change. | `src/lib/evidence.ts:469-574`; PR #1051 audit item 7 | 2026-07-22 | +| #036 | P3 | rec | 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 promotion migrations but never used as a retrieval filter. Promotion is unconditional on `clinical_validation_status`, so unverified documents are publicly searchable — compensated by keeping `unverified_source` in the frontend-visible warning set. A hard schema flag touches RLS and the clinical-risk-gated retrieval RPCs; weigh against the existing compensating control before acting. | `supabase/schema.sql:61-108`; `src/lib/search-scope.ts:181-236`; PR #1051 audit item 3 | 2026-07-22 | +| #037 | P3 | rec | D5 trust-cap-all-claims flag parked OFF | `NEXT_PUBLIC_RAG_TRUST_CAP_ALL_CLAIMS` extends authority gating from high-risk claims to **all** supported claims (`deriveTrust`). Ships OFF by design; flipping it caps trust to `medium` for routine claims across the board — a product/clinical-UX decision, not a defect. Both states are test-pinned. Next action: product decision, then flip and re-baseline the UI expectations. | `src/lib/answer-render-policy.ts:159-177`; PR #1051 audit item 11 | 2026-07-22 | +| #038 | P3 | rec | Consolidate shared comparison behavior | Several clinical modes expose comparison workflows with similar selection, empty-state and mobile-dock needs. Define one shared behavioral contract before another comparison surface is added; keep mode-specific clinical content separate. This is a design-system recommendation, not a current defect. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | +| #039 | P3 | rec | Consolidate catalogue toolbar patterns | Catalogue/search pages have independently evolved filter, sort, result-count and mobile toolbar behavior. Inventory the existing implementations and converge only the repeated interaction contract; do not flatten mode-specific search semantics. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | +| #040 | P3 | rec | Add targeted visual-regression baselines | Keep a small approved baseline set for high-value desktop/mobile surfaces and accessibility modes instead of screenshotting every route. Start with account/settings, document viewer, mode homes and bottom-composer interactions; define an intentional-update workflow before enabling blocking comparisons. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | +| #079 | P3 | task | Disposition retained worktrees in bounded cleanup batches | **Outcome:** the retained reconciliation tail is gradually classified without another disruptive all-worktree sweep. **Next:** process no more than ten worktrees per explicitly scheduled pass using current owner/process metadata, open-PR state, exact review-ledger coverage, ancestry, and cherry-pick-aware content proof. **Success:** remove only clean, inactive, bundled worktrees whose content is merged or explicitly rejected; record every disposition and retain recovery evidence. **Stop:** preserve dirty, active, secret-bearing, post-freeze, paused, or ambiguous work and never use reset, force deletion, broad clean, or process killing. | final reconciliation inventory retained 104 independent worktrees; session 2026-07-24 | 2026-07-24 | +| #085 | P3 | rec | Upload-limit client/server sync is unguarded | `NEXT_PUBLIC_MAX_UPLOAD_MB` (client, build-time inlined) and `MAX_UPLOAD_MB` (server, runtime) default 150/150 but nothing keeps them in sync — no zod link, gate, or test. Lower server-only → the 413-after-full-transfer UX FV-04 (#1064/#1069) was built to prevent; lower client-only → false pre-check rejection of files the server would accept (breaks #1064's client-is-a-strict-superset invariant). Both documented in `.env.example`/`docs/deployment-architecture.md` but unenforced; the client value is also frozen at `next build`, so changing the Railway service var without an image rebuild silently no-ops. Cheapest guard: a `check:*` script (or CI assertion) that fails when the two configured values disagree. | session 2026-07-28 (FV-04 adversarial workflow, PR #1069); ID #085 after #084 claimed on main | 2026-07-27 | +| #086 | P3 | task | Repository maturity backlog — remaining structural work | **Outcome:** the deferred repository-maturity backlog ships as verified draft PRs, one per structural change. **Canonical runbook:** [`docs/maturity-backlog-workorders.md`](maturity-backlog-workorders.md). **Remaining:** X3 `rag.ts` decomposition; X7 finish the `src/lib` domain reorg; X6 clinical/retrieval/answer coverage floors; X5 ACL-migration consolidation (provider-gated); L1 archive the retired `backfill:*` one-shots + the dead `ci-change-scope` token; L4 ledger rotation; M1 repo-host hardening (maintainer, audit §8). **Next:** X3 on user go-ahead. **Stop:** RAG/retrieval items need the flag + go-ahead; X5 is live-DB provider-gated. | `docs/maturity-backlog-workorders.md`; audit §8/§10; session 2026-07-28 | 2026-07-28 | +| #088 | P3 | task | Watch for union-driver duplication as open PRs merge the repaired ledger | **Outcome:** the 2026-07-28 ledger repair does not resurface as duplicated rows. **Next:** on the first few open PRs that merge `origin/main` after the repair, run `npm run check:branch-review-ledger` on the merged head. Ordinary 3-way merges take main's repaired lines cleanly; only a same-hunk conflict would let `merge=union` keep both the corrupted and repaired copies. **Success:** three consecutive post-repair merges pass the guard. **Stop:** if duplication appears, remove exact duplicates only (the ledger contract allows that) and never rewrite surrounding records. | branch-review-ledger hygiene pass rewrote 226 historical lines; session 2026-07-28 | 2026-07-28 | +| #090 | P2 | task | Upgrade the eslint ecosystem to clear remaining dev-scoped high advisories | **Outcome:** full `npm audit` reports zero high advisories from the eslint toolchain. **Next:** in a dedicated dependency pass, upgrade eslint and its plugin/config set together (npm offers `eslint@10.8.0`, `isSemVerMajor`); residual highs (`@eslint/config-array`, `@eslint/eslintrc`, `eslint`, `eslint-config-next`, `eslint-plugin-import`, `eslint-plugin-jsx-a11y`, plus the advisory's numeric `<=5.0.7` hit on the unused `brace-expansion@1.1.16` / `2.1.2` maintenance lines that still ship an unpatched `main`) cascade from this toolchain. **Success:** `npm run lint` clean, `verify:cheap` green, full-audit highs cleared, no rule-config regressions. **Stop:** an eslint major previously broke `eslint-plugin-react` here — do not bundle into an unrelated PR, and do not force with `npm audit fix --force`. Production `npm audit --omit=dev` is already clean after the exceljs `archiver@8` / `unzipper@0.12.5` overrides on PR #1314; this item is eslint-dev cleanup only. | session 2026-07-28 brace-expansion triage (PR #1314) | 2026-07-28 | +| #091 | P2 | issue | Results band cannot express a partial-source failure | **Outcome:** a favourites/results count is never asserted while some of its sources failed to load. **Detail:** `SearchResultsBandStatus` is a single flat status, so a page fed by several registries has no way to say "3 loaded, 1 failed". `saved-registry-favourites-status.ts:29` works around this with `itemCount > 0 && folded !== "ready" ? "ready" : folded`, and `favourites-command-library-page.tsx` applies the same mask a second time — so a partial failure renders a confident count with no fault indication, which is the exact defect class PR #1316 removed everywhere else. Neither favourites page consumes the true `registryStatus` the fold already returns. **Next:** decide between (a) a `partial` arm on the status union carrying a "some sources unavailable" note beside an honest count, or (b) surfacing `registryStatus` as a separate inline notice and dropping the mask. **Stop:** do not widen the mask to more surfaces before the shape is decided. | PR #1316 review thread PRRT_kwDOSh5Fis6UUf-k; session 2026-07-28 | 2026-07-28 | +| #092 | P3 | task | Refetch pulse deferred on auth-backed registries (privacy invariant) | **Outcome:** a background refresh keeps the prior count visible instead of a skeleton, without weakening identity clearing. **Detail:** the `refetching` status is built in the band and adopted only on `formulation-home-page.tsx`, where the lag is `useDeferredValue` over static data. It is deliberately NOT adopted on `use-registry-records.ts:85`, `use-medication-catalog.ts:76` or `use-differential-catalog.ts:133`, which all clear data on entering loading. `use-differential-catalog.ts:122` states why: "Auth must clear prior identity's matches immediately", and `:164` that "a later retype of any prior query cannot resurrect authorized matches." **Next:** if adopted, guard preservation on identity AND query equality, and pin with a test that an identity change still clears immediately. **Stop:** never hold records across an auth transition. | PR #1316 plan phase 6; session 2026-07-28 | 2026-07-28 | +| #093 | P2 | issue | Next streaming `S:` clone causes Playwright strict-mode violations under CI load | **Outcome:** duplicate-element strict-mode failures stop appearing on loaded CI runs. **Detail:** under full-suite CI load Next.js leaves a hidden duplicate page root in the stream, so a `getByTestId` that is unique locally resolves to 2 elements in CI (seen as `differentials-search-results` on PR #1316, and previously noted on PR #1294 against main). It does not reproduce in isolation, on a single spec, or locally. The documented workaround is to scope the locator to the visible root. **Reproduced locally 2026-07-28** (isolated _production_ build via `run-playwright.mjs`, full `verify:ui`): `ui-tools.spec.ts:563` duplicated `forms-home` and `ui-smoke.spec.ts:3001` duplicated `favourite-row-lithium-monitoring-guideline`; in both, copy 1 is nested under `mobile-composer-reserve-pad`. Both pass when run alone, so it is load/order-dependent, not build-mode dependent — this also corrects an earlier note that CI uses `next dev`; it does not. **Strongest evidence (CI run `30345484316`, 2026-07-28): `ui-overlap.spec.ts:199` on `/` asserted `toHaveCount(1)` successfully and then the same `header#search` locator resolved to 2 a statement later, one of them hidden.** A duplicate that appears _after_ a passing count assertion is a stream/hydration artifact by construction, not a static double mount and not something a CSS or component change can cause. That makes four distinct testids across four specs with the identical shape. **Mitigated, not fixed, on `main` (2026-07-28):** `3a8edb93` rewrapped `gotoHome` in `tests/ui-overlap.spec.ts` to retry count-and-visibility together via `toPass`, so a transient second header no longer trips strict mode there — its own note says "checking count then immediately calling waitFor races that flicker into a strict-mode violation". That hardens one helper; the duplicate root itself is unchanged and other specs remain exposed. **Confirmed pre-existing:** at `631d90d2`, the commit before PR #1316's first commit, that spec already documented "two `header#search` nodes" and "a second transient `header#search` can exist briefly" — so this predates that branch. **Next:** with a full-suite repro now available, bisect the preceding specs to find the state that triggers the second mount, then either scope the shared helpers to the visible root once or fix the mount. **Stop:** do not paper over new occurrences with `.first()` before the duplicate itself is explained. | PR #1316 CI runs; PR #1294 note on main; session 2026-07-28 | 2026-07-28 | +| #094 | P2 | rec | Design-system gates assert structure, not rendered effect | **Outcome:** a style contract cannot pass while the style is inert. **Detail:** PR #1316's accent rail shipped inert because `.search-band` sat in `@layer components`, which loses to Tailwind's utilities layer regardless of specificity — and the test asserted `toHaveClass("search-band")`, i.e. class presence, not effect. Computed style showed `1px rgb(229,231,235)` where `2px rgb(11,111,134)` was intended. The same shape of gap let a rail-colour assertion compare a colour against a width and pass unconditionally. **Next:** for contracts where the visual IS the requirement (rails, forced-colors thickness, tap targets), assert `getComputedStyle` in a Playwright case rather than class names in a DOM test, and add the unlayered-component convention to the design-system contract check. **Stop:** do not convert existing passing DOM tests wholesale; add computed-style proof only where the effect carries the meaning. | PR #1316 Codex P2 finding; session 2026-07-28 | 2026-07-28 | +| #097 | P3 | issue | Gitleaks reports a false red when the PR head moves mid-run | **Outcome:** a red `Gitleaks` means a secret was found, not that someone pushed. **Detail:** on 2026-07-28 the job triggered for head `9bace1d1` checked out that merge ref, then queried the API and built its range against head `40278453` — pushed seconds later and absent from the checkout. Git rejected the range (`fatal: Invalid revision range`), so it scanned `~0 bytes`, logged `no leaks found in partial scan`, and exited 1. The scan did not run at all, which is worse than a normal failure because the natural reading is "noise, ignore it". It cleared on its own once the head stopped moving (`23 commits scanned`, `~198 KB`, `no leaks found`). Both range endpoints resolve in any complete checkout — verified locally against the branch and the PR merge ref — so this is not a `fetch-depth` problem. **Next:** pin the scan to a range the job controls (`base.sha`..the checked-out head) instead of re-querying the API mid-run, so a concurrent push cannot invalidate it. **Related:** same push-churn family as #095. **Stop:** do not weaken the gate to a soft-pass; the fix is a stable range, not a tolerated failure. | PR #1316 runs 30344938800 / 30346797225; session 2026-07-28 | 2026-07-28 | +| #098 | P2 | task | Offline round-trip budget harness for the hot routes | **Outcome:** per-scenario Supabase round-trip counts are pinned by a test, so an extra round trip on a hot path is a red gate rather than an inference. **Done 2026-07-29:** the measurement gap is closed — `Server-Timing` now covers `auth`/`ratelimit`/`scope` on `/api/answer`, `auth`/`ratelimit`/`search`/`total` on `/api/search`, and `auth`/`ratelimit` on `/api/answer/stream` (previously the route the UI actually calls emitted no header at all). Headers flush before the first SSE frame, so in-stream stages cannot reach a header and must NOT be routed through the governed `progress`/`final` contract. `tests/answer-route-preamble.test.ts` pins admission-before-scope (no scope call while the limiter is pending or after a deny) and the client-disconnect abort signal. **Next:** generalise it — wrap the Supabase client in a counting proxy and assert per-scenario query budgets over the existing offline suites — `scripts/eval-rag-offline.mjs`, `scripts/test-rag-offline.mjs`, `scripts/rag-offline-contract.mjs` and the contract fixture `scripts/fixtures/rag-offline-contract-tests.json`. **An earlier version of this row named `test-cache-path.mjs` and `check-rag-fixtures.mjs`** (corrected 2026-07-29, PR #1377 review, matching the audit's own retraction): neither exercises a RAG request — the first computes Vitest/TypeScript cache paths, the second only validates fixture manifests — so building the harness on them would have counted nothing. Sequence before #099 and #101: it is the enabler and the standing guard. No providers, no DB. | `docs/audit/latency-audit-2026-07-28.md` measurement plan; `src/lib/server-timing.ts`; `src/lib/answer-stream-contract.ts:18-21` | 2026-07-29 | +| #099 | P2 | task | Remove the remaining fixed per-request round trips | **Outcome:** the answer path stops paying avoidable per-request Supabase round trips. **Done 2026-07-29:** shared-cache-hit promotion deferred off the response path with its mid-request staleness guard intact and documented (`rag.ts:3234`, `rag-cache.ts`); scope resolution overlapped with the rate-limit RPC, signal threaded so a client disconnect finally cancels its paginated queries (`answer/route.ts`). **REFUTED on PR #1377 review — do not retry:** the same pass also overlapped scope with the rate-limit RPC and aborted it on deny, claiming the limiter could "deny for free". It cannot. With caller-supplied `filters` or explicit ids, scope passes its zero-query early returns (`search-scope.ts:242,253`) into the paginated `documents` loop at `:269`, and an `AbortSignal` cancels the client request without un-executing a statement Postgres already began — so throttled traffic kept burning database capacity while collecting 429s, against `capacity-review.md:106-113`'s first-soft-failure warning. Scope is behind admission again, pinned by `tests/answer-route-preamble.test.ts`. Re-attempting the overlap requires a non-database admission gate ahead of the durable limiter first. **Remaining:** (a) the 8 `setCachedSearch` awaits — deferring changes `throwIfAborted` semantics and widens a real mutation window because the clone happens after an `await`, so each branch needs discharging individually; (b) batch the anonymous subject+global rate-limit pair, which needs a NEW atomic RPC modelled on `consume_summary_rate_limits_atomic` and cannot be called until the operator applies it — `Promise.all` is the WRONG fix because it consumes the global bucket even when the subject bucket already denied; (c) stop the proxy and route handler resolving identity twice per authenticated request — no in-process memo can do this (different `Request` objects), so the proxy must forward unspoofable verified claims via a header it controls. Cross-references #011: halving auth resolutions eases the ~10-connection Auth cap that `capacity-review.md:106-113` calls the first hard failure. | `docs/audit/latency-audit-2026-07-28.md` L1-1/L1-3/L1-4; `src/lib/api-rate-limit.ts:276-282`; `src/proxy.ts:125` | 2026-07-29 | +| #100 | P2 | rec | Buffered answer generation has no incremental verified delivery | **Outcome:** a clinician sees verified answer content before the whole generation completes. Highest-leverage latency finding in the 2026-07-28 audit: generation is buffered (`openai.ts:465`) and delivered in ONE `final` SSE frame, so time-to-first-content equals total latency — a strong answer inside its 25 s SLO still shows a blank panel for 25 s. The 15 s `sse-heartbeat` exists because that silence routinely exceeds 15 s; it instruments the defect rather than fixing it. **Naive token streaming is REFUTED, not merely unbuilt:** `answer-stream-contract.ts:18-21` removed `token`/`revising` deliberately because a rolling deployment would "re-expose unvalidated clinical prose", and raw tokens bypass the numeric-faithfulness gate the 2026-07-01 audit filed as H1. **Only admissible shape:** progressive disclosure of already-verified units (evidence/sources at retrieval-complete, then per-section after that section clears verification) over the existing whitelisted `progress` event. Needs a clinical-governance decision plus a canary pair. Also add the refutation to `docs/rag-behaviour/refuted-approaches.md`. Cross-references #021. **Stop:** do not re-land `token` streaming. | `docs/audit/latency-audit-2026-07-28.md` L0-1; `src/lib/answer-stream-contract.ts:18-21`; `src/lib/sse-heartbeat.ts` | 2026-07-29 | +| #101 | P3 | rec | Canary-gated retrieval parallelisation candidates | **Outcome:** independent retrieval stages stop running serially, proven by a live canary pair. Candidates: metadata/memory/visual hydration triples repeated on four branches (`rag.ts:2460,2493,2521` and three more) while `rag.ts:2751-2804` already parallelises three RPCs in one `Promise.all`, so the omission is inconsistency rather than intent; the nested `await`-in-loop scope enumeration (`search-scope.ts:202,328`); typeahead results never cached (`rag.ts:2698-2711`); 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 needs 36/36 retrieval plus recall 1.0 and zero per-case rr regressions. Distinct from #001 (semantic rerank). Resolved #075 and #083 are the precedents for why these are gated rather than free. **Stop:** needs the #098 harness and explicit canary approval first. | `docs/audit/latency-audit-2026-07-28.md` L2-1/L2-2/L2-8/L1-5 | 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. The documents-list and `(status,id)` uses stay ordering-safe; the RAG-path index is canary-gated, 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 | +| #103 | P2 | issue | Wide table-facts trigram index missing from `schema.sql` | **Outcome:** the migration chain and `schema.sql` agree on `document_table_facts` trigram indexes. `supabase/migrations/20260714190000_document_table_facts_trgm_idx.sql` creates a wide 5-column trigram index that is **absent from `supabase/schema.sql`**, so local replay and the live database can diverge. Distinct from #102: different owner and verification path. **Next:** confirm whether the wide index exists live, then take one of exactly two routes — **retained:** mirror `document_table_facts_text_trgm_idx` into `supabase/schema.sql` beside the narrow one and regenerate `drift-manifest.json`; **redundant:** drop it through a new forward migration, never by deleting `20260714190000`. **`drift-allowlist.json` is NOT a third option** (PR #1377 review): its own header scopes it to _"Known live-vs-`schema.sql` divergence"_, so it can silence a live drift finding but cannot reconcile the migration chain with the mirror — a fresh `supabase db reset` still runs `20260714190000` and creates the index while `schema.sql` still omits it, leaving this row's stated outcome unmet. **No offline gate catches this today:** the migration↔`schema.sql` parity test (`tests/drift-detection.test.ts:59-68`) only asserts one migration's `schema_drift_snapshot` function definition, not an index inventory — which is why this sits open rather than red in CI, and why a replay-to-schema inventory comparison is the check that would have caught it. Note the narrow `document_table_facts_title_row_param_trgm_idx` (`schema.sql:6425`) is the one the effective RPC expression (`:6726`) actually matches, so the wide index may be genuinely redundant — do not drop it without live scan evidence, per the monitored-not-auto-fixed index policy. | `docs/audit/latency-audit-2026-07-28.md` limitations; `npm run check:drift` | 2026-07-29 | +| #105 | P3 | task | Verify the `#017`-exempt client latency wins in a browser | **Outcome:** the two zero-payload client fixes are confirmed in a real browser. `#017` gates _payload_ decisions (#012/#013/#016 are all byte-count items); a `loading` fallback ships zero bytes and a resource hint ships ~60, so neither can be justified or refuted by a Lighthouse number — that is why these were not held behind #017. **Implementation shipped 2026-07-29; browser verification still PENDING:** 10 of 11 `ssr:false` dashboard surfaces had NO `loading` fallback and rendered nothing between HTML arrival and chunk execution — all now use the shared `LoadingPanel` (`role="status"` + accessible label); Supabase `preconnect`/`dns-prefetch` added, since `AuthProvider` awaits a cross-origin `getUser()` on mount that every auth-gated fetch queues behind and there were no resource hints anywhere in `src/`. Shipped with `verify:cheap` + `verify:pr-local` only. **Next:** run `npm run verify:ui` once the heavy-run lock is free, and confirm the preconnect appears in `` on a live page. **Stop:** the two sidebar dialogs are intentionally excluded — they mount on open, so a fallback would render into a closed dialog. | `docs/audit/latency-audit-2026-07-28.md` L3-4/L3-5; `src/components/clinical-dashboard/clinical-dashboard-lazy.tsx` | 2026-07-29 | +| #106 | P2 | rec | Ingestion worker and indexing agent are verified by grepping their own source | **Outcome:** the ingestion worker and indexing agent are verified by executing code, not by asserting on their own source text. **Detail:** measured 2026-07-29 via `npm run test:coverage` — `worker/main.ts` (2,015 lines) and `supabase/functions/indexing-v3-agent/index.ts` (1,966 lines) each report **0% executed lines**; no test imports either module. Both are covered only by `readFileSync` + `toContain` assertions in `worker-safe-logging.test.ts`, `worker-visual-capture.test.ts` and `document-metadata-merge.test.ts`, which pass whenever a string is present and break on harmless refactors; `document-metadata-merge.test.ts` additionally reimplements the SQL deep-merge in TypeScript and tests the reimplementation rather than the worker. Area totals: `worker/` 18.6% lines, `supabase/functions/` 4.5%. **Next:** continue the extraction pattern that already works here — `indexing-v3-agent/behavior.ts` (167 lines, 96%) and `ingestion-worker/auth.ts` (30 lines, 90%) — pulling the highest-risk decision points out of `worker/main.ts` (job claim/retry, generation commit, failure classification) into importable modules with executing tests, retiring the matching source-text assertion as each lands. Roughly cost-neutral: each extracted test replaces a grep assertion. **Stop:** do not try to make the 2,000-line entrypoint importable in one pass; extract incrementally and keep each step green. | session 2026-07-29 test-coverage analysis | 2026-07-29 | +| #107 | P2 | rec | Component state matrices are the largest untested surface | **Outcome:** loading / empty / error / disabled states on interactive components are covered by executing tests, not only by E2E happy paths. **Detail:** measured 2026-07-29 — production components (excluding mockups) sit at **38.2% lines / 22.8% branch** across 12,602 lines, with **83 of 208 files at zero executed lines**; there are 51 `.dom.test.tsx` files against 195 components. Playwright does visit these routes, so they are smoke-covered, but branch coverage is where the state matrix lives and smoke journeys rarely reach it. Worst by uncovered lines: `global-search-shell.tsx` (7%), `mode-action-popup.tsx` (21%), `answer-content.tsx` (27%), `document-search-results.tsx` (32%), `universal-search-command-surface.tsx` (39%), `master-search-header.tsx` (43%). A concrete first target with clinical meaning: `calculator-ui.tsx` now covers all exported scoring logic, but `seedCheckboxDefaults`, `toggleCheckboxAnswer` and `selectOptionAnswer` stay uncovered because they are module-private and only reachable through React event handlers — `seedCheckboxDefaults` is what makes an all-negative CAGE / SAD PERSONS screen read as a valid 0 rather than incomplete, so a regression there is a false-negative risk. **Next:** treat as a per-PR convention rather than a backfill push — `docs/testing.md` already prescribes the state matrix, so the gap is enforcement. Start with `global-search-shell.tsx`, which `docs/search-chrome-behaviour.md` treats as a contract surface. Keep additions in the jsdom tier (measured ~0.54s per file) instead of new Playwright journeys (~231 production journeys already run serially at `workers: 1` against a 45-minute CI budget). **Stop:** do not chase the coverage percentage by backfilling low-risk components; the re-ratcheted broad floor in `vitest.config.mts` holds the line. | session 2026-07-29 test-coverage analysis | 2026-07-29 | +| #108 | P3 | task | Five verified-landed remote branches await deletion (blocked in-session) | **Outcome:** the five branches whose content is fully on `main` are gone. **Detail:** a full-history branch-cleanup review on 2026-07-29 verified these introduce an empty diff against `main` and back no open PR: `claude/clinical-kb-pwa-review-asi3wb` @ `df29f311b60cadf8e43bf51283a9d6f496b295e3`, `claude/dazzling-blackwell-f348d0` @ `c9bec8f9dce38cb647de9aa64ebf08bf7823a524`, `codex/document-reader-condensed-view` @ `b5cdbf301d517239ffe9ed941b9ebe809aea0bfd`, `cursor/page-anchored-search-composer-30ee` @ `7ff134ca7f614db527b8d142676640305533669d`, `cursor/pr-1379-babysit-ledger-9365` @ `be2de03f855cb7fdfccea4bb74d05eb4c9bf6c61`. **The HEADs are recorded because they are unrecoverable once the refs are deleted:** `hasCompletedCleanupReview` (`scripts/sweep-branch-ledger.mjs:83-93`) matches a completed row on branch name AND HEAD together, so without them no later operator could ever append the required `branch-cleanup` rows. Each candidate now also has its own `branch-cleanup-deletion-pending` ledger row keyed to its own HEAD. Deletion could not be performed: the session git proxy rejects ref deletion with **HTTP 403**, and the GitHub MCP toolset exposes no delete-branch capability. The remaining 87 were deliberately NOT cleared — their touched files still differ from `main`, which is the conservative direction. **Next — ORDER MATTERS:** append the completed `branch-cleanup` row for each branch FIRST, from a checkout that still has the objects, and only then delete the refs. `resolveHead` (`scripts/branch-review-ledger.mjs:155-167`) runs `git rev-parse --verify ^{commit}` and refuses to append a HEAD that is not a commit in the repository, so the reverse order is unexecutable once the refs are gone and their objects are pruned. The `n/a - ` escape hatch does not help here: `hasCompletedCleanupReview` only matches a 7-40 char hex HEAD, so an `n/a` row would leave the branch resurfacing in every future sweep. **Progress 2026-07-30 — the prerequisite is DONE; only the deletion is left, and it needs a caller who can delete refs.** Re-verified against the live remote first, which mattered: `claude/clinical-kb-pwa-review-asi3wb` and `claude/dazzling-blackwell-f348d0` are **already gone** (surfaced by `git remote prune`, deleted by someone with the permission this session lacks), so the list is three, not five. The remaining three are still at exactly the recorded HEADs, and the proof was re-run and strengthened: for each, `tree(tip) == tree(merge-base)` byte-for-byte, so the branch nets zero content change from where it forked and nothing on it is absent from `main`. That is stronger than either check the guide names — `--cherry-pick` still reports 13/6/4 patch-unique commits on them, which is the squash-merge false positive, and none is an ancestor of `main`, so `--merged` would also miss them. A completed `branch-cleanup` row keyed to its own HEAD is now appended for each of the three (`npm run check:branch-review-ledger` passes at 1272 records), so the ORDER MATTERS constraint above is satisfied and the refs can now be deleted safely at any time. Deletion re-attempted and still blocked: `git push origin --delete` exits 1 with **HTTP 403** from the session git proxy (`recentRelayFailures` empty, so it is credential scope, not a relay fault), and a tool search confirms the GitHub MCP set exposes `create_branch` but no delete-branch capability. **Next:** delete these three refs from the GitHub UI or any session whose credentials permit ref deletion — `codex/document-reader-condensed-view`, `cursor/page-anchored-search-composer-30ee`, `cursor/pr-1379-babysit-ledger-9365`. Nothing else is required first. **Stop:** do not widen to the other 87 without per-branch content proof. | session 2026-07-29 branch cleanup; ledger `branch-cleanup-deletion-pending` @ 855aa291 | 2026-07-29 | +| #110 | P3 | task | Design-system project token manifest lags its stylesheet | **Outcome:** the claude.ai/design token panel matches the shipped stylesheet. **Detail:** PR #1375 pushed a recompiled `_ds_bundle.css` (Clinical Sky, `--e0`–`--e4`, 4px radius grid, `--tracking-eyebrow`/`--leading-display`/`--leading-prose`) plus the four changed guideline docs to project `08d6f126`, but `_ds_manifest.json` is converter-generated and still advertises `--text-4xs: 0.5rem`, the old `--radius-lg/xl/2xl` values, and `--tw-leading`/`--tw-tracking` entries scoped to the retired `.leading-[…]` / `.tracking-[0.08em]` utilities. Rendering is correct; only the token inventory lags. Hand-editing was rejected — `kind`/`scope`/`annotation` are converter heuristics and a wrong panel is worse than a stale one. **Next:** in a session with the `/design-sync` skill, `npm ci`, then `npm install --prefix .ds-sync --no-save --package-lock=false esbuild ts-morph @types/react @tailwindcss/cli geist`, read `.design-sync/NOTES.md`, and run `resync.mjs --remote` so bundle and manifest regenerate together. **Stop:** do not hand-author `_ds_manifest.json`; the converter is not a published npm package and ships with the skill. | PR #1375; `.design-sync/NOTES.md`; project `08d6f126` (`_ds_needs_recompile` marker present) | 2026-07-29 | +| #116 | P2 | issue | An unmergeable PR runs no CI at all, with no signal that it stopped | **Outcome:** a PR that has silently stopped being tested says so. **Detail:** on 2026-07-30 PR #1400 ran **no** `CI`, `Gitleaks` or `Semgrep` workflow across three consecutive pushes, and nothing anywhere said why. Cause: the PR had a real content conflict with `main`, so GitHub could not build `refs/pull/1400/merge`, and every `pull_request`-triggered workflow is skipped in that state. `pull_request_target` ones (`PR Policy`) still ran, and CircleCI posted failure ~3s after each push - faster than a checkout, so no step executed. The symptom reads as "CI is broken" or "my tests fail", and about 40 minutes went into replicating CI steps locally (all green) before the cause was found. **Diagnostic:** compare check-run counts with another open PR (3 vs 16-19), list workflow runs and note only `pull_request_target` fired, then confirm with `git merge-tree --write-tree origin/main HEAD`. **Next:** make staleness visible instead of silent - e.g. a `pull_request_target` job that fails when `mergeable_state` is `dirty`, so a conflicted PR shows one red check naming the conflict rather than an empty check list. `npm run sync:pr-branches` covers the behind-but-clean case only. **Stop:** never conclude "CI is failing" from a missing check - the absence of a check is not a failing check. | PR #1400; session 2026-07-30 | 2026-07-30 | +| #120 | P2 | issue | `verify:phone-chrome` exits 0 while reporting failed browser tests | **Outcome:** the phone-chrome gate cannot report success when no test executed. **Evidence 2026-07-30 (PR #1396):** `npm run verify:phone-chrome` finished with **exit code 0** while its own output ended `13 failed`. Every one of the 13 failed at browser launch (`browserType.launch: Executable doesn't exist ... chrome-headless-shell`), so zero assertions ran, yet the gate returned success. This is the green-when-broken case `AGENTS.md` warns about ("Exit code 0 alone is not proof") realised in a gate that is supposed to be the proof. **Next:** make the runner propagate the Playwright exit status, and fail loudly on a launch error rather than treating a zero-test run as a pass. Stop: do not paper over it by grepping output in the caller — the runner owns the status. | `scripts/verify-phone-chrome.mjs`; `scripts/run-playwright.mjs` | 2026-07-30 | +| #121 | P3 | issue | Container Playwright browser build lags the pinned client | **Outcome:** browser gates run in remote sessions without hand-patching. **Evidence 2026-07-30:** the repo's Playwright client resolves headless-shell build `1234`; the container image provides `1194` at `/opt/pw-browsers`, so every browser test fails at launch. Worked around in-session by symlinking `chromium_headless_shell-1234/chrome-headless-shell-linux64/chrome-headless-shell` to the `1194` `headless_shell` binary plus its sibling resources — container-local, nothing committed, and it disappears with the session. `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1` means the mismatch cannot self-heal. **Next:** decide whether the image pins the browser build or the repo pins a client matching the image; until then any remote session claiming browser proof must state which it used. | `docs/testing.md`; container `/opt/pw-browsers` | 2026-07-30 | +| #122 | P2 | issue | `ci/circleci: verify` fails on every branch and its log needs operator access | **Outcome:** the CircleCI status is trustworthy signal again, or it stops reporting. **Evidence 2026-07-30:** `ci/circleci: verify` was `failure` on every open PR sampled — #1396, #1407, #1405, and #1400, which is a **docs-only** `AGENTS.md` change — plus #1403's head. It is sharply bounded in time: #1393's head **passed** at build 638 (03:57), and builds 645 (04:09) onward all failed. The job's entire contents were mirrored locally on PR #1396's exact tip and every part is green — `format:check` clean, `lint` exit 0, `typecheck` exit 0, `npm run test` `432 passed (432)` / `4473 passed \| 4 skipped`, and the PyMuPDF-gated `tests/pdf-extractor.test.ts` (the repo's only `process.env.CI`-gated tests) `6 passed (6)` under a locally built `PyMuPDF==1.28.0` venv with `PYTHON_BIN` set exactly as `.circleci/config.yml` does. So the failure is in the job's **environment**, not repo code. Around 40 builds fired in ~40 minutes across 8 open PRs in that window, so credit/quota exhaustion is the leading hypothesis — **explicitly unverified**: the CircleCI project is private and no CircleCI token is available to any agent session, and `api/v1.1/project/gh/BigSimmo/Database/` returns `Build not found` unauthenticated. **Next:** an operator opens one failing build and reads the failing step; if it is quota, either raise it or remove the CircleCI status so it stops masking real reds. **Stop:** do not chase this from a PR branch — it is not branch-specific, and no agent can read the log. Do not go looking for a CircleCI token. | `.circleci/config.yml`; PR #1396 session 2026-07-30 | 2026-07-30 | +| #125 | P3 | issue | `ui-therapy-nav-scroll.spec.ts` cites a spec file that does not exist | **Outcome:** a reader following the comment finds the coverage it names, or the comment stops naming it. **Detail:** the spec's comment points at `mode-nav-bar-anchoring.spec.ts`, left behind when PR #1390 moved `ModeNav` into the universal header; no such file exists anywhere in the repo. Harmless at runtime, but it sends the next person looking for anchoring coverage to a file that is not there, and it is the kind of stale pointer that makes a reader distrust the surrounding comments. Either repoint it at the coverage that actually exists (`ui-mode-nav-density.spec.ts`, landed in #1405) or delete the reference. | Noticed and explicitly deferred in PR #1405's body to keep that diff scoped to `#113` | 2026-07-30 | ## Resolved / archive Move resolved rows here with the resolution date and a one-line outcome. Keep them — do not delete. -| ID | Type | Summary | Outcome | Resolved | -| ---- | ----- | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | -| #087 | issue | `npm run check:knip` reported pre-existing dependency findings | NOT A DEFECT — false positive. The findings (unused `rimraf`/`tsx`/`@testing-library/dom`, unlisted `playwright-core`) only appeared because the worktree had no `node_modules` of its own and tooling resolved from the parent checkout. After `npm ci` in the same worktree, `npm run check:knip` exits 0 with no ignore-list change. Durable lesson: never act on a knip finding from a worktree that has not been installed. | 2026-07-28 | -| #089 | issue | Branch-review-ledger hygiene deferred from PR #1275 | Closed by the 2026-07-28 hygiene pass. The MD056 cell-count and duplicate-row findings dispositioned as "belongs in a dedicated main ledger hygiene pass" on PR #1275 are fixed: 140 mojibake lines restored byte-exact from git history, 6 residual separators repaired, 46 exact duplicates removed, 21 wrong-width rows normalised, and 4 heading+bullet records converted to table rows — 1067 records, all six cells. Root cause closed too: `npm run ledger:lookup` / `ledger:append` (`scripts/branch-review-ledger.mjs`) replace hand-written rows, and `check:branch-review-ledger` now fails on mojibake, cell width, heading records, impossible dates, table gaps, and (from 2026-07-29) unresolvable HEADs and near-duplicates. | 2026-07-28 | -| #111 | issue | `ui-overlap` phone-inset case still flakes under load | RESOLVED 2026-07-29 (PR #1391). The inset measurement now retries inside `expect(async () => …).toPass({ timeout: 15_000 })` (`ui-overlap.spec.ts:159`) — the same retry-the-measurement shape PR #1375 applied to the eight overlap cases. The 2px symmetry tolerance and the assertions themselves are byte-for-byte unchanged, so a genuinely asymmetric header still fails once the retry budget is spent; only a transient mid-remount sample settles. Full spec green across 4 consecutive runs (14 passed each). Do not relax the tolerance if this recurs — the assertion is the point; make the measurement robust instead. Source: PR #1375 flake triage; session 2026-07-29 | 2026-07-29 | -| #112 | issue | `issues:next-id` has no concurrency protection | RESOLVED 2026-07-30. `npm run check:outstanding-issues` now gates this file, in `verify:cheap` and in the `static-pr` CI job (the gate-manifest check refuses a local gate that CI does not run). It fails on a duplicate id, an id present in both tables, a marker at or below the highest id, a malformed row, and a missing heading or marker — so every shape the 2026-07-29 triple collision took is now a red gate rather than a silent row loss. Verified by replaying that collision against the real file: two rows claiming `#110` produced "#110 appears 2 times (lines 151, 164)", and a lost marker bump produced "issues:next-id=113 is not above the highest id #114". The checker honours `\|` escapes — its first run against the live file flagged row #042, which is correctly escaped, and a gate with false positives is a gate people switch off. NOT fixed: the underlying race. Ids are still allocated by read-modify-write with no lock, and this file still has no `merge=union` driver; what changed is that a collision can no longer land silently. Source: session 2026-07-29 PR sweep; PR #1391 conflict resolution; `.gitattributes` | 2026-07-30 | -| #113 | issue | `ModeNav` clips its labels at every phone width on `main` | RESOLVED 2026-07-30 (PR #1405, landed on `main` as `020c1260`). The two lower density bands used `grid-auto-columns: 1fr`, so the widest slot set every slot's width and the label's `truncate` hid the shortfall — nothing overflowed and nothing failed, the word was simply gone. `display: flex` at every band removes the mode rather than retuning it; thresholds went `16/26/34rem` -> `22/33/42rem` against remeasured intrinsic widths (471px for four labels, not the 394px originally budgeted). `tests/ui-mode-nav-density.spec.ts` asserts at both band boundaries and one pixel either side, registered in BOTH Playwright allowlists. **Carry-forward:** the ~8% threshold headroom is not padding — a measured-plus-one-pixel attempt (21/31rem) passed locally and failed CI by exactly 1px, because the variable face rasterises wider on `ubuntu-24.04`. A threshold calibrated to one machine's font metrics is calibrated to nothing. | 2026-07-30 | -| #114 | issue | The live Web-Vitals instrument cannot measure its own noise | RESOLVED 2026-07-30 (PR #1407, landed as `a73db460`). Each route/strategy cell is measured `SAMPLES` times (default 3), the MEDIAN is graded, and a cell whose samples straddle a threshold is reported as incomplete evidence rather than resolved either way — so #017's "evidence is too noisy" stop rule can finally fire. Mid-run Chrome drift disqualifies the verdict. **Second defect found in review and fixed in the same PR:** `samples` is a free-text `workflow_dispatch` input, so `samples=1` reached the grader as a valid integer and reproduced the exact single-run matrix this work removes — verified against the pre-fix logic, it reported 0 breaches, 0 incomplete and printed the bold "Every mobile route is within" verdict. Now refused in three places (`main()` before reading any report, `renderTable`'s disqualifier list, and the workflow before spending a live measurement pass). | 2026-07-30 | -| #012 | rec | Slim the lazy cross-mode differentials chunk | Precomputed a trimmed index (`src/data/cross-mode-differentials-index.json` via `scripts/build-cross-mode-differentials-index.mjs`) so the lazily-loaded cross-mode chunk imports a ~53 KB catalog instead of statically pulling the ~1.2 MB differentials snapshot (only that dynamic path reached it). A drift test plus `check:cross-mode-index` (in verify:cheap) lock the index to the live projection. | 2026-07-27 | -| #029 | issue | Residual answer-quality fallback stubs | Closed after fixing each causal cluster independently. Active-community ED, community-home-visit, clozapine blood-threshold/typo, discharge source-gap recovery, and Best Practice Prescription now use narrowly validated, source-bound answers or auditable recovery; cited provider refusal prose can no longer masquerade as grounded, and terminal gaps retain no claim citations. The final 44-case gate reported 30/30 substantive grounded supported answers, 14/14 unsupported correct, zero review fallbacks, zero citation/numeric failures, and zero route-ceiling failures. Measurement still reports review fallback separately and denies targeting credit for echoed boilerplate. | 2026-07-27 | -| #019 | issue | Preserve admission/discharge sources through comparison fallback | The actual fallback path now selects source-bound facts, preserves one admission and one discharge citation from distinct documents, and terminates at an evidence gap for qualified, negated, unrelated, title-only, single-sided, or same-document traps. Both exact live cases complete in about one second with zero provider calls; the final 44-case canary passed them with two citations each, while the 36-case retrieval canary held recall 1.0 and zero RR regressions. Retrieval scores, aliases, clamps, and comparator ordering were unchanged. | 2026-07-27 | -| #084 | task | Persist per-result irrelevant-at-10 grading evidence | `eval-retrieval` now persists each top result's `relevanceGrade` and `matchedDeclaredSignals`; focused fixtures cover ideal and zero-grade rows. The final golden artifact contains 338 graded top rows, including 33 grade-zero rows. This closes the reproducibility gap only: fixture labels, ranking, thresholds, and provider behavior were not changed, and human disposition remains #023. | 2026-07-27 | -| #080 | rec | Re-test the removed admission-to-discharge alias widening | Restored the two approved NMHS Admission-to-Discharge titles only on the eval-expectation surface. Canonical document-identity dedupe plus maximum bipartite matching prevents one dual-listed physical document from satisfying both comparison slots. Focused matching tests, both targeted admission cases, the final 36-case golden retrieval run, and the 44-case answer run passed; runtime retrieval/ranking behavior was not changed. | 2026-07-27 | -| #083 | issue | Documents-only universal search timed out on staging tenancy | A current staging nightly reproducer showed the documents-only search losing its synthetic fixture after the federated typeahead timeout was reduced to 750 ms. Current main retains 750 ms for multi-domain requests and uses the established 6,000 ms budget only when documents are the sole requested domain; fake-timer coverage proves both paths. RAG impact: no retrieval, ranking, ordering, alias, score, or result-selection change—only availability of the explicitly focused request. | 2026-07-27 | -| #082 | issue | Bot branch-sync heads leave required checks unapproved | Retired the automatic `GITHUB_TOKEN` PR branch-update workflow instead of weakening required-check approvals or introducing a privileged automation token. The existing helper remains dry-run by default, verifies its apply identity, and refuses missing or bot identities. The fast GitHub Actions policy check rejects both direct workflow `update-branch` calls and indirect apply-helper invocation. | 2026-07-27 | -| #058 | task | Verify production content before any seed write | Read-only production counts on project `sjrfecxgysukkwxsowpy` found 276 clinical registry, 328 medication, and 232 differential records. The required tables are non-empty, so no seed or production write was needed. | 2026-07-27 | -| #069 | task | Validate hosted table-facts RPC latency | Read-only profiling on the correct hosted project separated sample 1 (`first_unprimed`) from five `warm_repeat` samples; managed Supabase buffers were not flushed, so no true-cold claim is made. First-unprimed client/DB execution was 662.916/141.537 ms (clozapine), 277.661/96.229 ms (lithium), and 322.598/148.378 ms (metabolic). Warm client median/p90 was 187.029/198.907, 167.803/174.391, and 189.065/243.324 ms; warm DB execution median/p90 was 88.292/89.355, 64.342/65.566, and 107.448/148.417 ms. Earlier exact clinical probes were lower again. Plans are not the multi-second tail; no hosted migration, ranking, or provider configuration changed. | 2026-07-27 | -| #051 | task | Stabilise the live answer-quality canary before more RAG tuning | Closed after the scheduled structured report supplied a comparable second 36-retrieval/44-answer datapoint. Content gates stayed stable, the prior citation failure cleared, and #019 repeated with an identical diagnostic signature. Retrieval latency was investigated separately: #069 subsequently found acceptable table-facts database plans, so the broad scheduled tail was not treated as ranking debt. The report/trend tooling is now sufficient to compare future approved runs; no scheduled rerun or tuning was dispatched. | 2026-07-27 | -| #054 | task | Reconcile local and hosted secrets/config | Completed production names-only reconciliation on 2026-07-27. The correctly identified primary checkout received distinct gitignored local safety/query-hash/deep-probe values. A hardened checker now pins GitHub to `BigSimmo/Database` and Railway to the live production project/environment plus `Database`/`worker`, catches multiline schema and `.env.example` drift, and verifies GitHub secrets/variables and per-service Railway contracts without emitting provider values. All required names passed; the Ops Digest workflow is active with a successful scheduled run; both Railway services have later successful deployments; Supabase names-only proof found the expected cron/Vault configuration. Value equality remains deliberately unobservable, staging stays #056, webhook activation stays #025, and legal/ZDR work stays #053. | 2026-07-27 | -| #064 | task | Reconcile the preserved browser and contrast patch | Landed via PR #1250 squash `b91b4600171be08198e92bcf19b7d67e8207cb2f`. Opacity-free disabled Previous/Continue styling plus native-disabled/focus/axe Playwright coverage is on `main`. Historical `agent/formulation-disabled-contrast` remained unrecovered; conflicted PRs #1219/#1223/#1226/#1231/#1249 were closed without merge. | 2026-07-26 | -| #081 | issue | Open PR #1196 would undo the #030 alias tightening | Closed as no longer live: PR #1196 was closed 2026-07-25 as superseded by #913/current `main` (~680 commits behind, conflicting), and its successor #1198 does not touch `src/lib/eval-document-matching.ts`. The generalized alias-disjointness and single-document contracts landed in PR #1215 fail closed if any later branch re-adds the dual-listed admission aliases, so the regression route is guarded rather than watched. | 2026-07-25 | -| #077 | issue | Concurrent tasks can re-dirty the canonical primary checkout | Added cooperative primary-checkout write lease with dirty/operation fail-closed checks, stale-owner recovery, and lifecycle start/cleanup wiring; focused concurrency tests refuse a second primary writer while read-only/feature worktrees stay unblocked. | 2026-07-25 | -| #078 | task | Generate a deterministic reconciliation evidence pack | Added report-only atomic evidence pack with dispositions, markers, archive refs, bundle verify/hash, worktree counts, and local/base equality; fixture tests prove determinism/redaction and no false completion record on interrupt. | 2026-07-25 | -| #066 | task | Land and prove the streamlined six-item sidebar | Proven on `origin/main` via PR #1174 (`4dc76306 Land streamlined six-item sidebar`). Six-item rail shipped; open ledger row was stale post-merge. | 2026-07-25 | -| #067 | issue | Reconciliation preflight test times out under full-suite load | Fixed in PR #1191 (`e2488dbb`) by calling `collectReconciliationState()` in-process; PR #1203 further injects a fixture `repositoryRoot` so the contract no longer scales with the live worktree farm. No global timeout raise or heavy-test lock bypass. | 2026-07-25 | -| #007 | rec | `/tools` vs `/?mode=tools` parallel Tools entry points | Resolved as `/tools` canonical (PT-11 already documented on `/applications` redirect). Sidebar, appModeHomeHref, universal-search, prefetch, sitemap, and reachability now use `/tools`; `/?mode=tools` remains a dashboard-mode alias. Reachability allowlist entry removed. | 2026-07-24 | -| #030 | issue | Wide-tier alias lets one doc satisfy both comparison slots | Fixed on `cursor/search-correctness-030-075-6273`: removed dual-listed Admission-to-Discharge titles from AdmissionCommunityPts so one retrieved source cannot make allHit true for both comparison slots; fail-closed contracts in `tests/eval-document-matching.test.ts`. RAG impact: no retrieval behaviour change — eval matching only. Hardened after merge: coverage dedupes by document identity and assigns by maximum matching (#080). | 2026-07-24 | -| #075 | issue | Search-scope label enumeration can truncate after 1,000 rows | Fixed on `cursor/search-correctness-030-075-6273`: `loadScopeLabels` pages document_labels with deterministic order/batching past the Supabase 1k cap; multi-page >1000 contracts in `tests/search-scope.test.ts`. Isolated from mixed PR #1132. RAG impact: no retrieval behaviour change — label pagination only. | 2026-07-24 | -| #009 | rec | Confirm `/api/jobs` is intentionally server/ops-only | Kept as deliberate administrator/ops listing: no client `fetch("/api/jobs")` (UI uses `/api/ingestion/jobs`); documented in `docs/api-jobs-ops-surface.md` plus wiring/codebase-index/site-map notes. Not abandoned — do not remove without updating API contract tests. | 2026-07-24 | -| #010 | task | Un-built "Coming soon" controls across forms/favourites | Audited forms/favourites/presentation placeholders: all use honest `disabled` or `aria-disabled` + coming-soon copy (or presentational `ToggleSwitch` without `onToggle`). No fake-interactive controls; leave unwired until features land. Recorded in `docs/wiring-conventions.md`. | 2026-07-24 | -| #032 | rec | Governance ranking weighting: REFUTED, not debt | Reinforced as guardrail only in `docs/rag-behaviour/refuted-approaches.md` (Refutation 3), README, and safeguards — do **not** implement `review_due`/unknownCurrentness ranking penalties or boosts. No retrieval/ranking code changed. RC8 filter path remains the only revisit route behind canary gates. | 2026-07-24 | -| #041 | rec | Extend the existing Factsheets reading model | Brief recorded in `docs/factsheets-reading-model-brief.md`: extend Easy Read/Standard on existing Factsheets routes; reject a second patient-facing Factsheets mode unless concrete need + source-governance plan exist. | 2026-07-24 | -| #063 | rec | Define “Current Clinical Work” before implementation | Product/privacy/persistence brief recorded in `docs/current-clinical-work-brief.md`. Default v0 = no new storage (tab/URL resume); Class C free text needs privacy clearance. Stop without demand evidence. No UI/schema implemented. | 2026-07-24 | -| #076 | task | Reproduce malformed fallback PDF image/table crops | Reproduced truncated page-edge `table_crop`s on current-main with `worker/python/fixtures/malformed-table-crop-page-edge.pdf`. Root cause: `pymupdf_find_tables` stops at the last fully detected row; fix extends the candidate from contiguous cell drawings, recovers the on-page score-5 remnant, and emits `table_crop_edge_incomplete` / `crop_completeness=0.9` when content continues past the page. PR #1176. Broad PR #1129 retention/padding changes not merged. | 2026-07-24 | -| #070 | issue | Presentation mobile tabs misroute Overview/Map/Related | Fixed in PR #1135: Overview/Map/Related deep-link to diagnosis `?tab=` sections; Compare stays on the presentation page. Regression in `tests/mobile-interaction-regressions.test.ts`. (Provisional PR-branch IDs `#068`–`#072` were renumbered after `main` claimed `#068`/`#069`.) | 2026-07-24 | -| #071 | issue | Evidence/Clinical Notes Add fakes success without persistence | Fixed in PR #1135: sticky Add controls use the focusable coming-soon placeholder pattern instead of optimistic `setAdded(true)`. | 2026-07-24 | -| #072 | issue | Tools hub exposes false Sort/More affordances | Fixed in PR #1135: Sort is a status label, More filter targets coordination/saved without a fake menu chevron, and the favourites shortcut is labelled Saved/Favourites. | 2026-07-24 | -| #073 | issue | Presentation compare dock CTA is a self-link no-op | Fixed in PR #1135: dock shows non-link "Comparing (N)" status while already comparing. | 2026-07-24 | -| #074 | issue | Mode-action popup hard-reloads internal clinical routes | Fixed in PR #1135: `master-search-header` uses `router.push` for DSM/Specifiers/Formulation actions and mode href fallback. | 2026-07-24 | -| #068 | task | Regenerate full drift-manifest snapshot after schema hygiene | Full Docker `npm run drift:manifest` replay succeeded on a Docker-capable host; `supabase/drift-manifest.json` now carries live `def_hash` values for the plpgsql table-facts body (offline generator_note removed). | 2026-07-24 | -| #052 | issue | Reindex can overlap a fresh agent-enrichment pass | PR #1143 retained the friendly full/retry preflight and closed its check-then-enqueue race with an owner-scoped transactional RPC. Reindex enqueue and the agent claim path serialize on the document row; disposable PostgreSQL proved both interleavings, and exact-head migration replay/unit/build/Chromium/policy/security checks passed. | 2026-07-24 | -| #062 | issue | Upload crash can strand a queued document without a job | Aged owner-scoped `queued`-without-open-job rows are detected by `reindex:health`; the six-hour autopilot raises a durable alert, and guarded recovery uses PR #1143's transactional RPC so enqueue is owner-scoped, idempotent and atomic. `recover:ingestion --include-stranded-queued` remains dry-run/confirmation-first; scheduled production mutation is not enabled. | 2026-07-24 | -| #060 | issue | Safety Plan Generator contradicted the privacy contract | PR #1119 removed patient identifier entry, leaves the post-export name line blank, and aligned tool, privacy and PIA copy. DOM/privacy tests and Chromium copy/print/network coverage prove working content remains in React memory with no fetch/XHR; hosted Production UI, build, unit, policy, safety, static-analysis and secret checks passed. Support-contact details remain classified as sensitive local-only working content. | 2026-07-24 | -| #061 | issue | Missing answer relevance metadata was treated as source-backed | PR #1125 now requires explicit source-backed relevance for trusted/grounded presentation and prevents visual tables, clinical-note sections and quotes, and comparison metadata from bypassing the render model. Three actionable P2 review paths were fixed; focused policy/DOM tests, offline RAG, production-readiness, build, unit, static, security, and Production UI gates passed. No retrieval, ranking, generation, provider, or data behavior changed. | 2026-07-24 | -| #034 | issue | Answer cache can serve stale governance metadata | Current-source verification found direct route coverage already asserts RAG-cache invalidation on document PATCH, source review, label, bulk, and reindex mutation paths. The residual test recommendation is already met; changing the protected cache key is unnecessary. | 2026-07-24 | -| #014 | rec | Realize the `next/image` win on signed previews | Superseded: `SignedImage` uses `next/image` for layout and sizing but deliberately sets `unoptimized`, preventing bearer signed URLs from entering the unauthenticated optimizer cache where cached content could outlive the token. No optimization task remains unless private-image delivery changes. | 2026-07-24 | -| #026 | task | Wire the Supabase document-change trigger | PR #1100 merged after disposable PostgreSQL replay and hosted migration replay. Production migration history and read-only catalog proof confirm the enabled metadata trigger, security-definer function, pinned search path and denied anonymous/authenticated execution; `npm run check:drift` reports no unexpected live drift. Delivery remains intentionally inert until the operator inputs tracked in #025 are configured. | 2026-07-24 | -| #031 | issue | Populate canary Source Governance table | The answer-quality step now consumes the preceding `golden-retrieval.json` only for source-governance reporting. Offline replay of run `30018289898` populated 338 top results, including 202 review-required entries, while retaining zero retrieval cases and no additional threshold failures. Retrieval and ranking behavior are unchanged. | 2026-07-24 | -| #020 | task | Validate eval:quality cost readout post-fix | Confirmed on merged-main canary run `30018289898`: Answer Metrics reported 9 nonzero-cost cases and an estimated answer cost of `$0.234736`; the structured report retained the same value. The PR #1050 estimator fix is operationally proven. | 2026-07-23 | -| #003 | task | Staging tenancy release evidence outstanding | Ran GitHub Action and validated isolation | 2026-07-21 | -| #002 | task | Process-ownership fix not yet isolated on `main` | Fixed process isolation using child.pid termination | 2026-07-21 | -| #008 | rec | Dead href builders in `document-flow-routes.ts` | Not dead code (false positive): `documentReaderHref`/`documentEvidenceHref` are live via the mock wrappers in `src/components/document-search-mockups.tsx` + `src/components/master-document-flow-mockups.tsx` (rendered under `src/app/mockups/document-search/`) and covered by `tests/document-flow-routes.test.ts`; removing breaks the build. Only the production non-mock hrefs are unlinked from prod UI — a wiring gap, not dead code. | 2026-07-22 | -| #015 | task | Content-first fallback regression tests | Added `tests/registry-record-loader.dom.test.tsx` (8) + `tests/medication-record-page.dom.test.tsx` (6) covering content-first fallback paint, live swap-in, spinner/skeleton, error + not-found/unauthorized states, and the invariant that no authoritative verification badge shows before live governance reconciles (registry fixture-flag neutralization + medication governance-drop-on-error). | 2026-07-22 | -| #004 | rec | Rescope provider-gated RAG safety ideas | Closed obsolete — rescue source (754-line RAG-safety worktree) unrecoverable/pruned across all refs; answer-quality thresholds + deep-health already shipped on `main` (#585/#587); only cost-cap preflight was genuinely missing and, per session decision, dropped rather than re-filed. | 2026-07-22 | -| #006 | issue | Globe "Language & region" button had no handler | Resolved on main with the repository's disabled "Coming soon" placeholder convention and button-wiring coverage. Future language/region work remains a feature request, not an inert-control defect. | 2026-07-22 | -| #042 | issue | Invalid optional credentials fell into anonymous access | PRs #1078/#1079 introduced `absent \| valid \| invalid`, return 401 for presented invalid credentials, preserve authoritative header precedence and prefer the current-project session cookie. The archived anonymous-upload metadata patch was rejected as stale because uploads are already administrator-only before duplicate lookup. | 2026-07-22 | -| #043 | issue | Readiness could report healthy or throw on Supabase errors | PR #1080 now fails readiness closed for returned and thrown dependency failures, preserves recognized actionable messages, and prevents raw dependency-error disclosure. | 2026-07-22 | -| #044 | issue | Publication approval was not bound to immutable reviewed state | PR #1081 added a canonical reviewed-state digest, row locks, active-job rejection and a new forward migration with replay/schema/type/drift evidence. | 2026-07-22 | -| #045 | issue | Bulk reindex discarded partial-success results | PR #1084 reserves preflight conflicts for non-2xx responses; completed mixed batches return per-item success/failure/missing results, and the UI refreshes successful work. | 2026-07-22 | -| #046 | issue | DOCX extraction lacked explicit resource budgets | PR #1085 added pre-inflate declared-size checks and post-read fail-safes for artifact count, per-artifact bytes, aggregate media, Word XML and extracted UTF-8 text. | 2026-07-22 | -| #047 | issue | XLSX extraction could construct unbounded results | PR #1086 bounds worksheets, non-empty rows, rendered cells and UTF-8 output while preserving sparse-column rendering. | 2026-07-22 | -| #048 | issue | Account copy overstated sync/privacy and enabled unavailable SSO | PR #1087 now maps copy to actual favourites/preferences persistence, identifies browser-session recents, removes the contradictory "never shared" claim and clearly disables unavailable providers using the accessible placeholder contract. | 2026-07-22 | -| #049 | issue | Process diagnostic exposed a Cursor worker API key | The exact worker was stopped, the key was revoked server-side, both local encrypted worker-secret records were removed, and authorized repository/backup scans found no plaintext copy. Follow-up guardrails now prevent repository process inventory from serializing command lines and redact heavyweight-lock command text before persistence or errors. | 2026-07-23 | -| #050 | issue | Next.js 16.2.10 remained in a high-severity security range | Upgraded `next` and `@next/env` to 16.2.11, regenerated the npm lockfile, confirmed the production dependency audit is clean, and passed focused framework checks, `verify:cheap`, and the full Chromium UI gate. | 2026-07-23 | +| ID | Type | Summary | Outcome | Resolved | +| ---- | ----- | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | +| #087 | issue | `npm run check:knip` reported pre-existing dependency findings | NOT A DEFECT — false positive. The findings (unused `rimraf`/`tsx`/`@testing-library/dom`, unlisted `playwright-core`) only appeared because the worktree had no `node_modules` of its own and tooling resolved from the parent checkout. After `npm ci` in the same worktree, `npm run check:knip` exits 0 with no ignore-list change. Durable lesson: never act on a knip finding from a worktree that has not been installed. | 2026-07-28 | +| #089 | issue | Branch-review-ledger hygiene deferred from PR #1275 | Closed by the 2026-07-28 hygiene pass. The MD056 cell-count and duplicate-row findings dispositioned as "belongs in a dedicated main ledger hygiene pass" on PR #1275 are fixed: 140 mojibake lines restored byte-exact from git history, 6 residual separators repaired, 46 exact duplicates removed, 21 wrong-width rows normalised, and 4 heading+bullet records converted to table rows — 1067 records, all six cells. Root cause closed too: `npm run ledger:lookup` / `ledger:append` (`scripts/branch-review-ledger.mjs`) replace hand-written rows, and `check:branch-review-ledger` now fails on mojibake, cell width, heading records, impossible dates, table gaps, and (from 2026-07-29) unresolvable HEADs and near-duplicates. | 2026-07-28 | +| #111 | issue | `ui-overlap` phone-inset case still flakes under load | RESOLVED 2026-07-29 (PR #1391). The inset measurement now retries inside `expect(async () => …).toPass({ timeout: 15_000 })` (`ui-overlap.spec.ts:159`) — the same retry-the-measurement shape PR #1375 applied to the eight overlap cases. The 2px symmetry tolerance and the assertions themselves are byte-for-byte unchanged, so a genuinely asymmetric header still fails once the retry budget is spent; only a transient mid-remount sample settles. Full spec green across 4 consecutive runs (14 passed each). Do not relax the tolerance if this recurs — the assertion is the point; make the measurement robust instead. Source: PR #1375 flake triage; session 2026-07-29 | 2026-07-29 | +| #112 | issue | `issues:next-id` has no concurrency protection | RESOLVED 2026-07-30. `npm run check:outstanding-issues` now gates this file, in `verify:cheap` and in the `static-pr` CI job (the gate-manifest check refuses a local gate that CI does not run). It fails on a duplicate id, an id present in both tables, a marker at or below the highest id, a malformed row, and a missing heading or marker — so every shape the 2026-07-29 triple collision took is now a red gate rather than a silent row loss. Verified by replaying that collision against the real file: two rows claiming `#110` produced "#110 appears 2 times (lines 151, 164)", and a lost marker bump produced "issues:next-id=113 is not above the highest id #114". The checker honours `\|` escapes — its first run against the live file flagged row #042, which is correctly escaped, and a gate with false positives is a gate people switch off. NOT fixed: the underlying race. Ids are still allocated by read-modify-write with no lock, and this file still has no `merge=union` driver; what changed is that a collision can no longer land silently. Source: session 2026-07-29 PR sweep; PR #1391 conflict resolution; `.gitattributes` | 2026-07-30 | +| #113 | issue | `ModeNav` clips its labels at every phone width on `main` | RESOLVED 2026-07-30 (PR #1405, landed on `main` as `020c1260`). The two lower density bands used `grid-auto-columns: 1fr`, so the widest slot set every slot's width and the label's `truncate` hid the shortfall — nothing overflowed and nothing failed, the word was simply gone. `display: flex` at every band removes the mode rather than retuning it; thresholds went `16/26/34rem` -> `22/33/42rem` against remeasured intrinsic widths (471px for four labels, not the 394px originally budgeted). `tests/ui-mode-nav-density.spec.ts` asserts at both band boundaries and one pixel either side, registered in BOTH Playwright allowlists. **Carry-forward:** the ~8% threshold headroom is not padding — a measured-plus-one-pixel attempt (21/31rem) passed locally and failed CI by exactly 1px, because the variable face rasterises wider on `ubuntu-24.04`. A threshold calibrated to one machine's font metrics is calibrated to nothing. | 2026-07-30 | +| #114 | issue | The live Web-Vitals instrument cannot measure its own noise | RESOLVED 2026-07-30 (PR #1407, landed as `a73db460`). Each route/strategy cell is measured `SAMPLES` times (default 3), the MEDIAN is graded, and a cell whose samples straddle a threshold is reported as incomplete evidence rather than resolved either way — so #017's "evidence is too noisy" stop rule can finally fire. Mid-run Chrome drift disqualifies the verdict. **Second defect found in review and fixed in the same PR:** `samples` is a free-text `workflow_dispatch` input, so `samples=1` reached the grader as a valid integer and reproduced the exact single-run matrix this work removes — verified against the pre-fix logic, it reported 0 breaches, 0 incomplete and printed the bold "Every mobile route is within" verdict. Now refused in three places (`main()` before reading any report, `renderTable`'s disqualifier list, and the workflow before spending a live measurement pass). | 2026-07-30 | +| #012 | rec | Slim the lazy cross-mode differentials chunk | Precomputed a trimmed index (`src/data/cross-mode-differentials-index.json` via `scripts/build-cross-mode-differentials-index.mjs`) so the lazily-loaded cross-mode chunk imports a ~53 KB catalog instead of statically pulling the ~1.2 MB differentials snapshot (only that dynamic path reached it). A drift test plus `check:cross-mode-index` (in verify:cheap) lock the index to the live projection. | 2026-07-27 | +| #029 | issue | Residual answer-quality fallback stubs | Closed after fixing each causal cluster independently. Active-community ED, community-home-visit, clozapine blood-threshold/typo, discharge source-gap recovery, and Best Practice Prescription now use narrowly validated, source-bound answers or auditable recovery; cited provider refusal prose can no longer masquerade as grounded, and terminal gaps retain no claim citations. The final 44-case gate reported 30/30 substantive grounded supported answers, 14/14 unsupported correct, zero review fallbacks, zero citation/numeric failures, and zero route-ceiling failures. Measurement still reports review fallback separately and denies targeting credit for echoed boilerplate. | 2026-07-27 | +| #019 | issue | Preserve admission/discharge sources through comparison fallback | The actual fallback path now selects source-bound facts, preserves one admission and one discharge citation from distinct documents, and terminates at an evidence gap for qualified, negated, unrelated, title-only, single-sided, or same-document traps. Both exact live cases complete in about one second with zero provider calls; the final 44-case canary passed them with two citations each, while the 36-case retrieval canary held recall 1.0 and zero RR regressions. Retrieval scores, aliases, clamps, and comparator ordering were unchanged. | 2026-07-27 | +| #084 | task | Persist per-result irrelevant-at-10 grading evidence | `eval-retrieval` now persists each top result's `relevanceGrade` and `matchedDeclaredSignals`; focused fixtures cover ideal and zero-grade rows. The final golden artifact contains 338 graded top rows, including 33 grade-zero rows. This closes the reproducibility gap only: fixture labels, ranking, thresholds, and provider behavior were not changed, and human disposition remains #023. | 2026-07-27 | +| #080 | rec | Re-test the removed admission-to-discharge alias widening | Restored the two approved NMHS Admission-to-Discharge titles only on the eval-expectation surface. Canonical document-identity dedupe plus maximum bipartite matching prevents one dual-listed physical document from satisfying both comparison slots. Focused matching tests, both targeted admission cases, the final 36-case golden retrieval run, and the 44-case answer run passed; runtime retrieval/ranking behavior was not changed. | 2026-07-27 | +| #083 | issue | Documents-only universal search timed out on staging tenancy | A current staging nightly reproducer showed the documents-only search losing its synthetic fixture after the federated typeahead timeout was reduced to 750 ms. Current main retains 750 ms for multi-domain requests and uses the established 6,000 ms budget only when documents are the sole requested domain; fake-timer coverage proves both paths. RAG impact: no retrieval, ranking, ordering, alias, score, or result-selection change—only availability of the explicitly focused request. | 2026-07-27 | +| #082 | issue | Bot branch-sync heads leave required checks unapproved | Retired the automatic `GITHUB_TOKEN` PR branch-update workflow instead of weakening required-check approvals or introducing a privileged automation token. The existing helper remains dry-run by default, verifies its apply identity, and refuses missing or bot identities. The fast GitHub Actions policy check rejects both direct workflow `update-branch` calls and indirect apply-helper invocation. | 2026-07-27 | +| #058 | task | Verify production content before any seed write | Read-only production counts on project `sjrfecxgysukkwxsowpy` found 276 clinical registry, 328 medication, and 232 differential records. The required tables are non-empty, so no seed or production write was needed. | 2026-07-27 | +| #069 | task | Validate hosted table-facts RPC latency | Read-only profiling on the correct hosted project separated sample 1 (`first_unprimed`) from five `warm_repeat` samples; managed Supabase buffers were not flushed, so no true-cold claim is made. First-unprimed client/DB execution was 662.916/141.537 ms (clozapine), 277.661/96.229 ms (lithium), and 322.598/148.378 ms (metabolic). Warm client median/p90 was 187.029/198.907, 167.803/174.391, and 189.065/243.324 ms; warm DB execution median/p90 was 88.292/89.355, 64.342/65.566, and 107.448/148.417 ms. Earlier exact clinical probes were lower again. Plans are not the multi-second tail; no hosted migration, ranking, or provider configuration changed. | 2026-07-27 | +| #051 | task | Stabilise the live answer-quality canary before more RAG tuning | Closed after the scheduled structured report supplied a comparable second 36-retrieval/44-answer datapoint. Content gates stayed stable, the prior citation failure cleared, and #019 repeated with an identical diagnostic signature. Retrieval latency was investigated separately: #069 subsequently found acceptable table-facts database plans, so the broad scheduled tail was not treated as ranking debt. The report/trend tooling is now sufficient to compare future approved runs; no scheduled rerun or tuning was dispatched. | 2026-07-27 | +| #054 | task | Reconcile local and hosted secrets/config | Completed production names-only reconciliation on 2026-07-27. The correctly identified primary checkout received distinct gitignored local safety/query-hash/deep-probe values. A hardened checker now pins GitHub to `BigSimmo/Database` and Railway to the live production project/environment plus `Database`/`worker`, catches multiline schema and `.env.example` drift, and verifies GitHub secrets/variables and per-service Railway contracts without emitting provider values. All required names passed; the Ops Digest workflow is active with a successful scheduled run; both Railway services have later successful deployments; Supabase names-only proof found the expected cron/Vault configuration. Value equality remains deliberately unobservable, staging stays #056, webhook activation stays #025, and legal/ZDR work stays #053. | 2026-07-27 | +| #064 | task | Reconcile the preserved browser and contrast patch | Landed via PR #1250 squash `b91b4600171be08198e92bcf19b7d67e8207cb2f`. Opacity-free disabled Previous/Continue styling plus native-disabled/focus/axe Playwright coverage is on `main`. Historical `agent/formulation-disabled-contrast` remained unrecovered; conflicted PRs #1219/#1223/#1226/#1231/#1249 were closed without merge. | 2026-07-26 | +| #081 | issue | Open PR #1196 would undo the #030 alias tightening | Closed as no longer live: PR #1196 was closed 2026-07-25 as superseded by #913/current `main` (~680 commits behind, conflicting), and its successor #1198 does not touch `src/lib/eval-document-matching.ts`. The generalized alias-disjointness and single-document contracts landed in PR #1215 fail closed if any later branch re-adds the dual-listed admission aliases, so the regression route is guarded rather than watched. | 2026-07-25 | +| #077 | issue | Concurrent tasks can re-dirty the canonical primary checkout | Added cooperative primary-checkout write lease with dirty/operation fail-closed checks, stale-owner recovery, and lifecycle start/cleanup wiring; focused concurrency tests refuse a second primary writer while read-only/feature worktrees stay unblocked. | 2026-07-25 | +| #078 | task | Generate a deterministic reconciliation evidence pack | Added report-only atomic evidence pack with dispositions, markers, archive refs, bundle verify/hash, worktree counts, and local/base equality; fixture tests prove determinism/redaction and no false completion record on interrupt. | 2026-07-25 | +| #066 | task | Land and prove the streamlined six-item sidebar | Proven on `origin/main` via PR #1174 (`4dc76306 Land streamlined six-item sidebar`). Six-item rail shipped; open ledger row was stale post-merge. | 2026-07-25 | +| #067 | issue | Reconciliation preflight test times out under full-suite load | Fixed in PR #1191 (`e2488dbb`) by calling `collectReconciliationState()` in-process; PR #1203 further injects a fixture `repositoryRoot` so the contract no longer scales with the live worktree farm. No global timeout raise or heavy-test lock bypass. | 2026-07-25 | +| #007 | rec | `/tools` vs `/?mode=tools` parallel Tools entry points | Resolved as `/tools` canonical (PT-11 already documented on `/applications` redirect). Sidebar, appModeHomeHref, universal-search, prefetch, sitemap, and reachability now use `/tools`; `/?mode=tools` remains a dashboard-mode alias. Reachability allowlist entry removed. | 2026-07-24 | +| #030 | issue | Wide-tier alias lets one doc satisfy both comparison slots | Fixed on `cursor/search-correctness-030-075-6273`: removed dual-listed Admission-to-Discharge titles from AdmissionCommunityPts so one retrieved source cannot make allHit true for both comparison slots; fail-closed contracts in `tests/eval-document-matching.test.ts`. RAG impact: no retrieval behaviour change — eval matching only. Hardened after merge: coverage dedupes by document identity and assigns by maximum matching (#080). | 2026-07-24 | +| #075 | issue | Search-scope label enumeration can truncate after 1,000 rows | Fixed on `cursor/search-correctness-030-075-6273`: `loadScopeLabels` pages document_labels with deterministic order/batching past the Supabase 1k cap; multi-page >1000 contracts in `tests/search-scope.test.ts`. Isolated from mixed PR #1132. RAG impact: no retrieval behaviour change — label pagination only. | 2026-07-24 | +| #009 | rec | Confirm `/api/jobs` is intentionally server/ops-only | Kept as deliberate administrator/ops listing: no client `fetch("/api/jobs")` (UI uses `/api/ingestion/jobs`); documented in `docs/api-jobs-ops-surface.md` plus wiring/codebase-index/site-map notes. Not abandoned — do not remove without updating API contract tests. | 2026-07-24 | +| #010 | task | Un-built "Coming soon" controls across forms/favourites | Audited forms/favourites/presentation placeholders: all use honest `disabled` or `aria-disabled` + coming-soon copy (or presentational `ToggleSwitch` without `onToggle`). No fake-interactive controls; leave unwired until features land. Recorded in `docs/wiring-conventions.md`. | 2026-07-24 | +| #032 | rec | Governance ranking weighting: REFUTED, not debt | Reinforced as guardrail only in `docs/rag-behaviour/refuted-approaches.md` (Refutation 3), README, and safeguards — do **not** implement `review_due`/unknownCurrentness ranking penalties or boosts. No retrieval/ranking code changed. RC8 filter path remains the only revisit route behind canary gates. | 2026-07-24 | +| #041 | rec | Extend the existing Factsheets reading model | Brief recorded in `docs/factsheets-reading-model-brief.md`: extend Easy Read/Standard on existing Factsheets routes; reject a second patient-facing Factsheets mode unless concrete need + source-governance plan exist. | 2026-07-24 | +| #063 | rec | Define “Current Clinical Work” before implementation | Product/privacy/persistence brief recorded in `docs/current-clinical-work-brief.md`. Default v0 = no new storage (tab/URL resume); Class C free text needs privacy clearance. Stop without demand evidence. No UI/schema implemented. | 2026-07-24 | +| #076 | task | Reproduce malformed fallback PDF image/table crops | Reproduced truncated page-edge `table_crop`s on current-main with `worker/python/fixtures/malformed-table-crop-page-edge.pdf`. Root cause: `pymupdf_find_tables` stops at the last fully detected row; fix extends the candidate from contiguous cell drawings, recovers the on-page score-5 remnant, and emits `table_crop_edge_incomplete` / `crop_completeness=0.9` when content continues past the page. PR #1176. Broad PR #1129 retention/padding changes not merged. | 2026-07-24 | +| #070 | issue | Presentation mobile tabs misroute Overview/Map/Related | Fixed in PR #1135: Overview/Map/Related deep-link to diagnosis `?tab=` sections; Compare stays on the presentation page. Regression in `tests/mobile-interaction-regressions.test.ts`. (Provisional PR-branch IDs `#068`–`#072` were renumbered after `main` claimed `#068`/`#069`.) | 2026-07-24 | +| #071 | issue | Evidence/Clinical Notes Add fakes success without persistence | Fixed in PR #1135: sticky Add controls use the focusable coming-soon placeholder pattern instead of optimistic `setAdded(true)`. | 2026-07-24 | +| #072 | issue | Tools hub exposes false Sort/More affordances | Fixed in PR #1135: Sort is a status label, More filter targets coordination/saved without a fake menu chevron, and the favourites shortcut is labelled Saved/Favourites. | 2026-07-24 | +| #073 | issue | Presentation compare dock CTA is a self-link no-op | Fixed in PR #1135: dock shows non-link "Comparing (N)" status while already comparing. | 2026-07-24 | +| #074 | issue | Mode-action popup hard-reloads internal clinical routes | Fixed in PR #1135: `master-search-header` uses `router.push` for DSM/Specifiers/Formulation actions and mode href fallback. | 2026-07-24 | +| #068 | task | Regenerate full drift-manifest snapshot after schema hygiene | Full Docker `npm run drift:manifest` replay succeeded on a Docker-capable host; `supabase/drift-manifest.json` now carries live `def_hash` values for the plpgsql table-facts body (offline generator_note removed). | 2026-07-24 | +| #052 | issue | Reindex can overlap a fresh agent-enrichment pass | PR #1143 retained the friendly full/retry preflight and closed its check-then-enqueue race with an owner-scoped transactional RPC. Reindex enqueue and the agent claim path serialize on the document row; disposable PostgreSQL proved both interleavings, and exact-head migration replay/unit/build/Chromium/policy/security checks passed. | 2026-07-24 | +| #062 | issue | Upload crash can strand a queued document without a job | Aged owner-scoped `queued`-without-open-job rows are detected by `reindex:health`; the six-hour autopilot raises a durable alert, and guarded recovery uses PR #1143's transactional RPC so enqueue is owner-scoped, idempotent and atomic. `recover:ingestion --include-stranded-queued` remains dry-run/confirmation-first; scheduled production mutation is not enabled. | 2026-07-24 | +| #060 | issue | Safety Plan Generator contradicted the privacy contract | PR #1119 removed patient identifier entry, leaves the post-export name line blank, and aligned tool, privacy and PIA copy. DOM/privacy tests and Chromium copy/print/network coverage prove working content remains in React memory with no fetch/XHR; hosted Production UI, build, unit, policy, safety, static-analysis and secret checks passed. Support-contact details remain classified as sensitive local-only working content. | 2026-07-24 | +| #061 | issue | Missing answer relevance metadata was treated as source-backed | PR #1125 now requires explicit source-backed relevance for trusted/grounded presentation and prevents visual tables, clinical-note sections and quotes, and comparison metadata from bypassing the render model. Three actionable P2 review paths were fixed; focused policy/DOM tests, offline RAG, production-readiness, build, unit, static, security, and Production UI gates passed. No retrieval, ranking, generation, provider, or data behavior changed. | 2026-07-24 | +| #034 | issue | Answer cache can serve stale governance metadata | Current-source verification found direct route coverage already asserts RAG-cache invalidation on document PATCH, source review, label, bulk, and reindex mutation paths. The residual test recommendation is already met; changing the protected cache key is unnecessary. | 2026-07-24 | +| #014 | rec | Realize the `next/image` win on signed previews | Superseded: `SignedImage` uses `next/image` for layout and sizing but deliberately sets `unoptimized`, preventing bearer signed URLs from entering the unauthenticated optimizer cache where cached content could outlive the token. No optimization task remains unless private-image delivery changes. | 2026-07-24 | +| #026 | task | Wire the Supabase document-change trigger | PR #1100 merged after disposable PostgreSQL replay and hosted migration replay. Production migration history and read-only catalog proof confirm the enabled metadata trigger, security-definer function, pinned search path and denied anonymous/authenticated execution; `npm run check:drift` reports no unexpected live drift. Delivery remains intentionally inert until the operator inputs tracked in #025 are configured. | 2026-07-24 | +| #031 | issue | Populate canary Source Governance table | The answer-quality step now consumes the preceding `golden-retrieval.json` only for source-governance reporting. Offline replay of run `30018289898` populated 338 top results, including 202 review-required entries, while retaining zero retrieval cases and no additional threshold failures. Retrieval and ranking behavior are unchanged. | 2026-07-24 | +| #020 | task | Validate eval:quality cost readout post-fix | Confirmed on merged-main canary run `30018289898`: Answer Metrics reported 9 nonzero-cost cases and an estimated answer cost of `$0.234736`; the structured report retained the same value. The PR #1050 estimator fix is operationally proven. | 2026-07-23 | +| #003 | task | Staging tenancy release evidence outstanding | Ran GitHub Action and validated isolation | 2026-07-21 | +| #002 | task | Process-ownership fix not yet isolated on `main` | Fixed process isolation using child.pid termination | 2026-07-21 | +| #008 | rec | Dead href builders in `document-flow-routes.ts` | Not dead code (false positive): `documentReaderHref`/`documentEvidenceHref` are live via the mock wrappers in `src/components/document-search-mockups.tsx` + `src/components/master-document-flow-mockups.tsx` (rendered under `src/app/mockups/document-search/`) and covered by `tests/document-flow-routes.test.ts`; removing breaks the build. Only the production non-mock hrefs are unlinked from prod UI — a wiring gap, not dead code. | 2026-07-22 | +| #015 | task | Content-first fallback regression tests | Added `tests/registry-record-loader.dom.test.tsx` (8) + `tests/medication-record-page.dom.test.tsx` (6) covering content-first fallback paint, live swap-in, spinner/skeleton, error + not-found/unauthorized states, and the invariant that no authoritative verification badge shows before live governance reconciles (registry fixture-flag neutralization + medication governance-drop-on-error). | 2026-07-22 | +| #004 | rec | Rescope provider-gated RAG safety ideas | Closed obsolete — rescue source (754-line RAG-safety worktree) unrecoverable/pruned across all refs; answer-quality thresholds + deep-health already shipped on `main` (#585/#587); only cost-cap preflight was genuinely missing and, per session decision, dropped rather than re-filed. | 2026-07-22 | +| #006 | issue | Globe "Language & region" button had no handler | Resolved on main with the repository's disabled "Coming soon" placeholder convention and button-wiring coverage. Future language/region work remains a feature request, not an inert-control defect. | 2026-07-22 | +| #042 | issue | Invalid optional credentials fell into anonymous access | PRs #1078/#1079 introduced `absent \| valid \| invalid`, return 401 for presented invalid credentials, preserve authoritative header precedence and prefer the current-project session cookie. The archived anonymous-upload metadata patch was rejected as stale because uploads are already administrator-only before duplicate lookup. | 2026-07-22 | +| #043 | issue | Readiness could report healthy or throw on Supabase errors | PR #1080 now fails readiness closed for returned and thrown dependency failures, preserves recognized actionable messages, and prevents raw dependency-error disclosure. | 2026-07-22 | +| #044 | issue | Publication approval was not bound to immutable reviewed state | PR #1081 added a canonical reviewed-state digest, row locks, active-job rejection and a new forward migration with replay/schema/type/drift evidence. | 2026-07-22 | +| #045 | issue | Bulk reindex discarded partial-success results | PR #1084 reserves preflight conflicts for non-2xx responses; completed mixed batches return per-item success/failure/missing results, and the UI refreshes successful work. | 2026-07-22 | +| #046 | issue | DOCX extraction lacked explicit resource budgets | PR #1085 added pre-inflate declared-size checks and post-read fail-safes for artifact count, per-artifact bytes, aggregate media, Word XML and extracted UTF-8 text. | 2026-07-22 | +| #047 | issue | XLSX extraction could construct unbounded results | PR #1086 bounds worksheets, non-empty rows, rendered cells and UTF-8 output while preserving sparse-column rendering. | 2026-07-22 | +| #048 | issue | Account copy overstated sync/privacy and enabled unavailable SSO | PR #1087 now maps copy to actual favourites/preferences persistence, identifies browser-session recents, removes the contradictory "never shared" claim and clearly disables unavailable providers using the accessible placeholder contract. | 2026-07-22 | +| #049 | issue | Process diagnostic exposed a Cursor worker API key | The exact worker was stopped, the key was revoked server-side, both local encrypted worker-secret records were removed, and authorized repository/backup scans found no plaintext copy. Follow-up guardrails now prevent repository process inventory from serializing command lines and redact heavyweight-lock command text before persistence or errors. | 2026-07-23 | +| #050 | issue | Next.js 16.2.10 remained in a high-severity security range | Upgraded `next` and `@next/env` to 16.2.11, regenerated the npm lockfile, confirmed the production dependency audit is clean, and passed focused framework checks, `verify:cheap`, and the full Chromium UI gate. | 2026-07-23 | +| #095 | issue | `PR required` reports failure for concurrency-cancelled jobs | RESOLVED 2026-07-30. The `pr-required` aggregate now distinguishes a cancelled job from a failed one: `require_success` / `require_skipped_or_success` route a `cancelled` result through a shared `cancelled_error` helper that names the newest run for the head and tells a reader who finds no newer run that the run verified nothing and must be re-run rather than merged past. **The tempting fix was rejected as unsafe:** treating `cancelled` as neutral, or skipping the aggregate via `if: !cancelled()`, would make the red disappear, but GitHub counts a SKIPPED required check as PASSING, so a hand-cancelled run on the current head would become mergeable with nothing verified. The result therefore stays RED; only the diagnosis cost was removed. Guarded by seven cases in `tests/ci-cache-safety.test.ts` that EXECUTE the extracted aggregate script under synthetic job results rather than grepping the YAML, including one pinning `if: always()` plus the wiring and one asserting no cancelled required job can exit 0; three are mutation-proven against the pre-fix aggregate. Archived by the 2026-07-30 triage pass: fixed with tests, no residual action. Source: PR #1316 runs 30340972329 / 30341225585 | 2026-07-30 | +| #096 | task | PR #1316 review follow-ups — adoption-gate coverage closed | RESOLVED 2026-07-30. Every sub-item is dispositioned. The band adoption gate's root-path gap closed on PR #1394 — root and href-less modes now resolve to `src/app/(search-app)/page.tsx` — and closing it surfaced two further defects in the same gate the original finding did not name: the hand-rolled walk was capped at two import hops where the root route's real chain is four, and it followed neither `layout.tsx` (where that route's band actually comes from) nor `dynamic(() => import(...))` (how the dashboard code-splits its mode workspaces). All three were fixed together with a bounded BFS, each verified load-bearing by reverting it and watching the gate fail. Four findings — favourites hub counts, the document-search status derivation, the 401 session-expiry path, the record-path duplicate notice — were already fixed independently. The Therapy Compass retry-waiter finding was corrected to NOT a live defect: `retryWaitersRef` is genuinely unscoped but no caller observes it. The seven Codex follow-up SHAs remain unreachable and were never pushed; the PR #1316 review threads are the durable source. Archived by the 2026-07-30 triage pass. Source: PR #1316 review sweep | 2026-07-30 | +| #104 | rec | Worker's triple image read is deliberate, not debt | NOT DEBT — archived so a fourth audit does not re-file it. The 2026-07-28 latency audit listed L4-2 (`worker/main.ts` reads each extracted image up to 3x per document — hash, caption on cache miss, upload) as "CONFIRMED with no fix evidence", carried forward from the 2026-07-01 audit's L11. **That was wrong.** The 2026-07-01 disposition table already recorded it as a deliberate peak-memory trade-off, and the rationale is documented in place at `worker/main.ts:866-869`: holding every extracted image Buffer for a document with hundreds of multi-MB page images would multiply the worker's peak memory, and disk I/O is the cheaper resource for a background pipeline. The three reads (`:872`, `:1034`, `:1129`) are real but accepted. This row carried no next action, so it is archived rather than left open — revisit only if ingestion throughput becomes a measured complaint AND a bounded-buffer design is proposed. **Stop:** do not re-file this as debt. Source: `docs/audit/repo-audit-2026-07-01.md` L11 + disposition table | 2026-07-30 | +| #109 | issue | Remote sessions clone shallow, invalidating branch analysis | RESOLVED 2026-07-29, hardened in review. `scripts/sweep-branch-ledger.mjs` refuses outright on a shallow clone via the exported `shallowCloneRefusal`, printing no inventory and exiting 1 in both text and `--json` mode, before the fetch and before any branch maths; `docs/branch-cleanup-guide.md` §Safety Rules gains the `is-shallow-repository` precondition ahead of its numbered steps. An indeterminate result (empty output from a swallowed `git` failure) is refused as its own failure, and the same guard had to move OUT of the reconciliation preflight CLI and INTO the exported `collectReconciliationState`, because `buildReconciliationEvidencePack` calls the collector directly and stamped `status: complete` — in a `--depth 1` clone the guarded CLI exited 1 while the evidence-pack command exited 0 and persisted shallow ahead/behind as completed evidence. The collector now throws `UnverifiedHistoryError` so every current and future caller fails closed by default. Review found two further routes to a false-empty inventory from half-checked refspecs; coverage requires exactly `refs/heads/*` to `refs/remotes/origin/*`. Proven against real `--depth 1` clones in `tests/repo-hygiene.test.ts`, `tests/reconciliation-preflight.test.ts` and `tests/reconciliation-evidence-pack.test.ts`. Source: session 2026-07-29 | 2026-07-30 | +| #115 | rec | Band adoption gate treats a discovered import as rendered | RESOLVED 2026-07-30 by redesign rather than another patch. `tests/search-results-band-adoption.test.ts` no longer asks whether a file mentions the band but whether anything the route actually mounts reaches it: it parses each module with `@babel/parser` into a small graph — exported name to local declaration, local to the identifiers its body references, and which locals render the band — then walks from the route's default export, carrying at each hop the set of exports the importer mounts. A static `import { X }` is followed only when `X` is reachable from a mounted declaration; `dynamic(() => import(...).then((m) => m.Named))` follows only that binding; a bare side-effect import is not followed at all; `export *` never supplies a default. **Why redesign:** six false greens were reported in one day, all one defect — presence is not reach — and two of the six had been introduced by an earlier patch to the same walker. Verified: all five production search routes still reach the band, gutting `services/page.tsx` and `tools/page.tsx` to `
` reports an orphan each, and fourteen temp-dir fixtures cover both directions with two mutation-confirmed. Accepted residual: reachability is per module and control flow inside a mounted declaration is not modelled. Source: PR #1400 | 2026-07-30 | From 61570219c63802d9c0a6ba0115ef146504478635 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Thu, 30 Jul 2026 18:00:20 +0800 Subject: [PATCH 2/3] docs: record PR 1428 review --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 12cebf10a6..68e2fc45c0 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -166,3 +166,4 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-07-30 | codex/chat-dependency-pr-review-dependency-pr-review-20260730 | 2aee8c74e64bd09954fe1b474c73b25805063de0 | open PR changed-scope review | APPROVE: setup action description now matches clean npm install with npm-download caching; no behavior change. | check:github-actions PASS; diff review; no unresolved threads | | 2026-07-30 | claude/top-search-design-mockups-w53znc | 939d5799b9999f3f63928e1b2c95d097f07eff90 | open PR changed-scope review | APPROVE: PR 1400 closeout and issue IDs 131-134 are unique, internally consistent, and preserve the append-only ledgers. | check:branch-review-ledger PASS; check:outstanding-issues PASS; diff review; no unresolved threads | | 2026-07-30 | cursor/safe-branch-cleanup-78a8 | c5190f38834ef2e928edc4876d971aa9d5d69fe7 | open PR changed-scope review | APPROVE after fixes: cleanup refs are discoverable, provider evidence is accurate, and issue 108 closure is preserved against current main. | check:branch-review-ledger PASS; check:outstanding-issues PASS; two review threads resolved | +| 2026-07-30 | claude/outstanding-issues-triage-24c8ow | 8d2710fd6cbdc84e8c50a6c9bc0a1e1a0cd612c8 | open PR changed-scope review | APPROVE: completed items 095, 096, 104, 109, and 115 move to archive with no deletion, duplicate ID, or stale next-id. | check:outstanding-issues PASS; check:branch-review-ledger PASS; diff review; no unresolved threads | From 1831cd244ed12bd0b6eca06e9b1c5e83f066fd20 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Thu, 30 Jul 2026 18:07:31 +0800 Subject: [PATCH 3/3] docs: align issue 109 resolution date --- docs/outstanding-issues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index 3770b54dea..5ddaa6d1a7 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -169,7 +169,7 @@ Move resolved rows here with the resolution date and a one-line outcome. Keep th | #095 | issue | `PR required` reports failure for concurrency-cancelled jobs | RESOLVED 2026-07-30. The `pr-required` aggregate now distinguishes a cancelled job from a failed one. `require_success` / `require_skipped_or_success` are thin wrappers over one `record()` collector that reads **each job's own `result`** and appends to a `failures` or `cancellations` array; both arrays are filled before anything is reported. **Genuine failures win:** every failure is emitted as its own `::error::` and a concurrent cancellation is demoted to a `::warning::`, so a run that is cancelled AND broken cannot read as an excuse (refinement reported by Codex on PR #1409). Cancelled with nothing failing stays **RED**, and the message states the two possibilities rather than asserting supersession: it points the reader at a newer `PR required` run on the PR's current head SHA, and says that if there is none the run was cancelled by hand and must be re-run rather than merged past. It deliberately does NOT read the workflow-level cancelled status function — an earlier revision passed that through an `env:` value, which is invalid because GitHub allows those functions only in `if:` conditions, so the whole file failed to parse, ran as `.github/workflows/ci.yml` instead of `CI`, and created zero jobs; valid YAML but invalid Actions schema, so prettier and every local gate passed it. **The tempting fix was rejected as unsafe:** treating `cancelled` as neutral, or skipping the aggregate via a not-cancelled condition, would make the red disappear, but GitHub counts a SKIPPED required check as PASSING, so a hand-cancelled run on the current head would become mergeable with nothing verified — `if: always()` is therefore deliberate. Guarded by ten cases in `tests/ci-cache-safety.test.ts` that EXECUTE the extracted aggregate script under synthetic job results rather than grepping the YAML. **Corrected in review (Codex, PR #1428):** the first version of this record described a shared `cancelled_error` helper that never existed and claimed the error names the newest run; both were carried over from an obsolete sentence in the open row and neither matched the shipped implementation. Source: PR #1316 runs 30340972329 / 30341225585; PR #1409 | 2026-07-30 | | #096 | task | PR #1316 review follow-ups — adoption-gate coverage closed | RESOLVED 2026-07-30. Every sub-item is dispositioned. The band adoption gate's root-path gap closed on PR #1394 — root and href-less modes now resolve to `src/app/(search-app)/page.tsx` — and closing it surfaced two further defects in the same gate the original finding did not name: the hand-rolled walk was capped at two import hops where the root route's real chain is four, and it followed neither `layout.tsx` (where that route's band actually comes from) nor `dynamic(() => import(...))` (how the dashboard code-splits its mode workspaces). All three were fixed together with a bounded BFS, each verified load-bearing by reverting it and watching the gate fail. Four findings — favourites hub counts, the document-search status derivation, the 401 session-expiry path, the record-path duplicate notice — were already fixed independently. The Therapy Compass retry-waiter finding was corrected to NOT a live defect: `retryWaitersRef` is genuinely unscoped but no caller observes it. The seven Codex follow-up SHAs remain unreachable and were never pushed; the PR #1316 review threads are the durable source. Archived by the 2026-07-30 triage pass. Source: PR #1316 review sweep | 2026-07-30 | | #104 | rec | Worker's triple image read is deliberate, not debt | NOT DEBT — archived so a fourth audit does not re-file it. The 2026-07-28 latency audit listed L4-2 (`worker/main.ts` reads each extracted image up to 3x per document — hash, caption on cache miss, upload) as "CONFIRMED with no fix evidence", carried forward from the 2026-07-01 audit's L11. **That was wrong.** The 2026-07-01 disposition table already recorded it as a deliberate peak-memory trade-off, and the rationale is documented in place at `worker/main.ts:866-869`: holding every extracted image Buffer for a document with hundreds of multi-MB page images would multiply the worker's peak memory, and disk I/O is the cheaper resource for a background pipeline. The three reads (`:872`, `:1034`, `:1129`) are real but accepted. This row carried no next action, so it is archived rather than left open — revisit only if ingestion throughput becomes a measured complaint AND a bounded-buffer design is proposed. **Stop:** do not re-file this as debt. Source: `docs/audit/repo-audit-2026-07-01.md` L11 + disposition table | 2026-07-30 | -| #109 | issue | Remote sessions clone shallow, invalidating branch analysis | RESOLVED 2026-07-29, hardened in review. `scripts/sweep-branch-ledger.mjs` refuses outright on a shallow clone via the exported `shallowCloneRefusal`, printing no inventory and exiting 1 in both text and `--json` mode, before the fetch and before any branch maths; `docs/branch-cleanup-guide.md` §Safety Rules gains the `is-shallow-repository` precondition ahead of its numbered steps. An indeterminate result (empty output from a swallowed `git` failure) is refused as its own failure, and the same guard had to move OUT of the reconciliation preflight CLI and INTO the exported `collectReconciliationState`, because `buildReconciliationEvidencePack` calls the collector directly and stamped `status: complete` — in a `--depth 1` clone the guarded CLI exited 1 while the evidence-pack command exited 0 and persisted shallow ahead/behind as completed evidence. The collector now throws `UnverifiedHistoryError` so every current and future caller fails closed by default. Review found two further routes to a false-empty inventory from half-checked refspecs; coverage requires exactly `refs/heads/*` to `refs/remotes/origin/*`. Proven against real `--depth 1` clones in `tests/repo-hygiene.test.ts`, `tests/reconciliation-preflight.test.ts` and `tests/reconciliation-evidence-pack.test.ts`. Source: session 2026-07-29 | 2026-07-30 | +| #109 | issue | Remote sessions clone shallow, invalidating branch analysis | RESOLVED 2026-07-29, hardened in review. `scripts/sweep-branch-ledger.mjs` refuses outright on a shallow clone via the exported `shallowCloneRefusal`, printing no inventory and exiting 1 in both text and `--json` mode, before the fetch and before any branch maths; `docs/branch-cleanup-guide.md` §Safety Rules gains the `is-shallow-repository` precondition ahead of its numbered steps. An indeterminate result (empty output from a swallowed `git` failure) is refused as its own failure, and the same guard had to move OUT of the reconciliation preflight CLI and INTO the exported `collectReconciliationState`, because `buildReconciliationEvidencePack` calls the collector directly and stamped `status: complete` — in a `--depth 1` clone the guarded CLI exited 1 while the evidence-pack command exited 0 and persisted shallow ahead/behind as completed evidence. The collector now throws `UnverifiedHistoryError` so every current and future caller fails closed by default. Review found two further routes to a false-empty inventory from half-checked refspecs; coverage requires exactly `refs/heads/*` to `refs/remotes/origin/*`. Proven against real `--depth 1` clones in `tests/repo-hygiene.test.ts`, `tests/reconciliation-preflight.test.ts` and `tests/reconciliation-evidence-pack.test.ts`. Source: session 2026-07-29 | 2026-07-29 | | #115 | rec | Band adoption gate treats a discovered import as rendered | RESOLVED 2026-07-30 by redesign rather than another patch. `tests/search-results-band-adoption.test.ts` no longer asks whether a file mentions the band but whether anything the route actually mounts reaches it: it parses each module with `@babel/parser` into a small graph — exported name to local declaration, local to the identifiers its body references, and which locals render the band — then walks from the route's default export, carrying at each hop the set of exports the importer mounts. A static `import { X }` is followed only when `X` is reachable from a mounted declaration; `dynamic(() => import(...).then((m) => m.Named))` follows only that binding; a bare side-effect import is not followed at all; `export *` never supplies a default. **Why redesign:** six false greens were reported in one day, all one defect — presence is not reach — and two of the six had been introduced by an earlier patch to the same walker. Verified: all five production search routes still reach the band, gutting `services/page.tsx` and `tools/page.tsx` to `
` reports an orphan each, and fourteen temp-dir fixtures cover both directions with two mutation-confirmed. Accepted residual: reachability is per module and control flow inside a mounted declaration is not modelled. Source: PR #1400 | 2026-07-30 | | #108 | task | Five verified-landed remote branches await deletion (blocked in-session) | DONE 2026-07-30. Deleted the three remaining empty remote refs after re-verify (tree(tip)==tree(merge-base), 0 files vs main, no open PR): `codex/document-reader-condensed-view` @ `b5cdbf301d517239ffe9ed941b9ebe809aea0bfd`, `cursor/page-anchored-search-composer-30ee` @ `7ff134ca7f614db527b8d142676640305533669d`, `cursor/pr-1379-babysit-ledger-9365` @ `be2de03f855cb7fdfccea4bb74d05eb4c9bf6c61`. The other two candidates were already gone. Completed `branch-cleanup` ledger rows were already present; deletion succeeded from this session (prior 403 cleared). | 2026-07-30 | | #088 | task | Union-driver ledger duplication watch after repair | CLOSED 2026-07-30. Post-repair merges take main's repaired lines; residual exact-dupe babysit twins from stock `merge=union` are addressed by the custom `merge=ledger` driver (union + exact-row dedupe), `npm run ledger:dedupe`, and the Run PR anti-churn ledger policy. Success criterion (three consecutive post-repair guard passes) met on ordinary main syncs; ongoing exact-dupe class is now gated rather than watched. | 2026-07-30 |