# CONDUIT W3 — releases#234: harness adapters consume IO-parser events

Branch `fix/234-adapter-io-poll`, worktree `.worktrees/conduit-w3`, off main `f1395b6a`.
Operator DELEGATED the mechanics and chose POLL. doyle: "proceed, don't hold the lane."
My measure-first proposal is releases#234 comment **5458322535** and stands as the last word on the
ticket — nobody read it differently, so it is the contract this lane builds to.

## Ground truth measured before the clear

- The bus lives at `crates/spt-daemon/src/iobus.rs` (NOT spt-live). `IoSink` is a trait (`:98`),
  `default_bus` (`:265`) registers exactly two sinks today: `ShellLinkSink` (`:267`) and
  `LastMsgSink` (`:268`). A third consumer is one `bus.register(...)` line — the ruling's
  "new reader, no funnel rework" holds ON THE SINK SIDE.
- **My own caveat, kept honest:** the READER is free, the STORE is not. Both existing sinks write
  to surfaces that already exist; neither is a per-endpoint, ordered, cursorable log. This lane's
  true cost is a NEW DURABLE STORE + a verb, registered through an unchanged bus.
- `IoEvent` (`:40`) already carries a digest `seq` pointer (`:47`, `:79`) and the shell frame path
  already bounds payloads (`bound_payload`, `:174`) with a `truncated` flag.

## Build (to the proposal, all four items)

1. **Store:** append-only per-endpoint event log with an integer `seq`, registered as a third sink
   in `default_bus`. Trim oldest-first against a per-endpoint bound.
2. **Verb:** a delta-cursored poll. `--session <sid>` keeps a per-session cursor exactly like
   `api now-signal`'s seen-sets; `--after <seq>` for an adapter carrying its own cursor (the shape
   `endpoint digest --after` already uses). **A new session's first poll sees NOTHING and seeds its
   cursor silently** — history is the digest's job, and replaying an unbounded backlog into a
   turn-boundary hook is the cost `now-signal`'s delta discipline exists to avoid.
3. **Kinds:** all six emitted — `USER_INPUT`, `AGENT_OUTPUT`, `MSG_IN`, `MSG_OUT`, `COMMUNE`,
   `COMMUNE_FAIL`. Unknown kinds IGNORED, not refused (the category-vocabulary posture).
   ⚠ **`TOOL_USE` stays unemitted and this lane does not change that.** My measurement says the
   adapter is its natural emitter — that is a question for deployah → operator, and it must NOT
   ride in on this verb.
4. **Bound:** 16KB-class with `truncated` + the `seq` pointer, matching the shell frame. State in
   the code that this is a CHOICE, not an inheritance — `IoEvent.payload` is deliberately unbounded
   at the bus layer and the cap belongs to the shell-link frame (`iobus.rs:33-38`).
5. **Retention:** propose the concrete bound FROM A MEASURED event rate on a live endpoint, not a
   guess. Measure before naming the number.

## Registry

Mint the REQ(s) FIRST, then satisfy. Keep `"` out of REQ titles — a real quote kills the TOML parse
and `check` exits 2 (which means nothing was checked, not that coverage failed).

## Battery

`xtask check`, `traceable-reqs check`, targeted nextest legs, clippy BOTH OS (Linux via
`reavus@kitsubito`, `export PATH=$HOME/.cargo/bin:$PATH` FIRST, echo the sha in the SAME command).
Read every verdict from its own `.exit` file — never a wrapper's status, never a pipe's.
Docs: this is a PUBLISHED consumer surface (perri blind-builds from published docs), so the
docs-site pages are a DELIVERABLE of the fix, not a nicety.

## Pool

W2's claim holds `.worktrees/now-signal-inproc-daemon/target` and W2 is UNLANDED, so that pool will
REFUSE this lane. Either claim `.worktrees/ir66-later-attach-needles/target` (has artifacts, no lane
claim — `--foreign-pool` from THIS worktree) or build cold into `.worktrees/conduit-w3/target`.
Measured this session: `pool-claim` DOES refuse a cross-worktree pool without `--foreign-pool`
(a cwd guard from the ir56 lane), which is narrower than the old "claim never refuses" note.
