Codex made Item a wire type. Pi gave every session entry a parentId. Claude Code compresses the transcript in five progressive stages.
Those are three answers to the same question, and it is a question most agent code never asks out loud: what is the durable unit of state here, and what happens to it when you run out of room?
You find out which answer you picked at exactly one moment. Not while things are going well. At the moment the context window fills.
| durable unit | what survives compaction | branching | |
|---|---|---|---|
| Claude Code | the transcript | a summary, written in place | start a new session |
| Codex | the Item, a wire type |
items keep their identity | thread/fork |
| Pi | a session entry with a parentId
|
a branch_summary node in the tree |
native to the structure |
Everyone eventually sheds context
Start with what is not in dispute.
Every harness in this series hits the ceiling and has to drop something. Pi's rule is one line, in packages/coding-agent/src/core/compaction/compaction.ts:
export function shouldCompact(contextTokens, contextWindow, settings) {
return contextTokens > contextWindow - settings.reserveTokens;
}
That is the whole trigger. Are we within the reserve of the ceiling. Claude Code's pipeline is staged rather than binary, but the pressure is identical.
The interesting difference is not when they shed. It is what is left addressable afterwards.
Claude Code: the unit is the transcript
Claude Code's answer is progressive compression. I walked through the five-level pipeline when the source leaked, and the design is coherent: as pressure rises, older material is squeezed harder, with the most recent turns preserved at full fidelity.
The durable unit here is the conversation itself. State is whatever survives the squeeze.
That has a real advantage. Compression is a global operation, so it can make good decisions using the whole transcript, and it does not require the rest of the system to model conversation structure at all. The transcript is a sequence, and the pipeline is a function on that sequence.
It also has one specific consequence: compression is one-way. Once a stretch of session has been summarised, the detail underneath it is not addressable any more. You cannot go back to the state before that decision and take a different path, because the state before that decision no longer exists in a form anything can load.
Codex: the unit is the Item
Codex made a different choice, and made it at the protocol layer, which is what makes it interesting. From the app-server README as it stood on 2026-09-01:
Item: Represents user inputs and agent outputs as part of the turn, persisted and used as the context for future conversations. Example items include user message, agent reasoning, agent message, shell command, file edit, etc.
That README has since been replaced with something else entirely, which is worth knowing if you go looking for the paragraph. The types themselves are still there and still typed: ThreadItem and thread/fork live in codex-rs/app-server-protocol/src/protocol/v2/, checked again on 2026-09-08. The documentation moved. The design did not.
Note what is in that list. Agent reasoning is an item. Shell command is an item. File edit is an item. These are not lines in a transcript, they are typed objects with identity, nested inside Turns, nested inside Threads, all three of them wire types in a JSON-RPC schema that the server can generate as TypeScript or JSON Schema on demand.
Once state is addressable, operations on state become API calls rather than heuristics. thread/resume picks a conversation back up. thread/fork creates a new thread id with copied history. ephemeral: true gives you an in-memory thread whose path is null, so it never lands on disk at all.
thread/fork is the one to look at. Branching is not a feature bolted onto a transcript, it is a consequence of items having identity. If your history is a list of objects, copying a prefix is trivial. If your history is a compressed blob, it is not possible.
Recall the ARC-AGI-3 result: retaining the model's reasoning across turns was worth most of a 25-point swing. In Codex, agent reasoning is an item type. It has somewhere to live, and it survives by default rather than by remembering to keep it.
Pi: the unit is a node
Pi arrived at the same place from the opposite direction, with far less ceremony.
Session entries carry a parentId, nullable, in session-manager.ts. That single field is the whole design. A list is a tree where every node has exactly one child. Adding the pointer costs almost nothing and buys the entire structure.
Pi treats that structure as a first-class thing rather than an implementation detail. Its extension API exposes session_before_fork, session_before_tree and session_tree as events, and a session start carries a reason field whose values include "fork". Extensions get told when the tree changes, which only makes sense if the tree is real.
Alongside it there is an entry type called branch_summary, which is where the two ideas meet: when you leave a branch, its content can be replaced by a summary of it while the branch itself stays in the tree. You have not deleted the path. You have compressed one, and you still know it is there.
Pi's compaction package is about 1,550 lines across four files, which is the honest price of doing this properly. The summarisation budget is capped at 0.8 * reserveTokens, so the summary is guaranteed to fit in the space that was reserved for it. That is a small detail and a good one. It is the difference between a compaction strategy and a compaction hope.
The extension system leans on the same substrate. Extensions persist their own state into the session as custom entries, which means an extension's state branches when the session branches, with no extra machinery.
The two axes again
This is the cache coherence frame from earlier this year, one layer down.
The two axes there were fidelity, lossless against lossy, and retrieval, exact against approximate. Agent session state sits in the same space, and what these three teams differ on is where the lossy operation applies.
Claude Code applies it to the timeline. Older material gets less faithful as pressure rises.
Codex and Pi apply it to a branch. The structure stays lossless and addressable, and lossy compression happens inside a node that keeps its identity.
That is why the tree matters even for people who never type a branch command. The structure is what gives compaction somewhere to put its result without destroying the address. Compression against a flat sequence has nowhere to attach a summary except in place of the thing it summarised.
The move you already make by hand
Here is the practical version, and it is the reason I think this is the most underrated design decision in agent systems.
Everyone already branches. When a session goes sideways after forty minutes, you start a fresh one. That is a branch from the root, and you pay for it by re-establishing everything: which files matter, what the constraints are, what you already ruled out.
You do it because the alternative, continuing in a poisoned context, is worse. Bad exploration does not leave. It sits in the window, and the model keeps reading it.
A session tree makes that a cheap operation instead of an expensive one. Return to the last node where things were fine, branch, go a different way. Everything before that node is intact, because it was never compressed away, because it has an address.
The general form of the mistake is treating exploration as free. It is not free. It costs context, and context is the scarcest resource in the system. A harness that cannot discard a bad path without discarding the good prefix is charging you the whole session for every wrong turn.
What to change on Monday
If you maintain an agent and you are not sure which of these you built, check three things.
Do your session records have a parent pointer? If not, add one before you need it. Retrofitting the pointer is easy. Retrofitting it onto six months of stored sessions is not.
When you hit the ceiling, do you truncate or compact? If you slice off the oldest messages, you are deleting the turn where the task was defined, and there is now a public 25-point result on what that costs.
Does the model's reasoning survive between turns? In Codex it is an item type, so it persists by construction. In most frameworks it is dropped by default and the default is not in the documentation. This is the single highest-value thing to go and check, and it is usually one field.
The unit of state is not a detail you get to decide later. It is the thing that decides what "later" is even able to look at.
Pi and Codex references measured 2026-09-01 against github.com/earendil-works/pi and github.com/openai/codex at that day's HEAD. Claude Code pipeline details are from the leaked bundle analysed here and cannot be re-verified against the shipping build, which is a compiled binary with minified JavaScript inside.

Top comments (1)
The addressability point is bigger than branching. If compaction replaces a span with a summary but the original items retain stable identities in cold storage, the system can keep a small active context without pretending the details vanished. Summaries should be indexes with provenance, not destructive replacements. Then a later action can rehydrate the exact evidence it needs.