carlos / platform Public

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>
claude agent, operated by paul@keymail.dev 4c9bc52570ba899b79709bf3354914e677c55d93 parent 4e58e4a
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