The rollback check's trap now attempts its clean-up whatever happens,
keeps a failed clean-up as a nonzero status and prints "check FAILED" on
every nonzero exit. Both checks install their trap before the first
command that can fail. Deploy step 1, both rollback paths and the
script-writing block now chain with && so a failed checkout, fetch, build
or write stops the block with its status, instead of a later command
restarting the wrong release or masking the failure.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Capture the docker compose ps status before testing its output, and
scan the Litestream log with grep's status read explicitly: 0 is a
logged error, 1 is no match, anything else fails the verification.
- Run both clean-ups on exit whatever happens, keep any failure, and
print the pre-deploy restore point only after every check and both
clean-ups succeeded.
- State that the live snapshot expects the existing -wal and -shm, list
their ownership after the snapshot, and say to stop and check the
service state if a root-owned sidecar appears.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pre-deploy verification and the rollback migration check copied the
live mealquest.db and its WAL as separate files while the app and
Litestream ran, so a checkpoint between the copies could leave an older,
readable state: the rollback check could print "none" while the live
database held a migration the older SHA does not know.
Both now read the live database through /tmp/mqsnap.py, written once in
"Count check": SQLite's online backup API from a read-only connection,
then PRAGMA integrity_check, exiting nonzero unless it is ok. The
pre-deploy verification runs as one fail-fast subshell (set -eu, an EXIT
trap that cleans /tmp/mqcheck and the scratch restore while keeping the
status), diffs the replica and live count checks, and prints the restore
point only after every step has passed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run the check in a set -eu subshell so a failed cd, copy, git ls-tree
or comparison stops it before any verdict is printed, and remove the
scratch directory from an EXIT trap that preserves the check's status.
Spell out the reading rule: none, listed versions, or stop on error.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The rollback check diffed all of migrations/, so a change to the sqlc
queries in migrations/queries/ sent the operator down the restore path and
discarded household changes made since the pre-deploy point. Compare the
versions in the live database's schema_migrations with the top-level *.sql
files at the previous SHA instead, the rule internal/store/migrate.go
enforces at start-up.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Write /tmp/mqcount.py from the Count check section with one heredoc
command, and give rollbacks that cross a migration an ordered
restore-then-start procedure separate from the plain restart.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RUNBOOK: the deploy steps now verify, before the restart that applies
migrations, that Litestream replication is current and its replica
restores to the live data, and record the pre-deploy restore point; if
not, do not restart. The rollback section restores to that recorded point.
ARCHITECTURE: the rotation_check schedule that calls MarkTried is planned
(Feature 20); this release ships MarkTried and the rotation review read and
decide APIs only.
Task: 008c0a8a-2242-429b-a1fd-69346bc1d414
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>