Skip to content
Merged
8 changes: 8 additions & 0 deletions .claude/skills/issues/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ Parse the intent from natural language too — the exact syntax is a convenience
ID and its one-line outcome. Reconciliation moves the row to **Resolved / archive** and updates
any recommended-queue references.
- **`/issues update <id> <text>`** — queue an immutable `update` request for an open canonical ID.
- **`/issues queue <id> <field>=<value>`** — queue an immutable `queue` request that corrects this
item's **Recommended execution queue** row: `--acuity`, `--capability`, `--when`, `--estimate`,
`--outcome`. Use it whenever a re-grade or a change of timing lands on the Open-items row, because
the queue is what an operator actually reads to decide what to start, and until ledger `#M6JNR8`
it could only be pruned on close — so a stale A1 entry could keep sending sessions at closed work.
Order and the `ID(s)` cell are not editable: Order is renumbered automatically on every close, and
changing membership is an `add` or a `done`, not an edit.
- **`/issues capture`** — scan the current session for recommendations, follow-ups, deferrals, and
unfixed problems that surfaced but were not recorded. Propose them as a numbered list and add the
confirmed ones (dedupe against existing rows first — do not re-add something already tracked).
Expand All @@ -86,6 +93,7 @@ paragraph; put the smallest next action in **Detail / next action**.
npm run issues:add -- --pri P2 --type issue --summary "…" --detail "…" --source "…"
npm run issues:done -- '#151' --outcome "Resolved 2026-07-31 by PR #1494. …"
npm run issues:update -- '#151' --detail "…"
npm run issues:queue -- '#151' --acuity A3 --when "After the next release"
```

Each command creates one validated UUID JSON file under `docs/outstanding-issues-inbox/`; it does
Expand Down
35 changes: 34 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,38 @@ shell command with `CLAUDE_ALLOW_PR_FOLLOW=1`, or delete the marker the deny mes
Sessions that never create a PR are untouched, so `Run PR` sweeps, `pr-ci-fix` work, and
review sessions on someone else's PR still function normally.

## Automated review coverage (owner decision, 2026-08-22)

CodeRabbit's included allowance is exhausted and the organisation has reached its usage spending
cap, so it declines most reviews with "Review limit reached" — measured on PRs #2113, #2252, #2255,
#2256, #2263 and #2278 between 2026-08-18 and 2026-08-22. The owner has decided to **leave the cap
as it is and accept that CodeRabbit review is intermittent**, rather than raise it (`#CCZ4HB`).

**Read `docs/decisions/ccz4hb-review-coverage.md` before acting on this.** A separate analysis
written the same day measured the cause and it is not primarily billing: CodeRabbit's included
reviews refill at one per hour (24/day) while this repo merged 25.4 PRs/day on average over the
preceding month. It also found that 285 of the last 1,190 merged PRs (24%) changed only
documentation and 190 (16%) changed only the repo's own record-keeping files — roughly six review
credits a day spent on files a code-review bot has nothing to say about. Two levers therefore remain
open at **no cost and no loss of safety**, and the owner's decision above does not foreclose them:
configure CodeRabbit to skip documentation-only PRs, and stop opening PRs whose only content is a
bookkeeping record. Do those before anyone proposes raising the cap again.

What the decision means in practice, and what it does not mean:

- The ChatGPT Codex connector reviewed PR #2278 on 2026-08-22 in the same run CodeRabbit skipped, so
as of that date automated review is reduced rather than absent. Do not treat that as permanent —
the connector reported its own usage limit on PR #2113 on 2026-08-18, so it can lapse too.
- Draft PRs are skipped by CodeRabbit outright, so a PR that stays in draft gets nothing from it
even when allowance is available. Undrafting mid-CI also cancels the in-flight run.
- **Do not weaken, skip, or relax any required check to compensate.** The required gates are now
carrying more of the load, not less, and the whole point of accepting reduced bot review is that
the deterministic checks stay strict.
- Clinical-risk and RAG-surface diffs still require their PR-body preflight sections in full; those
are enforced by `scripts/pr-policy.mjs` and are unaffected by review-bot availability.

Reducing PR churn (below) remains the cheapest way to get more value from the allowance that exists.

## PR bundling (reduce one-task-one-PR churn)

