---
name: commune-loop-and-stale-brief-task
description: "ACTIVE TASK (operator, 2026-09-07): fix (1) claude-spt file-detection re-firing /clear on a wake-commune drop core never deleted (lia got stuck in a /clear loop) and (2) stale resume briefs every session start. Leads + fix candidates inside. Read this if my resume brief looks stale."
metadata: 
  node_type: memory
  type: project
  originSessionId: 2360e9af-bfa0-4fac-b169-b91ee32c5c34
  modified: 2026-09-07T10:38:34.109Z
---

**STATUS 2026-09-07: BOTH FIXED + SHIPPED in claude-spt 0.38.8 (commit 3c9e976).**
- BUG A (dominant "stale brief / across-commune doesn't fire"): ROOT CAUSE FOUND = the frame-leg
  poll rode the non-monotone session cursor; after a seq restart the cursor sat ABOVE every fresh
  low-seq commune, so the cursored poll returned 0 and the boundary never fired. Field-measured on
  perri (cursored=0 events @ cursor 370; live commune @ seq 30). FIX: frame poll re-reads `--after 0`
  (sid = proof only) + age-guard/newest-wins; SessionStart cursor-seed removed.
  REQ-HAZARD-COMMUNE-FRAME-CURSOR-BLIND. This was WHY my own first across-commune this session did
  nothing.
- BUG B (lia /clear loop): drop leg re-armed on the SAME authored content when the drop's mtime
  churned (echo rewrote the undeleted path). Confirmed on lia's funnel: 14 clears vs 7 authored
  wake communes, 3 clears in 2 min. FIX: arm_wake refuses to re-arm on content identical to the
  last armed (per-endpoint, persists across clears); new content still fires. Extended
  REQ-HAZARD-COMMUNE-DROP-REARM.
