Additions
adding things rarely breaks production
migrate / risk · 01 — The model
Every op Zanith can emit has a published level. Every level has a numeric score on a 0–100 scale. Every score is gateable in CI. The data below is verbatim from engine/src/migrations/risk.ts — the same classifier the runner uses.
--max-risk N refuses to apply anything above N. Defaults to ∞ (off).
Color tracks level; ops cluster by danger. Notice that addColumn appears in three places — its level depends on whether it's nullable, has a default, or is required without one.
02 — Ops, by family
Eight families, each with its own danger profile. Color tracks level — emerald for safe, amber for medium, rose for destructive. Spot the gradient at a glance.
adding things rarely breaks production
removing data is expensive — most of these need consent
type and nullability changes can fail silently — flagged early
renaming breaks any client reading the old name — prefer expand-contract
RLS changes affect every read and write on the table
the safe alternatives — restorable until cleanup
long-running. Use during off-peak windows.
the planner can't reason about it — review by hand
03 — Reasons
Every operation carries one or more reason codes alongside its risk level. Pipelines that need to gate on the cause — not just the severity — read these. Twenty-one codes, grouped into seven families.
adds_nullable_columnback-compatadds_required_column_without_defaultfails on dataadds_indexslow on big tablesadds_unique_constraintfails on dupesadds_foreign_keyfails on orphansadds_extensionidempotentdrops_column_with_datadata lostdrops_table_with_datadata lostdrops_indexplan regressiondrops_unique_constraintdupes possibledrops_foreign_keyorphans possibledrops_extensioncascading breaknarrows_column_typetruncationchanges_column_typedata lost or failschanges_column_nullable_to_not_nullfails on NULLsrebuilds_tablefull row copyrequires_backfilllong runningrls_changeevery read affectedrename_tablebreaks old clientsrename_columnbreaks old clientsraw_sqlunclassifiable04 — The gauntlet
An op moves from parsed to appliedthrough three checkpoints. They don't replace each other — they layer. Tighten any one of them in CI without touching the others.
A column has NULLs but the op wants NOT NULL. The migration never starts.
An op scores 80 but CI's --max-risk is 35. The plan is rejected before any write.
A drop is in the plan but --allow-destructive wasn't passed. Fix the op or pass the flag — never silent.
Preflight is clean, score is under budget, destructive ops have consent. Apply runs and audits each step.
The main page — the system overview, the contrast table, the lifecycle in summary.
/migrateEvery stage of a migration: generate, plan, verify, apply, audit. Real terminal output for each.
/migrate/lifecycleInternal stages, deployment patterns, output modes.
/migrate/verifyFive artifact kinds, the bookkeeping table, the seven-command CLI.
/migrate/recoverQuery the audit tables — replay flows, drift, post-mortem patterns.
/migrate/auditApply migrations and inspect risk from a UI. Same engine, same gates.
/studio