Five languages, roles behind a foreign key, and Minesweeper out of the platform. This one has a SCHEMA CHANGE, which is why the plan is longer than usual: two foreign keys onto roles.name with ON UPDATE CASCADE, and the CHECK that hardcoded four role names dropped. The plan carries the exact SQL, guarded and idempotent, and treats bun db:push as verification rather than the mechanism — push can fail and still exit 0, and this is precisely the create-then-constrain shape that catches it. Checked before cutting the tag, so the plan can state it rather than hope: every host is on deploy/2026.08.23.04, which is the release that created and seeded `roles`. alpha, fofos, production-pertento-bridge and deadpool-rezio-bridge each report roles=4 and zero orphans. macbookpro has no psql client, so only its tag could be read — the plan says so and tells the commander to check it there rather than assume. The SQL was run against a scratch database first: applies, applies again unchanged, produces both FKs with ON UPDATE CASCADE and no ON DELETE, drops ck_role_permissions_role, leaves ck_role_permissions_not_owner. Seeded with an orphan role it raises and creates nothing, which is the case that would otherwise half-apply. tsgo clean, 1409 tests, docs:check green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
deploy/
One file per release, and one report per release.
deploy/
├── 2026.08.21.01.md the plan — what to run, in order, on which hosts
└── 2026.08.21.01.report.md what actually happened, written by the commander
The plan is written by hand before the release commit. The report is written by the deploy commander afterwards and pushed back here.
A release is a tag, deploy/<version>, and nothing else triggers a deploy — not a commit message,
not a branch. A tag cannot be written into prose by accident, which a code word can and which every
document about deploying does.
What marks a release deployed is LOCAL: $OFFICER_ROOT/data/deploys/<version>.done. Not the report.
If the lock were the pushed report, a failed push would mean the release looked undeployed and the next
tick would roll it out again — and pushes here fail for unrelated reasons. So the report is an artefact,
valuable and not load-bearing.
See prompts/deploy-commander.md.