Every `newtask`/`handoff` cycle mints a dedicated `claude/<task-slug>` branch and PR, so a
Expand Down Expand Up @@ -1020,7 +1052,8 @@ named PR). Future process only.
stale head that already shares hot files with the open queue.
- The legacy `docs/branch-review-ledger.md` and `docs/outstanding-issues.md` are **serial-only**:
normal PRs must not add rows there. `npm run ledger:append` creates an immutable review record;
`npm run issues:add|update|done` creates one immutable inbox request. One fresh-base,
`npm run issues:add|update|queue|done` creates one immutable inbox request (`queue` corrects a
recommended-execution-queue row; see ledger `#M6JNR8`). One fresh-base,
cross-worktree-locked `npm run issues:reconcile` operation applies landed requests to the
canonical issue ledger. `check:ledger-write-discipline` rejects direct table-row edits,
changed request records, deleted requests, and a canonical issue diff that does not exactly
Expand Down
56 changes: 55 additions & 1 deletion data/outstanding-issues-snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"p2": 43,
"p3": 33,
"queued": 11,
"pending": 32,
"pending": 41,
"resolved": 359
},
"queue": [
Expand Down Expand Up @@ -883,6 +883,12 @@
"summary": "#99KVDF: Resolved 2026-08-21 by accepting the measured stop decision: do not retry folding AnswerCard support text into VerificationNotice because the 4.5px saving does not remove a row.",
"created_at": "2026-08-21"
},
{
"request_id": "337fc2e1-c50c-4f80-b865-58c76af4ff81",
"action": "done",
"summary": "#1K6T35: OWNER DECISION 2026-08-22: leave point-in-time recovery OFF and deliberately accept a worst-case recovery point of roughly 24 hours on the live clinical corpus. This row required either enabling the paid add-on or accepting the RPO and amending the plan's unsatisfiable standing rule; the second was chosen and the rule is amended in docs/database-remediation-plan.md under 'Recovery point — owner decision, 2026-08-22', which now requires confirming and recording the latest daily backup timestamp before a mutating phase, stating what a restore would lose, and having a written tested reversal path for anything that rewrites or drops data or policy objects. Scope is explicitly this remediation programme only: the rag-ingestion-reindex plan's stop-before-production-reindex-when-PITR-disabled condition is untouched.",
"created_at": "2026-08-22"
},
{
"request_id": "3552c196-bf4f-49c8-a39b-28f8876d8caf",
"action": "done",
Expand Down Expand Up @@ -925,6 +931,18 @@
"summary": "#CHW9N3: Resolved 2026-08-22: the loading contract retains its separate fourteen-route inventory for consolidated and standalone homes, while a dedicated assertion now requires every entry in exported standaloneModeHomePaths to remain in that inventory; the dashboard root's in-shell pending skeleton stays separately guarded, and documents now has ModeHomeRouteLoading.",
"created_at": "2026-08-21"
},
{
"request_id": "54bbb5e0-a573-47e5-9daa-f7eedc4e6b73",
"action": "done",
"summary": "#M6JNR8: Resolved 2026-08-22: scripts/outstanding-issues.mjs gains updateQueueRow and scripts/ledger-inbox.mjs gains a 'queue' action (npm run issues:queue), so a recommended-execution-queue row can be corrected through the same immutable-request + serial-reconcile transaction as every other ledger mutation — option (a) of the two this row offered, not the audited hand-edit path. Acuity, Capability, When, Estimate and Outcome are editable; Order and the ID(s) cell deliberately are not, since Order is renumbered on every close and changing membership is an add or a done. Queue edits carry their own queueRowFingerprint, so they are optimistic against the QUEUE row rather than the Open-items row, and mutationConflicts namespaces them separately so a re-grade and a detail update for the same id can land in one batch. The #231 instance this row was filed for is queued as a 'queue' request in the same PR.",
"created_at": "2026-08-22"
},
{
"request_id": "5bdfac3c-a3b4-4f00-869c-9a59cc756e11",
"action": "done",
"summary": "#CCZ4HB: OWNER DECISION 2026-08-22: leave the CodeRabbit spending cap as it is and accept that automated review from that bot is intermittent. Option (c) of the three this row offered — not a gate, not a cap raise. Recorded in AGENTS.md 'Automated review coverage (owner decision, 2026-08-22)' with the measurements (skipped on PRs #2113, #2252, #2255, #2256, #2263, #2278), the fact that the ChatGPT Codex connector still reviews and is unaffected, the draft-PR interaction, and the standing prohibition on weakening any required check to compensate. Re-open only if the Codex connector also stops reviewing, or if the owner revisits the billing decision.",
"created_at": "2026-08-22"
},
{
"request_id": "65494c5f-927b-4d0d-8564-5933a7c723f3",
"action": "update",
Expand Down Expand Up @@ -961,6 +979,12 @@
"summary": "No gate can detect a React Server Component boundary violation: a component with an event handler and no \"use client\" throws only at request time",
"created_at": "2026-08-21"
},
{
"request_id": "8db683bd-8be5-4f86-912a-bcf40eb0404c",
"action": "cancel",
"summary": "Cancel request 54bbb5e0-a573-47e5-9daa-f7eedc4e6b73: Reissued as a single closure that also carries the evidence from cancelled request 993f7ca7, which had independently re-proven the defect in another session. Requests are immutable, so folding that evidence into the outcome means replacing this one rather than editing it.",
"created_at": "2026-08-22"
},
{
"request_id": "993f7ca7-5277-4399-82fc-19ff981823e4",
"action": "update",
Expand All @@ -979,6 +1003,18 @@
"summary": "Cancel request 3552c196-bf4f-49c8-a39b-28f8876d8caf: Duplicate done request created by overlapping command completion; retain 4bc642e9-5fba-4b43-90e8-407fe2c11408.",
"created_at": "2026-08-21"
},
{
"request_id": "acc8b0f7-b363-4888-9eb8-1712f48e2779",
"action": "cancel",
"summary": "Cancel request 993f7ca7-5277-4399-82fc-19ff981823e4: Superseded: this request re-grades #M6JNR8 to P1 so the queue-edit gap gets fixed sooner, and the fix has now landed in this PR (updateQueueRow plus the 'queue' inbox action), so the row is being closed rather than re-prioritised. Nothing from it is lost: its empirical proof — that a probe commit changing queue row 1 from A1 to A2 was refused by check:ledger-write-discipline with 'does not exactly match the audited application of 0 moved inbox request(s) from the base', and that the stale row had by then mis-directed two sessions — is carried verbatim into the closure outcome, which is the durable record. Cancelled only because two mutations on one row need an explicit decision, not because the finding was wrong; it was correct and it is the reason this was built.",
"created_at": "2026-08-22"
},
{
"request_id": "ae7596a8-9064-4554-9530-4d145a02672c",
"action": "queue",
"summary": "#231: RE-SCOPED 2026-08-22 (was A1 / retrieval-budget investigation). The retrieval-side premise is CLOSED and measured: retrieval costs 955 ms text / 6,720 ms hybrid against a 25,000 ms fast budget (4-27%), so it cannot bind it. What remains is the generation-side R4 residual — chronic ~30 s strong-route provider_timeout on metformin-renal-dosing and valproate-pregnancy, with a safe source-backed extractive fallback already in place — plus the 2026-08-22 Gate E finding that the remaining provider_timeout label cannot be attributed to one mechanism and partly reflects the quality-retry ladder. **Gate:** focused answer-route tests offline first; live probe only with explicit provider approval. **Stop:** do not weaken quality gates to hide timeouts; flag RAG surfaces before edit.",
"created_at": "2026-08-22"
},
{
"request_id": "afaa753b-41ae-4bee-8c63-89c649cdcfd5",
"action": "done",
Expand Down Expand Up @@ -1015,12 +1051,24 @@
"summary": "#99KVDF: Resolved 2026-08-21 by accepting the measured stop decision: do not retry folding AnswerCard support text into VerificationNotice because the 4.5px saving does not remove a row.",
"created_at": "2026-08-21"
},
{
"request_id": "e0508db3-fdbc-4d76-9d6a-5e592b77d198",
"action": "add",
"summary": "Two zero-cost levers to restore automated review coverage remain unbuilt",
"created_at": "2026-08-22"
},
{
"request_id": "eeac3580-2340-415d-824f-e31c23d8e2f7",
"action": "done",
"summary": "#0HFDWD: Resolved 2026-08-21: CI and PR-policy UI classifiers include the narrow mode, route, copy, navigation, and therapies library owners; self-tests pin app-modes and route ownership as UI.",
"created_at": "2026-08-21"
},
{
"request_id": "f046ea4a-214f-4917-8740-4f56565fee0b",
"action": "done",
"summary": "#M6JNR8: Resolved 2026-08-22: scripts/outstanding-issues.mjs gains updateQueueRow and scripts/ledger-inbox.mjs gains a 'queue' action (npm run issues:queue), so a recommended-execution-queue row can be corrected through the same immutable-request + serial-reconcile transaction as every other ledger mutation — option (a) of the two this row offered, not the audited hand-edit path. Acuity, Capability, When, Estimate and Outcome are editable; Order and the ID(s) cell deliberately are not, since Order is renumbered on every close and changing queue membership is an add or a done. Queue edits carry their own queueRowFingerprint, so they are optimistic against the QUEUE row rather than the Open-items row, and mutationConflicts namespaces queue targets separately so a re-grade and a detail update for the same id can land in one batch. Both writers gained mutation-tested self-tests. EVIDENCE CARRIED FROM CANCELLED REQUEST 993f7ca7 (a second session, 2026-08-22, independently tasked with fixing the #231 queue row and unable to): a probe commit changing queue row 1 from A1 to A2 was refused by npm run check:ledger-write-discipline with 'docs/outstanding-issues.md does not exactly match the audited application of 0 moved inbox request(s) from the base', and the probe was reverted; that session re-graded this row P2 -> P1 on the grounds that the stale queue row is the first thing the session-start hook surfaces and had by then mis-directed two sessions. That re-grade is superseded by this closure, not rejected. The #231 instance is corrected by a 'queue' request in the same PR.",
"created_at": "2026-08-22"
},
{
"request_id": "f7a561a7-1fe2-4aaa-bbfc-90d24baecc7a",
"action": "add",
Expand All @@ -1032,6 +1080,12 @@
"action": "cancel",
"summary": "Cancel request afaa753b-41ae-4bee-8c63-89c649cdcfd5: Duplicate done request created by overlapping command completion; retain 5201bfa7-4966-4583-abff-02305de82219.",
"created_at": "2026-08-21"
},
{
"request_id": "fac29c48-c558-4249-97ed-c86e61b61b97",
"action": "update",
"summary": "#9X40BT: detail → MEASURED 2026-08-22 (read-only list_branches on sjrfecxgysukkwxsowpy): there are currently ZERO preview branches. The only entry returned is the default 'main' branch, which is the production project itself and is not billable preview compute. So the uncapped exposure this row describes is POTENTIAL, not active — nothing is running to clean up, and no agent-side cleanup step exists to take. The one remaining action is the dashboard setting itself, which no agent can change: Supabase dashboard -> Project Settings -> Integrations -> GitHub -> lower 'Automatic branching' limit from 3 to 1, or disable branching. Prior context stands and is the reason lowering is safe: CI's Migration replay job (db-reset-verify, supabase migration up --local) independently replays the whole chain on every database-touching PR, so preview branches are a second net rather than the only one. STOP unchanged: do not change Supabase project settings without explicit owner approval. PRIOR RECORD: Dashboard read 2026-08-21 (the same read that settled D4) shows Automatic branching ON with limit 3 and 'Supabase changes only' enabled, and the same screen warns that Branching Compute is NOT covered by the organisation's Spend Cap. Preview branches did earn their keep once (the 20260819100200 guard failure on PR #2151 was caught by a preview branch building from the chain alone), so this is a cost/benefit decision, not a cleanup.",
"created_at": "2026-08-22"
}
]
}
24 changes: 22 additions & 2 deletions docs/database-remediation-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,31 @@ against):

