carlos / platform Public

spec: host lifecycle claim rev 5.2, CC22 creator CAS bound to the registration-time read

The creator's claim CAS is If-Match the ETag read at registration (or
If-None-Match when absent); 412 restarts from step 0; claims are never
deleted, so the purge fence is released by writing free.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 465fee969892704650ff26e33cacadd8c418a7be parent 64eec93
1 file changed, +17 −4
  • docs/superpowers/specs/2026-10-02-host-lifecycle-claim-design.md +17 −4

CI passed — run details

diff --git a/docs/superpowers/specs/2026-10-02-host-lifecycle-claim-design.md b/docs/superpowers/specs/2026-10-02-host-lifecycle-claim-design.md
index df41dd1b4..4ec253ad7 100644
--- a/docs/superpowers/specs/2026-10-02-host-lifecycle-claim-design.md
+++ b/docs/superpowers/specs/2026-10-02-host-lifecycle-claim-design.md
@@ -1,6 +1,6 @@
# The host lifecycle claim
-Design, 2026-10-03, **rev 5.1** (rev 5 plus CC20, CC21). A small primitive cited by:
+Design, 2026-10-03, **rev 5.2** (rev 5 plus CC20 to CC22). A small primitive cited by:
- **#199**, deleting an instance and parking its data:
`2026-10-02-replica-ownership-design.md`, which this spec supersedes
@@ -277,6 +277,11 @@ recovery probe are distinct permissions (J-D1).
**C5. Revocation needs stop evidence** (§6.1) from every box that may run
the host, **or** a lost-box fence (§7).
+**C5a. A claim object is never deleted (CC22).** "Free" is a written
+state, so an `If-None-Match: *` from a stale actor can never succeed
+against a host whose claim has existed. Only the very first claim for a
+host is created create-only.
+
**C6. Read errors never act.** A failed or malformed read of a claim,
record or listing means wait, or refuse. It never means absent.
@@ -312,7 +317,8 @@ new operation will write (CC11). A pending chore there must finish first.
### 5.1 Create (R3-12, J-N4)
-0. **App admission (J2-R2-2).** Register the create in
+0. **App admission (J2-R2-2).** Read the host's claim first and keep
+ its ETag, or note that it is absent. Then register the create in
`apps/<acct>/<app>/admission.json` with a CAS, as `{create id, host,
expires}`, which succeeds only while the app's admission is `open`.
**A registration is a lease (CC16):** the creating console renews it
@@ -322,7 +328,11 @@ new operation will write (CC11). A pending chore there must finish first.
1. The console assigns `CreatedAt` = now (UTC, nanoseconds). A caller
cannot supply it. `CreateRecord` and `CreateSidecarRecord` refuse a
zero or caller-supplied time on this path.
-2. **CAS** the claim from free or absent (§9) to
+2. **CAS** the claim, **conditional on exactly what step 0 read
+ (CC22)**: `If-Match` the ETag read then, or `If-None-Match: *` if it
+ was absent. A 412 means something wrote the claim since, possibly a
+ purge fence. The create then restarts from step 0, with a new
+ registration. The claim moves from free or absent (§9) to
`{holder: new, op: {creating, id, intent: {record body, key}}}`. This
is refused if the incarnation is in `retired_incarnations`, or if the
app holds a purge reservation (§5.3).
@@ -416,7 +426,9 @@ site now uses:
The paused creator's claim CAS (§5.1 step 2) is from free or absent,
so it now fails, and the create is refused before any record exists.
- The fence is released to free at the purge's completion (`done`).
+ The fence is released at the purge's completion (`done`) by
+ **writing a free claim**, never by deleting the object. Deleting it
+ would let a paused creator's `If-None-Match: *` succeed again (CC22).
The creator also re-reads its registration as its last step before
the claim CAS, and stops if it is gone or admission is closed. That is
cheap, but not sufficient on its own; the fence is what guarantees
@@ -840,6 +852,7 @@ the model catches it.
| CC19 | C3, the staging-work lease exception |
| CC20 | §6.2a at-rest sweep |
| CC21 | §5.3 step 1b, the purge fence |
+| CC22 | §5.1 steps 0 and 2, §5.3 fence release, C5a |
## 13. S3 behaviour this relies on, verified