# Long-body idle-inject HEAD-TRUNCATION rig-probe — JIT plan

> Owner: perri. Split: perri = adapter-side rig-probe (this plan); doyle = core inject
> framing (seed #9). Opened 2026-07-07 post-checkpoint.
>
> **RESOLVED 2026-07-07, same night — rig never needed.** Field data + code walk sufficed
> (doyle distinguisher): all 4 truncations were turn-start idle-injects (mid-turn arrivals
> intact all night); doyle's spool rows COMPLETE (1669B full row vs 322B received suffix →
> truncation past spool); NON-deterministic — an 1854B body rode the same leg intact →
> timing race (readiness/enter-coalescing settle class), NOT a size cap. Core fix target =
> typed-inject pacing (settle-before-head / bracketed-paste / verify-echo) — doyle's, banked
> under seed #9. Adapter side fully exonerated (findings below); the one adapter hardening
> (loud dangling-frame marker, doyle-ACKed) shipped @347f755 as
> REQ-HAZARD-DANGLING-FRAME-LOUD / KH §2.9, rides the next release cut.

## Scope

Field suspicion: long message bodies delivered on the **idle path** (translation-binary
inject → submit) arrive with their **head truncated**. Probe reproduces (or clears) this
against the installed adapter + core 0.28.0, adapter-side only — if truncation appears at
the inject seam itself, that is doyle's core framing half; hand evidence over, do not dig
core source (public-surface constraint).

## Field samples

Log a `(total, head_lost, suffix)` triple per instance — three+ points settles
invariant-vs-coincidence (doyle, banked for the W5 hunt).

- 2026-07-07 instance #4 (perri→doyle idle-inject): total 1299 B, head lost 977 B, suffix 322 B.
- 2026-07-07 instance #5 (perri→doyle idle-inject): total 1636 B, head lost 989 B, suffix 647 B.
- 2026-07-07 instance #6 (perri→doyle idle-inject): total 1266 B, head lost 970 B, suffix 296 B.
- 2026-07-07 instance #7 (perri→doyle idle-inject): total 1436 B, head lost 970 B, suffix 466 B.
- Three-point read: **LOST-HEAD invariant CONFIRMED (970–989 B, ~2% spread)** — constant
  swallowed budget regardless of body size; suffix = total − budget. Fits the pacing-race model
  (pre-settle window eats a fixed byte budget; whatever lands post-settle survives). Sits just
  under 1024 — one write-granularity chunk minus framing; doyle found no 1024 constant in
  spt-term inject paths, so the chunk lives deeper (translation binary or PTY input buffer).
  ~~Open refinement: the 19 B jitter may be per-message framing variance.~~ **ROOT PINNED
  (count-based, 2026-07-07):** spt-proto chunks envelopes over `EVENT_LINE_THRESHOLD=400` into
  `<EVENT-PART seq=K/N id=8hex>` lines (doyle, chunk.rs); part overhead 55 B (56 + escaped orig
  attrs on the first part — attrs ride the wire ESCAPED: `type=&quot;msg&quot; from=&quot;perri&quot;`
  = 43 B, so first body slot = 400−56−43 = 301, non-first = 345). Swallowed budget = the first
  THREE part-lines = 301+345+345 = **991 wire bytes**; my four escaped lost-heads measure
  992/990/988/992 (±2 B). The raw-byte "jitter" was just per-message escape inflation. Doyle's
  deliberately-unchunked <400 B controls arrived intact — consistent. Core fix target: the
  receiving inject path drops the first 3 EVENT-PART lines pre-settle (count-based, not a byte
  window). W5, doyle's hunt. **Interim agent practice: keep inter-agent sends under ~350 B raw
  (unchunked = immune); split long reports doyle-style (1/3, 2/3, 3/3).**
- **Wire ground truth (scratch-relay capture, 2026-07-07):** full part line = 394 B; first-part
  open tag 60 B (`seq`+`id`+`type`+`from`, attr quotes unescaped) → body slot **321 B**;
  non-first tag 36 B → slot **345 B**; close tag 13 B. Attrs = seq/id/type/from ONLY (verbatim
  seq=1/4 line sent to doyle; nothing hidden upstream).
- **FINAL MODEL (doyle, arc CLOSED):** not part-count — the swallow is a raw typed-STREAM BYTE
  window, ~1148–1152 B including line framing (395+395+~360), terminal-side, cutting MID-part-3
  body (992 = 321+345+326; ~19–23 B of part-3 tail survives). Explains every receive-side
  artifact: truncated arrivals always start mid-word (arbitrary byte cut, never a part
  boundary); series spread = seq-digit + cut-position noise. W5 fix shape: instrument the
  inject leg for the pre-settle window; settle-before-type or ack-paced parts. Sub-400 B send
  discipline remains the interim protocol.

## Open design questions

- Truncation trigger: absolute byte size, line count, or PTY write chunking? Probe sizes
  must bracket plausible chunk boundaries — AND the ~320 B suffix hypothesis (bodies at
  300 B / 350 B / 1 KB / 4 KB / 8 KB / 64 KB).
- Multi-line idle envelope (checkpoint-commune wave, adapter v0.7.0) frames bodies — does
  truncation hit inside the envelope (adapter bug) or before it (core inject bug)?

## Findings so far (2026-07-07, pre-rig — construction + code walk)

- **Direction asymmetry:** perri→doyle truncates (4/4), doyle→perri intact — receiver-side
  seam on doyle's endpoint.
- **Adapter emit exonerated by construction:** `emit_capped_pretool` is all-or-pointer
  (cap on wrapped size; under-cap = full body, over-cap = spill + pointer envelope);
  partial emission impossible, and a partial JSON envelope wouldn't parse in CC.
- **`render_frames` faithful:** whole-body copies; a mid-frame-truncated poll output drops
  the dangling frame WHOLE (silent `break`), never a suffix. A delivered suffix therefore
  originates core-side: `spt api poll` frame emission, or the idle-inject typing leg
  (head keystrokes eaten pre-settle — same class as the 0.13.2/3 enter-coalescing fix).
- **Distinguisher handed to doyle:** idle-vs-mid-turn at his 4 receive times + spool.db
  row-vs-received diff. Rig only spins if his framing look wants live size-matrix data.

## Adapter hardening (mine, next cut)

- Loud dangling-frame marker in `render_frames` instead of silent whole-frame drop
  (mint a REQ in `traceable-reqs.toml` before building, per repo rules).

## Tasks

1. Rig: SPT_HOME-isolated candidate pair (recipe = counter-43, test-rig-only), sender +
   idle receiver session.
2. Send matrix: bodies with numbered head/tail sentinel lines at 1 KB, 4 KB, 8 KB, 16 KB,
   64 KB; each body's first line is a unique marker so head loss is unambiguous.
3. Assert on the receiver transcript/side-effect file: first sentinel present? If missing,
   record exact surviving prefix offset per size.
4. Verdict: adapter envelope intact + head lost ⇒ core inject framing (file to doyle with
   offsets). Envelope itself mangled ⇒ adapter fix here (new REQ + KH entry first).

## Gate

Probe matrix run to completion with recorded offsets; finding filed (to doyle or as
adapter REQ). `traceable-reqs check` green if any adapter change lands.
