---
name: v0384-commune-frame-replay
description: "2026-09-06 v0.38.4/0.38.5 SHIPPED + FIELD-VERIFIED (clean frame-leg test 09:07Z) — the io funnel's seq is NOT unique (restarts, old blocks stay), so a low cursor replayed week-old COMMUNE frames into the arming leg and fired unrequested /clear; fixed with a session-start age guard. Core half filed (iolog seq reset), not mine."
metadata: 
  node_type: memory
  type: project
  originSessionId: b634e01e-b25d-4474-9b82-bedb17444913
  modified: 2026-09-06T08:45:38.664Z
---

**v0.38.4 (2026-09-06) — replay guard.** The frame leg that arms an across-commune boundary
(shipped v0.37.0, [[v0370-commune-drop-detect]]) reads COMMUNE frames off the io funnel with a
cursor. The funnel's `seq` is NOT unique: it restarts and the old blocks stay in the log, so a
cursor below the restart replays ancient communes — including ones carrying a live `!!wake!!`
marker — into the arming leg. Measured on my own funnel: 443 events / 370 distinct seqs / max
dup 3 / spanning a week / 11 COMMUNE frames with live markers.

**Fix (adapter half, shipped):** SessionStart stamps `state/session/<eid>.started`; the frame
leg refuses any COMMUNE frame older than that stamp (15-min ceiling when no stamp exists),
ordered AHEAD of the authorship ([[v0383-echo-commune-authorship]]) and marker checks.
`REQ-HAZARD-COMMUNE-FRAME-REPLAY`, mutation-checked. Also added the observability that found
it: the frame poll logs its window + COMMUNE seqs; every arm logs seq, at_ms, which marker
matched, its byte offset with context, and a payload head. Tracked as claude-spt-bs#30 (set
done 2026-09-06).

**v0.38.5 (2026-09-06, same day) — the SECOND face: stale wakes.** Operator reported doyle's
new sessions opening on "wake message was stale — v0.66.0 era, already shipped". Doyle's funnel
replay showed SEVEN clears that day and ZERO authored marker communes: none of them was a commune
he wrote. The loop armed on the FIRST marker frame in log order (a replayed block lists ahead of
the live tail) and `return`ed — so even a genuine across-commune would have parked the replayed
frame's directive. Fix: newest eligible frame (max at_ms) wins; a replayed frame is older than a
fresh commune by construction. REQ-COMMUNE-FRAME-NEWEST, two window-order tests, mutation-checked.
⭐ One defect, two faces reported by two people as two bugs ("unrequested clear" vs "stale wake")
— before treating a report as new, replay the funnel and check whether the premise ("he communed
across") is even true. It was not.

**FIELD-VERIFIED 2026-09-06 09:07Z (frame leg, clean).** A detached writer dropped the commune
3s after its tool returned, so no hook saw the file; the Stop hook armed off the ingested frame
(seq 102, at_ms 09:05:24Z) 111s later. 21 replayed frames (seq 120..343) refused by the age
guard; seq 102 was listed LAST with the LOWEST seq (numbering restarted) and won as newest by
at_ms — the 0.38.5 rule doing real work. Echo landed as its own frame (seq 117), no overwrite.
Trap from attempt 1: PostToolUse OF the Write sees the drop 888ms later (ingest 2.8-5.8s) — the
drop leg fires first unless the writer is detached. Record: docs/plans/ECHO-COMMUNE-DROP-COLLISION.md.

**Core half — filed, NOT mine, do not re-derive:** doyle confirmed in source that spt-store's
`iolog.rs` `last_seq_at` seeks to len-256KB and `read_to_string` fails when that offset lands
inside a multi-byte UTF-8 char, returning 0, so append mints seq 1. Only logs >256KB; 9/9
resets showed a continuation byte at that window start. Filed on releases; lane
`IOLOG-SEQ-RESET-JIT.md`. Repair shape: renumber damaged logs once above the global max. My
age guard composes with that repair and does not depend on it landing.

⭐ **Why:** a cursor is only a position if the numbering is monotone. When a poll answers
"nothing", check whether the numbering RESTARTED before believing the silence — and when it
answers "something", check whether that something is older than the session reading it.

⭐ **How to apply:** any consumer keyed on a sequence number from a log I do not own gets an
age/epoch guard alongside the cursor, and the poll logs its window so a replay is visible in
the trace rather than inferred from the symptom. The instrument that settles a funnel
question is `spt api io-events <id> --session-id <sid> --after <seq>` — non-destructive,
writes no cursor — read the payload the predicate actually ran on. See
[[wrong-direction-refutation]] for how I nearly talked a peer out of this hazard.
