docs: roll notes fix round 1 (seed obligation clearing order, kernel test names, supersession pointers)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1 file changed,
+48
−18
docs/superpowers/plans/2026-10-07-zero-downtime-deploys-6-roll-notes.md+48 −18
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 26a212213..5657bb506 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 |
| @@ -174,6 +174,8 @@ on #272 too. |
| - Host retries ride the unsigned restart-scope log; a box that can write the |
| bucket can clear another box's failure hold (Astra plan 6 R1 #5, plan Q4). |
| + Superseded on zdd-flag-readiness-2 (`6e9e84bd`): grants ride |
| + `host-retry.json`, not the restart-scope log. |
| Paul's ruling (2026-10-11): host retries stay on the unsigned restart-scope |
| log for now, with the same trust as `carlos restart`. Signing retries in the |
| console, as `carlos promote --retry` is signed, is owed before the flag goes |
| @@ -249,8 +251,12 @@ merge) closes most of them. A closed item below says "Closed on |
| zdd-flag-readiness-2 (<commit>), pending merge". That means built and |
| task-reviewed on the branch: not merged, not released, not rolled, and only |
| Paul closes it on #272. The branch's whole-branch review (Astra) and the |
| -3000-seed rollout-simulation sweep come after these notes. Two items stay |
| -open: claims-on swaps, and Paul's sign-off of the spec amendment. |
| +3000-seed rollout-simulation sweep come after these notes. Still open or |
| +still owed after the branch: claims-on swaps (a future feature); Paul's |
| +sign-off of the spec amendment; the `ensureParent` kernel positive control; |
| +Paul's copy review of the declined-box page strings; and the dev |
| +mixed-version check (see "Real-box checks"). The code for the other items |
| +is closed on the branch, pending merge. |
| - Nested live-link race on a declined box: needs an expected identity on |
| `Control.Start`, root-side (task 9). Closed on zdd-flag-readiness-2 |
| @@ -364,7 +370,8 @@ by 076bd9c0 (final review round 1), pending merge. |
| - **Schema 3 failure book.** The failure book file is written as schema 3 |
| (it was 2 on zdd-flag-readiness). Downgrading an agent with the flag on |
| makes that box refuse managed starts, and that includes a round 1 agent |
| - (on `main` since `8ebd9d8c`), which accepts only schema 1 and 2. Recover by upgrading the agent again. Do not |
| + (on `main` since `8ebd9d8c`), which accepts only schema 1 and 2. Recover |
| + by upgrading the agent again. Do not |
| delete the file to get past the refusal: it holds the failure records and |
| hold-ran markers that keep instances off builds that failed or ran on |
| them. A file of schema 1 or 2 is adopted on first open and given a new |
| @@ -385,7 +392,8 @@ by 076bd9c0 (final review round 1), pending merge. |
| `cgroup.ErrLimitNotApplied` and is never done by moving the PID after |
| exec. Wakes of that box show the maintenance text, and the refusal is the |
| box's condition, not a failure recorded against the build; a swap abandons |
| - its attempt and is retried, as for the populated legacy parent below. The flat (flag-off) start is unchanged. Recovery: turn the |
| + its attempt and is retried, as for the populated legacy parent below. The |
| + flat (flag-off) start is unchanged. Recovery: turn the |
| flag off and restart the agent, or fix the kernel or cgroup state. The same |
| refusal covers a parent that cannot be made, does not delegate memory, or |
| whose backstop or child limit cannot be written. The log line begins |
| @@ -395,22 +403,38 @@ by 076bd9c0 (final review round 1), pending merge. |
| beside the instance's database (three lines: `carlos seed obligation v1`, |
| `db=owned-by-seed` or `db=there-before-seed`, `seed=flat` or |
| `seed=nested`). A write that fails refuses the seed before anything runs. |
| - The file is removed only when the seed's processes are proven gone, and a |
| - database the seed created goes with it. While the file is there no wake |
| + The file is removed only when the seed's processes are proven gone. After |
| + a failed seed, a database the seed created goes with it; a successful |
| + seed's discharge keeps the database. While the file is there no wake |
| treats the database as warm, and a wake that cannot prove the seed gone |
| fails with `activator: seed unproven; processes remain` and holds the lease |
| to its TTL. The obligation is a file, so it survives an agent restart. It |
| does not end in `.db`, so the litestream glob never takes it. A box on a |
| kernel without `cgroup.kill` proves a seed by its leader's exit alone |
| - (logged). To clear a stuck one, first prove the seed's processes gone |
| - yourself: 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. Only then delete `<db>.seed-unproven` (and, if its body says |
| - `db=owned-by-seed`, the database, `-wal` and `-shm` beside it, so the next |
| - wake seeds again). Never delete it to get past a refusal while a process |
| - may still be writing the database. A planted non-empty directory at that |
| - name keeps that tenant's row down until it is removed by hand. |
| + (logged). |
| + |
| + Normally do nothing by hand. Once the processes are gone, the next wake |
| + proves it, discharges the obligation itself, removes a seed-owned database |
| + 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>`, |
| + `<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. |
| + |
| + 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 |
| + delete the obligation to get past a refusal while a process may still be |
| + writing the database. A planted non-empty directory at that name keeps |
| + that tenant's row down until it is removed by hand. |
| - **The armed agent writes its failure book on start.** Since c12c35c3 an |
| agent with the flag on saves the book when it opens it, even when there |
| was no file, and refuses managed starts if it cannot (the same refusal as |
| @@ -438,9 +462,12 @@ the unit tests cannot stand in for a real cgroupfs. |
| `internal/activator/genparent_kernel_linux_test.go`: |
| `TestGenerationStartRefusedBesideADirectParentMember`'s "sibling control" |
| (g1 starts and stays up, is capped in `g1`, and g2 starts beside it) and |
| - "recovery control" (kill the legacy process, the parent empties, g1 starts), |
| - and the live-g1 respawn: a live g1 survives each sidecar respawn and the |
| - parent's `cgroup.procs` stays empty. The same checks need to hold with the |
| + "recovery control" (kill the legacy process, the parent empties, g1 starts). |
| + The live-g1 respawn control is `killSidecarRespawn`, in |
| + `internal/activator/claimspawn_kernel_linux_test.go`, run by |
| + `TestClaimContainmentCleanupRespawn`: a live g1 survives each sidecar |
| + respawn and the parent's `cgroup.procs` stays empty. The same checks need |
| + to hold with the |
| `CLONE_INTO_CGROUP` placement the box really has, and the refusal needs |
| watching on a kernel where it is unavailable. |
| - Task 8's seed and normal-stop kernel control: a forked helper killed by |
| @@ -519,6 +546,9 @@ discussion's items. |
| An instance that already has a config file converges on its bytes as |
| before. This answers Q3's restart concern; closing Q3 is Paul's call. |
| - **A host retry rides the unsigned restart-scope log (plan Q4).** |
| + Superseded on zdd-flag-readiness-2 (`6e9e84bd`): grants ride |
| + `host-retry.json`, not the restart-scope log; see "Retries: supported |
| + transitions" above. |
| `carlos instances retry` asks the box to retry through the restart-scope |
| log. That log is not signed, and the box's `RetryHost` |
| (`internal/rollout/manager.go:501-515`) trusts any request that names a host |