# JIT plan — Phase 2: IO funnel + now-signal + core shortform (→ omp-spt 0.4.0)

> Parity pass v2 phase 2 (`PARITY-V2-PLAN.md`), operator-approved 2026-09-06. Sister source:
> claude-spt 0.32.0 / 0.33.0 / 0.34.0 / 0.35.0 / 0.36.0. Contract source (public only):
> harness-contract `api.md` §Activity and presence + §Situational awareness, `manifest.md` §`[io]`
> + §`[io.now_signal]`, `shells/frames.md` §io + §Shortform + §now-signal, integration checklist.
> All verified present on installed spt 0.67.0 (`spt api state|now-signal|io-events --help`).

## Design (grounded in the published contract)

### A. IO feed — the turn's text rides `api state` (REQ-IO-TURN-FEED)
- `before_agent_start`: `api state busy <id> --payload-stdin` ← the user's prompt. **Exclude**
  delivery-stub turns (`<msg from=…/>` woke it): a peer's message is never reported as USER_INPUT.
- `message_end` (assistant): keep the newest assistant message pending; report the *previous*
  pending one as a mid-turn span: `api state busy <id> --payload-stdin --mid`.
- `completeTurn`: `api state idle <id> --payload-stdin` ← the still-pending final message (the
  turn's close). Nothing pending ⇒ `idle` with no payload. **Every span exactly once** — track by
  message identity after the turn baseline; core never dedups.
- A payload-carrying call is an event: it bypasses `setState`'s same-state short-circuit.
  Report failure is logged, never fails the turn (emission is observation, not control).
- Payload via stdin only (`runCommand(args, input)` already pipes stdin). 16 KB frame cap is core's.

### B. now-signal at every boundary (REQ-NOW-SIGNAL-INJECT)
- Turn start (`before_agent_start`): `api now-signal <id> --session <sid> --user-input <prompt>
  --spec-manifest` → append stdout (if any) to the systemPrompt additions, after the startup brief.
- Every in-turn `context` boundary (the PreToolUse-equivalent — one per provider request):
  `api now-signal … --agent-output <assistant text since last poll>`; non-empty stdout is appended
  as a `custom` message (`customType: "spt-now-signal"`, `display: false`, `attribution: "user"`)
  exactly like the `api poll` output already is. Delta-only ⇒ quiet boundaries add nothing.
- Inline `--user-input/--agent-output` args: cap each at 8 KB (Windows command-length), core only
  scans them for names/monics.
- `promptHints()` retired → `[[hints]]` in the manifest (same five hints, keywords/regex) surfaced by
  the HINTS category. `updateNotices` retired → UPDATES category. Startup brief stays (identity is
  not a category). REQ-PARITY-TARGETED-HINTS / REQ-PARITY-UPDATE-NOTICE re-pointed to this path.
- `[io.now_signal]`: default picture (OMP renders every category); no `only`/`without`.

### C. Core shortform (REQ-IO-COMPLIANCE + REQ-HAZARD-SHORTFORM-DOUBLE-FIRE)
- Manifest `[io] compliance = true` **in the same commit that deletes** `parsePeerShortforms` /
  `dispatchShortforms` (+ their tests). Core parses the AGENT_OUTPUT payloads from A; outcomes reach
  the model only through DISPATCH_RESULTS via B. Seals: `;;…;;` now works from inside a turn.
- Hazard test: with `[io] compliance = true` the extension exports no shortform parser and
  `agent_end` issues no `send`; and A reports every assistant message (a tag in an early message
  must reach core, not just the last one — sister 0.35.0).
- Startup brief: dispatch outcomes arrive in DISPATCH_RESULTS only; no confirmation line; never
  resend on silence; `SEAL_BARE_MIDTURN` note.
- REQ-PARITY-PEER-SHORTFORM title → core-parsed shortform; KNOWN-HAZARDS #7 (MODE-1 backtick
  corruption) stays as record: the parser it guarded is gone.

### D. Docs / release
- PARITY.md matrix rows 0.32–0.36 → covered; baseline → 0.4.0 → latest sister at close.
- harness-contract.md + quickstart: IO feed, now-signal, shortform-by-core, seals.
- CHANGELOG 0.4.0; floor stays 0.67.0. `adapter/manifest.schema.json` refreshed from the published
  schema (carries `io`) — landed with this plan.

## Tasks
1. traceable-reqs.toml: add REQ-IO-TURN-FEED, REQ-NOW-SIGNAL-INJECT, REQ-IO-COMPLIANCE,
   REQ-HAZARD-SHORTFORM-DOUBLE-FIRE (doc/impl/unit); retitle REQ-PARITY-PEER-SHORTFORM.
2. Extension: A then B then C (C last, one commit with the manifest flag).
3. Manifest: `[io] compliance = true`, `[[hints]]` × 5.
4. Tests (harness `onRun` intercepts): payload on busy/idle/mid, exactly-once across tool turns,
   stub turns carry no USER_INPUT, now-signal stdout injected at start + boundary, empty stdout adds
   nothing, now-signal/report failure never breaks a turn, no parser + no agent_end send.
5. KNOWN-HAZARDS #12 (double-fire window). Docs. CHANGELOG. Bump 0.4.0 (8 gated files).
6. Gates: `node tests/omp-extension.mjs` · `sh ci/run-gates.sh` PASS · `traceable-reqs check` ·
   docs regen. Release per RELEASE-RUNBOOK (3 helpers, pack, tag, `gh release create --latest`),
   `spt adapter update omp-spt` on this node, report to doyle.

## Open questions (answer from public docs first, then doyle)
- `io-events` says "from releases#234" for third-party consumers — confirm 0.67.0 emits USER_INPUT /
  AGENT_OUTPUT on `api state --payload-stdin` by running a probe endpoint and polling `io-events`.
- Does OMP's `message_end` fire for assistant messages only, or also tool-result messages? Filter on
  `message.role === "assistant"`.
