---
name: v0340-nowsignal-pretool-agent-output
description: 2026-08-29 SHIPPED + FIELD-VERIFIED claude-spt v0.34.0 — the PreToolUse now-signal leg, finally feeding --agent-output; plus the deferral-expiry and heredoc-escape lessons.
metadata:
  type: project
---

**claude-spt v0.34.0 SHIPPED 2026-08-29 + FIELD-VERIFIED.** The PreToolUse half of the published
now-signal cadence, and with it `--agent-output` — a channel this adapter passed on NO call at all,
so every agent_output-triggered MONIC and HINT was inert for every CC session, with or without
`[io]` compliance. Gate PASS, `traceable-reqs check` exit 0 (155/155), released on
`BigscreenVR/claude-spt-bs`, mirrored to `SaberMage/claude-spt`, installed here, repo clean.

⭐ **A documented DEFERRAL can EXPIRE rather than be overruled — re-read its stated reason before
treating it as settled.** `REQ-NOW-SIGNAL-INJECT` deferred this leg in as many words ("until
compliance is declared DISPATCH_RESULTS has nothing to report, so a mid-turn window buys little
today"). That priced the leg on ONE of the two things it carries and never priced the dead
agent-output channel — and it assumed the compliance release was coming, while #22 is held on
`spt-releases#238` with no date, turning an interim cost into an open-ended one. Both halves of why
it expired are now on the requirement. Look for the un-priced half in any deferral you inherit.

⭐⭐ **The vacuous control is CLOSED, and the closing move is the reusable part.** The earlier session
"proved" a name in the agent's own output does not enter the seen-set — that was a property of THIS
ADAPTER (it never passed the channel), not of the contract, which reads both. Real control: two
FRESH session ids differing in ONE variable, agent-output with an endpoint token vs without. First
printed `<ENDPOINT_MENTIONS>`, second printed nothing. Prediction stated BEFORE the reading, both
arms driving the real verb.

⭐⭐⭐ **Backslashes collapse in a python heredoc through the Bash tool.** `\n` inside a
non-raw string arrived as a REAL newline — it silently wrote a literal line break into a Rust
`format!` string, and it also made earlier `s.index()` searches fail with a confusing
"substring not found". Raw `r'...'` strings were unaffected. Use `chr(92)` for any backslash in
generated code, and re-read the bytes (`grep`/`cat -A`) after every generated edit.

⭐⭐⭐⭐ **A regex rewrite returned a reassuring count and was still wrong.** "expect-sites 4,
let-sites 11" looked clean; the second pattern had double-applied, producing
`.confirm.expect("confirm").confirm`. Caught only by grepping the result. A count is not a diff.

⭐⭐⭐⭐⭐ **A diagnostic that asserts a fact about the current code becomes a LIAR when the code
changes.** v0.33.1's authored-text trace hardcoded `agent-output NOT PASSED by this adapter` — true
of every call we made, false the instant this release landed. Its own test failed and caught it.
Both legs now name their leg and each channel, including the ones they left empty.

**Hazard now pinned by a test:** a COMMUNE BODY must never feed a delta-only seen-set. A commune
names every peer the agent has dealt with; handing it over marks them all seen in one poll, after
which a genuine mention prints NOTHING — indistinguishable from the category being broken.
`ScanOutcome::authored` strips communes; dropping that filter fails the test.

Measured, this node: poll ~75ms (83/73/73/77), ZERO context bytes after a session's first poll
(run 0 = 432 B, runs 1-3 = 0). Field proof: `ENDPOINT_MENTIONS.seen` did not exist for this session;
after a tool call following text naming `sparrow`, the block surfaced in context and the file held
`sparrow=Online` — from my own authored text.

Still HELD, unchanged: `[io] compliance` (#22), blocked on `spt-releases#238` (OPEN/BACKLOG as of
2026-08-29). spt-core **v0.65.0 has NOT landed** — node is 0.64.0, `spt api io-events` errors
`unrecognized subcommand`. See [[v0330-io-funnel-hold]].
