feat(db): migration-history probe, guard-migration contract, and index-monitoring ratchet (remediation Phase 6) - #2058
Conversation
…x-monitoring ratchet (remediation Phase 6)
Phase 6 of docs/database-remediation-plan.md, repo-side only. The 6.1 migration is authored and
mirrored but NOT deployed; deployment is a separately approved production window after Phase 4.
- 6.1 `20260818090000_schema_drift_snapshot_history_probe.sql`: schema_drift_snapshot() v2 also
returns `migration_history` (supabase_migrations versions whose statements are NULL/empty — the
mark-applied / history-repair fingerprint from forensics §1.1) plus `migration_history_probe`
(ok | no_history_table | no_statements_column). Mirrored into schema.sql; drift-manifest.json
regenerated from a Docker replay (probe executed: no_history_table, snapshot_version 2).
- check:drift reports every live no-statements version as `! [migration_history] no_statements`
unless a structurally valid `migration_history` allowlist entry (reason + guard {class,
migration, objects}) covers it; the category is never compared manifest-vs-live; a live
snapshot without the probe prints an info line naming the pending deploy.
- 6.2 Guard-migration contract in docs/database-drift-detection.md + AGENTS.md; guard classes
validation (mandatory from 2026-08-18) / superseded / no_ddl; tests/migration-history-guards.test.ts
verifies each allowlisted version's guard file really covers its objects. Seeded five §1.1
versions with repo-provable superseded guards; the remaining §1.1 rows and the 2026-07-12 batch
are deliberately unallowlisted (expected first-run findings — they need real guard migrations).
- 6.3 tests/search-health-index-coverage.test.ts + supabase/search-health-unmonitored-indexes.json:
every repo-defined index on documents, document_chunks, document_index_units,
document_embedding_fields, document_memory_cards, rag_retrieval_logs is monitored by
search_schema_health() or explicitly listed with reason + disposition (44 entries, 8
monitor-candidates incl. the three §1.3-absent indexes on those tables). Failed with exactly 44
names before the list existed.
- Residual queued via issues:add (deploy window, first-run triage, guard migrations for
pre-contract rows). #316/#56 untouched (owned by other sessions).
RAG impact: no retrieval behaviour change — observability-only snapshot extension and repo-side tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Updates to Preview Branch (claude/database-drift-allowlist-48839e) ↗︎
Tasks are run on every commit but only new migration files are pushed.
View logs for this Workflow Run ↗︎. |
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 52 minutes Limit details: You’ve used all 1 included review currently available under your plan. You completed 95 included PR reviews in the past 7 days; at that activity level, included reviews refill at 1 review per hour. Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (14)
Comment |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Summary
Remediation plan Phase 6 (
docs/database-remediation-plan.md§6.1–6.3), built repo-side only. Worker chat with no owned ledger row;#316and#056were not touched.supabase/migrations/20260818090000_schema_drift_snapshot_history_probe.sqlredefinespublic.schema_drift_snapshot()(v2): same inventory plusmigration_history— everysupabase_migrations.schema_migrationsversion whosestatementsis NULL or empty (the mark-applied / history-repair fingerprint from forensics §1.1) — andmigration_history_probe(ok/no_history_table/no_statements_column, never a silent empty array). Read via a guarded dynamic query so a replay without the history schema still succeeds. Mirrored byte-identically intosupabase/schema.sql;supabase/drift-manifest.jsonregenerated from the Docker replay (the new body executed there: probeno_history_table,snapshot_version2).scripts/check-drift.tsnever compares this category manifest-vs-live; each live row is reported as! [migration_history] no_statements <version>unless a structurally validmigration_historyallowlist entry covers it, and a live snapshot without the probe prints an info line naming the pending deploy.mismatch public.schema_drift_snapshot()function finding (repo-ahead body) — that is the pending deploy, not a body regression, and the script says so.docs/database-drift-detection.md("Migration-history probe", "Guard-migration contract") andAGENTS.md("Supabase project safety"): any mark-applied / history repair MUST ship a fail-fast validation migration per20260804110240.supabase/drift-allowlist.jsonmigration_historyentries carryreason+guard {class, migration, objects}; classesvalidation(mandatory for versions from 2026-08-18),superseded,no_ddl(pre-contract history only).check:driftenforces the structural half at runtime (a malformed entry never silences a row);tests/migration-history-guards.test.tsverifies each guard file really covers its listed objects. Seeded five §1.1 versions with repo-provablesupersededguards (20260701010000,20260701020000,20260701030000,20260701060000,20260702000000). The remaining §1.1 rows and the 2026-07-12 batch are deliberately not allowlisted — they will be the expected findings of the first post-deploy run and need real guard migrations, not bare allowlist entries.tests/search-health-index-coverage.test.ts+supabase/search-health-unmonitored-indexes.json: every repo-defined index (migration replay ∪ manifest;*_pkeyexempt) ondocuments,document_chunks,document_index_units,document_embedding_fields,document_memory_cards,rag_retrieval_logsmust be insearch_schema_health()required_indexes/index_aliases(parsed from the latest definer, cross-checked withschema.sql) or listed with a reason and disposition. Seeded 44 entries, 8monitor-candidateincluding the three §1.3-absent indexes on those tables (document_chunks_anchor_idx,document_index_units_heading_path_idx,documents_registry_projection_lookup_idx); the other 17 absent indexes are on tables outside this scope. The test failed with exactly those 44 names before the list existed.npm run issues:add(deploy window, first-run triage, guard migrations for the pre-contract rows, Phase 4.4 decision on the monitor candidates). Forensics file gained a Phase 6 section.RAG impact: no retrieval behaviour change — observability-only snapshot extension and repo-side tests.
Verification
npm run verify:pr-local— every gate green excepttest, which failed only ontests/session-start-hook.test.ts(5 tests, exit127head: command not foundin this Windows Git Bash plusEPERMon a temp dir); reproduced alone with the same environmental cause and unrelated to this diff. Full suite otherwise:Test Files 1 failed | 641 passed | 2 skipped (644),Tests 5 failed | 6861 passed | 27 skipped (6893);lint,typecheck,check:pr-policy,check:ledger-write-discipline,docs:check-links(1838 repo path references resolve) all passed. The three not-reached checks were run directly:Offline RAG fixture and manifest validation passed (36 golden cases, 25 suites).,data/medication-interaction-index.json is up to date (523 rows).,docs/medication-interaction-lexicon-review.md is up to date (28 catalogue terms).npm run check:migration-role—Hosted migration-role guard passed: active hosted SQL/tooling uses postgres and immutable applied history is unchanged.npm run drift:manifest—Replay complete in 46s,Wrote supabase/drift-manifest.json(Docker, digest-pinned image,--pull=never).tests/drift-detection.test.ts tests/supabase-schema.test.ts tests/migration-history-guards.test.ts tests/search-health-index-coverage.test.ts tests/live-drift-workflow.test.ts tests/migration-history-placeholders.test.ts tests/forward-codify-retrieval-targets.test.ts tests/hosted-migration-role-guard.test.ts—Test Files 8 passed,127 passed.44 index(es) on retrieval-critical tables are neither in search_schema_health() required_indexes/index_aliases (20260706010000_search_schema_health_m13_guard.sql) nor listed …; guards test with one entry pointed at a nonexistent guard →allowlist entry 20260701030000 (superseded → 20260706010000_does_not_exist.sql): guard file missing; both green after restore.npm run verify:ui— not applicable (no UI change).npm run verify:release— not run (no release claim).npm run eval:retrieval:quality— not applicable (no retrieval, ranking, selection, chunking, or scoring change; nosrc/lib/rag/**ormatch_*body touched).npm run eval:rag/npm run eval:quality— not applicable (no answer-generation change).npm run check:production-readinessandnpm run check:driftagainst live — provider-backed; this task was authorised as repo-side only with no hosted access.check:driftagainst live is what the deploy window will run.npm run check:deployment-readiness— not applicable.Risk and rollout
snapshot_version1→2, two new keys) and read-only;check-drift.tschanges only add a category, so existing object comparisons are byte-for-byte unchanged (existing unit tests still pass). Two new tests are ratchets: they will fail future PRs that add an unmonitored index on a retrieval-critical table or amigration_historyallowlist entry without a real guard — that is the intent. Once deployed,check:driftwill report roughly nine §1.1 versions plus the six 2026-07-12 batch versions as unguardedno_statementsfindings on top of today's red state; the follow-up (queued) is to author validation guard migrations for them.20260706200000body via a new migration restores v1 (the function iscreate or replace, no data). Deleting the two JSON files and tests removes the ratchets.Clinical Governance Preflight
Complete this section when the change touches ingestion, answer generation, search/ranking, source rendering, document access, privacy, production env, or clinical output.
Clinical KB Database(sjrfecxgysukkwxsowpy)Notes
supabase/migrations/**+supabase/schema.sql(operational-risk surface, migration replay gates apply) and readssearch_schema_health(); does not touchsrc/lib/rag/**or anymatch_*body.schema_drift_snapshot/check-drift/drift-allowlist/search_schema_health/drift-manifestsurfaces before starting (#292): none open.docs/database-remediation-coordination.mdis not onmainyet (open PR docs(db): land and re-baseline the remediation coordination board (#316) #2044) and was read from that head.🤖 Generated with Claude Code