---
name: active-only-semantics-f035
description: "spt send --active-only vs --idle-only delivery semantics (corrected) + F-035 core bug (relay-delivers active-only across the busy→idle drain, waking the agent)"
metadata: 
  node_type: memory
  type: reference
  originSessionId: 57495b6e-3f65-40bd-b9b7-a7177ccb8a8d
---

**`spt send` delivery-channel flags (operator-confirmed 2026-07-09 — I initially had this BACKWARDS):**
- **`--active-only`** = message delivers ONLY via the target's POLL channel (the no-interrupt hook
  channel), NEVER via the relay/translation-binary. Delivers only while the agent is BUSY (polling
  mid-turn); by design it CANNOT wake the agent. This is the correct flag for the shortform tag
  confirm-back (mid-turn fold, no wake) — hook.rs scan_and_dispatch uses `--active-only --ephemeral`.
- **`--idle-only`** = the opposite: delivers ONLY when the agent is IDLE (via the relay).
- **`--ephemeral`** = drop if not deliverable in its window, instead of spooling until delivered.

**`spt send` RESULT tokens (published `spt how-to send`):** `SENT:<t>` = delivered live to a listening
agent; **`QUEUED:<t>` = target is NOT LISTENING at that instant → spooled, drains on its next listen.
"Not listening" = OFFLINE **or** simply BUSY (a working live agent is not draining its listener).**
QUEUED is SUCCESS, do not retry. `NO_PERCH` = no perch exists (genuinely unreachable). Binary
`send_landed` correctly counts SENT/QUEUED/DEFERRED as landed. **v0.18.6 SHIPPED (2026-07-09, commit
bcf7451, tag v0.18.6, GH release live):** the `messaging-perch` brief wrongly said "QUEUED = target
OFFLINE" → agents misread a spooled QUEUED send as a failed/offline one. Reworded to offline-OR-busy.
Strings-only (binary unchanged from v0.18.5); skeleton untouched (its send text already said just
"spooled").

Do NOT infer these from "never wakes an idle target" alone — that help phrasing misled me into thinking
active-only DELIVERS to active targets via relay. It does not: active-only = poll-only, full stop.

**F-035 — ACCEPTED (c) core-contract-violation, VERIFIED by doyle 2026-07-09.** Root (doyle): `send_windowed`
(spt-msg/deliver.rs:188-200, ADR-0028 W2) spool-TAGS the delivery window (`active_only` = spool-only-no-wake);
the busy→idle PARKED-DRAIN surfaces active_only rows (idle-wake) AND fails to DROP ephemeral on a window-miss,
ignoring the row's window tag. Fix scoped **REQ-SEND-WINDOW-DRAIN-HONOR** for a FOLLOWUP core release (bundled
w/ the gateway hotfix); todlando doing RCA-first; **perri = field-verify partner** — doyle pings on publish, I
run the live repro (end-of-turn `@<…@>` confirm-back must NOT wake). Filed repo docs/SPT-CORE-FINDINGS.md
@52f42d3. Below = the original report / mechanism. Core VIOLATES the `--active-only` contract: Repro: a live agent emits a shortform `@<target …@>` as its FINAL turn
output → the Stop hook fires the confirm-back `spt send --from <self> <self> --active-only --ephemeral`
while the perch is still BUSY, then marks idle one line later → the confirm was RELAY-delivered and woke
the agent as a fresh `<EVENT from="self">[tag-send] delivered → …</EVENT>` turn (field, endpoint lia,
operator screenshot). Hypothesis: a message parked while busy is flushed by the idle-edge
IDLE_PARKED_DRAIN VIA THE RELAY, ignoring BOTH --active-only (poll-only) AND --ephemeral (should drop).
Fix is CORE-side (honor the flags across the busy→idle drain).

**UPDATE 2026-07-09 — ROOT PIVOTING to ADAPTER (pending live-repro):** todlando re-RCA proves spt-core
CLEAN on v0.30.2 (all send/deliver/relay/spool legs guard active_only vs relay). New prime suspect =
adapter **`--include-deferred` busy-poll route**: both hook poll sites (UPS hook.rs:978-985, PreToolUse
1147-1153) = `state busy` → `api poll --include-deferred` → render → additionalContext. `--include-deferred`
DRAINS the active_only channel (active_only REPLACED --deferred). Live agent: UPS fires on a relay-injected
EVENT (relay wake = the "prompt") → the --include-deferred poll drags the DEFERRED active_only self-confirm
into that turn. Going-idle itself does NOT poll (handle_stop = scan-outbound + state idle). CAVEAT: adapter
polls are hook RESPONSES, cannot CREATE a turn → explains active_only CONTENT riding a real wake, not a
standalone spurious wake. LIVE-REPRO at operator bounce window pins adapter-root vs residual delivery path.

**FIX SHIPPED — adapter v0.18.7 (2026-07-09, commit aaf212f, tag v0.18.7, GH release live; `spt adapter
update claude-spt` to pick up).** `scan_and_dispatch` now RETURNS the tag confirm (Option<String>) instead of
self-sending `--active-only --ephemeral`: PreToolUse INLINES it as `<sptc_tag_send>` additionalContext (own
active window, never spools); Stop DROPS it (end-of-turn). Peer sends + `--include-deferred` untouched. 252
tests green (+2 handler tests: pretool_inlines_the_tag_send_confirm, stop_drops_the_end_of_turn_tag_confirm).
doyle closed genuine-spurious BY MECHANISM (active_only spools → can't self-wake → always rides a real wake =
pure adapter), cleared me to ship without the bounce. Remaining = belt-and-suspenders live-repro at the bounce
window (NOT gating). Below = the locked-plan record (now executed):

**LOCKED with doyle (2026-07-09) — F-035 is ADAPTER-tracked (my lane), spt-core EXONERATED, NO min_spt_core
floor bump (core behavior unchanged; todlando's regression-guard locks the invariant).** FIX SHAPE (adapter-only):
the defect was the self-confirm ROUND-TRIP (not the drain) — stop round-tripping the tag SELF-confirm via
`spt send --active-only`; instead emit it INLINE as PreToolUse additionalContext on the dispatching hook (rides
its OWN active window, never spooled) and DROP it on the Stop/end-of-turn path. **KEEP `--include-deferred`** —
do NOT touch it (load-bearing for F-021 mid-turn peer delivery). **REPRO GATE before shipping the fix:** at the
operator bounce window, rule out genuine-spurious — instrument whether active_only ALONE ever wakes lia (a wake
EVENT carrying ONLY the active_only, no other relay msg): relay-EVENT stream vs hook-poll drain. active_only-alone-wakes
=> reopens an spt-core delivery bug (tell doyle); only-ever-rides-a-real-wake => pure adapter, implement+ship the
fix. Ping doyle at repro with the genuine-spurious verdict. Findings doc @7176fe4.

**Discipline reaffirmed:** the adapter is CORRECT as-is (already passes the right flags). I prototyped a
Stop-hook idle-before-scan reorder (v0.18.6) then REVERTED it — masking a core bug adapter-side violates
the public-surface-only rule (AGENTS.md §1). Report the finding; do not work around. No v0.18.6 shipped.
