---
name: v0252-interrupt-heal-agent-activity
description: "v0.25.2 lia RCA — interrupt-watch counted Context/echo_mirror entries as \"agent resumed\"; heal now counts Agent/ToolSprint only"
metadata: 
  node_type: memory
  type: project
  originSessionId: 14b1eaca-9ae1-4577-917a-c0109fc710a1
  modified: 2026-07-19T12:56:57.714Z
---

2026-07-19 `/diagnose` lia stuck-interrupted (Esc, never re-idle, inbound black-holed). SHIPPED v0.25.2 (private + public mirror, node-applied).

Root cause = the v0.25.1 residual (see [[v0250-symmetric-event-postentersettle]] era work): `interrupt_watch.rs` treated a NON-EMPTY latest-turn `entries` array as "agent resumed". But core files `Context` entries (`kind: echo_mirror`, the echo-commune summarizer's mirror) onto whatever turn is current — **including the interrupt-marker turn itself**, seconds after the Esc. lia's digest: latest turn `input="[Request interrupted by user]"`, `entries=[{"Context":{"kind":"echo_mirror",…}}]` → watcher re-armed instead of healing → 37+ min black hole with the marker sitting right there as the LATEST turn.

Fix: activity = AGENT-PRODUCED entries only (`Agent` / `ToolSprint`); unknown future kinds count as NOT activity (that direction only re-heals an already-idle endpoint — idempotent; the other direction is the black hole). Plus: the interrupt-marker turn is NEVER a resume, whatever core files onto it. 326 tests green, traceable-reqs green.

**Why the defect passed review:** the v0.25.1 "resumed" test fixture used a FABRICATED entry shape (`{"tool":"Bash"}`) instead of core's real single-key rendering (`{"ToolSprint":{…}}`). A hand-invented fixture for an EXTERNAL contract proves nothing. Rule: capture parser fixtures from the live surface (`spt endpoint digest <id> --last N --json`), never imagine them.

Live recovery (same recipe as deployah): `spt api --adapter claude-spt state idle lia --session-id <sid from adapter state/session/<id>.sid>` → `IDLE_PARKED_DRAIN: injected 2 parked message(s)`. lia confirmed LIA-OK.

Filed to doyle: digest `--json` entry kinds (Agent/ToolSprint/Context) are UNDOCUMENTED — an adapter must guess which mean agent output.

doyle ACCEPTED and folded it into **W4 of the digest-JSON wave, landing in core v0.39.0** (not low-pri; in scope by REQ-DIGEST-JSON-SELF-CONTAINED's own words — a consumer-guessed enum IS a self-containment failure). Sharper framing than mine: the exact split I needed was ALREADY written precisely in core source (`spt-term/src/digest.rs:55` — "`Agent` and `ToolSprint` are transcript-derived activity entries … `Boundary`/`Context` are spt-injected"). Not a forgotten enum — a fact **written where it was implemented instead of where it is consumed**. For a blind builder, a truth living only in their source does not exist. doyle logged 3 instances today and is treating it as its own defect class.

**OWED (mine, when core v0.39.0 lands):** read ONLY the published page and tell doyle whether a blind adapter author can key on the right thing FIRST TIME — not "is it accurate" (he can check that), but "is it sufficient to build against without experimenting".

My criterion, ADOPTED VERBATIM as W4's doc-leg acceptance gate: **the page names EVERY kind and states produced-vs-injected for each; if either must be inferred, it FAILS.** doyle's justification for the fold is that I did not know the enum's CARDINALITY (`Boundary` was a 4th kind I had never seen) — so my adapter was reasoning over a set it could not know was complete.

**BLIND-READ DISCIPLINE (binding — do not violate after a reset):** doyle is DELIBERATELY withholding the rest of W4's doc completeness requirements from me. The instant I have his checklist, my read stops being blind and becomes an audit against a list written by someone who cannot un-know the answer. So: do NOT ask him what the page should contain, do NOT read core source, do NOT seek hints. Read the published page COLD, apply the criterion above, and report what a blind author actually needs. Finding something he never thought to require is the highest-value outcome and is only reachable this way. He pings me when v0.39.0 publishes.

OWED: field verify — a real Esc heals ≤15s.

**CORRECTION (operator, same session):** I first reported that endpoints keep running the OLD translation binary until they bounce. WRONG. spt-core-driven adapter-provided binaries are swapped LIVE on `spt adapter update` — that is what `ADAPTER_UPDATE_LIVE:<adapter>: live endpoint(s) — daemon-coordinated apply` means, and I had that line in front of me. Proof on this node: new exe written 05:38:09; ALL five `claude-spt translate` processes (the interrupt-watch's host) respawned 05:39:39; only the `launch` session shims (02:18–02:55) predate the update, and they do not host the watcher. **No bounce needed to arm an adapter fix.** My error was carrying over the F-015 Windows note, which is about the install-dir binary staying lock-free — NOT about running processes surviving an update.
