carlos / platform Public

plan: R2 must wait for TWO readiness-capable console versions

Found in the task 9/10 review, and it inverts the naive ordering.

Once console.service is Type=notify, a start succeeds only if the started
binary sends READY=1. But adoption's restart-failure path (§4b,
WithStartUnit) rolls the symlink back and starts the PREVIOUS version — and
if that version predates the readiness commit it never signals, so the
ROLLBACK's start times out and fails. The box is then left with no console
AND a poisoned new version: the §4b hole again, except now the recovery
path is the thing that fails.

So R2 happens only after a SECOND post-R1 roll, when whatever the rollback
lands on can also signal. Until then the unit stays Type=simple, which is
the state both boxes are in today.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev ede1e1c26f445008f675d5cd29ff798faa4b6fc9 parent 3f45dcb
1 file changed, +23 −3
  • docs/superpowers/plans/2026-08-23-console-as-an-app.md +23 −3
diff --git a/docs/superpowers/plans/2026-08-23-console-as-an-app.md b/docs/superpowers/plans/2026-08-23-console-as-an-app.md
index 5e4e2afb0..2d97cc537 100644
--- a/docs/superpowers/plans/2026-08-23-console-as-an-app.md
+++ b/docs/superpowers/plans/2026-08-23-console-as-an-app.md
@@ -1900,9 +1900,29 @@ Verify: `/proc/<pid>/exe` names the merge sha on both boxes, and `curl -s https:
### R2 — pinfra: `Type=notify` on `console.service`
-Add `Type=notify` (and a `TimeoutStartSec` that comfortably exceeds console startup) to `console.service` in the pinfra repo; apply to both boxes; `systemctl daemon-reload` and restart once. Task 10's code is already deployed by R1, so readiness will be signalled.
-
-Verify: `systemctl show -p Type --value console` says `notify`, and the console is active and serving.
+**DO NOT flip this at the first opportunity.** Found in the Task 9/10 review, and it
+inverts the naive ordering:
+
+Once the unit is `Type=notify`, a start only succeeds if the started binary sends
+`READY=1`. Adoption's restart-failure path (§4b, `WithStartUnit`) rolls the symlink
+back and starts the **previous** version — and if that previous version predates the
+readiness commit, it never signals, so the rollback's start TIMES OUT and fails. The
+box is then left with no console AND a poisoned new version: the §4b hole again, but
+worse, because the recovery path is now the thing that fails.
+
+So the precondition is **two consecutive readiness-capable console versions on disk**
+under `/opt/carlos/versions/` — i.e. R2 happens only after a SECOND post-R1 roll, so
+that whatever the rollback lands on can also signal. Until then `console.service`
+stays `Type=simple` and §4a's residual (a build that starts and then misbehaves)
+stays open, which is the state we are in today anyway.
+
+Then: add `Type=notify` and a `TimeoutStartSec` comfortably exceeding console
+startup to `console.service` in the pinfra repo; apply to both boxes;
+`systemctl daemon-reload` and restart once.
+
+Verify: `systemctl show -p Type --value console` says `notify`; the console is
+active and serving; and `ls /opt/carlos/versions/` shows at least two
+readiness-capable builds.
### R3 — Stamp the right route