Expand description
The tool surface a run was sent, kept once and cited by hash.
§The gap this closes
RunConfig records the system prompt in
full, and says why: “the text lets a replay rebuild the request.” It
records the tool surface as names, with a doc comment naming the risk it
saw — “a tool added, removed or renamed between recording and replay
changes what the model could have done.”
Add, remove and rename are the three that never happen. Re-describe is the one that happens constantly — 49 commits touched tool definitions in three weeks of this store — and it is invisible to a list of names. Render order is tools → system → messages, so a replay was rebuilding the second half of the prefix byte-exactly and the first half from whatever the registry says today. Every description edit since a recording silently changes the bytes the model sees before anything else.
Measured: 12 of 13 counterfactual probes came back inconclusive, on a pinned seed and a quiet box, deterministically across repeats — median divergence one tool call in. Six probes in one session, with steer points from 10 to 33, all gave up at the same call: a trajectory-dependent cause cannot do that, and a per-session constant can.
§Why a hash and a store rather than the specs inline
Costed rather than preferred: the specs are 69 KB against a 25 KB average session file, so inlining would quadruple the session store and put, in most sessions, more bytes of tool description than conversation. A hash alone is cheap and gives up the rebuild, which is the point of recording it.
So the specs are written once per distinct surface and cited by hash — the
precedent is ValidationRecord’s rules_hash, “keyed to the exact rule
set measured, because a tally that mixes generations measures nothing.”
Surfaces change tens of times over a corpus, not once per session, so the
store dedupes to a few megabytes where inlining would cost tens.
§Three states, and the one that matters is Unknown
A names-only recording must never read as matching. Every session written
before this field exists — all of them, on the day it lands — is
Fidelity::Unknown, and a probe over one is inconclusive for a named
reason instead of mysteriously. That is the whole reason this is
Option<String> and not String: absent is not equal, the rule
crate::homeostat and crate::backlog both state at length.
Fidelity::Differs needs no blob at all — comparing today’s hash against
the recorded one answers it — so legibility arrives the day the field
ships and rebuildability accumulates afterwards. That ordering is worth
more than either half alone: it turns an inconclusive probe from a mystery
into a labelled cause immediately.
§What it does not do
It does not recover the existing corpus. Nothing can: those recordings never held the specs, and the descriptions they were sent are only in git history that cannot be matched to a session. The appraisal corpus and the validation ledger start from zero the day this ships, and anyone budgeting on the sessions already on disk should read that first.
Nothing here is ever deleted, for the same reason a published bundle is not: a surface blob is what makes an old session replayable, and a retention policy over it would quietly cost the recordings it was keeping.
Structs§
- Surface
Store - Where surface blobs live. One file per distinct surface, named by its hash.
Enums§
- Fidelity
- How faithfully a replay can reproduce what a recording was sent.
Functions§
- fingerprint
- A stable identity for one tool surface.