-
Notifications
You must be signed in to change notification settings - Fork 68
Description
I've stumbled upon Messages as Commits: Claude Code’s Git-Like DAG of Conversations, which is very enlightening.
Teasers:
Claude Code conversations are functionally identical [to git branches]. Each message object points to the previous message via parentUuid, so you can fork a conversation by creating a new message whose parentUuid points to some earlier message instead of the latest one. Conversations are reconstructed by walking parent pointers back from "leaf" messages.
In other words, a "session file" is just whatever file Claude Code is currently appending entries to. Conversations themselves are virtual, reconstructed from the DAG across all entries and files.
And I can only sympathize with the way they put it:
Okay, so far so good, but now things get a little weird. Let's say you launch Claude Code and your session ID is 123; you chat, messages are written to 123.jsonl, and then you exit Claude Code. Later, you launch a new Claude Code session, say 456—on startup, Claude Code looks at recent session files and generates a summary for the conversation stored in 123.jsonl; however, because the current active session is now 456, the summary entry gets written to 456.jsonl!
So the summary for a conversation that lives in 123.jsonl is stored in 456.jsonl. This seems unintuitive at first but ultimately works because of how Claude Code loads and merges session data, which merits its own section.
Several questions surface after reading that:
- Would it make sense to always consider all sessions in a project?
- Does our rendering logic currently handle a session where
/resumeis used? - In a similar way, how could we support the command:
/fork Create a fork of the current conversation at this point
Maybe we should identify messages that are fork points in a special way?
Or, when we detect fork points, show an index at the top:
- session summary / first message
- message_23 (uuid=A) <= fork point
- message_23.1 (parentUuid=A)
- message_45 (uuid=B) <= fork point
- message_45.1 (parentUuid=B)
- message_45.2 (parentUuid=B)
- message_45 (uuid=B) <= fork point
- message_65 (parentUuid=A)
- message_23.1 (parentUuid=A)
- message_23 (uuid=A) <= fork point
I think showing the messages that are fork points in such a summary makes sense (= at which state did we fork?).
Another excerpt from that article:
In fact, you could literally shuffle all the lines in all the files and Claude Code would still reconstruct the same set of conversations and summaries
I wonder if we shouldn't revisit renderer.py once more, starting with creating this DAG from all sessions .jsonl... The chronological sort doesn't make sense if we're resuming/forking a lot during a session. OTOH, this would break the way we do pagination and the "append" only update model.