How Claude Code Rebuilds a Session from a JSONL Log
Understanding that Claude Code's session recovery is log replay, not process snapshot, explains why terminal history and model context can diverge — and gives developers a concrete debugging path when the model seems to forget earlier conversation.
Session recovery in Claude Code is a two-phase process: first, it reconstructs a resumable conversation from a local JSONL file; only after the user types a new message does it assemble the model context. The JSONL stores every message, tool call, and state change as append-only JSON records, linked by uuid and parentUuid fields. Rewind operations create fork points without deleting old messages — new messages simply append to the file and point back to an earlier parent, leaving multiple conversation paths in the same log.
Compaction boundaries and summaries are also written into the JSONL. When a session is compacted, a boundary record with a null parentUuid cuts off the old chain, and a summary message becomes the new head. Recovery then walks forward from the boundary, never re-reading the original messages that were compacted. Unfinished turns — a tool call without a result, or a user message without a reply — get cleaned up into a structurally valid conversation without re-executing any tools.
Work state like agent configuration, Git worktree paths, file read caches, and file-history snapshots are stored alongside messages in the same JSONL. During recovery, Claude Code reads these records by type and writes them back into the running process. The result is a restored environment, not a new state file. The terminal shows the full conversation path, but the model may receive less: context assembly starts from the last compaction point and may trim or summarize further before the next request.
JSONL as a state-recovery format is underappreciated: it's human-readable enough to debug, structured enough for a program to replay, and append-only by nature, so crashes rarely corrupt earlier records.
The separation between terminal display and model context is a deliberate design choice, not a bug. It means developers can scroll through full history while the model works with a compressed subset, but it also creates a debugging trap when the model misses details visible on screen.
Storing worktree state and agent configuration in the same log as conversation messages turns the JSONL into a portable session file — move the file and the project directory, and the session can theoretically resume on another machine.
Rewind without deletion is a clever UX pattern: it preserves the full decision tree in the log, so users can revisit abandoned paths later, but it also means JSONL files grow monotonically and may need occasional cleanup.