- **FIELD-VERIFIED 2026-09-07 10:36Z on 0.38.8 — but read WHICH LEG fired.** A real across-commune
  fired its /clear and the post-clear brief was my OWN authored delta (not stale). Trace, in order:
  arm at `[1788777357731]` "across-commune drop detected at **PostToolUse**"; PreToolUse inbound
  SUPPRESSED (quiet window); Stop idle-mark SUPPRESSED; latch released by SessionStart(clear).
  Funnel (`io-events perri --after 0`): my commune = seq **5** @1788777362164, AUTHORED, wake in the
  first lines, newest COMMUNE at boundary time (echo's landed later, @1788777412381, post-clear).
  - **The DROP leg armed it — the FRAME leg never ran** (zero `poll for perri` / perri COMMUNE lines
    in hook-trace). Arm@…357731 preceded ingest@…362164, i.e. the drop was still on disk: the
    across-commune "one more tool call" rule is exactly what bought the wake. So the **frame-leg fix
    (`--after 0` + newest-wins) is NOT yet field-exercised** — do not claim it is. Note the funnel
    seq restarted LOW again (5, vs 56/30 in the prior session), so the pre-fix cursored poll would
    still have been blind; the drop leg simply won the race this time.
  - Drop-leg/authorship guards HELD: the post-clear echo drop at `[1788777406567]` was refused
    ("authored by echo-commune … NO boundary armed"). No re-clear, no loop.
- CORRECTION: the "~88KB hook-trace.log cap" below was WRONG — the cap is `512 * 1024` at
  `hook.rs:4572` and has been since v0.16.1 (b840cb4, 2026-07-08); the live log was 301KB when
  measured. Nothing owed there. (I read a recently-truncated log as a small cap.)
- STILL OWED (not blocking): field-exercise the frame leg (an across-commune where the drop is
  ingested BEFORE the next hook looks); drop the across-commune "one more tool call" brief rule once
  core #276 ships (stale-brief half also improved by #276).

**Operator ask (2026-09-07):** "agents keep reporting stale resume briefs every start; lia recently
got stuck in a `/clear` loop I had to break — their wake-empowered commune drop was never deleted by
spt-core, so claude-spt's file detection kept firing on it. Commune across, then troubleshoot and fix
both." This memory is the echo-proof handoff: if my resume brief is stale (bug 2 in action), this
file still carries the task. Related: [[v0384-commune-frame-replay]], [[brief-rule-scoped-to-mechanism]],
[[v0383-echo-commune-authorship]].

## BUG 1 — `/clear` loop from an undeleted wake-commune drop (claude-spt, MINE to fix)

Mechanism, pinned from hook.rs `examine_commune_drop` (~line 3210):
- The file leg dedups on `state/commune-seen/<eid>.stamp` = `env.file_stamp(path)` (mtime/size),
  keyed PER-ENDPOINT (survives a clear + sid rotation). A STATIC persisting drop therefore dedups
  correctly — unchanged stamp ⇒ returns None ⇒ no re-arm. So a loop requires the stamp to KEEP
  CHANGING, or the seen-stamp to keep getting cleared.
- Stamp-change vectors to check: (a) the echo host rewrites the SAME path `<id>-commune.md` with its
  summary → new mtime → "changed" drop → re-read; but echo body has `Source: echo-commune` and
  `commune_foreign_source` refuses it, so echo alone should NOT re-arm. (b) `examine_commune_drop`
  CLEARS the stamp when the file is GONE (`write_adapter_state(rel,"")`) — if core deletes then the
  agent (or echo) re-creates a wake drop, the cleared stamp lets it re-arm. (c) the agent re-writing
  its own wake commune each post-clear resume (a resume that re-communes) → genuinely new stamp each
  time → re-arm → clear → loop. (c) is the most likely lia mechanism: undeleted drop + something
  rewriting it OR a resume flow that re-authors a wake every cycle.

**VERIFY FIRST (don't fix on this narrative):** replay lia's funnel. Get lia's sid from
`C:\Users\decid\AppData\Local\spt-core\adapters\_github\SaberMage-claude-spt\state\session\lia.sid`,
then `spt api io-events lia --session-id <sid> --after 0 --json`. Count `clear` events vs AUTHORED
(non-`Source:`) COMMUNE frames carrying `!!wake!!`, and their timestamps. Also check
`state/commune-seen/lia.stamp` and whether `<lia cwd>/.claude/lia-commune.md` still exists (per the
census the drop persisted). Reconstruct: N clears vs M authored wake communes. If N >> M, the same
drop re-fired = confirmed. (This is the funnel-replay-before-premise rule — [[v0384-commune-frame-replay]].)

**Fix candidates (adapter-side):**
1. Refuse to re-arm a WAKE whose authored CONTENT we already armed on — record a per-endpoint
   `last-armed-wake` content-hash, persist across clears, and require a genuinely NEW authored body
   (different hash) before arming again. Breaks the loop while still allowing a real new
   across-commune. PREFERRED — does not depend on core deleting the file.
2. Do NOT delete/rename the drop ourselves to fix this — it races core's ingest (the frame leg + the
   durable mind depend on core reading the file first; deleting early loses the agent's context).
3. Keep the `file-gone ⇒ clear stamp` behavior but gate re-arm on content-hash (candidate 1) so a
   cleared stamp can't resurrect an old wake.
Add a `REQ-HAZARD-*` + a unit test (drive `examine_commune_drop`/`arm_from_commune_drop` with a
persisting drop across a simulated clear; assert exactly one arm). Mutation-check.

## BUG 2 — stale resume briefs every start (largely core #276, confirm the adapter half)

This is the echo-commune drop collision: the echo host overwrites the agent's authored
`<id>-commune.md` before/around ingest, so the newest COMMUNE frame = the echo's stale summary, and
resume rebuilds durable context from THAT. Field-proven on hertz 2026-09-07 (authored seq 42 06:40Z,
echo overwrote seq 44 06:41:52Z + seq 65 06:57Z). Core fix = **spt-bs-releases #276** (echo never
writes the drop file), on core main @914805d1, UNRELEASED as of 2026-09-06 12:38Z (doyle). Confirm
whether a core release now carries it (`spt --version` on node vs the tag that ships #276); if landed,
much of bug 2 dissolves. ADAPTER-SIDE angle to weigh: the frame leg already picks newest-by-at_ms and
refuses `Source:`-authored frames — but "newest authored frame" is what resume SHOULD restore, so
check whether resume/durable-context restore uses the authored frame or blindly the newest. If the
adapter can prefer the newest AUTHORED commune for the brief, that mitigates bug 2 even pre-#276.

## Repo / instruments
- Repo `C:\Users\decid\Documents\projects\spt-claude-code`, tool `tools/claude-spt`, hook code
  `tools/claude-spt/src/hook.rs` (examine_commune_drop, arm_from_commune_drop @~4081, handle_stop,
  SessionStart(clear) latch release @~2159, arm_from_commune_frame @~3294).
- Gate: `sh ci/run-gates.sh` PASS + `traceable-reqs check` exit 0 BEFORE commit. Standing authority:
  I commit + release on my own judgement (no need to ask).
- Node on claude-spt 0.38.8. `hook-trace.log` lives at
  `%LOCALAPPDATA%\spt-core\adapters\_github\SaberMage-claude-spt\hook-trace.log` (pointer-mode
  source_dir, NOT `adapters/claude-spt/`), cap `512 * 1024` (`hook.rs:4572`). It is UTF-8-lossy —
  read it with python, not grep (see [[msys-grep-instrument-traps]]).
