Watch
1
0
Fork
You've already forked souveraine
0
souveraine/saf/memory/02-replay.md
Fimeg aae9344fe1 saf: write the replay page, retire two stale claims
Replay had no page while compaction's premise depends on it — microcompact
blurs tool output that a text-only projection never sent.

Compaction's open edges still said pressure counts only text; 7530d6d made
it exhaustive. Two links pointed at pages that were never written.
2026-08-13 20:45:21 -04:00

3.2 KiB

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: the messages stored for a conversation. Everything that talks to a model — her turn, her subconscious a moment later, a terminal, a voice, a web surface — replays that same record. None of them holds a version 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.

What crosses

A stored message is made of blocks. Each one has a prose form for replay:

block crosses as
text itself
tool use Tool use: name(input)
tool result Result (name): output, or Error (name): output
reasoning [Reasoning]: …
image natively, or a marker saying it was not seen

Nothing is silently dropped. A block with no prose form does not exist — the match is exhaustive, so a new kind of block fails to compile rather than quietly going missing.

This is what makes a turn's own work available to the turn after it. When tool blocks were dropped on replay, 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 payload to blur.

Two properties that are load-bearing

Both were learned from a failure, and both look like details until they break.

One wire message per stored message. Replaying block by block splits a single assistant turn that called several tools into adjacent assistant messages, and OpenAI-shaped providers reject that outright: assistant tool_calls must be followed by tool messages. The message boundary is part of the record.

No tool roles and no call-id obligations. Inside a live turn, a tool call and its result are both known to exist, and the exact linkage is used. Across persisted turns that linkage is brittle: a turn killed mid-round leaves a call whose result never landed, and replaying it as half a pair fails the whole request. So the content of a tool round survives as prose while the wire obligations do not. The record is not the protocol.

The only thing that varies

Whether the model can see. 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 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 build their own messages, with real tool_call_id linkage. That is correct and separate — replay is for what is already on disk. The two should not be merged; they answer different questions.