migrate / lifecycle · 01 — The state machine
Five stages, three failure paths, one success terminator.
The main page showed the happy path. This page draws every transition — including the three places a migration can stop short. Each stage below has its own deep section with the real CLI output, the real flags, and the artifacts written.
02 — Generate
The diff becomes the migration.
Compare the declared schema against the live database. The differences become operations in a migration file — type-safe, auto-named, ready to review. No hand-written DDL.
- iduuid
- emailtext
- created_attimestamptz
- last_logintimestamptz
- iduuid
- emailtext
- created_attimestamptz
- legacy_uuiduuid
Four operations, ordered safely. Reversal logic written automatically. Ready to plan.
03 — Plan
A verdict before anything writes.
The planner classifies every operation, scores it, and checks the worst score against your budget. Read-only — nothing in the database changes. Either you get a green verdict, or you find out exactly which step would have broken.
Tighten the budget in CI. Loosen it for emergency hotfixes. The planner refuses to run anything that crosses the line — the alternative is finding out at apply time, with prod half-changed.
04 — Verify
A throwaway database takes the hit first.
The migration applies on a shadow Postgres. The result gets introspected and compared to the declared schema. Drift — anything missing — blocks the apply on production. The shadow is throwaway by design: gates are off, nothing persists, you can rebuild it any time.
the shadow can be cheap
Local · docker-compose
Separate Postgres on a separate port. Reset between verifies.
CI · service container
GitHub or GitLab spawns a fresh container per job. No reset needed.
Same cluster · ephemeral schema
Same Postgres server, isolated by search_path. Cheaper than a second cluster.
05 — Apply
Every step writes its own row.
Apply walks the plan one operation at a time. An advisory lock keeps two CI runners from colliding. Each step writes its outcome — done, failed, skipped — to a real audit table before, during, and after it executes. Mid-flight failure leaves a paper trail without marking the migration applied.
- 01 · addColumndone
- 02 · addIndexdone
- 03 · softDropColumndone
- 04 · backfilldone
All four steps land. The migration row gets written. Ledger tells the whole story.
- 01 · addColumndone
- 02 · addIndexdone
- 03 · backfillfailed
- 04 · softDropColumnpending
Step 3 errors. Step 4 never runs. The failure is recorded in the ledger; the migration is not marked applied — re-run picks up where it stopped.
06 — Audit
History as data, not log lines.
Migration history lives in three real Postgres tables in the same database your app uses. Wire monitoring against them, replay any past schema state, build post-mortems. No log scraper required.
_zanith_migrationsAnswers which migrations have run.
1 row per migration
_zanith_migration_stepsAnswers what each step did.
N rows per migration
_zanith_schema_snapshotsAnswers the schema before + after.
2 rows per migration
For the queries you actually run against these — risk profiles, slow ops, failed-step post-mortems — see /migrate/audit.
Six doors. All open.
Back to Migrate
The main page — the system overview, the contrast table, the lifecycle in summary.
/migrateRisk model
Every op kind, the level it gets, the score, and the reason classification.
/migrate/riskShadow-DB verify
Internal stages, deployment patterns, output modes.
/migrate/verifyRecovery (Phase 2)
Five artifact kinds, the bookkeeping table, the seven-command CLI.
/migrate/recoverAudit + history
Deeper queries on the audit tables, replay flows, and post-mortem patterns.
/migrate/auditStudio · Migrate tab
The same lifecycle with a UI — apply migrations, watch risk, click-restore artifacts.
/studio