# LIFECYCLE-TRUTH W5 — wave-gate report (todlando, 2026-07-07)

**Wave head @f4cec33 (branch `lifecycle-truth`, pushed).** W5 = delivery integrity.
All three REQs built + locally gated; requesting doyle's two-leg gate (Win + kitsubito
Linux) + delivery seam sweep (inject / translation / spool / chunk).

Ruling: `docs/W5-DISPATCH-RULING.md` @5e82a22. Build order C → B → A (C instruments A/B rigs).

## The three REQs

- **W5-C — REQ-SPOOL-TAKE-AUDIT `[impl,unit]` @c41185d.** Additive nullable spool taker
  columns (`taken_leg`/`taken_sid`/`taken_pid`/`taken_at_ms`) stamped in the SAME UPDATE
  as `delivered=1`; `TakerAudit` + audited take variants (existing fns delegate `None` =
  zero test churn) + `audit_rows_at` + hidden `spt spool-audit --json`. All four legs wired
  (relay-backlog / hook-poll / idle-inject / psyche-reserved). No schema break.

- **W5-B — REQ-IDLE-PARKED-DELIVERY `[impl,unit,int]` @435ff2e.** Root: the F-023 idle-edge
  drain fires only on the ACTIVE→IDLE transition, so an already-parked-idle session never
  gets an edge. Fix (ruling B = BOTH): shared `inject::drain_idle_spool` core; **primary** =
  send-time inject fires for an already-idle target; **belt** = pulse-tick sweep re-offers
  pending rows. Int on a REAL broker + xlate binary (parked row drains, stamped `idle-inject`).

