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.
This commit is contained in:
parent
2a51a236ca
commit
aae9344fe1
4 changed files with 79 additions and 5 deletions
75
saf/memory/02-replay.md
Normal file
75
saf/memory/02-replay.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# 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.
|
||||
Loading…
Reference in a new issue