Replay the migration chain onto staging to parity, and record that the chain does not reproduce schema.sql (Phase 2, #056) - #2093
Conversation
…se 2, #56) Phase 2 of docs/database-remediation-plan.md, run in an owner-authorized staging window. Target was Clinical KB Staging (ikoiolksxqxfxgiyqpnu) via the Supabase MCP connector, with the ref re-verified on every call; production (sjrfecxgysukkwxsowpy) was never a mutation target. The gap re-measured as 28 versions at the start of the window, not the 26 recorded on 2026-08-17 — ten earlier history holes plus eighteen versions after 20260719055623. The whole chain was replayed in version order. Staging now holds 194 rows in supabase_migrations.schema_migrations, latest 20260814151000, zero statements IS NULL, and a two-way version diff against supabase/migrations is empty. Every replayed row was read back and its md5 compared against md5sum of the repository file: all 28 match byte-for-byte. supabase db push was unavailable (SUPABASE_ACCESS_TOKEN absent per #183, staging DB password operator-only) and MCP apply_migration was rejected because it stamps connector-generated versions, which docs/staging-setup.md forbids. Each migration was executed verbatim via execute_sql with an explicit schema_migrations row carrying the repository's own version and name. check:drift against staging is not yet proven. scripts/check-drift.ts forwarded only three of the five identity keys to checkSupabaseProjectConfig, so any staging URL was rejected as a production mismatch; that narrowing is fixed here, mirroring scripts/check-supabase-project.ts. The remaining blocker is the staging service-role key, which is not in this environment and which the MCP connector cannot supply. The gate was deliberately not run rather than risk pointing it at production via the only local .env.local. Six findings from the clean replay are recorded, none patched — notably that the 20260804110240 guard passed with no prebuild because staging never lost the indexes production lost, that four migrations exist as duplicate earlier/later version pairs which a plain --include-all push would apply in the wrong effective order, and that 20260724120000 silently drops the work_mem setting 20260724000000 applies. No schema file, migration, or drift manifest was changed. No production clinical document was copied to staging and no ingestion worker was started.
…uce schema.sql check:drift now runs against staging and is red with 19 findings. Because staging carries the complete, byte-verified migration chain, this is not staging staleness: it is the committed chain and supabase/schema.sql disagreeing, and check:drift builds its expected side from schema.sql. Seven match_* def_hash mismatches are SET work_mem, which pg_get_functiondef renders and def_hash does not strip. grep -c work_mem supabase/schema.sql returns 0 and only 20260724000000 sets it, so schema.sql is the stale side and the fix is repo-side, not a production deploy. The eighth work_mem target, match_document_table_facts_text, is absent from the drift list precisely because 20260724120000 re-created it without restating work_mem — the exception confirms the mechanism. Eight further objects are declared in schema.sql that no migration creates or drops: five document_embedding_fields indexes, documents_status_idx, and the documents_updated_at and ingestion_jobs_updated_at set_updated_at triggers. Three table column-set mismatches and one index def mismatch remain unclassified. This bears on #316's recorded work_mem hypothesis, now measured on staging for seven of production's ten mismatched RPCs with no production call. It does not close Phase 1.2 and #316 was deliberately not updated from this session. The superseded #56 inbox request is cancelled and replaced rather than edited, per the immutable-request rule. Measurement base: ed43a64. origin/main has since advanced to 6b72b3a (195 migrations, including a schema_drift_snapshot history probe); re-measuring against that manifest is recorded as follow-up.
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in:36 minutes Limit details: You’ve used all 1 included review currently available under your plan. You completed 101 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 (7)
Comment |
This pull request has been ignored for the connected project Preview Branches by Supabase. |
Uh oh!
There was an error while loading. Please reload this page.
Applies the queued inbox requests into docs/outstanding-issues.md, including the Phase 2 staging-parity result for #56: staging replayed to 194/194 parity with every row md5-verified, and check:drift run against staging returning 19 findings showing the migration chain does not reproduce supabase/schema.sql. Four cancellation decisions are honoured, including the superseded #56 request that recorded check:drift as blocked before the gate was actually run. Re-run from base dda4956 so the batch is complete: the previous attempt was cut against 9d83245 and left one later-arriving request pending, which the write-discipline guard correctly rejected as a partial transaction.
* docs(issues): reconcile 17 queued ledger requests after PR #2093 Applies the queued inbox requests into docs/outstanding-issues.md, including the Phase 2 staging-parity result for #56: staging replayed to 194/194 parity with every row md5-verified, and check:drift run against staging returning 19 findings showing the migration chain does not reproduce supabase/schema.sql. Four cancellation decisions are honoured, including the superseded #56 request that recorded check:drift as blocked before the gate was actually run. Re-run from base dda4956 so the batch is complete: the previous attempt was cut against 9d83245 and left one later-arriving request pending, which the write-discipline guard correctly rejected as a partial transaction. * docs(ledger): supersede the reconciliation review record after the re-cut
…nchor Two stale pointers in the playbook's context briefing, both of which misdirect a fresh session before it reads anything else. The phase status block still said Phase 1.2 was outstanding and gating Phase 3, and named "complete 1.2, then Phase 2" as the next step. Both have since landed: 1.2 in PR #2087 (all ten mismatches attribute-only SET work_mem, zero body divergence, zero repo-ahead, zero UNCLASSIFIED) and Phase 2 in PR #2093 (staging at full parity, check:drift against staging red with 19 items — the finding the phase existed to produce, with a re-measure owed once staging carries 20260818090000). Phase 6 is repo-side complete with its migration deploy still owed. The block now records that and defers to docs/database-remediation-coordination.md as the live board rather than becoming a second source of truth beside it. It also flags that Phase 3 has been reframed: zero repo-ahead bodies means no eval canary, which the Phase 3 prompt further down this file still assumes. That prompt and the traps list are deliberately left untouched here — the reframing and its owner decisions belong to the coordination board, and duplicating them would recreate the drift this change is repairing. The tracking anchor quoted the row title "Live DB is missing 21 repo-defined indexes and 10 retrieval RPC bodies diverge". The row has since been retitled to "Live DB has 20 currently missing repo-defined indexes and 10 retrieval RPC bodies diverge; weekly live-drift has been red since 2026-07-26 with no routing", so the exact-title lookup the playbook instructs could no longer match. The "21" was preserved deliberately in #1979 because it was the lookup key; correcting the row is what broke it. Anchor updated to the current title, verified character for character against the row on main. No issues:update queued: #316 and #56 are owned by other running chats, and a second pending mutation on either row would make the inbox refuse the batch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKNFogaYfCQBvFVqFQnfRt
Summary
Phase 2 of
docs/database-remediation-plan.md, executed in an owner-authorized staging-onlywindow. Two independent items, each its own commit:
5067c4aad— staging replayed to migration parity (#056). The gap re-measured as 28versions at the start of the window, not the 26 recorded on 2026-08-17. The whole chain was
replayed in version order; staging went from 166 rows to 194, latest
20260814151000, zerostatements IS NULL, and a two-way version diff againstsupabase/migrations/is empty in bothdirections. Every replayed row was read back and md5-compared against its repository file — all 28
match byte-for-byte.
523f8a55b—check:driftagainst staging now runs, and is red with 19 findings. This is thesubstantive result. Because staging carries the complete, byte-verified chain, this is not staging
staleness: the committed migration chain and
supabase/schema.sqldisagree, andcheck:driftbuilds its expected side from
schema.sql. Getting the check to target staging at all required asix-line fix to
scripts/check-drift.ts(below).The drift result
Seven
match_*def_hashmismatches areSET work_mem, whichpg_get_functiondefrenders anddef_hashdoes not strip.grep -c work_mem supabase/schema.sqlreturns 0, and only20260724000000sets it — soschema.sqlis the stale side and the fix is repo-side, not aproduction deploy. The eighth work_mem target,
match_document_table_facts_text, is absent fromthe drift list precisely because
20260724120000re-created it without restatingwork_mem. Theexception confirms the mechanism.
Eight further objects are declared in
schema.sqlthat no migration creates or drops: fivedocument_embedding_fieldsindexes,documents_status_idx, and thedocuments_updated_at/ingestion_jobs_updated_atset_updated_attriggers. Three table column-set mismatches and oneindex def mismatch remain unclassified.
Programme consequence: until
schema.sqland the chain are reconciled, a production driftfinding cannot be assumed to mean production drifted — for the seven work_mem functions the opposite
holds. That argues for reconciling
schema.sqlto the chain before spending a production windowon Phase 3.
Code change
scripts/check-drift.tspassed only three of five identity keys tocheckSupabaseProjectConfig,dropping
SUPABASE_STAGING_PROJECT_REF/SUPABASE_STAGING_PROJECT_NAME. With those absentresolveStagingProjectreturnsnull,expectedfalls back to production, and any staging URL isrejected as a mismatch — even though
requireServerEnv()on the line above is already staging-awareand
scripts/check-supabase-project.tspasses all five. Proven against identical env:RAG impact: no retrieval behaviour change — no
src/lib/rag/**, ranking, selection, eval harness orgolden-fixture file is touched. The only executable change is the drift check's project-identity
plumbing. The seven
match_*functions appear here as measurements of existing divergence, and noRPC body was created, altered, or deployed anywhere.
Safety
ikoiolksxqxfxgiyqpnu, ref re-verified before every mutating call.Production
sjrfecxgysukkwxsowpywas never a mutation target; the only production interaction inthe window was
list_projects.documentsanddocument_chunksremain0. The staging environment was not recreated.supabase/schema.sql, nodrift-manifest.json. Six clean-replayfindings are recorded rather than patched.
supabase db pushwas unavailable (SUPABASE_ACCESS_TOKENabsent,#183); MCPapply_migrationwas rejected because it stamps connector-generated versions, which
docs/staging-setup.mdforbids.Each migration ran verbatim via
execute_sqlwith an explicit history row carrying therepository's own version and name.
Verification
npm run verify:pr-localon5067c4aad: green throughcheck:runtime,check:installed-lock-parity,format:changed,sitemap:check, all four docs checks,check:branch-review-ledger,check:outstanding-issues,check:ledger-write-discipline,lint,typecheck.npm run test:Test Files 2 failed | 638 passed | 2 skipped. Both pre-existing and unrelated —run-to-run failure sets were disjoint,
worker-observabilitypasses in isolation (contentiontimeout), and
session-start-hook.test.tsfails on a Git-Bash-on-Windows path-format mismatch;neither it nor the hook it exercises is in this diff.
check:outstanding-issues,check:ledger-write-discipline,docs:check-linksre-run green on523f8a55b.Known scope limit — follow-up required
Measurement base is
ed43a64f2.origin/mainadvanced 121 commits during the window and nowcarries 195 migrations, including
20260818090000_schema_drift_snapshot_history_probe.sql,which redefines the
schema_drift_snapshot()function this check calls;schema.sqlanddrift-manifest.jsonalso changed. So: staging is one migration behind again, and the 19 findingswere measured against the previous manifest. The structural findings (work_mem absent from
schema.sql; objects no migration creates) are base-independent, but re-measuring against 195 isowed and is recorded as follow-up rather than asserted here.
#056is updated by queued inbox request; the earlier request was cancelled and replaced ratherthan edited, per the immutable-request rule.
#316was deliberately not updated — Phase 1.2owns it.
Clinical Governance Preflight
Clinical KB Database(sjrfecxgysukkwxsowpy)Notes against the items above, since this PR ran a hosted mutation:
project.tsdefault was altered, so therepository's configured target remains
Clinical KB Database(sjrfecxgysukkwxsowpy). Thestaging mutation happened in a separate owner-authorized window against
Clinical KB Staging(ikoiolksxqxfxgiyqpnu), declared throughSUPABASE_STAGING_PROJECT_REF/_NAMEin an uncommitted local env. Production was never amutation target; the only production interaction was
list_projects.scripts/check-drift.tschange only forwards two existingproject-identity env keys into an existing guard call. It grants no new access, widens no scope,
and touches no client bundle —
check:driftwas already service-role, server-side, CLI-only.ingestion or source-governance code is touched; no migration,
supabase/schema.sql, ordrift-manifest.jsonis modified. Staging's corpus stayed empty (documents= 0,document_chunks= 0) and no production clinical document was copied to it. Nothing here alterswhat a clinician sees, so the SaMD classification is unaffected.
Risk and rollout
Risk: low, and the hosted part is already done and reversible by inaction. The repository diff
is documentation plus a six-line env-forwarding fix in a CLI-only gate script; nothing ships to
users, no migration or schema artifact changes, and no client bundle is affected.
The one irreversible-by-diff action is the staging replay, which already happened in its authorized
window. Rollback for it is not a revert of this PR — the PR does not perform it. Should staging ever
need to be returned to its prior state, the 28 applied versions are enumerated in the forensics
document and staging holds no data to lose (
documents= 0,document_chunks= 0); the supportedrecovery is to re-run the chain, not to hand-delete history rows.
Rolling back the repository change is an ordinary
git revert, whose only effect is thatcheck:driftcan no longer target staging.Production is unaffected by merging this. No production DDL, data change, config, or deploy is
involved, and the drift findings recorded here are measurements, not fixes.
Verification
npm run verify:pr-local— green throughcheck:runtime,check:installed-lock-parity,format:changed,sitemap:check, all four docs checks,check:branch-review-ledger,check:outstanding-issues,check:ledger-write-discipline,lint,typecheckcheck:outstanding-issues,check:ledger-write-discipline,docs:check-linksre-run greenon the evidence commit
npm run test—2 failed | 638 passed | 2 skipped; both pre-existing and unrelated(run-to-run failure sets were disjoint,
worker-observabilitypasses in isolation, andsession-start-hook.test.tsfails on a Git-Bash-on-Windows path-format mismatch; neither itnor the hook it exercises is in this diff)
npm run check:driftagainst staging — exit 1 by design: the red result is the findingthis PR reports, not a failed gate
verify:ui/verify:release— no UI, routing, styling or build-affectingchange in the diff, so no plausible regression path for browser gates to detect.