- **W5-A — REQ-INJECT-MULTILINE-INTEGRITY `[impl,unit,int]` @f4cec33 (the hard one).** Root:
  the typed idle-inject leg ate HEAD bytes nondeterministically (pre-settle typed-stream
  race: bytes typed into a PTY whose input reader is not yet attached are swallowed
  mid-stream; a live-SENT inject leaves no spool copy → silent AND unrecoverable). doyle
  3-layer ruling A:
  - **Layer 1 (MUST, UNCONDITIONAL) — settle-gate.** A benign DSR readiness probe + wait for
    the session ring to produce output before typing ANY payload byte. Worker-local ONE-SHOT
    (the race is a startup condition; once settled, subsequent idle injects go straight
    through), bounded 400 ms so a non-echoing PTY pays the cost at most once. **This is the
    load-bearing root fix.**
  - **Layer 2 — echo-verify → re-drive once → LOUD spool.** After `{commit}`, verify a prefix
    of the typed payload echoed; on a miss re-drive once, on a second miss re-spool LOUD
    (`delivered=false`, recoverable) instead of silently truncating.
  - **Layer 3 — chunked-paced write** under the gate (the ruling's named fallback), so no
    single huge write races the terminal's coalescer.

## ⚠️ Premise refinements for a doyle ruling-check (two)

1. **Layer 2 is a DECLARED CAPABILITY (`SPT_INJECT_VERIFY_ECHO`), default OFF — not
   unconditional.** The ruling framed echo-verify as a MUST that runs always. In build I hit
   a hard wall: **universal echo-verify is infeasible.** Echo-verify must observe the typed
   payload echoing back, but —
   - a **raw-mode TUI** (every real harness — CC included) does NOT echo a benign *control*
     probe, so the settle probe cannot prove text-echo capability;
   - **ConPTY** does not surface the cooked echo of a *programmatically*-injected line at all;
   - **incidental output** (a prompt, a redraw) makes a head-prefix check false-read head-loss
     and would spuriously LOUD-spool + re-drive EVERY delivery (this concretely regressed the
     pre-existing `wan_arrival_to_idle_spt_hosted_injects_with_no_hook_poll` test — findstr on
     ConPTY produces output but not the inject echo).

   So Layer 2 is gated on a capability the **adapter declares** for harnesses whose input-echo
   is observable (a real TUI's input line renders typed text into its output stream — CC does
   this). This is **exactly parallel to the ruling's own "bracketed-paste where the harness
   declares it"** pattern. Default OFF ⇒ zero regression. **Layer 1 (settle-gate) ships
   UNCONDITIONAL and is the primary fix.** Requesting doyle confirm this gating (and whether to
   wire the capability through the manifest for CC in a W6/adapter follow-up vs the daemon-env
   opt-in shipped here).

2. **Bracketed-paste (ruling Layer 3 SHOULD) is DEFERRED; chunked-under-settle ships instead.**
   The seam is not clean: the translation binary owns the `{key}`/`{text}` interleave (a
   bracketed-paste wrap belongs to the binary's choreography, not a core transform), and F-019
   already found bracketed-paste **moot for CC submit**. The ruling explicitly names
   chunked-write-under-settle as the fallback where bracketed-paste is not declared — that is
   what ships. Flagging for a ruling-check that deferral is acceptable this wave.

## Seam (W5-A)

`broker.rs` `run_inject_worker` refactored: `drive_one_sequence` returns a typed outcome
(+ the typed payload text) so the caller layers settle / echo-verify / re-drive / loud-spool
around it. `OutputLog` gains `high_water` + `bytes_since` taps; the log `Arc` is threaded
through `build_translation` (3 call sites: spawn, fault-respawn, adapter-apply) into the worker.
All existing choreography semantics preserved (C-1 miss-tolerance, floor order, poison recovery).

## Tests (W5-A)

- **Units (broker.rs, 4):** `echo_verified` head-loss discrimination (prefix present vs
  swallowed-head-tail-only vs empty), `contains_subslice`, output-log `bytes_since` snapshot,
  `chunk_text` split+exact-reassembly. Each RED-first (revert flips the assert).
- **Int (inject_control_wedge.rs, 2):**
  - `w5a_bigmultiline_inject_arrives_byte_complete_across_a_loop` — LOOP N=4 large multi-line
    injects; delivered=N (both platforms); **forkpty-HARD / ConPTY-CAPTURE** byte-receipt of
    BOTH `HEADSTART` and `TAILEND` per inject (payload sized to fit the 24-row grid so the head
    does not scroll off a cold-attach repaint). The ruling's binding "loop N, byte-complete".
  - `w5a_forced_echo_miss_redrives_once_then_loud_spools` — deterministic + cross-platform:
    `SPT_INJECT_FORCE_ECHO_MISS` forces the head-loss verdict → the binary is driven TWICE (one
    re-drive, asserted via the xlate stdin-log event count) and the envelope is re-spooled LOUD
    (`pending ≥ 1`, recoverable). RED-first: remove the loud-spool branch → no pending row;
    remove the re-drive → one event.

## Local gate (this box, HFENDULEAM / Windows)

- `clippy --workspace --all-targets`: **clean** (0 warnings — CI denies workspace-wide).
- `xtask check`: **OK** (docs-drift + token; no CLI surface changed this wave).
- `cargo nextest run --workspace --no-fail-fast`: **1705 passed / 1706** (8 leaky, 1 skip).
  - The one red — `spt-store registry::tests::concurrent_registration_never_locks` — is the
    known AV-hold concurrency flake (W2 ledger): **PASSES isolated** (`1/1`), a contention
    flake under full parallel load, not a W5 regression.
  - Full inject seam suite **19/19** including the two new W5-A int tests + the recovered
    `wan_arrival_...` test (Layer 2 off by default = no spurious spool).
- `traceable-reqs check`: **exit 0.** REQ-INJECT-MULTILINE-INTEGRITY `+impl +unit +int`;
  REQ-IDLE-PARKED-DELIVERY `+impl +unit +int`; REQ-SPOOL-TAKE-AUDIT `+impl +unit`.

## Gate gotchas (for doyle)

- **Disk-full during the gate build (os-error-112, [[hfenduleam-disk-full-ci]]).** This box's
  `target/debug/deps` had ballooned to 135 G + 46 G of stale CLOSED-wave gate worktrees
  (`.worktrees/gate-{fbf8ab7,e75e856,e09ba21}` = W2/W3/W4). Freed the stale worktrees (their
  `crates/spt-daemon` subdirs stay "Device or resource busy" on a pinned handle — clear on
  retry). doyle's isolated-worktree fresh-target gate is unaffected, but the box needs a
  `target` clean before it re-fills.
- Full-workspace nextest LEAKS `target\debug\spt.exe` dev-daemons → next build os-error-5; kill
  SCOPED (Path == `<repo>\target\debug\spt.exe` ONLY, never the AppData live listeners).
- The seedmap starvation flake ([[seedmap-test-collides-live-daemon]]) fires in a bare `cargo
  test --lib` while a live daemon runs on this box, but NOT under nextest's process isolation.

## Linux leg = doyle's / kitsubito

The W5-A byte-receipt asserts (`HEADSTART`/`TAILEND` echo) are **forkpty-HARD** — the
gravity-linux/kitsubito leg carries them (ConPTY captures on Windows). Same platform-divergence
discipline as g1.
