carlos / platform Public

spec: host lifecycle claim rev 6.8, writer sessions (joint round 8)

Every writer session is admitted by an AdmitWriter claim CAS (holder.session
= its gen; warm or cold; journals the predecessor; never replaces the op, so
recovery happens inside landing/restoring/reverting/probes). Clean-close
certificates are per session and never deleted; a warm continuation moves
the session forward, so old markers can't authorize anything. Baseline
provenance from journalled predecessor and writer-written lineage. Empty
bootstrap. Withdraws rev 6.7's delete-the-marker rule. Joint R8 copied.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 5462d293adf967ae9d51ec6e7007e6bf754f7701 parent f9df764
2 files changed, +318 −86
  • docs/superpowers/specs/2026-10-02-host-lifecycle-claim-design.md +134 −86
  • docs/superpowers/specs/CODEX-FINDINGS-JOINT-R8.md +184 −0

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 722a354e1..ff6aeb578 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,9 +1,9 @@
# The host lifecycle claim
-Design, 2026-10-04, **rev 6.7**. Joint rounds 6 and 7 judged the claim ready for primitive planning. Rev
+Design, 2026-10-04, **rev 6.8**. Joint rounds 6 and 7 judged the claim ready for primitive planning. Rev
6.5 added the R6-4 rule. Rev 6.6 added writer epochs (R7-1, amadan #225).
-Rev 6.7 answers the moves session's three notes on §6.4a: consuming the
-marker, the first wake, and the warm baseline. A small primitive cited by:
+Rev 6.7 answered the moves session's notes on §6.4a. Rev 6.8 rewrites §6.4a
+around writer sessions, for joint round 8 (R8-1 to R8-4). A small primitive cited by:
- **#199**, deleting an instance and parking its data:
`2026-10-02-replica-ownership-design.md`, which this spec supersedes
@@ -93,7 +93,8 @@ replaced whole with conditional writes (`If-Match` on the ETag read).
"incarnation": "<hex>", "created_at": "<record CreatedAt>",
"location": "instances/shop.bab.oncarlos.com/<incarnation>",
"generation": "<op id of the installed generation, or \"\">",
- "stamp": 41
+ "stamp": 41,
+ "session": 57
},
"op": {
"kind": "serving",
@@ -165,6 +166,8 @@ Field rules:
changes nothing. The probe's later CAS issues `R`, and activating `R`
retires `S`. `F` was only ever active on the target, so its records
are retired there.
+- **`holder.session`** is the current writer session's admission `gen`
+ (§6.4a). Every `AdmitWriter` CAS moves it forward.
- **`holder.generation`** names the installed generation within
`holder.location` (CC18). The moves spec owns its values and its
markers. Its start check compares this field with all three of its
@@ -829,90 +832,134 @@ The guard is local because these writers are local. The claim decides
what each writer is allowed to do. The model must run these as
independent processes, with pauses between validation and the write.
-### 6.4a Writer epochs (R7-1, amadan #225)
+### 6.4a Writer sessions and epochs (R7-1, R8-1 to R8-4, amadan #225)
-**The hazard.** Litestream writes each file to a key built from its level
-and TXID range only, and it writes unconditionally. A dead writer's
+**The hazard.** Litestream writes each file to a key built only from its
+level and TXID range, and it writes unconditionally. A dead writer's
in-flight level-0 upload for `[N+1]` can land after the next writer has
-cold-restored and uploaded its own `[N+1]`. The old file then replaces
-the new one, and the next restore loses acknowledged writes. `lock.json`
-cannot revoke an upload S3 has already accepted.
-
-So **two writers whose uploads could overlap never share a generation**.
-
-- **Clean close.** A writer stopping cleanly does three things, in order:
- 1. waits until **every** upload it issued has returned success;
- 2. writes `<holder.location>/<generation>/closed.json` as
- `{stamp, max_txid, closed_at}`, conditionally (`If-None-Match`, so it
- is written once);
- 3. releases its lease.
-
- A writer that saw any upload fail, time out or come back ambiguous
- writes no `closed.json`.
-- **Lost replication state, defined.** When a box admits a writer, it
- continues the current `holder.generation` only if all of these hold:
- - `closed.json` exists for that generation;
- - its `max_txid` equals the generation's remote maximum TXID;
- - its `stamp` is the box's own `active_stamp`.
-
- **The marker is consumed when a writer opens it.** The admitted writer
- deletes `closed.json`, by the exact version it read, before it issues
- its first upload. If that delete fails, the writer does not continue
- the generation; it takes the cold-recovery path instead. A writer that
- continues after a clean close and then crashes with uploads in flight
- therefore leaves no marker, and the next wake recovers cold. The delete
- marker this leaves lets the writer's own later clean close write the
- key again with `If-None-Match`.
-
- **The first wake after a create or an adoption is a cold recovery.** No
- writer closed the origin generation. Nobody writes a marker it didn't
- earn, and an empty generation is not exempted, because a first writer
- that crashed with `[1,1]` in flight leaves the same state. A new
- instance's first generation snapshots an empty or near-empty
- database, so the cost is small. An adoption pays one full snapshot,
- once.
-
- Otherwise the previous writer may still have uploads in flight. That
- covers a crash, an out-of-memory kill, lease expiry or takeover, a
- shared-pool failover, a lost box, and a missing or unreadable marker.
- Then replication state is **lost**, and the start is a **cold
- recovery**. When in doubt, it is a cold recovery.
-- **A cold recovery is an installing CAS (§2, R6-4).** Before admitting
- any write, the box CASes the claim, conditional on the observed
- `holder.generation`. The CAS writes a **new `holder.generation`**, a
- fresh op id that is never reused, and a **new `holder.stamp`** (the
- resulting `gen`). It requires `op.exec` to be this box. The box then:
- 1. establishes its **baseline**:
- - **The warm local database**, if all of these hold:
- - its lineage marker (`<dbPath>.gen`) names the old generation;
- - it passes its integrity check;
- - the box's local stamp record shows **this box was the old
- generation's active writer**.
-
- A crash without disk loss leaves that database holding
- acknowledged writes that were never uploaded, so it is a superset
- of the remote.
- - **Otherwise, a restore from the old generation's remote.** Another
- box may have advanced the remote past this box's copy.
- 2. activates the new stamp locally (§6.4);
- 3. replicates **only** into the new generation's prefix.
-
- It is never a same-generation restart. A writer from the old
- generation that is still running is refused locally by the stamp (§6.4)
- and remotely by the prefix. Its late uploads land in the old
- generation, which nothing reads once the pointer has moved. They are
- parked as residue by the at-rest sweep (§6.2a, dead generations).
-- **Writers are bound to their generation.** A writer's replica client
- is constructed for the `{holder.location, holder.generation}` it
- validated under, and refuses any key outside
- `<location>/<generation>/`. That covers level-0 files, compaction,
- snapshots and retention. A writer never re-reads the generation while
- running. A new generation means a new writer.
-
-The moves spec owns the data-path side: restoring from one generation
-while replicating into another, and the deterministic tests for both
-completion orders and the race between initialization and the remote
-position (rev 7.8).
+uploaded its own `[N+1]`, replacing it. The next restore then loses
+acknowledged writes. Neither `lock.json` nor any client-side deadline
+can revoke an upload S3 has already accepted.
+
+**The rule.** Two writers whose uploads could overlap never share a
+generation, and **every** path that opens replication goes through
+writer admission. That includes:
+- an ordinary wake;
+- a committed move's target serving while the claim is still `landing`;
+- a restarted probe;
+- a reverted source;
+- an opened rollback recovery;
+- a push's writer.
+
+#### Writer sessions in the claim (R8-1)
+
+`holder.session` is the claim `gen` of the CAS that admitted the
+current writer session.
+
+**`AdmitWriter`** is one claim CAS, made by the box **before the writer
+issues any upload**. It requires:
+- the observed `holder.session` and `holder.generation`;
+- `op.exec`, or the moves spec's phase table for the current op, to
+ permit this box to open replication.
+
+It writes:
+- `holder.session = its own resulting gen`;
+- a **journal entry** in `op.journal.writers`:
+ `{session, box, generation, stamp, mode, predecessor: {session, box,
+ generation, stamp}}`. The predecessor is copied from the claim it
+ observed.
+- in **cold** mode, a new `holder.generation` (a fresh op id) and
+ `holder.stamp = the resulting gen`. This is an installing CAS (§2,
+ R6-4).
+
+It never replaces the operation. Kind, id, phase, intent, participants,
+obligations and the rest of the journal are kept. **Recovery inside
+`landing`, `restoring`, `reverting` or a probe is therefore an
+`AdmitWriter` within that operation**, never a new operation (R8-2). The
+moves spec's phase table says which box may be admitted in which phase.
+
+#### The clean-close certificate is one-use by construction (R8-1)
+
+A writer session closes cleanly only after **every** request it issued
+has completed with success (R8-5, the moves spec's evidence rule). That
+includes:
+- every level-0 upload;
+- every compaction and snapshot upload;
+- every retention delete;
+- every timed-out original whose outcome is unknown.
+
+A writer that cannot prove completion writes no certificate.
+Litestream's `Replicator.Close` drops the initial `SyncAndWait` error
+today, so certificate eligibility needs its own evidence, not `Close`'s
+return value.
+
+A clean writer then writes
+`<location>/<generation>/closed/<session>.json`
+`{session, box, max_txid, closed_at}`, with `If-None-Match`. The key is
+per session, and the marker is never deleted or reused.
+
+**Warm continuation** is an `AdmitWriter` in **warm** mode. It is legal
+only if `closed/<holder.session>.json` exists for the observed session,
+its `max_txid` equals the generation's remote maximum TXID, and its `box`
+is the admitting box or the warm baseline rules below allow otherwise.
+The admitting CAS moves `holder.session` forward, so that marker now
+names a dead session. A delayed, replayed or re-created marker for any
+earlier session can never authorize anything again. No marker is ever
+deleted, and no S3 delete or version semantics are involved. (Rev 6.7's
+"consume the marker by deleting it" is withdrawn: deleting an exact
+version doesn't create a delete marker, and it can expose an older
+version.)
+
+Anything else is **cold**: no certificate for the observed session, a
+mismatched `max_txid`, or any doubt. That covers a crash, an
+out-of-memory kill, lease expiry or takeover, a shared-pool failover, a
+lost box, and a writer that continued and then crashed with uploads in
+flight. That last writer's own session has no certificate, whatever
+older certificates exist.
+
+#### Baselines and provenance (R8-4)
+
+A cold `AdmitWriter` journals its predecessor (`predecessor.session`,
+`.box`, `.generation` and `.stamp`) **before** the recovery stamp is
+activated anywhere. The new writer's baseline is:
+- **the warm local database**, only if all of these hold:
+ - its local lineage file, written by the writer that opened it and
+ naming `{generation, session}`, equals the journalled
+ `predecessor.{generation, session}`;
+ - `predecessor.box` is this box;
+ - it passes its integrity check.
+
+ Re-initializing a local stamp record from the current claim (§6.4)
+ never writes a lineage file. So no claim read can manufacture evidence
+ of earlier ownership;
+- **otherwise, a restore from the predecessor generation's remote.**
+
+The writer then activates the new stamp locally (§6.4), and replicates
+**only** into `<location>/<new generation>/`.
+
+#### Empty bootstrap (R8-3)
+
+The first writer after a create, or after an adoption with nothing
+replicated, is admitted cold with a fresh generation. A generation may be
+**empty**: until the first committed transaction, no LTX file is
+written, and no file with a zero TXID is ever written. The app may start
+and create its database before replication opens. The writer is admitted
+before its first upload, and that first upload is the first
+transaction's file. The moves spec owns the litestream mechanics: the
+level-0 anchor and snapshot rules for position 0.
+
+#### Generation-bound writers
+
+A writer's replica client is constructed for the `{location, generation,
+session}` it was admitted with. It refuses any key outside
+`<location>/<generation>/`: level-0, compaction, snapshot and retention.
+It never re-reads the generation, so a new admission means a new writer.
+
+An old writer's late uploads land in the old generation, or in the
+current generation **before** the session that superseded it opened. A
+cold successor has a new generation. A warm successor exists only after
+the old session certified that every one of its requests had completed.
+Dead generations are parked by the at-rest sweep (§6.2a).
### 6.5 Door reads pinned to versions (J-N10)
@@ -1158,6 +1205,7 @@ the model catches it.
| J4-R4-4 | §5.3 step 4 |
| J4-Q2 | §5.1 step 5 |
| J5-R5-4 | §2 `holder.stamp`, §6.4 initialization, refusal with no record, the two retirements |
+| J8-R8-1 to R8-4 | §6.4a rewritten: writer sessions in the claim, per-session certificates (never deleted), admission inside any op, baseline provenance, empty bootstrap |
| moves 7.8 notes | §6.4a: marker consumed at open, first wake is cold, warm baseline only for the old generation's own writer |
| J7-R7-1 | §6.4a writer epochs: clean close, lost replication state, cold recovery as an installing CAS, generation-bound writers |
| J6-R6-4 | §2 `holder.stamp` invariant: any CAS that changes `op.exec` writes `holder.stamp` |
diff --git a/docs/superpowers/specs/CODEX-FINDINGS-JOINT-R8.md b/docs/superpowers/specs/CODEX-FINDINGS-JOINT-R8.md
new file mode 100644
index 000000000..6b36fc31c
--- /dev/null
+++ b/docs/superpowers/specs/CODEX-FINDINGS-JOINT-R8.md
@@ -0,0 +1,184 @@
+**A, B and C are not yet ready as the complete Phase 1 planning baseline.** Fresh writer generations are the right fix for R7-1, but marker reuse and admissions outside `serving` still permit the original collision. The library grounding and durability oracle have improved substantially.
+
+I reviewed A at `b10547e1` and B/C/D at clean HEAD `3c5f742a`. AGENTS.md was read first. The requested test command failed before compilation:
+
+```text
+go: creating work dir: mkdir /home/paulca/.cache/tmp/go-build…: read-only file system
+```
+
+This is a static review; I did not reproduce the reported sweeps. `bash -n internal/sim/hostlife/mutate.sh` passed. No files changed.
+
+The supplied change note contains no round-8 item; I used the committed diff and B §22.14. The answers file now includes **rev 6.7 answers**. Those clarify intent but do not change the requested rev 6.6 verdict.
+
+**Round-7 dispositions**
+
+| Finding | Spec | Model / executable evidence | Why |
+|---|---|---|---|
+| **R7-1: overlapping writers reuse LTX keys** | **Partly** | **Partly** | Fresh generations isolate the named schedules, but marker consumption/republication and non-`serving` admissions remain unsafe. |
+| **R7-2: permanent key exemptions hide durable writes** | **Resolved** | **Resolved** | Required writes are checked by identity; state is compared with replay of the carried writes. No permanent key mask remains. |
+| **R7-3: retention removes newest L0** | **Resolved** | **Resolved** | Both deletion paths preserve newest L0; the deterministic case exercises local position ahead of uploads. |
+| **R7-4: intent mistaken for completed installation** | **Resolved for the reported build path** | **Resolved within the artifact abstraction** | Separate intent/completion objects; snapshot and anchor precede publication. The model checks this ordering and crashes between steps. |
+| **R7-5: Store gate overclaims maintenance coverage** | **Resolved** | **D: resolved** | It now requires L1–L3 files, snapshot deletion, distinct contents with updates/deletes, and a restore requiring compacted continuation. |
+
+**R8-1 — P1: the clean-close certificate is not safely consumed once.**
+
+A §6.4a; [B §5.1 step 9](/home/paulca/amadan.net/carlos/platform-moves-design/docs/superpowers/specs/2026-10-02-move-any-app-design.md:850); [model admission](/home/paulca/amadan.net/carlos/platform-moves-design/internal/sim/hostlife/protocol_test.go:454); [installer PUT retries](/home/paulca/amadan.net/carlos/platform-moves-design/internal/sim/hostlife/world_test.go:509).
+
+There are two concrete failures.
+
+1. **Consumption is not awaited.** The model issues `rDelVer` and immediately starts the writer. Hold that delete; let the writer issue N+1, then crash with its disk before either request completes. The old marker still matches remote N and the active stamp. The replacement continues the generation, recreating R7-1.
+
+ Rev 6.6 does not specify consumption at all. B adds consumption without an explicit successful-completion gate. The newer rev 6.7 answer supplies that gate, but C does not implement it.
+
+2. **A delayed marker retry can recreate a consumed certificate.** Recovery writes its installer marker through `ackedPut`, which proceeds after one attempt succeeds while older attempts can remain outstanding. After the admitted writer deletes that marker, an older `If-None-Match` attempt can arrive while the key is absent and succeed. If the writer then crashes with N+1 outstanding, the recreated `{stamp,N}` still matches and authorizes another writer in the same generation.
+
+The claim-owner notes also contain an incorrect S3 premise: **deleting an exact version does not create a delete marker**. It removes that version and may expose an older version. Furthermore, `If-None-Match` permits creation when the current object is absent or a delete marker, so a tombstone alone would not solve delayed republication. [AWS DeleteObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObject.html), [AWS conditional writes](https://docs.aws.amazon.com/AmazonS3/latest/userguide/conditional-writes.html).
+
+**Fix:** make clean-close authority a one-use certificate with durable consumption before any writer activity. For example, retain an open/closed state object changed by conditional writes, with a unique writer-session token preventing reuse of an earlier state. Alternatively, bind consumption to a monotonic claim field. A delayed old close must never authorize a subsequent session. Add separate witnesses for delayed deletion and delayed marker republication with a **matching** stamp and TXID.
+
+**R8-2 — P1: writer epochs are bypassed while lifecycle operations still admit writers.**
+
+[B §7.9](/home/paulca/amadan.net/carlos/platform-moves-design/docs/superpowers/specs/2026-10-02-move-any-app-design.md:1797), [B §7.14](/home/paulca/amadan.net/carlos/platform-moves-design/docs/superpowers/specs/2026-10-02-move-any-app-design.md:1967), [model admission](/home/paulca/amadan.net/carlos/platform-moves-design/internal/sim/hostlife/protocol_test.go:379).
+
+The recovery CAS is allowed only from `serving`; the model checks replication state only when `!probe && op.Kind == "serving"`.
+
+A committed move admits public traffic while the claim remains `landing`, waiting for cleanup:
+
+1. Target A serves generation G and sends N+1.
+2. Its upload remains outstanding; A crashes and loses its database.
+3. Before finalization releases the claim, another wake restores G through N.
+4. That wake bypasses the epoch check because the claim is still `landing`.
+5. Its N+1 collides with A’s outstanding N+1.
+
+The same missing rule affects restarted probes, reverted-source writers and opened rollback recovery. A §6.4a says two potentially overlapping writers never share a generation; B/C implement a narrower rule.
+
+The new crash-with-disk fault is itself restricted to `serving`, so the sweep cannot exercise this committed-landing schedule. [Fault restriction](/home/paulca/amadan.net/carlos/platform-moves-design/internal/sim/hostlife/background_test.go:351).
+
+**Fix:** enforce writer-session admission for every path that opens replication. Define recovery within an existing lifecycle operation, preserving its journal, participants and obligations; do not replace that operation with an unrelated recovery. Test the collision during committed-but-unfinalized serving and during probe restart.
+
+**R8-3 — P2: first-wake recovery requires LTX files that cannot exist at position zero.**
+
+[B §7.14](/home/paulca/amadan.net/carlos/platform-moves-design/docs/superpowers/specs/2026-10-02-move-any-app-design.md:1968); [model recovery builder](/home/paulca/amadan.net/carlos/platform-moves-design/internal/sim/hostlife/moves_test.go:1691).
+
+Every create’s first wake now runs recovery, but a never-started instance can have no database and no replication position. The prescribed snapshot `[1,N]` then becomes `[1,0]`; its anchor would be `[0,0]`.
+
+Pinned LTX rejects zero minimum or maximum TXIDs. The model accepts the zero-position snapshot and skips the anchor, so it permits a bootstrap that the real encoder refuses. [Pinned header validation](/home/paulca/go/pkg/mod/github.com/superfly/ltx@v0.5.1/ltx.go:218).
+
+Production also deliberately starts a fresh application before replication because the application may create its database. [Existing first-boot ordering](/home/paulca/amadan.net/carlos/platform-moves-design/internal/activator/activator.go:1168).
+
+**Fix:** specify an empty-generation bootstrap explicitly. It must still allocate a fresh epoch, but either represent an empty installation without invalid LTX files or establish a valid initialized database and positive position before encoding. Preserve first-boot seed semantics. Add a real-library test starting with neither database nor replica.
+
+**R8-4 — P2: the warm-baseline rule lacks proof that this is the authoritative local database.**
+
+A §6.4a; [B recovery baseline](/home/paulca/amadan.net/carlos/platform-moves-design/docs/superpowers/specs/2026-10-02-move-any-app-design.md:1968); [model selection](/home/paulca/amadan.net/carlos/platform-moves-design/internal/sim/hostlife/moves_test.go:1682).
+
+A’s literal “restores from the old generation” can discard acknowledged, unuploaded writes even when the failed writer’s disk survives. B correctly allows the warm database, but matching `.gen` alone establishes identity, not freshness or prior writer ownership. A cached/restored copy of G at N must not replace G’s authoritative history through N+1 during takeover.
+
+The newer claim-owner answer requires three checks: matching generation, integrity, and a local record proving **this box was that generation’s active writer**. C checks incarnation and generation only. It does not encode the third condition. [Owner’s answer](/home/paulca/.cache/nearest-region/claim-rev61-answers.md:16).
+
+**Fix:** journal the predecessor’s generation, stamp and writer provenance before activating the recovery stamp. Use the warm database only with that provenance and integrity evidence, including committed WAL contents. Otherwise use a version-pinned remote restore. Reinitializing a stamp from the current claim must not manufacture evidence of earlier ownership.
+
+**R8-5 — P2: the model’s “every upload succeeded” condition checks only the current L0 queue.**
+
+[Clean close](/home/paulca/amadan.net/carlos/platform-moves-design/internal/sim/hostlife/ltx_test.go:304), [upload retry](/home/paulca/amadan.net/carlos/platform-moves-design/internal/sim/hostlife/ltx_test.go:332), [maintenance requests](/home/paulca/amadan.net/carlos/platform-moves-design/internal/sim/hostlife/ltx_test.go:425).
+
+Hold an L0 request until it times out; let its retry succeed. `ship` removes the queue entry and forgets the original request. `closeStep` can then write `closed.json` while that original remains outstanding. Compaction and snapshot requests are also outside its drain accounting.
+
+That differs from A/B’s explicit rule: any failed, timed-out or ambiguous upload prevents a clean marker. The existing witness holds all retries and completes them together, so it does not expose the difference.
+
+**Fix:** track every issued request for the writer lifetime, plus a sticky uncertainty flag. Include maintenance uploads. Add a witness where one retry succeeds while the earlier attempt stays unresolved.
+
+This matters when implementing #225: today’s `Replicator.Close` drops an initial `SyncAndWait` error, and a successful final close does not prove that every earlier background request succeeded. Marker eligibility needs its own evidence. [Production close](/home/paulca/amadan.net/carlos/platform-moves-design/internal/lease/lease.go:368).
+
+**R8-6 — P3: the retired-door coverage table contradicts its conclusion.**
+
+[B §14.2.5](/home/paulca/amadan.net/carlos/platform-moves-design/docs/superpowers/specs/2026-10-02-move-any-app-design.md:3252).
+
+The `door-reread+C3a` row says 414 failures, calls that “the partner’s count,” and gives the partner’s count as 361. Both retired rows still say the witness “violated,” with old door-failure counts. The following paragraph says the pairs add nothing.
+
+**Fix:** distinguish historical results from current results and remove claims of current witness evidence for retired witnesses. Aggregate failure counts alone do not establish redundancy.
+
+**What D establishes—and its limits**
+
+The tests establish the three requested facts for the tested, **nonempty** pinned-library fixtures:
+
+| Fact | Evidence |
+|---|---|
+| L9 plus synthetic L0 `[N,N]` continues at N+1 | Exact L0 ranges and subsequent restored row count are required. |
+| File integrity, without a post-apply database checksum | Real L0, snapshot and compactor output is decoded and verified with `HeaderFlagNoChecksum`, zero post-apply checksum and nonzero file CRC. |
+| The original root eventually loses its continuation | Two retention rounds remove that continuation; restoration rooted there must fail, while newest-root restoration must recover every expected row. |
+
+The revised [Store gate](/home/paulca/amadan.net/carlos/platform-moves-design/internal/sim/litestreamfacts/facts_test.go:734) now provides useful lifetime coverage: repeated continuation, L1–L3 compaction, snapshot rotation/deletion, time-based L0 expiry, exact contents, and a final restore requiring L1 continuation. R7-5 is resolved.
+
+Its production fidelity remains bounded:
+
+- It constructs the same broad `DB`/`Replica`/`Store` shape and separate metadata directory. Production uses L1/L2/L3 intervals of 1h/6h/24h, default 24h snapshot interval/retention, five-minute L0 retention checked hourly, and a ten-second DB monitor. The spike accelerates these and forces each write through `SyncAndWait`.
+- Every simulated disk loss follows a **successful Store close**. It does not exercise abrupt replacement through `Held`, lease loss, or the activator.
+- File-client aging uses the encoded timestamp; ordinary S3 retention listings use `LastModified`. Filename ordering is distinct from aging, and B now says that correctly.
+- File replacement/deletion does not exercise version visibility, multipart requests, delayed completion, or the close-certificate protocol.
+- The new unconditional-PUT test inspects source text; it is not an S3 concurrency test and can skip if source discovery fails.
+
+**Synthetic anchors are supported by the tested pinned paths, not guaranteed safe across arbitrary upgrades.** The tests now exercise verification, compaction and retention substantially better. Keep the versions pinned and treat the spike as a required upgrade gate, supplemented by writer-session and backend-concurrency tests. The missing empty-bootstrap case is a concrete gap in the current grounding.
+
+**Model fidelity**
+
+The established stamp rules still match A §6.4 and rev 6.5:
+
+- capture at work selection;
+- equality with the active stamp under the guard;
+- activation retires the previous active stamp;
+- artifact retirement changes no stamps;
+- missing records refuse until initialization;
+- return-to-source restores S; the later installation activates R and retags its artifacts.
+
+That agreement covers the represented database/files inventory. **The new §6.4a rules are not exact**, for R8-1, R8-2 and R8-5.
+
+The three reported model repairs are present:
+
+- Full files replace state and carry the full write log.
+- The new crash-with-disk branch removes every process. Its disk fault still represents database loss, rather than loss of every local artifact.
+- Recovery takes stop evidence before choosing its baseline.
+
+The new durability oracle resolves R7-2: later required writes are no longer hidden by an earlier exempt write, and a high lineage TXID cannot substitute for the missing write’s identity. Its purity checks remain present.
+
+Anchor initialization, successor-root selection, overlap planning and newest-L0 preservation match D at the stated abstraction level. The zero-position bootstrap is an additional mismatch. Also, C reads remote L0 position on each upload decision; the pinned replica caches that position until initialization or an error. C therefore exposes the remote-position shortcut more broadly than production.
+
+Static counting confirms **84 rules, 42 witness entries and 122 baseline/subset/full-set executions**. The harness checks every proper subset and the named first failure for the full set.
+
+The witness-only classifications for `recovery-generation` and `closed-marker` are **honest as reported coverage**. Zero detections in 100,000 sweep seeds does not establish safety or redundancy. The deterministic cases inspect restoration immediately after the harmful completion; the sweep’s later observations can miss a transiently damaged replica subsequently repaired by a snapshot. The `closed-marker` mutation also combines several checks, while its witness uses an obviously stale TXID; it does not independently establish safe consumption or replay protection.
+
+The nine combination-only classifications are supported for their selected scenarios, not globally minimal mutation sets. The two door rules are now declared subsumed separately; the contradictory table should be corrected.
+
+**Writer epochs and the three notes**
+
+Writer epochs address both R7-1 and today’s lease-failover hazard in #225: accepted writes from an old client must remain confined to a prefix the new writer never modifies. Lease ownership and process fencing remain necessary, but cannot revoke an accepted S3 request.
+
+1. **Consume the marker at open:** required, explicitly and durably, before writer activity. The proposed delete/recreate protocol also needs protection from delayed marker republication.
+2. **First create/adoption pays recovery:** a defensible conservative choice, now explicitly accepted by the claim owner. An empty remote listing is not proof that no first upload is outstanding. I would retain fresh epochs and define the empty bootstrap, rather than add console data-bucket authority merely to avoid this path.
+3. **Warm baseline:** preserve the failed active writer’s valid local database, including its WAL. `.gen` alone is insufficient; implement the owner’s provenance condition.
+
+| Artifact | Ready for Phase 1 implementation planning? | Reason |
+|---|---|---|
+| **A — claim rev 6.6** | **No, as a complete contract** | The existing claim/stamp primitive remains plan-ready, but the new writer-epoch contract needs safe certificate consumption and explicit recovery integration. |
+| **B — moves rev 7.8** | **No** | Epoch admission misses lifecycle serving phases; empty bootstrap and warm-baseline authority need implementable rules. |
+| **C — executable model** | **No, as the planning gate** | It must model safe certificate use, all writer admissions, complete close evidence and valid bootstrap states. |
+
+---
+
+**FIRST PROMPT:**
+Joint independent round-8 read-only review of the claim, moves, model and library spike.
+
+**WHAT:**
+Assess prior fixes, library grounding, writer epochs and Phase 1 readiness.
+
+**STATUS:**
+Complete: two P1, three P2 and one P3 findings. Go execution was sandbox-blocked.
+
+**NEEDED:**
+Nothing for this review.
+
+**RECOMMENDED:**
+Fix certificate consumption and lifecycle-wide epoch admission first; then align bootstrap, warm provenance and model evidence.
+
+**SESSION:**
+Safe to close. No files changed; nothing remains running.
\ No newline at end of file