Skip to content

Build1 publisher3 min readPublished

A nullable parentId buys Pi the entire session tree for one field

Codex made the session Item a wire type and Pi gave every entry a nullable parentId, while Claude Code squeezes the transcript in five stages. The three designs diverge on what stays addressable after compaction.

The Engineer · Build desk

Illustration accompanying A nullable parentId buys Pi the entire session tree for one field

What happened

  • Codex's app-server README of 2026-09-01 defined Item as user inputs and agent outputs persisted as context for future conversations, listing agent reasoning, shell commands and file edits as examples.
  • That paragraph has since been replaced, but a 2026-09-08 check found ThreadItem and thread/fork still typed in codex-rs/app-server-protocol/src/protocol/v2/.
  • Pi took the other route to the same structure, giving every session entry a nullable parentId in session-manager.ts and exposing session_before_fork, session_before_tree and session_tree to extensions.
  • Pi's whole compaction trigger is one function, shouldCompact, which fires when contextTokens exceeds contextWindow minus settings.reserveTokens.
  • Claude Code instead squeezes the transcript in five progressive stages, compressing older material harder while keeping the most recent turns at full fidelity, per a walkthrough of the leaked source.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • constraint After a Claude Code summary lands, the state that existed before it is unreachable, so taking a different path from an earlier point in that session stops being available.
  • capability Branching on Codex costs a call and a thread id, because the server copies a prefix of typed objects instead of guessing at transcript boundaries.
  • cost The structured designs push work outward to every client that has to model turns and threads; the compression pipeline needs no cooperation from clients, and its cost falls inside the harness.
  • decision A 25-point benchmark swing attributed to keeping reasoning across turns puts the choice of state type ahead of prompt work in the build order.

Copying a prefix of a list is cheap, and Codex sells that as an operation. `thread/resume` picks an existing conversation back up, and `thread/fork` creates a new thread id with the history copied into it [6]. Set `ephemeral: true` and you get an in-memory thread whose `path` is null, so the session never lands on disk [7]. None of those calls has to infer where a turn began, because Item, Turn and Thread are each wire types in the JSON-RPC schema, and the server will emit that schema as TypeScript or JSON Schema on demand [5][18].

Pi publishes three tree-change events to its extension API [17]. That only matters if code outside the core has to react when a session branches.

The compression design wins on integration cost. A staged pipeline over a sequence can use the whole transcript when it decides what to drop, and it does not require the rest of the system to model conversation structure at all, according to the dev.to post that quotes the code paths [13]. The price is that the operation runs one way. Copying a prefix of a list of objects is trivial; copying a prefix of a compressed blob is not possible [14]. Once a stretch of session has been summarised, the detail underneath is not addressable, and the state before that decision no longer exists in a form anything can load [12].

The same post reaches for a benchmark to justify the structured answer: on ARC-AGI-3, it says, retaining the model's reasoning across turns was worth most of a 25-point swing [15]. That is a measurement of one harness on one benchmark, and three conditions have to hold before it moves to another workload. The reasoning has to carry information the visible output does not, and the run has to be long enough for the loss to compound. The harness you are comparing against has to be discarding reasoning in the first place. Codex's own list of example items names agent reasoning alongside shell command and file edit [2], so on that protocol it survives without anyone remembering to keep it.

In my view the parent pointer is the cheaper of the two structured answers for most harnesses. A nullable field on a session entry is a smaller change than promoting your history to a protocol type with generated bindings [8][5], and the post's framing is right that a list is a tree where every node has exactly one child [16]. That holds while the sessions are read mostly by the harness's own code. If you are shipping a server that other clients drive, the typed items are what turns fork into an API call rather than a local convention, and those clients compile against the schema [5].

What to watch

  • Whether the replacement Codex app-server README documents Item, Turn and Thread with the same persistence guarantee the 2026-09-01 paragraph gave.
  • Whether Claude Code gains an addressable checkpoint before a compaction stage, the one change that would make a summarised session replayable.
  • Whether Pi's reserveTokens setting acquires a documented default, since shouldCompact is the only thing standing between a session and the ceiling.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories