# Replay

Compaction is what she lets go of. Replay is what she carries forward. They are
two halves of the same question, and replay is the one that decides whether
compaction means anything at all.

The code is `core::session::replay_messages`.

## One history

There is a single record: typed message blocks stored for a conversation. Her
turn, her subconscious a moment later, the TUI, and the Panel all read that
record. None of them holds a transcript of its own.

That is not tidiness. It follows from what the substrate already says: one
consciousness in two modes, and she is not the glass. A second projection is a
second opinion about what happened, and the two drift the moment one is
touched. There were three of them once. They disagreed about images before
anyone noticed.

One record does not mean one lossy encoding. A surface receives the blocks
verbatim and draws their types. A model provider receives the narrowest valid
wire projection of those blocks. Both are views of the same record; neither is
allowed to rewrite the record into its own authority.

## What crosses to a model

A stored message is made of blocks. The model projection preserves the
strongest valid form each block still has:

| stored shape | crosses as |
|---|---|
| text | assistant/user text |
| complete tool round | one assistant `tool_calls` message, then bound tool results |
| call with no result | native call plus an explicit did-not-complete result |
| result whose call is gone | `Result (name): output`, or `Error (name): output` prose |
| reasoning | omitted unless a provider-valid signed thinking block exists |
| image | native image, or a marker saying it was not seen |

Text crosses as speech. Tool calls and their results cross as a native tool
round when the pair exists; orphaned evidence degrades explicitly rather than
breaking the wire. Images cross natively when the model can see and otherwise
become an honest unseen marker.

Reasoning is different. It remains a typed `reasoning` block in persistence,
token accounting, compaction input, transcript APIs, and the Panel's
collapsible Thinking card. It is not assistant speech. Anthropic will only
accept replayed thinking with the original signature, which the generic
persisted block does not carry. The honest generic projection therefore omits
it. Flattening it into `[Reasoning]: ...` puts an internal trace into the
assistant's visible prose and teaches the next model to imitate the leak.

The projection matches exhaustively. Adding a block kind still fails review at
the owning matches rather than disappearing behind a wildcard.

Native tool rounds are what make a turn's own work available to the turn after
it. When tool blocks were dropped, everything she learned by *doing* survived
only as whatever she happened to write down in prose, and she would re-derive
findings she had already reached. It also left microcompact — whose entire job
is blurring old tool output — with nothing in the stored payload to blur.

Microcompact does not drop tools. It retains every call and replaces only the
bulk of older tool-result output with an explicit cleared marker, keeping the
recent results intact. The typed record is compacted first; replay projects the
result afterward.

## Three properties that are load-bearing

All three were learned from failures, and all look like details until they
break.

**One declaration for one stored assistant round.** Replaying block by block
splits a turn that called several tools into adjacent assistant messages.
OpenAI-shaped providers reject that shape. All calls from the stored assistant
message remain on one wire message.

**A native call is always answered.** Each result follows the assistant tool
declaration with its original call id. If a turn died before a result landed,
replay synthesizes an explicit did-not-complete result. If compaction removed
the call but left its result, that result cannot carry a tool role honestly and
degrades to named success/error prose. No half-pair reaches a provider.

**Doing never becomes saying.** A tool call must not become assistant prose
such as `Tool use: bash(...)`. That falsifies the record and teaches the model
that writing the sentence is how the action is performed. Calls stay calls;
results stay bound evidence whenever the pair can still be made valid.

## Surface hydration

`GET /v1/conversations/:id/messages` returns the stored
`ConversationMessage`s verbatim. The Panel maps `text` to speech, `reasoning`
to `ThinkingCard`, and tool use/result blocks to `ToolCard`. Live SSE follows
the same vocabulary: `assistant_message`, `reasoning_message`, tool call, and
tool return remain distinct events. A resume must use this typed path, never
`replay_messages`, which is the model projection.

That separation makes the visible invariant precise: Reasoning and Thinking
are the same typed thing. They may be folded or hidden by a surface, and kept
for later audit or reflection, but they may not be relabelled as ordinary text.

## The model capability that varies

Whether the model can see changes the image projection. Images ride as native
multipart content, or degrade to a marker that says plainly they were not
visible. The caller supplies that one fact; nothing else about the generic
projection changes.

A degraded image is named as unseen rather than dropped. A model told it did
not look can say so. A model handed a stub shaped like success proceeds as
though it had looked.

## Open edges

Live tool rounds inside a turn and replayed completed rounds both use real
`tool_call_id` linkage. Their repair rules differ: live execution knows whether
a result is still coming, while replay must make an interrupted persisted round
valid and truthful before it crosses the wire.

Persisting provider-valid signed thinking would permit a native reasoning
projection for providers that support it. Until the record carries that proof,
reasoning stays available to surfaces and later internal readers but does not
cross as invented assistant speech.
