carlos / platform Public

roll notes: readiness-3 review fixes, the activator kernel-test recipe (#272)

- Finding 5: the general kernel-test recipe makes its own directory with
  mktemp under $TMPDIR and removes only that, and says to rerun the shim
  tests under RestrictNamespaces=yes without TestGenStrictKernel.
- Finding 4: the arming line says the 4 s bounds the check's waits, not a
  reap held by stalled kernel I/O.
- Dev re-check step 7: runnable commands for TestClaimContainmentCleanupRespawn
  and TestGenerationStartRefusedBesideADirectParentMember on the dev box (a
  delegated system unit run as the user, for io), with the strict_primitive
  subtest run apart and without RestrictNamespaces, since it calls
  (*Prepared).Start, which keeps clone3 and is expected to fail there by
  Ruling 3. The Real-box checks entries point at it.
- The new operator line for a start whose context ended during placement,
  and the shim's restoring of the agent's ignored signals.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev bb2e6547d2312e88759e0d635bf1f4f2d59b86d5 parent 397a31f
1 file changed, +102 −16
  • docs/superpowers/plans/2026-10-07-zero-downtime-deploys-6-roll-notes.md +102 −16
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 dec929058..996bfe31c 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
@@ -583,6 +583,25 @@ review and CI come after these notes. Nothing here was run on a real box.
and the refusal is the box's condition, not a failure recorded against the
build. The "armed box without `CLONE_INTO_CGROUP`" note above is replaced by
this one.
+
+ A start whose own context ended while its shim was being placed (a seed's
+ wake that timed out or was stopped) is not a shim fault. It logs:
+
+ ```text
+ cgroup: <host>: the start's context ended during placement (<why>); nothing ran
+ ```
+
+ and answers the context's own error, as a direct start would. Flat or
+ generation, it does not fall back and is not the box's condition. The
+ member's wake event reads as such a wake always read (`wake failed: seeding
+ <host>: context canceled`).
+- **The shim keeps the agent's ignored signals.** A tenant started through
+ the shim has the same ignored signals as one started directly: the agent
+ sends its current set (`SigIgn` in `/proc/self/status`) with the spawn, and
+ the shim ignores those again just before it execs the tenant. Without
+ that, the shim's own Go runtime would have reset every ignored signal but
+ `SIGHUP` and `SIGINT` to its default. This changes nothing on a box whose
+ agent ignores no other signal.
- **The new decline code `cgroup-placement-failed`.** At arm time (the agent's
start with `CARLOS_SWAP=overlap`) the agent starts a real shim placement
into a scratch cgroup named `carlos-placement-check`, under its delegated
@@ -595,9 +614,15 @@ review and CI come after these notes. Nothing here was run on a real box.
The code is in the instance's JSON record (`rollout.unavailable`) and uses
the approved declined wording, so there is no new copy. A box whose check
- fails declines in at most about 4 s, inside the 20 s `carlos system update`
- gate. A box that passes logs `agent: overlap swaps armed (CARLOS_SWAP=overlap)`
- as before.
+ fails declines in about 4 s, inside the 20 s `carlos system update` gate.
+ The 4 s bounds the check's own waits. After them the agent kills and
+ reaps what it started, which takes milliseconds when the kernel ends a
+ killed process promptly. It does not when that process is stuck in
+ uninterruptible kernel I/O: SIGKILL ends it only when the I/O returns, and
+ the decline waits for that. So on a box whose disk or cgroupfs is
+ stalled, the decline can take longer than 4 s, and the update gate can
+ time out. A box that passes logs
+ `agent: overlap swaps armed (CARLOS_SWAP=overlap)` as before.
*Mixed versions.* Consoles and CLIs roll after boxes, so for a while a
rolled box can decline with a code the others do not know. A console older
@@ -650,23 +675,80 @@ review and CI come after these notes. Nothing here was run on a real box.
7. Run the two activator kernel tests that now start through the shim and
could not run on DOCS-T1, because user delegation there has no `io`
controller: `TestClaimContainmentCleanupRespawn` and
- `TestGenerationStartRefusedBesideADirectParentMember`
- (`CARLOS_CLAIM_SPAWN_KERNEL=1`, as "Real-box checks" says). They are owed
- on the dev box.
+ `TestGenerationStartRefusedBesideADirectParentMember`. They are owed on
+ the dev box. Both skip unless `CARLOS_CLAIM_SPAWN_KERNEL=1`, and both
+ call `cgroup.Setup`, which needs a delegated cgroup offering `cpu`,
+ `memory`, `io` and `pids`; without one they fail with
+ `missing delegated cgroup`.
+
+ Build the binary for the dev box from the checkout root on DOCS-T1, and
+ copy it to the dev box by the route the roll uses for binaries:
+
+ ```bash
+ GOOS=linux GOARCH=arm64 go test -c -o activator.test ./internal/activator
+ ```
+
+ On the dev box, in a fresh directory under `/tmp` (the activator's
+ socket paths need a short root), first the two shim checks, with the
+ shipped unit's `RestrictNamespaces=yes`:
+
+ ```bash
+ d=$(mktemp -d /tmp/zddk.XXXXXX)
+ install -m 0755 activator.test "$d/"
+ sudo systemd-run --wait --pipe --collect -p Delegate=yes -p User="$(id -un)" \
+ -p RestrictNamespaces=yes -E TMPDIR="$d" -E CARLOS_CLAIM_SPAWN_KERNEL=1 \
+ "$d/activator.test" -test.run '^TestGenerationStartRefusedBesideADirectParentMember$' -test.v -test.count=1
+ sudo systemd-run --wait --pipe --collect -p Delegate=yes -p User="$(id -un)" \
+ -p RestrictNamespaces=yes -E TMPDIR="$d" -E CARLOS_CLAIM_SPAWN_KERNEL=1 \
+ "$d/activator.test" -test.run '^TestClaimContainmentCleanupRespawn$/^Kill' -test.v -test.count=1
+ ```
+
+ The pass is `--- PASS` for `TestGenerationStartRefusedBesideADirectParentMember`
+ and its `sibling_control` and `recovery_control`, and for
+ `KillHost_then_respawn` and `KillSidecar_then_respawn`, with no
+ `--- SKIP`. These use a system unit run as you (`sudo`, `-p User=`)
+ because systemd delegates `io` to it. Where the user manager offers
+ `io` too, the same commands as `systemd-run --user`, without `sudo` and
+ `-p User=`, do as well; where it does not, they fail with
+ `missing delegated cgroup`.
+
+ Then the strict subtest, on its own and **without**
+ `RestrictNamespaces`:
+
+ ```bash
+ sudo systemd-run --wait --pipe --collect -p Delegate=yes -p User="$(id -un)" \
+ -E TMPDIR="$d" -E CARLOS_CLAIM_SPAWN_KERNEL=1 \
+ "$d/activator.test" -test.run '^TestClaimContainmentCleanupRespawn$/^strict_primitive$' -test.v -test.count=1
+ rm -rf "$d"
+ ```
+
+ `strict_primitive` calls `(*Prepared).Start`, the strict claims path,
+ which keeps its `clone3` placement by Ruling 3. Under
+ `RestrictNamespaces=yes` `clone3` answers ENOSYS, so that subtest is
+ expected to fail there (`cgroup.ErrStrictUnsupported`). That is the
+ claims-on precondition above, not a regression, and it is why it runs
+ without the line. Run as one `-test.run '^TestClaimContainmentCleanupRespawn$'`
+ under `RestrictNamespaces=yes`, the test fails on that subtest alone.
- **Kernel tests on DOCS-T1.** They skip unless asked for, and they need a
- delegated unit. Build the test binary, then run it in a transient user unit:
+ delegated unit. From the checkout root, build the test binary into a
+ directory of its own, run it in a transient user unit, and remove that
+ directory and nothing else:
```bash
- mkdir -p "$TMPDIR/k"
- go test -c -o "$TMPDIR/k/cgroup.test" ./internal/cgroup
+ d=$(mktemp -d "$TMPDIR/zddk.XXXXXX")
+ go test -c -o "$d/cgroup.test" ./internal/cgroup
systemd-run --user --wait --pipe --collect -p Delegate=yes \
- -E TMPDIR="$TMPDIR/k" -E CARLOS_GEN_KERNEL=1 -E CARLOS_SHIM_KERNEL=1 \
- "$TMPDIR/k/cgroup.test" -test.run '<regex>' -test.v -test.count=1
+ -E TMPDIR="$d" -E CARLOS_GEN_KERNEL=1 -E CARLOS_SHIM_KERNEL=1 \
+ "$d/cgroup.test" -test.run '<regex>' -test.v -test.count=1
+ rm -rf "$d"
```
- (Remove `$TMPDIR/k` afterwards. User delegation offers `cpu memory pids`,
- not `io`.) Task 4's edge-hardening test is a
- driver that starts its own units, so it runs outside any unit:
+ Run the shim, generation and placement-check tests a second time with
+ `-p RestrictNamespaces=yes`, the shipped unit's line, and leave
+ `TestGenStrictKernel` out of that run: it is the strict path, which keeps
+ `clone3` (Ruling 3). User delegation offers `cpu memory pids`, not `io`.
+ Task 4's edge-hardening test is a driver that starts its own units, so it
+ runs outside any unit:
```bash
mkdir -p /tmp/k/zddr3 && go test -c -o /tmp/k/zddr3/cgroup.test ./internal/cgroup
@@ -729,13 +811,16 @@ the unit tests cannot stand in for a real cgroupfs.
- Readiness round 3's dev re-check, replacing round 2's blocked steps: see
"Roll notes from zdd-flag-readiness-3". It includes the two activator
kernel tests below, which now place through the shim and need the dev box's
- `io` controller.
+ `io` controller. Its step 7 has the commands, and runs the strict subtest
+ of `TestClaimContainmentCleanupRespawn` apart, without
+ `RestrictNamespaces=yes`.
- Round 1's kernel checks, still as listed next.
`TestClaimContainmentCleanupRespawn` (now the
KillHost and KillSidecar respawn) and
`TestGenerationStartRefusedBesideADirectParentMember` on the dev box
- (arm64, `CARLOS_CLAIM_SPAWN_KERNEL=1`, delegated user service). Also check
+ (arm64, `CARLOS_CLAIM_SPAWN_KERNEL=1`, a delegated unit with `io`; the
+ commands are step 7 of the readiness-3 dev re-check). Also check
whether children of a killed kept cgroup, and cgroups systemd kills on
`carlos-edge` stop, are affected.
- The plan 2 kernel suite rerun.
@@ -1134,6 +1219,7 @@ Added by zdd-flag-readiness-3 (checked on that branch): `__cgexec`
`cgroup-placement-failed`, `cgroup.ErrLimitNotApplied`,
`cgroup.ErrStrictUnsupported`, `PrepareStrict`, `PrepareStrictParent`, the
agent log lines `placement before exec failed`, `generation start refused`,
+`the start's context ended during placement`,
`REFUSING CARLOS_SWAP=overlap (<code>)` and `overlap swaps armed`, the control
service's `reset-failed` log line, `classifyRollout` and `follows` in
`deploywatch.go`, `cmd/zddprobe`, and the kernel-test variables