migrate / recover
01 — The safety net
A drop is not a delete until you say it is.
Destructive ops rename aside to _zanith_shadow and write a row to _zanith_migration_artifacts with a checksum. Restore refuses if the archive was tampered with — proven in the proof suite on real Postgres.
Two strategies, three shapes, one forensic.
soft_drop_columnRenames the column to _zanith_dropped_<col>_<id>. Restore = rename back. Instant.soft_drop_tableRenames the table aside. Restore = rename back. Instant, zero copy.archive_columnCopies the table into _zanith_shadow, then drops the column. Stores row count + checksum.archive_tableMoves the whole table into _zanith_shadow and renames it. Instant — no row copy.rebuild_tableAfter a copy-swap rebuild, keeps the old table as _zanith_dropped_<table>_<id>.Renames the column to _zanith_dropped_<col>_<id>. Restore = rename back. Instant.
One row remembers everything.
Thirteen columns capture source, physical name, recovery SQL, row count, checksum, and expiry. Restore reads this row before it writes anything back.
Restore verifies before it writes. If parked data was edited out-of-band, the checksum won't match and recover restore refuses: Refusing to restore: archive checksum changed since archive time (recorded 9a15e1d…)
{
"id": "20260402::archive_column::p5_legacy.legacy_code",
"artifact_type": "archive_column",
"row_count": 5,
"checksum": "9a15e1d…"
}proof rows: AAA-001 · BBB-002 · CCC-003 · DDD-004 · EEE-005
3 read. 4 write.
zanith binary that applied the migration knows how to walk it back. Read verbs inspect and export; write verbs restore, reseed, or purge.read-only
recover listEvery recoverable artifact, newest first.recover inspect <t[.col]>Metadata, checksum, and the exact recovery SQL.recover export <t[.col]>Read archived rows out as csv / json / jsonl.
write
recover restore-column <t.col>Rename back, or re-add + backfill from the archive.recover restore-table <t>Move a soft-dropped or archived table back to public.recover reseed <t[.col]>Insert archived rows into a reshaped table (expand/contract).recover purge <id>Drop the archive data and delete the artifact row.
zanith cleanup --older-than 30d is not a recover verb — the top-level maintenance command for bulk-purging artifacts past their window.
Recovery is the safety net. See the rest of the rig.
Artifacts are written during apply and tracked in the audit tables. Here's where the surrounding stages go deeper.
Lifecycle, in depth
Generate, plan, verify, apply, audit — every flag, every output, every artifact path.
/migrate/lifecycleRisk model
6 levels, 21 reason codes, 7 gates, 31 op kinds. The full classifier.
/migrate/riskShadow-DB verify
The 4 internal stages of verifyOnShadow, deployment topologies, 4 verdicts.
/migrate/verifyAudit + history
5 Postgres tables track every migration, step, snapshot, and artifact.
/migrate/auditelsewhere