## Summary
Final planned extraction (step 8) from #655, directly against current
`main` after #685.
- Replay the **latest recorded child turn's** normal completion,
cancellation, or failure as historical `idle` before the load/resume
response, using the same stop-reason/error mapping as live turn endings.
- Emit no outcome for missing or still-running turn metadata. Do not
substitute an older completed turn for a latest running turn, or infer
endings from activity-item kind, missing content, or runtime loss.
- Reuse the legacy transcript's turn payload without a second full read.
Paginated history adds one optional latest-turn summary read (`limit:
1`, descending, no items); an unavailable summary retains available
transcript content and uses the existing history-gap advisory.
- Publish historical state directly, without changing the live
foreground cache, ending current interactions, establishing runtime
ownership, or enabling mutations. Parent closure fences late summaries.
V1 opt-in gating, v2 defaults, AIR behavior, current configuration
reporting, child identities, ancestry, and empty capabilities remain
unchanged. Dependencies and generated app-server types are untouched.
Existing AIR and parent-session golden transcripts are unchanged. Two
existing v2 child-replay snapshots gain the intended historical
`state_update`.
## Review size
15 files, +422/-22 lines. Most additions are tests/snapshots; runtime
changes touch only the history reader, standard replay dispatcher, and
standard child router. The router shares one explicit terminal-status
mapping between live and historical reporting, but historical
publication bypasses the live state cache.
## Validation
- `npm run typecheck`
- `npm test`: **2,064 passed**, 39 skipped, including **36 new v1/v2
outcome regressions**.
- `npm run build`
- `npm run test:integration`: **15 passed** with real Codex 0.164.0 and
a local mock Responses API. The v1/v2 completion cases now close the
parent and cold-load it before checking the recorded ending, avoiding a
cached live ending as a false positive.
- `npm run bundle:all`: all six targets compiled.
- `npm run codex-test -- -p 'Reply with exactly historical-outcomes-ok.
Do not read files or use tools.' -c <isolated-scratch-directory> -o
summary`: live-model smoke completed with `end_turn`.
- `git diff --check`
Wire regressions cover both history stores,
completion/cancellation/failure, unavailable or running final turns,
response ordering, no mutation/interrupt authority, independent
paginated summary failures, latest-turn selection, a subsequent
non-replaying resume, newer live permissions and actual work completion,
nested ancestry and repeated spawn records, late summaries after close,
and failures without details.
The full live-model E2E suite was not run. Cross-platform bundles were
compiled, not executed on each target OS.
## Specification / reference check
- Current specification `main`,
`5a5ab96ea8e0f6d3d5c5d7de3a7d29a87c79ddd2`: the [subagent RFD replay
rules and freshness
boundary](https://github.com/agentclientprotocol/agent-client-protocol/blob/5a5ab96ea8e0f6d3d5c5d7de3a7d29a87c79ddd2/docs/rfds/subagents.mdx#L724-L805)
distinguish historical pre-response state from confirmed current
activity and controls, and prohibit inventing outcomes from missing
history.
- The [explicit replayed-idle
clarification](https://github.com/agentclientprotocol/agent-client-protocol/blob/e839564daab1da2c68cb8a006c786dfcbf0e68f1/docs/rfds/subagents.mdx#L763-L809)
is still on the pending `rfd-replayed-subagent-outcomes` branch, **not
specification main**. It permits keeping a recorded idle outcome visible
without treating it as current activity or authorization. This
implementation is compatible with both versions and requires no new wire
fields.
- TypeScript SDK `main`, `c7e07388feed7a530f8211d0dae4951cb6d26d09`,
checked separately as reference/type evidence. Installed SDK **2.0.2**
performs real wire decoding in the regression tests; this change reuses
the existing v1/v2 state publication and actual response-write freshness
boundary.
This finishes the eight planned extraction scopes once merged; it does
not merge the old combined #655 wholesale. Child mutation controls and
directed-message extensions remain outside this work.