-
Notifications
You must be signed in to change notification settings - Fork 0
docs(issues): keep #248 open and track residual live drift #1906
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
b42d8eb
docs(issues): close #248 with migration-history forensics; open #312 …
claude cc82d60
docs(db): add database remediation and future-proofing plan; anchor i…
claude 5c9e1b6
docs(db): add multi-session remediation playbook with per-phase prompts
claude 953db86
Merge main and migrate ledger updates to inbox
BigSimmo c3990af
docs(db): address live-drift review feedback
BigSimmo 47e799d
docs: clarify verification evidence and provider confirmation
BigSimmo 0278652
fix(ledger): correct immutable record address
BigSimmo fc1c30a
fix(ledger): complete immutable record rename
BigSimmo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| # Live-drift forensics — 2026-08 | ||
|
|
||
| Evidence record for the phased database remediation plan and playbook. No hosted reads or mutations | ||
| were performed while creating this file. Add dated, source-linked evidence here as each approved | ||
| phase completes. |
1 change: 1 addition & 0 deletions
1
...ords/65bc60c1d30f0daf9efba33fea5fa2d4be509699f05b5ea443fe5c5e2792daad.record.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| | 2026-08-13 | claude/fable-tasks-issues-49hnvp | 5c9e1b6a6766efee147af97ff1ef2a53729f56e9 | PR #1906 failing CI conflict repair | Resolved the main-sync ledger conflict by preserving main #312 and converting the PR closure and follow-up into merge-safe inbox requests; removed numeric-ID assumptions from the remediation docs. | Latest GitHub Actions mergeability log inspected; decisive output lines for format, sitemap, documentation, link, and outstanding-issues checks were not captured in this record, so no pass status is claimed for them | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,198 @@ | ||
| # Database remediation & future-proofing plan — 2026-08 | ||
|
|
||
| Owner: operator (Josh) + specialist session. Source findings: open ledger `#248` (whose causal | ||
| conclusion awaits a read-only history and audit check) and the queued P1 live-drift follow-up. | ||
| Companion evidence: live-drift Actions runs | ||
| `30763871562` (2026-08-02) and | ||
| `31330856982` (2026-08-09), PR #1614, `supabase/migrations/20260804110240_restore_rag_search_health_indexes.sql`. | ||
|
|
||
| **Scope.** Fixes, in dependency order: the live-vs-repo schema gap (21 missing indexes, 2 | ||
| unexpected indexes, 10 diverged `match_*` RPC bodies), the unresolved cause of the affected | ||
| migration history, drift-detection routing, and the surrounding database debt (`#102`, `#011`, `#036`, | ||
| `#022`, `#025`, `#056`/`#057`, `#183`, `#188`/`#196`–`#200`, `#191`, `#098`/`#099`). Ends with | ||
| standing protections so unverified history repairs remain visible and cannot silently mask drift. | ||
|
|
||
| **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 | ||
| 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). | ||
|
|
||
| --- | ||
|
|
||
| ## Phase 0 — Enablement (repo-side, no hosted access; can start immediately) | ||
|
|
||
| 0.1 **Drift-failure routing** (the "red for three weeks, nobody told" fix). Extend | ||
| `.github/workflows/live-drift.yml`: on failure, create-or-update a pinned GitHub issue titled | ||
| `Live drift check failing` carrying the finding list, and mark it resolved-comment on the next | ||
| green run. Failure becomes a visible, assignable object instead of a silent red row. | ||
|
|
||
| 0.2 **Post-migration drift trigger.** Add a `workflow_run`/`push` trigger so live-drift also runs | ||
| after any push to `main` that touches `supabase/migrations/**` — drift is checked within minutes | ||
| of the change that could cause it, not up to a week later. | ||
|
|
||
| 0.3 **Session tooling** (`#183`): add `SUPABASE_ACCESS_TOKEN` (and Sentry token if desired) to the | ||
| operator environment/secrets so migration-history reads and CLI repair are possible in approved | ||
| windows. Names only in logs, never values. | ||
|
|
||
| 0.4 **Evidence file**: create `docs/audit/live-drift-forensics-2026-08.md`; all phase outputs land | ||
| there, dated. | ||
|
|
||
| Gate: `check:github-actions` (workflow pin/policy), `verify:pr-local` for the workflow edits. | ||
|
|
||
| ## Phase 1 — Forensics: establish the truth (read-only hosted window, ~1 h) | ||
|
|
||
| 1.1 **Migration-history fingerprint.** Run and record: | ||
|
|
||
| ```sql | ||
| select version, name, statements is null as no_statements, | ||
| coalesce(cardinality(statements), 0) as stmt_count | ||
| from supabase_migrations.schema_migrations | ||
| order by version; | ||
| ``` | ||
|
|
||
| Treat every `no_statements = true` row as a candidate mark-applied/repair signal, not proof of | ||
| cause. Record whether `20260705180000` has that signal, then pair it with relevant audit history | ||
| to distinguish skipped DDL from indexes that were created and later dropped. This turns the current | ||
| hypothesis into a named, evidence-backed conclusion. | ||
|
|
||
| 1.2 **RPC divergence dossier.** For each of the 10 mismatched `match_*` functions: | ||
| `select pg_get_functiondef(oid)` on live, diff against the repo's canonical body, classify: | ||
| **live-ahead** (hotfixed live, repo stale), **repo-ahead** (live stale), or **normalization | ||
| noise** (whitespace/qualifier only). No edits. This is the highest-stakes unknown in the whole | ||
| plan — live retrieval behaviour may currently depend on bodies the repo does not contain. | ||
|
|
||
| 1.3 **Index inventory & sizing.** For the 21 missing and 2 unexpected indexes: owning-table | ||
| `pg_relation_size`, and `EXPLAIN (ANALYZE, BUFFERS)` for the known hot queries | ||
| (`documents` title ILIKE, `document_chunks` content search, `rag_retrieval_logs` miss scan). | ||
| These are the before-measurements for Phase 4, including the `#231` latency hypothesis. | ||
|
|
||
| Stop rule: read-only; if anything unexpected appears (e.g. objects missing that the app needs | ||
| right now), report before proceeding. | ||
|
|
||
| ## Phase 2 — Staging rehearsal (`#056`, prerequisite for safe production work) | ||
|
|
||
| 2.1 Bring `Clinical KB Staging` to full migration parity (it is ~24 migrations behind). This | ||
| doubles as the rehearsal: the replay exercises every migration end-to-end, including the | ||
| `20260804110240` guard (staging must prebuild or take the transactional builds — it is idle, so | ||
| transactional is fine there). | ||
|
|
||
| 2.2 Run `check:drift` against staging. Staging green proves the repo chain is self-consistent | ||
| before production is touched. | ||
|
|
||
| 2.3 Keep `#057` (soak + rollback drill) queued for after Phase 3, against an exact candidate. | ||
|
|
||
| ## Phase 3 — RPC reconciliation (clinical-risk core; before indexes because it can change answers) | ||
|
|
||
| Per the Phase 1.2 classification: | ||
|
|
||
| - **Normalization noise** → fix the manifest normalizer / regenerate; no hosted change. | ||
| - **Live-ahead** → codify the live body into a new migration + `schema.sql` mirror. | ||
| PR carries `RAG impact: no retrieval behaviour change — codifying already-live body` and the | ||
| diff as evidence. This is the safe default and likely majority case. | ||
| - **Repo-ahead** → deploying the repo body changes live behaviour: requires the approved | ||
| eval-canary pair (baseline → post, 36/36, recall 1.0) around the deploy. Batch these | ||
| separately; one revertible migration each. | ||
|
|
||
| Stop rule: any RPC whose diff is not clearly classifiable gets its own decision item — never | ||
| "probably fine". | ||
|
|
||
| ## Phase 4 — Index restoration (mutating window, off-peak, batched) | ||
|
|
||
| 4.1 **Batch A — small tables** (`audit_logs`, `api_rate_limits`, `rag_aliases`, `rag_queries`, | ||
| `rag_query_misses`, `image_caption_cache`, `storage_cleanup_jobs`, `indexing_v3_agent_jobs`, | ||
| `document_index_quality`, `document_publication_approvals`, `medication_records`, | ||
| `ingestion_job_stages`, `document_summaries_owner_idx`): `CREATE INDEX CONCURRENTLY IF NOT EXISTS` | ||
| one at a time, verify `indisvalid AND indisready`, drop-and-retry any invalid build. | ||
|
|
||
| 4.2 **Batch B — large tables** (`document_chunks_anchor_idx`, `document_chunks_content_trgm_idx`, | ||
| `documents_title_trgm_idx`, `documents_registry_projection_lookup_idx`, | ||
| `document_index_units_heading_path_idx`, `document_images_*`): same procedure, off-peak, one at a | ||
| time, monitoring locks. Note: the two trigram indexes are the canonical `20260705180000` | ||
| definitions — restoring them is completing an already-recorded migration, **not** `#102`'s | ||
| canary-gated bare-column work, which stays separately gated. | ||
|
|
||
| 4.3 **Unexpected live indexes** (`document_table_facts_document_id_idx`, | ||
| `storage_cleanup_jobs_owner_id_idx`): decide codify (add to a migration + mirror) or | ||
| `DROP INDEX CONCURRENTLY` if redundant with a canonical index. No silent allowlisting. | ||
|
|
||
| 4.4 **Codify.** One reconcile migration per batch following the `20260804110240` pattern | ||
| exactly: the migration **validates** (presence, `indisvalid`/`indisready`, normalized | ||
| `pg_get_indexdef` match) and fails fast — it never builds. Mark applied only after live | ||
| validation passes. Mirror into `schema.sql`, `npm run drift:manifest`, extend | ||
| `search_schema_health()`'s `required_indexes` for any index the runtime should monitor. | ||
|
|
||
| 4.5 **Prove it.** Dispatch live-drift: must be green (or only reasoned allowlist warnings). | ||
| Paste the "Compared … against live" + zero-drift lines into the evidence file. | ||
|
|
||
| ## Phase 5 — Measure and close the loop | ||
|
|
||
| 5.1 Re-run the Phase 1.3 `EXPLAIN` set; record plan flips (seq scan → index scan) and timings. | ||
|
|
||
| 5.2 Re-test `#231`: with trigram indexes present, measure whether the 25 s fast-route budget | ||
| still binds. If latency collapses, `#231` may close or re-scope; if not, its investigation | ||
| continues with better data. | ||
|
|
||
| 5.3 If any Phase 3 RPC deploy changed behaviour: `eval:retrieval:quality` must show 36/36. | ||
| Run `check:production-readiness` once at the end of the window. | ||
|
|
||
| ## Phase 6 — Future-proofing (make recurrence structurally impossible) | ||
|
|
||
| 6.1 **History-integrity probe in drift.** Extend `schema_drift_snapshot()` (new migration) to | ||
| also return `schema_migrations` versions with `statements IS NULL`, and teach `check:drift` to | ||
| report any such row not on a reviewed allowlist (each allowlisted version needs a pointer to its | ||
| guard migration). A history-repair row without a validating guard migration becomes permanent, | ||
| visible drift. | ||
|
|
||
| 6.2 **Guard-migration contract.** Document in `AGENTS.md`/`docs/database-drift-detection.md`: | ||
| any history repair or mark-applied MUST ship a fail-fast validation migration (the | ||
| `20260804110240` pattern). Add a repo test asserting every allowlisted `statements IS NULL` | ||
| version has a matching `*_restore_*`/`*_reconcile_*` guard file. | ||
|
|
||
| 6.3 **Runtime coverage ratchet.** `search_schema_health()` monitors a curated index list; the 21 | ||
| missing indexes were invisible to it. Add a repo test that every index created by migrations on | ||
| retrieval-critical tables is either in `required_indexes` or consciously listed as unmonitored — | ||
| so coverage decisions are explicit. | ||
|
|
||
| 6.4 **DR codification** (`#188` → `#196`–`#200`): move `pg_cron` schedules and `app.*` GUCs into | ||
| idempotent migrations where the platform allows; write the operator runbook + names-only verify | ||
| script for Vault secrets, edge-function deploys, and dashboard settings. Then `#057`'s restore | ||
| drill validates the whole set. | ||
|
|
||
| 6.5 **Same-window dashboard items:** `#011` percentage-based connection-pool allocation; | ||
| `#200` re-entered auth/pool/keys config verification. | ||
|
|
||
| ## Phase 7 — Deferred structural debt (after drift is green and stays green) | ||
|
|
||
| - `#022` hosted apply of the BMJ attestation migration (its own approved window + human review). | ||
| - `#025` configure the Railway webhook so the deployed document-change plumbing goes live. | ||
| - `#036` explicit `public_corpus` marker migration (privacy hardening; supabase-schema-guardian | ||
| review). | ||
| - `#191` ACL-migration consolidation (maturity X5) — **last**, because rewriting migration files | ||
| is exactly the operation the new history-integrity checks exist to police; doing it after 6.1/6.2 | ||
| means the safety net is live. | ||
| - `#098`/`#099` round-trip budget tests on the answer path (repo-side, independent). | ||
|
|
||
| --- | ||
|
|
||
| ## Order rationale (why this sequence) | ||
|
|
||
| Truth before mutation (Phase 1), rehearsal before production (Phase 2), the change that can alter | ||
| clinical answers before the changes that only speed them up (Phase 3 before 4), measurement before | ||
| closure (Phase 5), and prevention built while the incident is fresh (Phase 6). Consolidation and | ||
| nice-to-haves last (Phase 7). Phase 0 is first because it needs no approval and immediately fixes | ||
| the failure that let this sit unnoticed. | ||
|
|
||
| ## Approval map | ||
|
|
||
| | Phase | Hosted access | Approval needed | | ||
| | ----- | ---------------------------------------------------------- | ---------------------------------------------------- | | ||
| | 0 | none (GitHub Actions secrets already hold the service key) | repo PR review only | | ||
| | 1 | read-only production | yes — read window | | ||
| | 2 | staging mutation | yes — staging window | | ||
| | 3 | production RPC deploys | yes — per batch; canary approval for repo-ahead RPCs | | ||
| | 4 | production DDL (concurrent) | yes — off-peak window per batch | | ||
| | 5 | read-only + one eval dispatch (~$1–2) if RPCs changed | yes for eval | | ||
| | 6 | one migration deploy (6.1); rest repo/docs | yes for 6.1 | | ||
| | 7 | per item | per item | | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.