Upgrading commission, and your database with it
You will be able to upgrade commission over a database that holds real work, know before you do it what will change, and get back to exactly where you were if it goes wrong.
Audience: anyone running a Commission binary against a database they care about — and
CI, which should check this before it ships one. Assumes: you have a database at
~/.commission/commission.db or $COMMISSION_DB.
The rules this guide operationalises are in
compatibility.md. The vocabulary is
positioning.md §6.
How commission upgrades a database
Commission carries its migrations inside the binary — an ordered, append-only array in
src/db/ddl.ts — and stamps how far a database has come in SQLite’s
PRAGMA user_version. There is no migration folder to deploy and no migration step
to forget: any commission command that opens an older database upgrades it, applying
each missing migration in its own transaction that bumps the version with it.
That means an interrupted upgrade — a laptop that sleeps, a container that is killed — leaves a database at a known version with no half-applied step in it. It also means the first command you run after installing a new Commission is the one that migrates, which is why the two guarantees below exist.
Before you upgrade: commission migrate --check
--check reports and never writes. It is the pre-upgrade validation step, and the
thing to put in CI:
$ commission migrate --check
/Users/you/.commission/commission.db
schema v8 — up to date$ commission migrate --check
/Users/you/.commission/commission.db is at schema v5; this commission speaks v8
missing: migration v6
missing: migration v7
missing: migration v8
hint: 3 pending (v6, v7, v8). Apply them with: commission migrate --db /Users/you/.commission/commission.db
— or open the database with any commission command, which migrates it automatically after
taking a backup.| situation | exit | what it means |
|---|---|---|
| database is at this binary’s level | 0 | nothing to do |
| no database at that path yet | 0 | a fresh one is created at the current level on first use |
| migrations pending | 2 | the database is behind this binary — apply them |
| database is newer than the binary | 2 | this binary must not open it — see below |
Both non-zero cases are exit 2, commission’s refusal code, so a CI step is just
commission migrate --check. Add --json for the machine shape, and --db <path> to
check a copy rather than your live database.
To apply them deliberately rather than as a side effect of the next command:
$ commission migrate
commission: backup written to /Users/you/.commission/commission.db.v5-20260726T101500Z.bak
commission: restore it with: rm -f "/Users/you/.commission/commission.db" "…-wal" "…-shm" && cp "…bak" "…db"
migrated /Users/you/.commission/commission.db
schema v5 → v8 (applied v6, v7, v8)The backup is automatic
Before the first migration runs against a database that has anything in it, commission
copies it — VACUUM INTO, so the copy is transactionally consistent rather than a
snapshot of whatever bytes were on disk — and prints the restore command with it:
commission: migrating database from schema v5 to v8
commission: backup written to /Users/you/.commission/commission.db.v5-20260726T101500Z.bak
commission: restore it with: rm -f "…/commission.db" "…/commission.db-wal" "…/commission.db-shm" && cp "…/commission.db.v5-20260726T101500Z.bak" "…/commission.db"Those three lines go to stderr, never stdout, so they cannot corrupt --json
output being parsed by an agent.
This matters because migrations are not reversible. Schema v5 moved every
tasks.assignee into the new claims table and then dropped two columns; nothing in
the binary can put a dropped column back. Commission never downgrades a database — the only
path backwards is the copy taken before the upgrade.
- The backup sits next to the database, named for the level it was taken at.
- Commission never deletes one. Delete old
*.bakfiles yourself once an upgrade has proven itself. - A fresh database is not backed up (there is nothing to lose), and neither is an open with no migrations pending.
- If the copy cannot be written — no space, no permission — commission refuses to
migrate rather than proceeding without it.
COMMISSION_NO_BACKUP=1overrides that, and is for throwaway databases and test fixtures, not for your data.
Restoring is the printed command, verbatim. It removes the WAL sidecars as well as the
main file: a stale -wal left beside a restored database is how a “restored” database
ends up being neither the backup nor the original.
When Commission refuses to open a database
$ commission board
this database is at schema v11 (/Users/you/.commission/commission.db); this commission speaks schema v8
missing: a commission build that ships schema v11 or newer
hint: upgrade commission — each release names its schema level in CHANGELOG.md ("Schema
version N"). Commission never downgrades a database: the only way back to v8 is restoring a
pre-upgrade backup.You are running an older commission against a database a newer one has already migrated —
two machines sharing a database over a synced folder, or a stale binary earlier in
$PATH. Commission does not touch it: it cannot know what a v11 schema contains, and a
binary that guesses is a binary that corrupts data quietly.
Install a commission that ships that schema level. Every release names its level in
CHANGELOG.md CHANGELOG.md, and commission migrate --check on the machine that
already works tells you which one you need. Downgrading the database is not supported;
if you must go back, restore a backup taken before the upgrade.
If a migration fails
It rolls back with its version bump, so the database stays at the last version that committed and the refusal names both the step that failed and the backup:
migration to schema v9 failed: no such column: x
hint: the database is unchanged at schema v8. Pre-migration copy: …v8-….bak — restore
with: rm -f … && cp …Do not edit the database by hand to “help it along”: a database at a version whose schema does not match is the one state commission cannot reason about. Restore the copy, report the message, and stay on the working binary.
The whole sequence
commission migrate --check # exit 0 = nothing to do, 2 = behind or ahead
cp ~/.commission/commission.db /tmp/pre-upgrade.db # optional: commission takes its own copy too
# …install the new commission…
commission migrate --check # now reports what is pending
commission migrate # applies them, prints the backup and its restore command
commission doctor # confirms the schema level and the rest of the installEvery command on this page is exercised by tests/migrations.test.ts, which builds a
v1 database, walks it through every migration to current, and asserts after each step
that the rows, the references between them, and the meaning of the moved data all
survived — including the restore command, which the test runs.
This page is docs/guides/upgrades.md in the Commission
repository, rendered in place — the site keeps no copy of it. The repository is private, so there is no edit link to follow.