Skip to content
commission

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.
situationexitwhat it means
database is at this binary’s level0nothing to do
no database at that path yet0a fresh one is created at the current level on first use
migrations pending2the database is behind this binary — apply them
database is newer than the binary2this 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 *.bak files 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=1 overrides 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 install

Every 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.