ShipSafe rehearses a database migration against a copy of realistic synthetic data before the release goes out. Given a candidate SQL migration file, it spins up a fresh temporary SQLite database, seeds five synthetic rows, and runs five checks in sequence on that database. The result is a GO or BLOCK verdict backed by a JSON evidence file you can inspect.
IBM Bob IDE investigated the failing migration in this project, identified the root cause, designed and implemented the safe fix, reran all checks, and produced the release decision.
The sample app tracks delivery orders. Version 2 of the app adds a
delivery_window
column — a time slot for each delivery. The unsafe first migration declared this column
NOT NULL
with no default value. SQLite refuses to add a required column to a table that already has
rows with no value for it — so the migration fails, and the release is blocked.
The repaired migration uses
DEFAULT NULL,
which SQLite accepts. Existing orders get a null delivery window (treated as "not yet
assigned"). The old app ignores the new column. Rollback to the old app after the new app
has written a row still works, because the old app's queries never mention
delivery_window.
Both results are from real CLI runs — no hardcoded verdicts. See the committed evidence:
Every run checks all five gates in order on the same temporary database. A FAIL in any gate marks all later gates SKIP and sets the verdict to BLOCK. GO requires all five to PASS.
| Gate | What it checks | Unsafe | Safe |
|---|---|---|---|
| migration | The candidate SQL runs without error | FAIL | PASS |
| preservation | All five seeded rows survive the migration unchanged | SKIP | PASS |
| old-version | App v1 reads a seed row and creates a new order on the migrated database | SKIP | PASS |
| new-version | App v2 reads a legacy row (null window) and creates an order with a window value | SKIP | PASS |
| rollback | App v1 reads a v2-written row and creates another order; column names recorded | SKIP | PASS |
The repository was handed to Bob IDE with the failing rehearsal already committed. Bob:
evidence/red-run.json output and identified the
OperationalError: Cannot add a NOT NULL column with default value NULL.ADD COLUMN delivery_window TEXT NOT NULL
declaration in examples/unsafe.sql.ADD COLUMN delivery_window TEXT DEFAULT NULL,
which SQLite accepts for tables with existing rows.migrations/candidate.sql, reran the rehearsal,
confirmed all five gates PASS, and verified the pytest suite still passes (23 tests).ShipSafe is a local SQLite simulation, not a production migration plan.
statements_in_script counts semicolon-delimited segments of the SQL text
before execution — a text property, not a count of statements SQLite actually ran.