spec: console release defences rev 5, Astra round 4 accept with fixes
Round 4 (committed unedited) accepted rev 4 with two P2 fixes, both applied. A non-canary route waiting for a dev proof stays in the fast loop. Readiness probes follow the route's effective upstream, Unix socket before TCP. Ready for Paul's review.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 files changed,
+39
−4
docs/superpowers/specs/2026-10-06-console-release-defences-design.md+26 −4docs/superpowers/specs/CODEX-FINDINGS-console-release-defences-r4.md+13 −0
CI cancelled — superseded by a newer push — run details
diff --git a/docs/superpowers/specs/2026-10-06-console-release-defences-design.md b/docs/superpowers/specs/2026-10-06-console-release-defences-design.md| index 295f05b44..d2917d481 100644 |
| --- a/docs/superpowers/specs/2026-10-06-console-release-defences-design.md |
| +++ b/docs/superpowers/specs/2026-10-06-console-release-defences-design.md |
| @@ -1,6 +1,10 @@ |
| # Console release defences |
| -Status: draft rev 4, 2026-10-06. Rev 4 answers Astra round 3 (`…-r3.md`, |
| +Status: draft rev 5, 2026-10-06, for Paul's review. Rev 5 applies Astra |
| +round 4 (`…-r4.md`, **accept with fixes**): rev 4's three fixes were judged |
| +sufficient, and two new P2s are fixed. 14 (a non-canary route waiting for a |
| +proof stays in the fast loop) is in §4.5, and 15 (readiness follows the |
| +effective upstream, Unix socket first) is in §2.4. Rev 4 answered Astra round 3 (`…-r3.md`, |
| reject). Of the six round-2 findings it re-checked, four were fixed. Two |
| needed more: findings 10 and 11, now in §2.3, where groups key on a non-empty |
| `Socket` or `Addr` and every group route is stamped to the bound route's |
| @@ -194,10 +198,16 @@ pass. |
| In order: |
| +Readiness follows the route's **effective upstream**: `Socket` if set, else |
| +`Addr`. `Socket` wins, as it does at the edge |
| +(`internal/registry/registry.go:40`), and named units may be socket-backed |
| +(`unitRail`, `cli.go:1624`) (codex 15, round 4). In order: |
| + |
| 1. The route's own health probe (`registry.Route.Health`, the probe-config |
| - JSON instances already use), sent to `rt.Addr`. |
| -2. Else, for a route with an `Addr`: a TCP connect to it succeeds. |
| -3. Else: the unit stays `active` with an unchanged MainPID for 5 s. |
| + JSON instances already use), sent as HTTP to the effective upstream: over |
| + the Unix socket for a socket-backed route, over TCP to `Addr` otherwise. |
| +2. Else, for a route with an upstream: a connect to it succeeds (Unix or TCP). |
| +3. Else: the unit stays `active` within one invocation for 5 s. |
| The three console routes get `{"path":"/healthz","status":200}` as part of |
| rollout (§6). The console's `/healthz` (`internal/console/server.go`) is |
| @@ -443,6 +453,15 @@ loop runs every 2 s, so a missing proof is cached per (route, sha256) for |
| 30 s: at most two bucket GETs a minute per listed route while waiting, none |
| once the build is live. |
| +**A waiting route stays in the fast loop (codex 14, round 4).** `fastMembers` |
| +(`internal/host/agent.go:1550`) includes canary routes on every tick, but |
| +drops a non-canary route as soon as it has a live version. So a non-canary |
| +route waiting for a proof would be rechecked only by the next serial bump or |
| +the hourly pass. While a listed route has an outstanding "not proven" verdict |
| +for its channel head, `fastMembers` keeps it as a member, and the 30 s cache |
| +above bounds what that costs. The console is on `canary/console` and is |
| +always a member; this is for any other listed app. |
| + |
| `carlos status --live` shows "waiting for a dev proof" for that host. |
| **Where the setting lives (codex 6).** The agent gets host.env through |
| @@ -587,6 +606,9 @@ they have a managed home they should go in instead. |
| routes with empty upstreams are never grouped; a same-app route on a |
| different upstream is left alone; the refusal record is written for every |
| host in the group; the next pass, agent or root, skips without relaying. |
| +- **Readiness:** a socket-backed named unit with a configured probe passes |
| + over its Unix socket; a route with both `Socket` and `Addr` is probed on the |
| + socket only. |
| - **Retry:** a plain re-promote of the refused version is still a no-op and |
| the refusal stands; `promote --retry` through the CLI (direct mode) and the |
| API door re-signs with a fresh `PromotedAt` and clears it; `--retry` against |
diff --git a/docs/superpowers/specs/CODEX-FINDINGS-console-release-defences-r4.md b/docs/superpowers/specs/CODEX-FINDINGS-console-release-defences-r4.md| new file mode 100644 |
| index 000000000..67f84da4f |
| --- /dev/null |
| +++ b/docs/superpowers/specs/CODEX-FINDINGS-console-release-defences-r4.md |
| @@ -0,0 +1,13 @@ |
| +| Round 3 finding | Rev 4 sufficient? | Why | |
| +|---|---|---| |
| +| 10. Rollback affects unrelated processes | Yes | §2.3 now groups by the same non-empty upstream, with `Socket` taking precedence over `Addr`. | |
| +| 11. Shared-host stamps and refusal coverage | Yes | §2.3 restores links and registry stamps even for siblings advanced by an interrupted pass, and records refusals for every group host. | |
| +| 13. Re-promoting does not trigger retry | Yes | §2.5 introduces `promote --retry` through both interfaces, explicitly requiring a fresh `PromotedAt`. | |
| + |
| +New findings at `924d2bf8`: |
| + |
| +14. **P2 — Proof-waiting routes outside canary channels can wait until the hourly pass.** [§4.5](/home/paulca/amadan.net/carlos/platform-design-console-release-defences/docs/superpowers/specs/2026-10-06-console-release-defences-design.md:439) relies on the two-second fast loop to reconsider a missing proof after its 30-second cache expires. However, [`fastMembers`](/home/paulca/amadan.net/carlos/platform-design-console-release-defences/internal/host/agent.go:1550) excludes non-canary routes that already have a live version. A promotion’s serial bump can run the gate once, find no proof, and leave the old version live; subsequent ticks then exclude that route. §4.4 specifies uploading the proof but no wake-up mechanism for its consumer. Require pending-proof routes to remain eligible for bounded rechecks, or specify a proof-arrival trigger that reaches them. |
| + |
| +15. **P2 — Readiness probes can reject a healthy socket-backed unit.** [§2.4](/home/paulca/amadan.net/carlos/platform-design-console-release-defences/docs/superpowers/specs/2026-10-06-console-release-defences-design.md:197) always sends a configured health probe to `rt.Addr`, although [`Route.Socket`](/home/paulca/amadan.net/carlos/platform-design-console-release-defences/internal/registry/registry.go:40) takes precedence, and [`unitRail`](/home/paulca/amadan.net/carlos/platform-design-console-release-defences/cli.go:1624) permits named units on socket-backed routes. With a configured probe and an empty `Addr`, a healthy unit serving its Unix socket never passes readiness: D1 rolls it back, the restored build fails the same check, and the refusal blocks further adoption. With both fields populated, D1 checks an upstream the route does not use. Make readiness follow the effective upstream, including Unix-socket HTTP probes. |
| + |
| +Verdict: accept with fixes. |
| \ No newline at end of file |