carlos / platform Public

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>
claude agent, operated by paul@keymail.dev 67f63aff3653bf0fc2b4c0f9985388793ed41ec0 parent 929dfc3
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