CH 10 · Session Log as Source of Truth
Chapter Goal
CH 09 covered the "account book" (session); this chapter unpacks it: why it's the source of truth of the whole machine — what the model remembers, the trajectory you see, the transcript you can export, the new branch you can fork, all derived from this one log, which itself only grows, never edits.
One Line: Session Log = Agent's Ledger, Memory, and Archive
The official definition of Session is hardcore, broken into three points:
- Append-only: only add records to the tail, no modification, no deletion of old ones.
- Typed events: each line is not free-form text, but a kind of "event" —
turn/start,user/message,assistant/message,tool/result,turn/end... - Single source of truth: the entire agent interaction history, this is the only one that's real; everything else is a projection of it.
Connecting it to CH 09 makes it very smooth: the "message flow" you saw in the last chapter, each step is actually writing an event into this log — turn/start opens the turn, step/start starts a step, user/message records what you sent, assistant/message records what the model replied, tool/result records what tools ran, turn/end closes the turn.
Mnemonic: the flow is "what's happening", the log is "the record of what happened", one-to-one between them.
Why It's the "Source of Truth"
Official words:
The model message history is derived from the log, never stored separately.
Meaning: in dsh, there is no second copy of the conversation record. You think "the model remembers what was said before", but the history the model sees is projected from the log; the Trajectory view you see, the transcripts you can export, the forks you can open — all rendered from this same log:
- Model conversation history:
deriveMessages()projects theMessage[]the model sees from the log — so "what the model remembers" = "what's in the log"; - Trajectory view: the
ASSISTANT / TOOLtimeline you saw in CH 04, is just a visualization of the log; - Transcript / export: full conversation text, replayed from the log;
- fork branching: grow a new session at some historical node;
- Telemetry / statistics: token usage, time taken, computed from the log;
- Persistence file: the
session.jsonl.zstdin$DSH_HOME/sessions.
Why must it be designed this way? Because only one source of truth means you never have "what's shown on the UI, what the model remembers, and what gets exported — three copies that don't match". Other views are all projections of the same log, with consistent rules, always consistent.
"What the Model Sees Is What Is Recorded": The Design Axis
This is a hard rule in dsh, mentioned in the previous chapter, expanded here:
Anything that reaches a model request must be reconstructable from the log, and the runtime checks it with an invariant.
It leads to two direct corollaries:
- To add something new for the model to see, you must add a new event type. For example, if you want the Agent to see an injected chunk of context, you can't bypass the log and stuff it directly into the request — instead, define a new session event, write it to the log, and project it out from the log. This makes every step traceable, and is the underlying guarantee of that homepage line "every run is traceable".
- The log is lossless. Even the raw streaming chunks returned by the model are preserved (
assistant/chunk), so replay can be token-by-token faithful, and the UI can restore it exactly.
One line: this design makes "traceable" not a slogan, but an architectural inevitability.
A quick cross-reference with CH 04: the /compact command you used to compress context, under the hood, just records a "I compressed" action in the log (compaction/* event), and projects a more refined form to the model from the log. It doesn't rewrite history — the original events are still in the log, only the projection the model sees is rearranged. That's also why the Agent "remembers" a more refined version after compression, but the original record is still complete.
What It Really Looks Like: Local Session Logs
Back to your own machine — take this computer as an example (after CH 05 ran headless, this exists here):
C:\Users\mortal\.dsh\
├─ profiles\ ← profile list (the "menu cards" from CH 08)
├─ sessions\ ← session logs here
│ ├─ --E-software-workspace-DeepSeek~0020harness~0020demo--\
│ │ └─ session-307edce2-...\session.jsonl.zstd ← the record from the CH 05 headless run
│ └─ --E-software-workspace-doubaowork-DeepSeekHarnessGuide--\
│ └─ session-f417b4dd-...\session.jsonl.zstd ← sessions used in this project
├─ storages\
├─ settings.yaml
└─ .credentials.yamlA few points:
- Per-workspace directories: directory names are escapes of workspace paths (spaces become
~0020), so you can see at a glance which session was produced while working where; - One folder per session, inside is
session.jsonl.zstd— a JSONL line-by-line append + zstd-compressed persistence file; - Echoing CH 05's "Ungrouped": these files are stored by workspace, but the Web UI's session list groups headless runs into Ungrouped — don't mix up the two dimensions;
- These files are the session's "memory archive", don't delete them casually. Delete them, and that session's "memory" is truly gone.
fork: Growing a New Session from the Log
Since the log can be replayed in full, naturally you can "regrow from a certain position" — the official team calls it fork: clone all events before a stable position as the opening of a new session, then go your separate ways. The use is direct: want to try a new path at some historical node without touching the original session. The specifics of how to use it, we'll get to in the later hands-on chapters; for now, just know "it exists, and the reason it can exist is precisely that the log can be replayed in full".
What you learned in this chapter
- [ ] State the three characteristics of the session log (append-only / typed events / single source of truth)
- [ ] Match CH 09's message flow to log events one-to-one (turn/start, user/message, assistant/message, tool/result, turn/end)
- [ ] Explain "model history is derived from the log, never stored separately", and why this prevents three copies that don't match
- [ ] State the two corollaries of "what the model sees is what is recorded" (adding new things = adding new event types; lossless log can be replayed token by token)
- [ ] Know where local session logs are ($DSH_HOME/sessions with per-workspace directories, session.jsonl.zstd), and the difference from the Web UI's "Ungrouped"
