fix wave 1: roll notes, a hand-cleared seed obligation is cleared with the agent stopped
No operator-reachable per-host wake exclusion exists (the maintenance fence is internal, a layout journal is acted on by the layout pass, a non-ready status does not stop a wake in flight), so the procedure stops carlos-edge for its whole length.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 file changed,
+33
−10
docs/superpowers/plans/2026-10-07-zero-downtime-deploys-6-roll-notes.md+33 −10
diff --git a/docs/superpowers/plans/2026-10-07-zero-downtime-deploys-6-roll-notes.md b/docs/superpowers/plans/2026-10-07-zero-downtime-deploys-6-roll-notes.md| index 5657bb506..31cd1147f 100644 |
| --- a/docs/superpowers/plans/2026-10-07-zero-downtime-deploys-6-roll-notes.md |
| +++ b/docs/superpowers/plans/2026-10-07-zero-downtime-deploys-6-roll-notes.md |
| @@ -418,17 +418,40 @@ by 076bd9c0 (final review round 1), pending merge. |
| and seeds again. The agent removes the database (`<db>`, `<db>-wal`, |
| `<db>-shm`) first, syncs the directory, and removes the obligation last, |
| so a crash or a wake in between never sees a database without its |
| - obligation. Clear one by hand only for a proof the agent cannot make. If |
| - you do: |
| - |
| - 1. First prove the seed's processes gone. The host's |
| - `carlos-exec@<host>` cgroup (and for a nested layout the host slice's |
| - seed unit) must show an empty `cgroup.procs` and `populated 0` in |
| - `cgroup.events`, and no process may still hold the database open. |
| - 2. Only when the obligation's body says `db=owned-by-seed`, delete `<db>`, |
| + obligation. Prefer that path: clear one by hand only for a proof the agent |
| + cannot make. |
| + |
| + A hand clear must not race a wake. Between your proof and your delete, a |
| + wake can discharge the obligation itself and start a new seed (Restore, |
| + `settleSeedObligation`), and you would then delete that new seed's |
| + database. There is no per-host way for an operator to hold off wakes. The |
| + activator's maintenance fence (`FenceAndStop` / `LiftFence`, |
| + `internal/activator/maintenance.go`) is used only by the move worker, the |
| + layout pass and the claim-stop coordinator, and nothing exposes it to an |
| + operator. A layout journal (`<data>/layout/<host>.json`) fences wakes too, |
| + but the layout pass reads and acts on it, so do not plant one. A row |
| + status other than `ready` is set only by the move worker and does not stop |
| + a wake already in flight. So clear an obligation by hand only with the |
| + agent stopped. Stopping `carlos-edge` stops every instance on the box, so |
| + plan for a short box-wide outage: |
| + |
| + 1. Stop the agent: `systemctl stop carlos-edge`. Wait until it is |
| + inactive. No wake can run until it starts again. Its delegated cgroups |
| + and the units bound to it (`carlos-instance-seed@`, |
| + `carlos-instance@`, `carlos-instance-gen@`, `carlos-instance-sidecar@`) |
| + stop with it. |
| + 2. Prove the seed's processes gone. The host's `carlos-exec@<host>` cgroup |
| + (and for a nested layout the host slice's seed unit) must show an empty |
| + `cgroup.procs` and `populated 0` in `cgroup.events`, or be absent, and |
| + no process may still hold the database open. |
| + 3. Only when the obligation's body says `db=owned-by-seed`, delete `<db>`, |
| `<db>-wal` and `<db>-shm`. |
| - 3. Remove `<db>.seed-unproven` last, never first. Deleting it first |
| - leaves a half-built database that the next wake serves as warm. |
| + 4. Sync the directory (`sync "$(dirname <db>)"`, which fsyncs it) so the |
| + deletes are on disk before the obligation goes. |
| + 5. Remove `<db>.seed-unproven` last, never first. Deleting it first |
| + leaves a half-built database that the next wake serves as warm. Sync |
| + the directory again. |
| + 6. Start the agent: `systemctl start carlos-edge`. |
| A body of `db=there-before-seed`, or one the agent does not recognise, |
| keeps the database, and the next wake serves it warm, by design. Never |