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>
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 |