Define the in-file RFC contract in docs/rfc/README.md § The file format: the header block (`# RFC: <title>` plus a dateless Status enum cross-checked against the lifecycle folder), the per-lifecycle body skeleton (a Problem opener everywhere; Proposal/Alternatives considered/ Acceptance criteria/Risks in proposed/; present-tense Decision/ Consequences with proposal-era headings banned in implemented/; the frozen proposal shape in rejected/), and a mandatory Alternatives considered section with a date-fenced grandfather comment for pre-format RFCs whose alternatives are not reconstructible from the record. Enforce it with a new doc-sync gate, scripts/verify-rfc-format.ts, and normalize all 112 RFCs to it: ~15 Status-line spellings collapse to the enum, 29 Context openers become Problem, the 39 legacy-format XXX debt markers are resolved and banned from reappearing, proposal-era sections in implemented RFCs are rewritten to shipped reality (including the web/fs/subagent seam RFCs' migration plans and test checklists, closing the doc-tiers deferred-work item on the web seam), every RFC gains an Alternatives considered section or the grandfather comment, and the bilingual pair is re-mirrored and re-recorded. Move the generated index tables out of README.md into a fully generated docs/rfc/INDEX.md — gen-rfc-index now writes the whole file, and verify-rfc-classification checks its freshness and rejects index-shaped rows in the curated README — which makes room for the format contract to live in the README front door instead of a separate FORMAT.md. The decision record, and the first RFC written in the new format, is docs/rfc/implemented/process/2026-07-05-uniform-rfc-format.md.
3.7 KiB
RFC: Persist assembled assistant messages, not stream chunks
Status: rejected — high-fidelity chunk replay, partial failed streams, and snapshot replay currently depend on persisted assistant/chunk events. Dropping chunks is only viable with a no-information-loss replay/artifact replacement.
Problem
The canonical session log currently persists every assistant/chunk exactly as streamed by the model. The session persistence RFC chose this for token-level replay fidelity and contiguous seq, but the cost has grown: JSONL fixtures are dominated by tiny delta records, snapshot scenarios replay the model by grouping chunk events, ACP load reconstructs prior assistant output from chunks, and any future log reader must distinguish durable message history from token-level trace.
For successful steps that assemble completed content, the loop already appends an assistant/message. That is the event deriveMessages() uses for the next model request. In other words, the normal resumable conversation state is already present without the chunks; chunks are a live rendering and deterministic-test artifact, not required conversation history. Failed or aborted streams are different: partial assistant output may exist only as chunks, and empty max-token steps may produce no assistant/message at all.
Proposal
Stop storing assistant/chunk in the canonical session log. The durable log keeps assistant/message, tool/call, tool/result, usage if retained, and turn boundaries. Live UIs can still receive token deltas through a deliberately transient stream event. Snapshot replay should move its model script into an explicit fixture sidecar or derive it from a recorded adapter artifact, rather than treating the canonical user session as a token tape. Scenarios that need partial failed-stream output must record that output in the replay fixture.
ACP session/load can replay prior assistant messages as complete content blocks instead of simulating the original token stream. A loaded transcript need not reproduce every historical delta; it must show the same completed assistant content and resume with a valid provider history.
Acceptance criteria
SessionEventMapdropsassistant/chunk, or marks it as non-persisted if a transitional live event is needed.- Session persistence docs no longer require every stream chunk to be stored verbatim.
llm-replayand ACP snapshots use an explicit replay fixture format or sidecar for model chunks.session/loadrenders completed assistant messages fromassistant/message.- Stored logs get much smaller and remain
seq-contiguous without chunk holes. - The session format version and recorded fixtures are refreshed; non-current stored logs are rejected per the pre-release format policy.
What we give up
The canonical user session no longer reconstructs the exact token stream of an old turn. It also loses partial assistant output from failed or aborted streams unless another event or fixture records it. That is too much information loss for the current resume, load, and snapshot contracts. Tests that need exact deterministic streams should own that fixture directly only if the production session log keeps enough fidelity for user-visible recovery.
Related
This supersedes the chunk-persistence choice in session persistence and affects ACP snapshot tests, whose current replay plugin derives its script from assistant/chunk events.