Migration tracking & DB-test CI (#1327)
Two related problems, one now addressed in CI, one still requiring an ops step.
1. DB-test harness β built (manual trigger for now)
.github/workflows/db-tests.yml boots a fresh Postgres with the Supabase CLI
(supabase db start, which reads supabase/migrations/ β the repo source of
truth), then runs scripts/run-db-tests.sh over supabase/tests/*.test.sql.
Itβs the first environment that can actually validate clean apply (needs Docker + the Supabase CLI, which CI has but local dev here did not). The #1325 rename (unique 14-digit version prefixes) fixed the version-collision that previously stopped a from-scratch apply after the first same-day migration β the CLI now applies migrations in correct order.
Itβs workflow_dispatch (manual) rather than a PR gate, because a
from-scratch apply does not yet complete. Running it surfaced two distinct
classes of latent issue in the first ~46 of 202 migrations:
- Invalid SQL β
20250222β¦_004_multi_tenancy.sqlhadALTER COLUMN tenant_id SET DEFAULT (SELECT β¦). A column DEFAULT canβt be a subquery (Postgres rejects it in every version), so it never ran in prod either. Fixed: pin thedefaulttenant to its real prod id (2de55acf-β¦) and use that literal as the default β faithful to prod and valid from scratch. - Cross-migration-set dependency β
20260326β¦_002_music_scores_lyrics.sql(main set) referencesmusic_scores, a table created in the separateapps/music/supabase/migrationsset. The main set isnβt self-contained, so it canβt apply standalone. Not fixed β this is structural (which set owns which tables) and belongs to the reconciliation below.
Two unrelated breakages this early imply more are scattered through the
remaining ~150 migrations. Closing the gap to a green PR gate is a deliberate
reconciliation effort, not a spot-fix chain β see Β§2. Flip the trigger to
pull_request once supabase db start applies everything clean.
2. Prod migration tracking β still broken (ops step needed)
Finding (2026-06-27): Supabaseβs hosted migration history is decoupled
from the repo. Creating a fresh dev branch replays Supabaseβs own recorded
migrations, not the repo files β and it still records only the first
migration under the old name (20250213_001_credits_system.sql), failing
immediately afterward. So:
- The renamed repo files (#1325) are the source of truth for
supabase db */CI, but Supabaseβs stored migration history predates the rename and is incomplete/colliding. - Prod schema itself is fine (applied out-of-band over time); itβs the ledger
thatβs wrong. There is no reliable automated record of which migrations are
applied to prod β reconciliation today is the manual
scripts/audit-migrations.py+scripts/migration-reconciliation-overlay.json(see migration-ledger-reconciliation.md).
Reconciliation plan (do deliberately, against prod β not blind)
- Snapshot the live prod schema (
scripts/snapshot-prod-schema.sqlvia the Supabase MCP/psql) and runscripts/audit-migrations.pyto get the current applied/not-applied verdict per (renamed) file. - Re-baseline Supabaseβs hosted migration history to match the repo: mark every
already-applied migration as applied at its new version
(
supabase migration repair --status applied <version>for each), so the ledger lines up withsupabase/migrations/. - Confirm a fresh dev branch then comes up
β¦HEALTHYwith all migrations recorded (notMIGRATIONS_FAILED). - From then on, treat
supabase_migrations.schema_migrationsas the source of truth and retire the manual overlay.
Recommended: squash to a prod-captured baseline
Given the historical migrations donβt faithfully reproduce prod (invalid SQL that never ran, cross-set dependencies β see Β§1), repairing all ~200 individually is costly and low-value. The cleaner path:
pg_dump --schema-onlythe live prod DB βsupabase/migrations/<ts>_baseline.sql.- Move the existing 202 files to an
archive/(kept for history, not applied). - The baseline becomes migration #1; new migrations stack on top. From-scratch apply == prod by construction, so the Β§1 harness can flip to a PR gate.
- Re-baseline the hosted ledger to the single baseline version.
This makes the ledger and from-scratch apply correct in one move instead of
chasing latent issues through 200 files. Requires a prod schema dump (DB
credentials / direct psql β not available via the MCP), so itβs an explicit ops
task. The apps/music and other app-specific migration sets should be scoped to
their own projects as part of this.
Until then, the Β§1 harness runs on demand (workflow_dispatch) and locally; it
is not yet a PR gate.