Skip to content

fix(db): read migration history through a service-role RPC so live-drift can pass (#316) - #2198

Merged
BigSimmo merged 2 commits into
mainfrom
claude/phase-4-index-restoration-b0f4ea
Aug 20, 2026
Merged

fix(db): read migration history through a service-role RPC so live-drift can pass (#316)#2198
BigSimmo merged 2 commits into
mainfrom
claude/phase-4-index-restoration-b0f4ea

Conversation

@BigSimmo

Copy link
Copy Markdown
Owner

Summary

The live-drift workflow's second step, Align migration history for Supabase Preview, has never been able to pass on this project. It read supabase_migrations.schema_migrations directly through PostgREST with Accept-Profile: supabase_migrations, and this project has never exposed that schema to the Data API, so the read returns 406 PGRST106 every time.

The defect stayed hidden because the drift comparison ran first and always failed, leaving this step skipped. Phase 6.2 cleared the last drift finding on 2026-08-19, the step ran for the first time ever, and it is now the sole reason the job concludes failure — and therefore the sole reason pinned issue #1963 stays open against a database whose drift block is empty.

Measured again on main this week, run 32378402265 (2026-08-20, weekly cron):

Compare live schema drift: success
Align migration history for Supabase Preview: failure
Compared 6 extensions, 38 tables, 1 views, 93 functions, 210 indexes, 48 policies, 170 constraints, 26 triggers, 2 storage_buckets against live.
No unexpected schema drift between live and supabase/schema.sql.
Unable to read remote schema_migrations via Accept-Profile (status 406: {"code":"PGRST106", ... "message":"Invalid schema: supabase_migrations"})

This PR repairs the read with the least authority that can do the job:

  • supabase/migrations/20260820120000_migration_history_versions_rpc.sql adds public.migration_history_versions()stable, security definer, set search_path to '', dynamic read guarded by to_regclass, returning {probe, versions} for every history row. revoke execute … from public, anon, authenticated plus grant execute … to service_role, exactly the established public.schema_drift_snapshot() pattern (20260706200000 / 20260818090000). Read-only: one select over the CLI history table.
  • scripts/check-migration-history-alignment.ts now tries the RPC first and falls back to the old Accept-Profile read only when the function itself is absent (404 / PGRST202). Every other outcome raises, including probe: no_history_table — a check that reports "aligned" because it could not look would be worse than the red job it replaces. When neither path works, the failure names the remedy.
  • supabase/schema.sql mirrors the definition byte-identically; supabase/drift-manifest.json regenerated.
  • tests/migration-history-alignment.test.ts (new, 7 tests) pins the preference order, the fallback condition, the honest-failure behaviour, and migration-vs-mirror parity.
  • Docs:docs/database-drift-detection.md gains a section on the alignment step and why this transport was chosen; docs/audit/live-drift-forensics-2026-08.md gains "Alignment-step repair — 2026-08-20" with the run IDs and pasted output; docs/database-remediation-coordination.md records the state and the required deploy order.

Two alternatives were considered and rejected, recorded in the forensics file so they are not re-litigated: exposing supabase_migrations to the Data API (widens the public PostgREST surface of a clinical project for one weekly read, and lives in dashboard config rather than in git), and routing the read through the management API with SUPABASE_ACCESS_TOKEN (puts an account-scoped token into CI secrets — far broader authority than the read needs, and still blocked on #183).

Deploy order. The Supabase GitHub auto-deploy is off (D4), so merging this PR applies nothing. supabase db push runs from this branch in an approved window before merge — the order Phase 4 used. Until the migration is applied, check:drift would report migration_history_versions as a missing function, so merging first would trade one red for another. Staging needs the same migration by the Phase 2 method to hold the parity Phase 4 restored.

Ledger: this is the fix for the queued request cc60253d (P2, live-drift alignment step); the canonical row is allocated by the next serialized npm run issues:reconcile. Tracking anchor #316.

RAG impact: no retrieval behaviour change — this adds a read-only, service-role-only catalog function used solely by CI migration-history alignment, and rewrites one CI script's transport; no retrieval RPC, ranking surface, selection, ordering, or fixture is touched.

Verification

  • 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 — full scratch replay of supabase/schema.sql into supabase/postgres:17.6.1.127, "Replay complete in 58s". This executes the new function body in a real Postgres, so the SQL is proven rather than reviewed. Manifest now carries 94 functions (was 93), with public.migration_history_versions() at acl: ["postgres=X/postgres", "service_role=X/postgres"] — least privilege confirmed by replay, no PUBLIC execute.
  • npx vitest run tests/drift-detection.test.ts tests/migration-history-guards.test.ts tests/search-health-index-coverage.test.ts tests/migration-history-alignment.test.ts — "Test Files 4 passed (4) / Tests 40 passed (40)".
  • npm run format (whole tree) and npm run verify:pr-local.

Not run, with reasons: no provider-backed gate (check:supabase-project, eval:*, verify:release) — none is needed for a repo-side change, and all require explicit approval. No browser gate — no UI surface is touched. The live proof that this fix works can only come from the deploy window plus the next live-drift dispatch, and is recorded as owed.

Clinical Governance Preflight

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy)
  • Service-role keys and private document access remain server-only
  • Demo/synthetic content remains clearly separated from real clinical sources
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative
  • Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed

Notes

  • The new function reads only supabase_migrations.schema_migrations (version and name). It touches no clinical document, no owner-scoped table, and no patient data, and it is unreachable by anon or authenticated.
  • It complements schema_drift_snapshot()'s migration_history, which returns only the rows recorded without executed statements. Neither replaces the other.

…ift can pass (#316)
The live-drift workflow's "Align migration history for Supabase Preview" step
has never been able to pass on this project. It read
supabase_migrations.schema_migrations directly through PostgREST with
Accept-Profile, and this project has never exposed that schema to the Data API,
so the read returns 406 PGRST106 every time.
The defect stayed hidden because the drift comparison ran first and always
failed, leaving this step skipped. Phase 6.2 cleared the last drift finding on
2026-08-19, the step ran for the first time ever, and it is now the sole reason
the job concludes failure - and therefore the sole reason pinned issue #1963
stays open against a database whose drift block is empty (run 32378402265,
2026-08-20: "Compare live schema drift: success", "No unexpected schema drift").
20260820120000 adds public.migration_history_versions(): stable, security
definer, search_path pinned to '', dynamic read guarded by to_regclass,
returning {probe, versions} for every history row, revoked from public/anon/
authenticated and granted to service_role alone - the established
schema_drift_snapshot() pattern. Exposing supabase_migrations to the Data API
would widen the public PostgREST surface of a clinical project for one weekly
read; routing through the management API would put an account-scoped token into
CI. Both are recorded as rejected in the forensics file.
check-migration-history-alignment.ts now prefers the RPC and falls back to the
old read only when the function itself is absent. Every other outcome raises,
including probe: no_history_table - a check that reports "aligned" because it
could not look is worse than the red job it replaces.
The migration is NOT deployed: D4 is off, so db push runs from this branch in an
approved window BEFORE merge, the order Phase 4 used. Until it is applied,
check:drift would report migration_history_versions as a missing function.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your current included review allowance is based on your included PR review attempts over the past 7 days.

Next review available in:40 minutes

Limit details: You’ve used the included review currently available. Your 85 included PR review attempts over the past 7 days set your current allowance 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?

Wait for the limit to reset, then comment @coderabbitai review or push new commits to the PR.

An organization admin can change what happens after included review limits in Billing.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f099eb19-8943-4bc7-9af7-2395290a2d55

📥 Commits

Reviewing files that changed from the base of the PR and between a1c2ced and 3b345da.

📒 Files selected for processing (8)
  • docs/audit/live-drift-forensics-2026-08.md
  • docs/database-drift-detection.md
  • docs/database-remediation-coordination.md
  • scripts/check-migration-history-alignment.ts
  • supabase/drift-manifest.json
  • supabase/migrations/20260820120000_migration_history_versions_rpc.sql
  • supabase/schema.sql
  • tests/migration-history-alignment.test.ts

Comment @coderabbitai help to get the list of available commands.

@supabase

supabaseBot commented Aug 20, 2026

Copy link
Copy Markdown

Updates to Preview Branch (claude/phase-4-index-restoration-b0f4ea) ↗︎

DeploymentsStatusUpdated
DatabaseThu, 20 Aug 2026 17:10:57 UTC
ServicesThu, 20 Aug 2026 17:10:57 UTC
APIsThu, 20 Aug 2026 17:10:57 UTC

Tasks are run on every commit but only new migration files are pushed.
Close and reopen this PR if you want to apply changes from existing seed or migration files.

TasksStatusUpdated
ConfigurationsThu, 20 Aug 2026 17:10:59 UTC
MigrationsThu, 20 Aug 2026 17:11:01 UTC
SeedingThu, 20 Aug 2026 17:11:02 UTC
Edge FunctionsThu, 20 Aug 2026 17:11:06 UTC

View logs for this Workflow Run ↗︎.
Learn more about Supabase for Git ↗︎.

@BigSimmo
BigSimmo enabled auto-merge (squash) August 20, 2026 17:10
@BigSimmo
BigSimmo merged commit a341832 into mainAug 20, 2026
24 checks passed
@BigSimmo
BigSimmo deleted the claude/phase-4-index-restoration-b0f4ea branch August 20, 2026 17:17
BigSimmo added a commit that referenced this pull request Aug 20, 2026
…estion (#2201)
* docs(ledger): record the PR #2198 migration-history alignment review
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs(db): record the 2026-08-20 alignment window and reopen the D4 question
The PGRST106 fix (PR #2198, squash a341832) merged before its production
window, so the window's pre-flight found 20260820120000 already applied and
db push was never run. Verified read-only against sjrfecxgysukkwxsowpy:
stmt_count 3 (executed statements, not mark-applied), prosecdef true,
provolatile s, search_path="", proacl postgres=X/postgres |
service_role=X/postgres, and the function returns probe ok with
version_count 211 against 211 history rows and 211 local files. No guard
migration is owed.
Two open items are recorded rather than absorbed.
D4 is unresolved again. created_by and idempotency_key are NULL on every row
from 20260818090000 onward, including ones this programme applied by operator
db push, so the history table carries no provenance signal. The 2026-08-19
observation recorded as "D4 is OFF" was that migrations sat pending WHILE a PR
was open; that never tested deploy-on-merge, and so never contradicted section
3.7's 34-second application after #2106's squash-merge. Until the dashboard is
re-checked, assume merging a migration PR deploys it to production.
Staging is one version behind (210 rows, function absent, pending set exactly
20260820120000). Both write paths were denied by the session auto-mode
classifier under the live-Supabase confirmation rule added by PR #2196, so
Phase 4's staging parity is open by one version until an operator applies it.
Also lands the owed PR #2198 review-ledger record, whose original push was cut
short when the branch was deleted on merge.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs(db): treat a merged migration PR as a production deploy until D4 is re-verified
The 2026-08-20 window update reopened D4 (Supabase GitHub auto-deploy), but the
status board's active instructions still told a coordinator the opposite: the
"Next dispatches" line, the D4 owner-decision entry, the 2026-08-19 "Resolved"
paragraph, and the pre-window forensics section all said D4 is OFF and every
migration needs its own explicit `db push`. A coordinator reading those could
merge a migration PR outside its approved window — the unapproved production
schema change the update exists to prevent.
All four now point at the reopened decision: assume merging a migration PR
deploys it to production until the dashboard toggle is re-verified. Push-before-
merge stays the required order, for the inverted reason.
Also lands the PR #2201 review-ledger record.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HoyoNN7nCUVDLaE84k6e6c
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@BigSimmo