# now-signal PreToolUse leg + the agent-output channel — JIT plan

Ship the **second half of the published now-signal cadence**: the PreToolUse leg, and with it the
`--agent-output` channel this adapter has never fed. Independent of claude-spt-bs#22 — the
compliance flip stays HELD on `spt-releases#238` and nothing here touches `tag_scan`'s grammar.

Grounded on the node-local book (`http://localhost:5474/harness-contract/api`, read this session):
*"Inject it on every UserPromptSubmit- and PreToolUse-equivalent; that cadence is what it is built
for"*, and `api now-signal <id> --session <sid> [--user-input <text>] [--agent-output <text>]`.

## Why now — the deferral's stated reason no longer holds

`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 weighed the
leg on DISPATCH_RESULTS alone. The other half of what the leg carries is a capability that is dead
**today**, at every session, with or without compliance: **agent_output-triggered MONICS and HINTS
cannot fire at all**, because PreToolUse is the only injection point where the agent has authored
text for the current turn. `REQ-NOW-SIGNAL-AUTHORED-TEXT-TRACE` already records this as the
deferral's real cost. Since #22 is blocked on #238 with no date, that cost is now open-ended.

## Measured before deciding (this node, 2026-08-29)

- **Cost: ~75 ms per poll** (4 runs: 83/73/73/77 ms), one process spawn. PreToolUse already spawns
  `state busy` + `poll`, so this is a third child on a hook that runs two.
- **Context cost after the first poll: ZERO BYTES.** Delta-only is not a claim read off a page —
  run 0 printed 432 B, runs 1-3 printed nothing at all. A tool-heavy turn pays latency, not context.
- **The channel works and we are the ones not feeding it.** Two fresh sids, one variable changed:
  agent-output `"I will ask doyle about the frames contract"` printed
  `<ENDPOINT_MENTIONS>doyle — online on LOCAL</ENDPOINT_MENTIONS>`; agent-output with no endpoint
  token printed no such block. Prediction stated before the reading; both arms drove the real verb.
  This is also the control the earlier session could not take — the old test proved only that THIS
  ADAPTER never passes the channel, which is a property of us, not of the contract.

## Hazards this release must not walk into

1. **A commune body must never ride `--agent-output`.** A commune is a whole context dump full of
   endpoint names. Handing it over would mark every one of them SEEN in one poll, and an
   already-seen mention is SILENT — which reads exactly like the feature failing (this is the
   documented shape of the lia anomaly, whatever caused that instance). `plan_dispatch` already
   separates commune texts from ordinary output; agent-output takes the ordinary texts only.
2. **The v0.33.1 trace line hardcodes a claim that this release makes FALSE.** It logs
   `agent-output NOT PASSED by this adapter`. The moment we pass it, that record lies about the
   input it exists to explain. Both legs must name what they actually passed.
3. **One read, one cursor.** The authored text comes from the batch `scan_and_dispatch` already
   consumed at its byte cursor — never a second independent tail read, which would double-count
   text across polls and could disagree with the dispatch leg about what "new" means.
4. **Subagents stay out.** `handle_pre_tool_use` already returns before any of this for a subagent
   (`REQ-HAZARD-SUBAGENT-DRAIN-STEAL`); a subagent's authored text must not enter the parent's poll.
5. **The cap is the argv cap, unchanged.** `now_signal_arg` (4000 chars, char boundary). Safe here
   for the same reason as `--user-input`: this text is SCANNED, so a cut costs at most a tail match.
   It is still NEVER safe for the io state payload.

## Tasks

- **T1 — one read feeds both.** `scan_and_dispatch` returns the ordinary (non-commune) authored
  texts alongside its confirm, so the poll and the dispatch agree on what is new by construction.
- **T2 — the PreToolUse leg.** `api now-signal <id> --session <sid> --spec-manifest
  [--agent-output <text>]`, placed AFTER the poll and park drain (the UPS ordering: a signal may
  never delay a message) and before emit. Skipped under the across-clear quiet latch, like the poll
  and the drain — the session is about to be rebuilt and a fresh sid is entitled to the picture
  once. Emits nothing when it prints nothing.
- **T3 — trace honesty.** Both now-signal call sites record the authored text they passed, per
  channel, under `$SPTC_HOOK_TRACE`. The UPS line stops asserting the adapter passes no
  agent-output; the PreToolUse line names its own leg.
- **T4 — requirements.** Mint `REQ-NOW-SIGNAL-PRETOOL-AGENT-OUTPUT` (registry FIRST). Amend
  `REQ-NOW-SIGNAL-INJECT` (deferral discharged, with why the reason expired) and
  `REQ-NOW-SIGNAL-AUTHORED-TEXT-TRACE` (its not-passed clause).
- **T5 — release v0.34.0.** CHANGELOG in product terms. Gate, three triples FRESH, package, tag,
  release, mirror. No cplugs skeleton republish — no skill or brief text changes.

## Gate

`sh ci/run-gates.sh` PASS re-run after the last file lands, and `traceable-reqs check` exit 0 —
both BEFORE the commit. Read the gate's real exit status, never a pipe's.
