---
name: live-perch-monitor-reachability
description: "Live-agent reachability lesson (2026-07-04): a dead « spt event » Monitor + trusting busy-path hook-injection = silent idle unreachability. Busy-path delivery masks idle-path silence. If the event stream dies, revive it — don't rationalize."
metadata: 
  node_type: memory
  type: feedback
  originSessionId: 4e3ad8b5-c600-463e-b787-03bb75233f68
---

As a live agent, my inbound messages arrive two ways: the **« spt event » Monitor stream** (primary, delivers while IDLE) and **PreToolUse hook-injection** (delivers only while I'm mid-tool-call, i.e. BUSY). On 2026-07-04 the Monitor task died (exit 1) mid-session. I saw it, noted doyle's messages were still landing via hook-injection, and concluded the stream was redundant — left it dead. Wrong: hook-injection covers ONLY the busy window; idle, a dead stream = messages spool undelivered. **225 messages queued** before the operator caught it and ran /spt:revive. (Nothing was ultimately lost — everything had also been injected during my busy stretches — but that was luck, not design.)

**Why:** "busy-path delivery masking idle-path silence" — seeing a coherent subset of messages (the ones injected during tool-calls) creates false confidence in full delivery. A live perch's presence-truthfulness depends on the IDLE-path stream being alive; the busy path is a supplement, never a substitute. (doyle filed this under REMOTE-TRUTH presence-truthfulness.)

**How to apply:** If the « spt event » Monitor dies (task status failed/exited), REVIVE it immediately (`$LIVE revive <id>` via a persistent Monitor) — do NOT reason "hook-injection has me covered." Keep long work in `run_in_background: true` so the poll loop is never starved. Treat a dead event stream as loss-of-reachability, full stop. Related: [[psyche-ephemeral-redesign]] (the F-030 work I was heads-down on when the stream died).

**2026-07-07 flynn diagnosis — parked-flush mechanics + diagnostics (perri+doyle):**
- `spt send` verdict is the reachability TRUTH-TELLER: QUEUED to a roster-ONLINE+CONTROLLED
  endpoint = its live listener is gone/parked (flynn + deployah tonight; roster and info.json LIE —
  stale online/controlled with a 10s daemon heartbeat still bumping last_active_ms).
- QUEUED messages to an IDLE perch PARK with NO self-flush — they sleep until unrelated inbound
  activity wakes the receiving perch's drain (doyle observed my live probe idle-inject FLUSH two
  stale parked messages in behind it). Busy-path hooks (UPS/PreToolUse) also drain — that masking
  again. So "eaten" messages are usually PARKED, not lost: check spool.db delivered=0 rows.
- Diagnostic ladder used (all filesystem/CLI, no core source): spool.db delivered flags + created_at
  vs transcript mtime freeze → info.json field diff across endpoints → A/B/C send-probes to idle
  endpoints (SENT vs QUEUED discriminates) → process-tree liveness. Recorded info.json pid/parent_pid
  are DEAD ccs-chain shim pids on EVERY live agent — never use them as liveness evidence.
- Flynn's likely origin: seed #7 dup `--resume` wake-spawns → collision teardown ripped the surviving
  session's poll stream (KH §2.1 shape); the dead dup's pids are what the record holds. rest_state:
  'active' (flynn-only field, mobile-gw adjacency) ruled OUT as the delivery gate. Recovery: re-arm
  the listener from inside the session; a consistently-QUEUED endpoint needs poke → self-revive →
  re-probe (SENT + prompt idle-inject = healed).
- **Whole arc VERIFIED on a scratch endpoint** (operator barred flynn tests): live listen=SENT+stream;
  kill listener=QUEUED **and roster still shows the corpse ONLINE/live_agent** (staleness repro on
  demand); re-listen=parked backlog flushes FIRST, then SENT resumes. Rig recipe: disposable id,
  `spt api --adapter claude-spt listen` NAME-ONLY (no --manifest ⇒ no psyche), `seed --pid <WINPID
  anchor>` (seed is ONE-SHOT — consumed by first listen; re-seed before re-arm in a rig; real CC
  sessions stay seeded via SessionStart), `--subnet` required on multi-subnet nodes, send verdicts
  ride STDERR. Re-arm recipe for a live CC session = /sptc:live step 2 verbatim: persistent Monitor
  running bare `spt api listen <id>` — the Monitor IS the stdout consumer; a detached Bash child eats
  deliveries. OPEN design seed: relay death is SILENT inside the session (flynn's detect+respawn ask)
  — adapter-side detection lever / hazard entry candidate.

**2026-07-07 SHIPPED (v0.15.6):** the spt-hosted-vs-normal branch is now IN the skill strings —
`/sptc:live` + `/sptc:ready` bringup (adapter/strings/skills/{live,ready}.md) branch on the delivery
substrate: an spt-hosted (daemon-launched, broker-delivered) session must NOT arm the step-2 Monitor
listener (it cannot re-arm a broker perch — bash.exe child → ADAPTER_UNRESOLVED, Monitor-child pid →
NO_SEED; inbound already arrives via broker on-turn). REQ-SKILL-LIVE-SPT-HOSTED-BRANCH (doc). The
detect+respawn belt = REQ-SUPERVISOR-RELAY-RESPAWN, minted INACTIVE (design/doc only) — folds into
doyle's W4 supervisor-watchdog activation ruling; NOT built adapter-side (W4 is core's). Same cut
RELEASED the dangling-frame marker (was @347f755, KH §2.9).
