carlos / platform Public

plan: task 6 must re-validate the staged manifest — the stat short-circuit is not a gate

Task 2's review traced the shared-gate claim and found it per-download:
ensureStaged returns early when the artifact is already on disk, so a
platform-binary rollback can point at an already-staged version whose
capability block the older binary does not understand. The converge pass
reads the staged sibling manifest anyway; it must ValidCapabilities what
it reads and refuse the route in the three-state posture.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell c53eefc3ffb21f31e6cf8dd82fdfb512f18c61b7 parent 474734e
1 file changed, +2 −0
  • docs/superpowers/plans/2026-08-03-app-capabilities.md +2 −0
diff --git a/docs/superpowers/plans/2026-08-03-app-capabilities.md b/docs/superpowers/plans/2026-08-03-app-capabilities.md
index dacef3fa6..bc2e42683 100644
--- a/docs/superpowers/plans/2026-08-03-app-capabilities.md
+++ b/docs/superpowers/plans/2026-08-03-app-capabilities.md
@@ -668,6 +668,8 @@ Converge's order of operations, pinned:
**Note (added post-Task-1-review, 2026-08-03):** `internal/caps`'s `reservedRuntimeDir` static list (extended in the Task 1 review) is the *first* guard against `runtime_dir` naming something that matters — but it is necessarily finite, and a box can have anything under `/run` that no static list will ever enumerate (a box-specific `/run/tailscale`, an operator's own daemon, etc.). Converge must ADDITIONALLY refuse a `runtime_dir` grant when `/run/<name>` already exists on the box and is **not** this route's own prior claim (i.e. not something this same host's own drop-in created) — an `os.Lstat` check alongside the existing box-wide `CheckClaims` collision pass, before anything is written. The static list is the first guard; this existence check is the second, and only the second one can catch a name the vocabulary's authors never thought of.
+**Note (added post-Task-2-review, 2026-08-03):** the "ship and adopt share one gate" claim is per-DOWNLOAD, not per-adopt: `ensureStaged` returns early on `os.Stat(VersionPath(...))` success, so a version whose artifact is already on disk is pointed live without its manifest being re-read — a platform-binary ROLLBACK can therefore point at an already-staged version whose capability block this older binary does not understand. Deliberately not fixed in `ensureStaged` (a bucket round-trip per host per pass is what §3c avoids). The converge pass is the backstop: it reads the staged sibling manifest for the live version anyway, so it must run `caps.ValidCapabilities` on what it reads and REFUSE the route (leave drop-ins alone, log the "update the platform binary" line) when validation fails — same three-state posture as an unreadable manifest.
+
---
### Task 7: converge inside `adoptHost` — before `PointLive`, rolled back with it