diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index 5ba66d462..c8a45a1ba 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -39,8 +39,8 @@ commit, push, open a PR, or deploy. Order numbers remain stable within a reconciliation snapshot. A gap means an item was completed or removed after current-main verification; it is not missing recommended work. -- **A1 — urgent:** active safety, privacy, data-loss, or release/launch blocker. One operator/legal - item is retained; there is no current A1 code defect. +- **A1 — urgent:** active safety, privacy, data-loss, or release/launch blocker. Credential + containment, the Safety Plan privacy contract, and the privacy/legal package are current A1 work. - **A2 — important:** confirmed correctness, clinical, privacy, reliability, or evaluation work. - **A3 — planned:** worthwhile work that is safe to defer until its trigger. - **Optional:** start only when measured need, ownership, and cost justify it. @@ -50,34 +50,43 @@ removed after current-main verification; it is not missing recommended work. | Order | ID(s) | Acuity | Capability | When | Estimate | Outcome, gate, verification, and stopping condition | | ----: | ---------------------- | -------- | ------------------------------------------- | ------------------------------------------------------------------ | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 1 | `#053` | A1 | Operator — legal/privacy | Start now; finish before real patient use/privacy-approved release | 4–8 hours internal; 1–6 weeks elapsed | Execute DPAs; decide ZDR/residency; obtain cache behavior in writing; review subprocessors; obtain APP 8 and APP 5/1 counsel sign-off. Do not change public copy before approval. | -| 2 | `#052` | A2 | High — ingestion concurrency | Ready; first code task | 0.5–1 day | Add red single/bulk tests, then block full/retry reindex during a fresh agent-enrichment lease while keeping stale leases and enrichment mode unchanged. Run focused safety tests and `verify:cheap`; stop if current `main` cannot reproduce it. | -| 3 | `#030` | A2 | High — evaluation semantics | After order 2 | 2–4 hours | Require distinct source identities for distinct comparison slots. Run focused matching tests, typecheck, and `verify:cheap`; stop without changing strict aliases, retrieval, or ranking. | -| 4 | `#019` | A2 | Specialist — RAG answer pipeline | Local reproducer now; behavior change after order 8 | 0.5–1 day reproducer | Reproduce admission-source loss in the fallback layer using PR #1096’s source shape. Any behavior change needs protected review and an approved baseline/post canary; stop if independently non-reproducible. | -| 6 | `#054` | A2 | Standard locally; Operator hosted | Local safety identifier now; hosted next approved window | 15–30 min local; 1–2 hours hosted | Presence-check and fill confirmed secret/config gaps with distinct per-environment values. Never record values. Require clean readiness/secret checks; stop on ambiguous environment or project identity. | -| 7 | `#022` | A2 | Operator — clinical governance + Specialist | Decision-ready | 1–2 hours policy; 0.5–1 day first ten | Decide BMJ attestation policy and review the ten highest-impact local documents. Record reviewer/evidence/time; stop after ten and remeasure warning debt. | -| 8 | `#051`, `#023` | A2 | Specialist — RAG diagnostics | After scheduled 2026-07-26 run | 2–4 hours | With GitHub-read approval, compare structured canary/browser/irrelevant-at-10 artifacts without dispatching a rerun. Record deterministic/provider/latency deltas and disposition residuals; stop without spending. | -| 9 | `#018` | A2 | Specialist — clinical RAG/retrieval | After order 8, one mechanism at a time | 1–2 days diagnosis | Give lithium, ADHD, and metabolic residuals separate current-main reproducers and candidates. Behavior canaries require approval; stop any item without a deterministic reproducer or on regression. | -| 10 | `#029` | A2 | Specialist — answer quality/clinical safety | After orders 8–9 | 0.5–1 day inventory; 1–3 days per fix | Re-enumerate current fallback stubs and fix one causal cluster at a time without weakening grounding/citation gates. Stop if a change merely makes the metric easier to pass. | -| 11 | `#001` | A2 | Specialist — retrieval/ranking | After order 8 and rollout approval | 0.5–1 day plus canary | Keep semantic reranking off unless an approved ambiguity comparison preserves 36/36, recall 1.0, zero per-case regressions, and shows measured gain; otherwise record keep-off and stop. | -| 12 | `#025` | A2 | Operator — Railway/GitHub/chat/Supabase | Next approved observability window | 1–3 hours/channel | Choose owned deployment, CI, ingestion, and SLO alerts; mock first, then one approved controlled provider event/channel. The merged Supabase trigger remains inert until its verified inputs are configured. Stop without an accountable responder. | -| 13 | `#055` | A2 | Specialist release owner + Operator | Before next full-confidence release/handoff | 2–4 hours plus runtime | On one exact SHA, run local/provider gates, Firefox/WebKit, required hosted CI, and close actionable GitHub threads. Stop at first failure and rerun only the repaired smallest gate. | -| 14 | `#056` | A2 | Operator — Supabase/Railway + Specialist | After cost/ownership approval | 0.5–1 day | Provision isolated `Clinical KB Staging` with synthetic data and distinct secrets. Verify identity, schema, indexing, health, and data boundary; never copy production clinical documents. | -| 15 | `#057` | A2 | High — release/SRE + Operator | After order 14 | 2–4 hours plus soak | Run documented staging soak and rollback against an exact candidate. Retain latency/error/rollback evidence; stop on unsafe data, identity mismatch, or unowned rollback. | -| 16 | `#058` | A2 | Operator — production data + Specialist | Next approved production verification window | 30–60 min read-only; 1–2 hours if needed | Verify registry/differentials/medications are non-empty before writing; seed only confirmed gaps idempotently. Stop when healthy or owner/project identity is ambiguous. | -| 17 | `#007` | A3 | Operator decision + Standard frontend | When product chooses canonical Tools route | 15–30 min decision; 0.5 day | Align navigation, redirect, sitemap, and reachability around one entry point. Stop while the standalone page has an unresolved requirement. | -| 18 | `#011` | A3 | Operator — Supabase capacity | Immediately before first compute scale-up | 30–60 min plus observation | Switch Auth to percentage allocation, record before/after, and run approved advisor/health checks. Stop if no scale-up is planned. | -| 19 | `#017` | A3 | High — performance/browser | Before order 23; approved live-site window | 1–2 hours | Capture reproducible mobile/desktop Lighthouse/Web-Vitals evidence and decide whether payload work is justified. Stop if metrics are acceptable or evidence is too noisy. | -| 20 | `#024` | A3 | High — Next.js/Playwright/WebKit | After order 8 or real Safari reproduction | 0.5–1 day | Distinguish test interception from a Safari defect. Apply test-only correction only with a discriminating repro; keep access-control assertions meaningful. | -| 21 | `#033` | A3 | Specialist — prompt/source governance | After orders 7–8 | 1–2 days plus approved eval | Design unknown-vs-adverse metadata wording and prompt tests. Require no supported-grounding drop and zero citation failures; stop on broad over-caveating or degradation. | -| 22 | `#037` | A3 | Operator — clinical/product + Standard | Next trust-policy review | 30–60 min; up to 0.5 day | Decide whether routine claims cap at medium trust. Record policy; if accepted, change only the flag/expectations and run focused tests. | -| 23 | `#012`, `#013`, `#016` | A3 | High — bundling/runtime performance | After order 19 or equivalent evidence | 0.5–2 days/route | Optimize only a production route with measured payload/render/motion harm. Require material gain plus focused, `verify:cheap`, and browser evidence; stop on small gain. | -| 24 | `#027` | Optional | Operator — SRE/provider | When an owned external alert path is wanted | 1–2 hours | Decide vendor/cost/privacy/owner; if accepted, prove one non-PHI outage and recovery alert. Stop when no responder owns it. | -| 25 | `#028` | Optional | Specialist privacy/observability + Operator | After privacy/ownership/cost approval | 1–3 days | Define vendor/region/retention/redaction/sampling/source-map envelope before SDK work. Prove no clinical text, identifiers, or secrets leave; stop if unacceptable. | -| 26 | `#038` | Optional | High — product/design architecture | When a new comparison surface is approved | 0.5–1 day | Define a shared interaction contract without flattening mode-specific content. Stop when no concrete new surface exists. | -| 27 | `#040` | Optional | High — visual QA/accessibility | When baseline owner/update workflow exist | 1–2 days | Establish a small stable desktop/mobile/accessibility baseline set. Do not make it blocking if flake or maintenance cost outweighs detection value. | - - +| 1 | `#059` | A1 | Operator — security + independent review | Immediate approved security window | 1–3 hours | Prove every previously exposed credential is revoked or rotated and replacements exist only in intended stores. Use provider-native evidence and presence-only checks; never expose values or act without provider/secret-store approval. | +| 2 | `#060` | A1 | Specialist — privacy + clinical | Ready; before real-patient use | 3–5 hours | Align Safety Plan copy, behavior, print, privacy notice, PIA, and tests. Default to identifier-free use unless transient processing is explicitly approved; stop before adding storage or provider transmission. | +| 3 | `#053` | A1 | Operator — legal/privacy | Start now; finish before real patient use/privacy-approved release | 4–8 hours internal; 1–6 weeks elapsed | Execute DPAs; decide ZDR/residency; obtain cache behavior in writing; review subprocessors; obtain APP 8 and APP 5/1 counsel sign-off. Do not change public copy before approval. | +| 4 | `#061` | A2 | Specialist — clinical safety | Ready; after `#060` | 2–4 hours | Add a red render-policy test, then fail closed when answer relevance metadata is absent. Preserve explicit source-backed behavior; stop before retrieval, ranking, or generation changes. | +| 5 | `#052` | A2 | High — ingestion concurrency | Ready; after `#061` | 0.5–1 day | Add red single/bulk tests, then block full/retry reindex during a fresh agent-enrichment lease while keeping stale leases and enrichment mode unchanged. Run focused safety tests and `verify:cheap`; stop if current `main` cannot reproduce it. | +| 6 | `#062` | A2 | Specialist — queue reliability | After `#052` | 0.5–1.5 days | Reproduce a queued document with no open job, then add the smallest idempotent owner-scoped recovery path. Prove exactly one job is created under crash, concurrency, and repeat; hosted changes require approval. | +| 7 | `#030` | A2 | High — evaluation semantics | After order 6 | 2–4 hours | Require distinct source identities for distinct comparison slots. Run focused matching tests, typecheck, and `verify:cheap`; stop without changing strict aliases, retrieval, or ranking. | +| 8 | `#019` | A2 | Specialist — RAG answer pipeline | Local reproducer now; behavior after order 13 | 0.5–1 day reproducer | Reproduce admission-source loss in the fallback layer using PR #1096’s source shape. Any behavior change needs protected review and an approved baseline/post canary; stop if independently non-reproducible. | +| 9 | `#054` | A2 | Standard locally; Operator hosted | Local presence checks now; hosted approved window | 1–2 hours | Reconcile safety-identifier, query-hash, deep-probe, Supabase/OpenAI, identity, and schedule configuration. Record status, never values; set only confirmed gaps and stop on target ambiguity or cross-environment reuse. | +| 10 | `#063` | A2 | High — frontend/browser | After higher-acuity local fixes; before release UI gate | 4–8 hours | Preserve the isolated dirty contrast/browser patch, reconcile it against current `main`, and run focused UI tests plus `verify:ui`. Stop rather than overwriting unrelated work or weakening access-control assertions. | +| 11 | `#064` | A2 | High — document-viewer UI | Only when the user explicitly resumes the paused task | 0.5–1.5 days | Finish the compact source-text accordion, citation/search auto-open, print restoration, and 320/390/1280 px coverage. Keep the preserved branch untouched until explicit resume; no provider calls. | +| 12 | `#022` | A2 | Operator — clinical governance + Specialist | Decision-ready | 1–2 hours policy; 0.5–1 day first ten | Decide BMJ attestation policy and review the ten highest-impact local documents. Record reviewer/evidence/time; stop after ten and remeasure warning debt. | +| 13 | `#051`, `#023` | A2 | Specialist — RAG diagnostics | After scheduled 2026-07-26 run | 2–4 hours | With GitHub-read approval, compare structured canary/browser/irrelevant-at-10 artifacts without dispatching a rerun. Record deterministic/provider/latency deltas and disposition residuals; stop without spending. | +| 14 | `#018` | A2 | Specialist — clinical RAG/retrieval | After order 13; one mechanism at a time | 1–2 days diagnosis | Give lithium, ADHD, and metabolic residuals separate current-main reproducers and candidates. Behavior canaries require approval; stop any item without a deterministic reproducer or on regression. | +| 15 | `#029` | A2 | Specialist — answer quality/clinical safety | After orders 13–14 | 0.5–1 day inventory; 1–3 days per fix | Re-enumerate current fallback stubs and fix one causal cluster at a time without weakening grounding/citation gates. Stop if a change merely makes the metric easier to pass. | +| 16 | `#001` | A2 | Specialist — retrieval/ranking | After order 13 and rollout approval | 0.5–1 day plus canary | Keep semantic reranking off unless an approved ambiguity comparison preserves 36/36, recall 1.0, zero per-case regressions, and shows measured gain; otherwise record keep-off and stop. | +| 17 | `#025` | A2 | Operator — Railway/GitHub/chat/Supabase | Next approved observability window | 1–3 hours/channel | Choose owned deployment, CI, ingestion, and SLO alerts; mock first, then one approved controlled provider event/channel. The merged Supabase trigger remains inert until its verified inputs are configured. Stop without an accountable responder. | +| 18 | `#055` | A2 | Specialist release owner + Operator | Before next full-confidence release/handoff | 2–4 hours plus runtime | On one exact SHA, run local/provider gates, Firefox/WebKit, required hosted CI, and close actionable GitHub threads. Stop at first actionable failure and rerun only the repaired smallest gate. | +| 19 | `#056` | A2 | Operator — Supabase/Railway + Specialist | After cost/ownership approval | 0.5–1 day | Provision isolated `Clinical KB Staging` with synthetic data and distinct secrets. Verify identity, schema, indexing, health, and data boundary; never copy production clinical documents. | +| 20 | `#057` | A2 | High — release/SRE + Operator | After order 19 | 2–4 hours plus soak | Run documented staging soak and rollback against an exact candidate. Retain latency/error/rollback evidence; stop on unsafe data, identity mismatch, or unowned rollback. | +| 21 | `#058` | A2 | Operator — production data + Specialist | Next approved production verification window | 30–60 min read-only; 1–2 hours if needed | Verify registry/differentials/medications are non-empty before writing; seed only confirmed gaps idempotently. Stop when healthy or owner/project identity is ambiguous. | +| 22 | `#007` | A3 | Operator decision + Standard frontend | When product chooses canonical Tools route | 15–30 min decision; 0.5 day | Align navigation, redirect, sitemap, and reachability around one entry point. Stop while the standalone page has an unresolved requirement. | +| 23 | `#011` | A3 | Operator — Supabase capacity | Immediately before first compute scale-up | 30–60 min plus observation | Switch Auth to percentage allocation, record before/after, and run approved advisor/health checks. Stop if no scale-up is planned. | +| 24 | `#017` | A3 | High — performance/browser | Before order 28; approved live-site window | 1–2 hours | Capture reproducible mobile/desktop Lighthouse/Web-Vitals evidence and decide whether payload work is justified. Stop if metrics are acceptable or evidence is too noisy. | +| 25 | `#024` | A3 | High — Next.js/Playwright/WebKit | After order 13 or real Safari reproduction | 0.5–1 day | Distinguish test interception from a Safari defect. Apply test-only correction only with a discriminating repro; keep access-control assertions meaningful. | +| 26 | `#033` | A3 | Specialist — prompt/source governance | After orders 12–13 | 1–2 days plus approved eval | Design unknown-vs-adverse metadata wording and prompt tests. Require no supported-grounding drop and zero citation failures; stop on broad over-caveating or degradation. | +| 27 | `#037` | A3 | Operator — clinical/product + Standard | Next trust-policy review | 30–60 min; up to 0.5 day | Decide whether routine claims cap at medium trust. Record policy; if accepted, change only the flag/expectations and run focused tests. | +| 28 | `#012`, `#013`, `#016` | A3 | High — bundling/runtime performance | After order 24 or equivalent evidence | 0.5–2 days/route | Optimize only a production route with measured payload/render/motion harm. Require material gain plus focused, `verify:cheap`, and browser evidence; stop on small gain. | +| 29 | `#027` | Optional | Operator — SRE/provider | When an owned external alert path is wanted | 1–2 hours | Decide vendor/cost/privacy/owner; if accepted, prove one non-PHI outage and recovery alert. Stop when no responder owns it. | +| 30 | `#028` | Optional | Specialist privacy/observability + Operator | After privacy/ownership/cost approval | 1–3 days | Define vendor/region/retention/redaction/sampling/source-map envelope before SDK work. Prove no clinical text, identifiers, or secrets leave; stop if unacceptable. | +| 31 | `#038` | Optional | High — product/design architecture | When a new comparison surface is approved | 0.5–1 day | Define a shared interaction contract without flattening mode-specific content. Stop when no concrete new surface exists. | +| 32 | `#040` | Optional | High — visual QA/accessibility | When baseline owner/update workflow exist | 1–2 days | Establish a small stable desktop/mobile/accessibility baseline set. Do not make it blocking if flake or maintenance cost outweighs detection value. | +| 33 | `#035` | A3 | Specialist — clinical evidence rules | Only after a bounded missed-conflict reproducer | 0.5–1 day design; code separate | Expand threshold-conflict detection only for one clinically reviewed class with positive and negative fixtures. Stop if no bounded class or if warnings spread to unrelated answers. | +| 34 | `#039` | Optional | High — frontend architecture | During a concrete catalogue-toolbar project | 0.5–1 day inventory; 1–3 days code | Converge only the repeated filter/sort/count/mobile contract without flattening mode-specific search semantics. Stop if no concrete project exists. | +| 35 | `#065` | A3 | High — product architecture + privacy | Only when the product owner wants to evaluate the feature | 0.5–1 day | Write a product/privacy/persistence brief for Current Clinical Work; define users, data classes, lifecycle, deletion, failure states, and smallest slice. Stop if demand or safe persistence cannot be established. | + + ## Open items @@ -87,6 +96,13 @@ removed after current-main verification; it is not missing recommended work. | ID | Pri | Type | Summary | Detail / next action | Source | Added | | ---- | --- | ----- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ---------- | +| #059 | P1 | task | Verify containment of previously exposed credentials | **Outcome:** credentials previously exposed outside authorised stores can no longer authenticate. **Next:** in an approved security window, revoke or rotate any still-valid OpenAI, Supabase service-role/database, and E2E credentials; update only intended secret stores and record status without values. **Success:** provider evidence shows every old credential rejected or retired, replacements are scoped to intended environments, and presence/readiness checks pass. **Verify:** provider audit or rotation evidence, presence-only checks, and secret scans that never print values. **Stop:** no provider or secret-store action without explicit approval; never paste values into Git, logs, issues, or chat. | session 2026-07-24 security reconciliation | 2026-07-24 | +| #060 | P1 | issue | Safety Plan Generator contradicts the privacy contract | **Outcome:** the tool, privacy notice, PIA, and tests agree on whether patient identifiers may be entered, copied, printed, or saved. **Next:** obtain a privacy/clinical decision, then default to identifier-free behavior unless transient identifier processing is explicitly approved and documented. **Success:** no contradictory copy; no identifier is persisted or transmitted without an approved basis. **Verify:** focused component/privacy/copy/accessibility tests, browser print/copy smoke, `verify:cheap`, and production-readiness. **Stop:** any new storage or provider transmission requires a separate review. | `src/components/patient-safety-plan.tsx:649`; `src/app/privacy/page.tsx:28`; `docs/privacy-impact-assessment.md:18` | 2026-07-24 | +| #061 | P2 | issue | Missing answer relevance metadata is treated as source-backed | **Outcome:** absent `relevance` metadata renders conservatively. `RagAnswer.relevance` is optional, but `relevance?.isSourceBacked !== false` treats `undefined` as source-backed. **Next:** add the red render-policy test, then make the smallest policy-only fix. **Success:** missing and explicit-false relevance fail closed; explicit source-backed relevance is unchanged. **Verify:** focused policy/provenance tests, `verify:cheap`, and production-readiness. **Stop:** do not expand into retrieval, ranking, or generation. | `src/lib/types.ts:1029`; `src/lib/answer-render-policy.ts:145` | 2026-07-24 | +| #062 | P2 | issue | Upload crash can strand a queued document without a job | **Outcome:** a crash between document and job creation cannot strand an upload indefinitely. **Next:** add the stranded-row reproducer, then choose the smallest idempotent atomic-enqueue RPC or bounded scheduled sweep consistent with ownership and rollback contracts. **Success:** exactly one recoverable job is created; existing open jobs do not duplicate; owner scope, retry, audit, and rollback remain intact. **Verify:** focused upload/recovery/schema tests, disposable replay if needed, `verify:cheap`, and production-readiness. **Stop:** hosted changes require approval. | `src/app/api/upload/route.ts`; `docs/webhooks.md:163-192` | 2026-07-24 | +| #063 | P2 | task | Reconcile the preserved browser and contrast patch | **Outcome:** the isolated dirty formulation/contrast patch is either safely landed or explicitly dispositioned. **Next:** rebase its intent against current `main` without overwriting the worktree, then run focused Playwright coverage and `verify:ui`. **Success:** intended disabled/contrast behavior is accessible, browser assertions remain meaningful, and unrelated work is preserved. **Stop:** do not discard or auto-merge the dirty worktree; pause on ambiguous ownership or scope. | `agent/formulation-disabled-contrast`; session 2026-07-24 | 2026-07-24 | +| #064 | 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 | +| #065 | P3 | rec | Define Current Clinical Work before implementation | **Outcome:** decide whether a workspace combining saved comparisons, partial formulation work, recent tools, and pinned source sets is worth building. **Next:** write a product/privacy/persistence brief only; do not build storage or UI. **Success:** the brief defines users, data classes, lifecycle, cross-device expectations, deletion, failure states, evidence of demand, and the smallest testable slice. **Stop:** close the idea if demand or safe persistence cannot be established. | session 2026-07-24; `src/lib/tools-catalog.ts:253-262` | 2026-07-24 | | #051 | P2 | task | Stabilise the live answer-quality canary before more RAG tuning | Diagnostics landed in PR #1095: structured JSON/Markdown artifacts now record the actual checked-out SHA, run identity and latency context, and the offline trend tool separates content, provider-route and latency outcomes. First validating run `30018289898` recorded the expected tree and cost, with 36/36 retrieval green, but one report cannot establish variability; PR #1097 prevents a single failure being mislabeled as repeated. Next: compare the scheduled 2026-07-26 structured report with this run. Do not spend on an immediate retry or reapply the archived lithium guard before that comparison. | PR #1095; run `30018289898`; PR #1097; archive ref `refs/archive/rejected-rag/20260723/monitoring-subject-gate` | 2026-07-23 | | #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 | | #052 | P2 | issue | Reindex can overlap a fresh agent-enrichment pass | Full/retry single and bulk routes consult `ingestion_jobs` but not the implemented `hasActiveAgentEnrichmentJob` predicate. Add red route tests, then block fresh `indexing_v3_agent_jobs.status='processing'` leases before mutation while keeping stale leases and enrichment mode unchanged. | `src/lib/ingestion-mutation-safety.ts`; single/bulk reindex routes; repository audit 2026-07-24 | 2026-07-24 |