**Standing rules for every phase.** No hosted mutation without explicit approval for that phase.
Never raw-SQL a drift fix — every live change is codified (migration + `schema.sql` mirror +
regenerated `drift-manifest.json`) in the same change. PITR/backup restore point captured before
any mutating phase. Every phase ends with pasted decisive evidence, not exit codes. RAG-protected
regenerated `drift-manifest.json`) in the same change. Every phase ends with pasted decisive
evidence, not exit codes. RAG-protected
surfaces (`match_*` RPCs, anything under `src/lib/rag/**`) are flagged before editing and
behaviour changes need a live eval-canary pair (36/36, recall 1.0, zero per-case rr regressions).

**Recovery point — owner decision, 2026-08-22 (`#1K6T35`).** This rule used to read "PITR/backup
restore point captured before any mutating phase". It was unsatisfiable: point-in-time recovery is a
paid add-on and is **off** on the live project (`supabase backups list` on 2026-08-19 reported
`pitr_enabled false`, `walg_enabled true`, seven retained daily physical backups). A checklist item
nobody can satisfy reads as met by default whenever nobody checks it, which is exactly what happened
through Phases 1-4. The owner has decided to **leave PITR off and accept a worst case of roughly 24
hours of data loss** on the clinical corpus, so the rule now states the true position:

> The only restore point is the most recent daily physical backup, up to ~24 h old. Before any
> mutating phase, confirm the latest backup's completion timestamp and record it, and state in the
> phase's evidence what would be lost by restoring to it. Phases whose every statement has an exact
> one-statement inverse and no data-loss surface (the Phase 4 index work) may proceed on that basis.
> Anything that rewrites or drops data or policy objects must additionally have a written, tested
> reversal path, because there is no restore point finer than the last nightly backup.

This decision covers **this remediation programme only**. It does not relax the separate
`docs/superpowers/plans/2026-08-20-rag-ingestion-reindex.md` gate, which stops before a production
re-index or backfill when PITR is disabled — that plan mutates the corpus itself and its stop
condition stands until it is revisited on its own terms.

---

## Phase 0 — Enablement (repo-side, no hosted access; can start immediately)
Expand Down
Loading
Loading