# Round 4 (merge-deciding) blind read: `endpoint digest --json` @ `745a023`

**Reader:** perri · read as an assembled section, not delta-only — the defect pattern in rounds 1-3
was never a wrong fact, it was two true facts that contradict when composed, so composition is what
I hunted.

## Verdict: NOT clean. Four findings; three are the same composition shape.

The five changes from my last round all landed correctly, and the generalized step 2 is a genuine
improvement over what I asked for. The findings below are all *new surface* created by the additions.

---

### 1. "Treat them as provisional until they commit" is unsatisfiable for injected entries

> *"Entries with no `seq` (open-turn, and all injected entries) have no durable id by design — treat
> them as provisional until they commit."*

Composed with the asymmetry note — *"`Boundary`/`Context` … never carry `seq` at all"* — the
instruction has no terminating condition for half the set it addresses. Open-turn entries commit and
take seqs; **injected entries never commit, ever.** A snapshot poller following this sentence holds
every `Boundary`/`Context` provisional forever, which in practice means one of two silent failures:
re-processing the same `echo_mirror`/`clear` on every pull, or dropping injected entries entirely.

This matters more than it reads: injected-only change is the exact class step 2 now (correctly)
warns the pair is blind to. So the page tells a snapshot consumer that injected entries are the
dangerous invisible class, then leaves them the only class with no durable identity and no usable
de-dupe rule. **Suggest:** say what injected entries key on instead — `(turn's input_seq, ts, kind)`
if that is stable — or state plainly that injected entries cannot be durably de-duplicated in
snapshot mode and that follow is the supported reading mode when they matter.

### 2. `input_seq` is prescribed as the durable turn id, but the schema says it is optional

The `Turn` table: `input_seq` — *"Omitted when absent."* The snapshot de-dupe rule: *"`input_seq`
identifies a turn."* Both true; together they leave a consumer with no turn identity exactly when
`input_seq` is absent, and no stated fallback. The table also documents `input: null` for a
**preamble turn**, which reads like precisely the case where there is no input record to carry a
seq — so the gap is not hypothetical, it is the first turn of many windows. **Suggest:** one clause
saying when `input_seq` is absent and what to key on then (window position + `partial`? the first
contained `seq`?), or state that turns lacking `input_seq` cannot be tracked across pulls.

### 3. The pair must not be composed across reading modes — and the page's own architecture is a hybrid

Three true statements: step 1 requires the pair be computed at a **constant window depth**; `--last`
is **ignored under `--follow`**, which always uses the **default** depth; and the default depth is
**adapter-overridable and unqueryable**, with the guidance *"do not hard-code it: pass `--last <N>`
when the depth matters."*

Compose them for the consumer the page itself recommends — follow stream **plus** the mandatory slow
unconditional snapshot pull. If the snapshot leg passes `--last N` (as instructed) while the follow
leg is stuck at the unqueryable default, the two legs compute `max seq` over **different windows**.
Any comparison across them manufactures the false "unchanged" step 1 exists to prevent. The follow
delta even carries `version`, described as *"the same counter the snapshot carries"*, which actively
invites the cross-mode comparison. **Suggest:** state that the pair is only ever compared within one
reading mode, and that a hybrid consumer keeps a separate comparator per leg.

### 4. "delivers an empty (or last-known) base" undercuts itself (minor)

The torn-down-endpoint paragraph opens with *"projects an empty digest rather than failing"* and then
says follow *"delivers an empty (or last-known) base."* Those are materially different for a
consumer: an empty base is unambiguous, a last-known base means **a dead endpoint can serve real
content** — including, for my component, an interrupt marker from before teardown. The paragraph's
conclusion (silence is not evidence of liveness either way) survives either reading, but a consumer
deciding whether content implies liveness gets opposite answers from the same sentence. **Suggest:**
pick one, or name the condition that selects between them.

---

## What I would build (unchanged in shape, one addition)

Follow-primary with a backoff resubscribe loop; positional dedupe within a stream; durable-id dedupe
on the snapshot leg; slow unconditional snapshot pull as the correctness belt **only**; endpoint
liveness established out-of-band via `endpoint list` / `api endpoint-info` — the page now says this
explicitly, which retires the flawed double-duty design I proposed in round 2. **Addition from
finding 3:** a separate `(version, max seq)` comparator per reading leg, never shared.

## What I would still infer

- The effective window depth of a follow subscription (the one number I cannot pass and cannot query).
  Finding 3 is the consequence; the unqueryability itself is already a filed seed, so I am not
  re-reporting it — only noting that follow is where it bites hardest, since `--last` is unavailable.
- Whether `input_seq` is absent specifically on preamble/partial turns (finding 2).
- Whether a `ts` on an injected entry is stable across pulls — it is the only candidate key left if
  finding 1 is resolved by keying rather than by declaration.

## Note on the clean-read question

I was asked to say plainly if nothing landed. Something landed, so I will not call this clean. But
the trend is real and worth recording: rounds 1-3 found defects in the *substance* of the contract
(shapes, lifetimes, silent-skip holes); round 4 found none of those — all four findings are in the
*seams between* newly-added guidance, and none would corrupt a consumer's data model the way the
version-collision or provenance defects would have. Findings 1 and 3 are still worth fixing before
merge: both cause a consumer to build something that works in testing and fails on a rare
composition, which is the failure mode this whole exercise exists to catch.

---

## Field evidence for finding 3 — the perturbation is OBSERVED, not theoretical (2026-07-20)

todlando composed finding 3 with `digesthub.rs`: the hub is keyed by endpoint id ALONE, every pull
publishes ITS OWN window depth into that shared state, bumps version, and fans a `from == 0` full
replace to every follower. I tested it on this node rather than reasoning about it.

**Our own watcher is the bystander.** `interrupt_watch::spawn(eid)` runs one thread **per hosted live
endpoint**, polling `spt endpoint digest <id> --last 12 --json` every **15s**, continuously, for the
life of every live session (`POLL_SECS = 15`, `WINDOW = 12`, spawned from `translate.rs:671`).

**Observed** — a passive `--follow --json` on a quiet endpoint, ~50s, no action by me:

```
frame 1: version=330 from=0 turns=6
frame 2: version=331 from=0 turns=12     <-- depth CHANGED under the follower
frame 3: version=332 from=11 turns=1
```

Frame 2 is a `from == 0` full replace in which the **window depth itself jumped 6 → 12**. A content
change slides a window; it cannot resize one. And **12 is claude-spt's `WINDOW`** — the depth
fingerprints our own interrupt watcher's pull. This is todlando's predicted mechanism, observed live.

*Alternative I cannot exclude from the outside:* frame 1 was a depth-12 window with only 6 turns of
history available and 6 real turns then arrived within seconds. On a quiet endpoint that is
implausible, but it is the honest competing reading, and it is decidable from inside `digesthub.rs`.

**Why this makes it a product fix, not a consumer-discipline fix:** our depth of 12 is not a
preference, it is the **v0.25.1 bug fix** — a `--last 1` window missed interrupt markers buried under
messages parked between the Esc and the heal tick. "Use the default depth" would re-introduce a
shipped, field-proven black-hole bug. So a legitimate consumer *needs* a non-default depth and cannot
avoid perturbing every other consumer of that endpoint. No amount of reader discipline closes it —
the hub key has to carry the depth (or each subscription its own window).

**Second-order, ours:** the planned `--follow` migration would have been perturbed by *our own*
watcher on the same node — two claude-spt components fighting through core's shared state, which
would have looked like a bug in the new code.
