Skip to main content
Version: 1.0.0 Last Updated: 2026-06-30 Related: MIGRATION_LANES.md · ADR-021 · design docs/superpowers/specs/2026-06-09-migration-collision-proofing-and-declarative-pilot-design.md
Read this before researching a collision. The collision-proofing machinery is already built and layered. When you hit a version collision, you do not need to review Supabase docs or design a bespoke fix — run the one blessed command below. The recurring pain was N parallel agents each independently re-researching and proposing different resolutions; this runbook is the one resolution.

The problem in one paragraph

Migrations are named YYYYMMDDHHMMSS_*.sql. That 14-digit prefix is the PRIMARY KEY of supabase_migrations.schema_migrations, so it is a global ordering key, not a real date (the tree even holds invalid hour-24/25 prefixes — treat versions as opaque integers, never date math). supabase db diff -f and supabase migration new both stamp the current second, and each worktree only sees its own migrations — so N parallel agents branched off development reliably mint colliding or out-of-order prefixes. A fresh collision hard-fails db push; a shadowed collision (one version already recorded on a remote) silently skips the second file → real schema drift.

The layered defense (where each catch happens)

Author/pre-commit/pre-push/PR stages are best-effort — two branches in flight at the same time are invisible to each other. The deploy boundary is the actual guarantee: no version collision ever reaches a remote as silent drift.

Prevent (do this every time you author a migration)

Hand-authored / caveat (DML, policy, grant) migration — mint the version, don’t let the CLI pick wall-clock:
Declarative DDL (supabase db diff -f) or supabase migration new — the CLI controls the timestamp, so reversion it immediately after generating:
db:migration:reversion is idempotent — a no-op when your migration already sorts after origin/development. It does a best-effort git fetch origin development first; pass --no-fetch offline, or a filename to target one file explicitly.

Resolve (you already have a collision)

You will land here from a red pre-push, migration-guard, or a deploy auto-PR.

1. The blessed command (handles the safe majority)

This keeps the lexicographically-first file at each colliding version and bumps the rest to the next free opaque version. For a fresh pair both files then apply once, in order.

2. Shadowed / REFUSED (human action — rare)

If db:migration-versions:fix prints REFUSED, the later file’s version is already recorded on the remote and the file is not re-apply-safe (raw CREATE TABLE / ADD COLUMN / CREATE POLICY with no IF NOT EXISTS / DROP … IF EXISTS). Do not force it. Per ADR-021 Addendum (2026-06-09):
  1. Make the file idempotent (add the IF NOT EXISTS / DROP POLICY IF EXISTS guards), or
  2. Rename it to the suggested next free version by hand and confirm it applies cleanly fresh.
The hard boundary: never rename a file whose current version is already recorded on the target remote — reversion targets the unrecorded twin only.

3. Local recovery after a bot/auto rename

A version you already applied locally that gets renamed: recover the local ledger with supabase migration repair --status reverted <old> --status applied <new>. Never supabase db reset on the shared local stack (.claude/rules/database.md).

Do NOT

  • ❌ Hand-pick a “probably free” timestamp, or copy another migration’s prefix and +1 by eye.
  • ❌ Edit an applied migration’s SQL to dodge a collision (ADR-021 — the only blessed edit is the automated version-prefix rename of an unapplied file).
  • ❌ Research Supabase docs to design a new resolution. The resolution is the command above.

References

  • Scripts: scripts/database/next-migration-version.ts, reversion-migration.ts, ensure-unique-migration-versions.ts, check-migration-version-collisions.mjs, migration-version-lib.ts (pure helpers).
  • npm: db:migration:next-version, db:migration:reversion[:check], db:migration-versions:check|fix, db:check:migration-collisions.
  • MIGRATION_LANES.md §5.8 (author-time allocator), ADR-021 (collision-edit exception), the 2026-06-09 collision-proofing design.