# Conversation forking: a new future from selected history

A conversation fork gives selected history a new continuation. A developer might preserve a working implementation, fork from the design discussion, and explore a different approach. The harness must decide exactly what the new conversation inherits: messages, tool results, summaries, settings, permissions, and possibly a provider's own session identity.

The important limit is that a conversation fork is not a rollback of the world. Suppose the original agent edited a file and sent a message. Forking from before those actions can remove them from the new model's context, but the file remains edited and the message remains sent. A separate worktree, filesystem snapshot, or compensating operation is needed if the experiment requires different external state.

This page compares the pinned [Codex](../../snapshots/codex/8c076942cb70/chapter.md), [pi](../../snapshots/pi/482c1381a00d/chapter.md), [Goose](../../snapshots/goose/34a222e3f2b9/chapter.md), and [Sidekick](../../snapshots/sidekick/463d653124e1/chapter.md) implementations. Sidekick links require access to its private repository. Source inspection establishes the cited mechanisms, not a guarantee about every frontend or external provider.

## Four operations that look similar

| Operation | Main question |
|---|---|
| Resume | How do I continue the same logical session? |
| Branch within a session | Which earlier history node becomes the active parent? |
| Fork into a new session | Which history and settings initialize a new identity? |
| Delegate to a child agent | What task and authority does another execution receive? |

A child can inherit context without being a user-facing fork. It usually adds ownership, cancellation, and result-delivery obligations. A fork can also exist without running anything: creating its history does not necessarily submit the next prompt. Keep those lifecycle decisions separate in APIs and UI labels.

## Codex: explicit snapshot semantics

Codex exposes two useful `ForkSnapshot` choices. `TruncateBeforeNthUserMessage` selects a prefix before a user-message boundary; when its index is beyond the available history, the active unfinished turn is dropped rather than accidentally copied as complete. `Interrupted` instead retains the current persisted history and, when it ends mid-turn, appends an interruption boundary. This makes the cut policy explicit rather than treating an arbitrary message slice as a valid completed conversation. [Snapshot contract][C1] [Snapshot construction][C2]

The new thread has its own identity and records source lineage. There are copied-history and reference-backed paths. A prepared referenced fork supplies frozen source history and model context, while the manager supplies fork-specific startup instructions and configuration. “Same conversation so far” therefore does not mean “reuse the parent's live callbacks and runtime object.” [Fork startup][C3] [Store preparation][C4]

Tool history introduces another invariant: a model-facing call needs a corresponding output, and an output must have a matching call unless it names an external tool event. Codex's history normalization fills missing call outputs and removes orphaned outputs. An interrupted fork should preserve uncertainty, not manufacture evidence that an unfinished tool succeeded. The referenced fork's model-context boundary also matters after compaction: visible history and the context used for the next request are related views, not necessarily identical lists. [Normalization][C5]

## pi: a tree leaf and a new session file are different choices

pi's session manager stores an append-only tree. `branch` moves the active leaf; future entries become children of that node without deleting the abandoned path. `branchWithSummary` additionally inserts a summary of that path. `createBranchedSession` performs a different operation: it extracts the root-to-leaf path into a new session, records parent-session provenance, recreates labels, and rechains retained entries so label removal does not orphan descendants. It also repairs affected compaction boundary references. [Branch and extraction][P1]

The runtime's `fork` defaults to a position before a selected user entry and returns that user's text for editing. An explicit `at` position retains the selected entry. It tears down the current runtime and creates the replacement around the selected session manager, which is important because extensions and other session-bound objects must belong to the new session. [Runtime replacement][P2]

Context reconstruction follows the chosen path and applies its latest compaction checkpoint. Model and thinking settings are recovered from entries on that path. Tree navigation can summarize abandoned work, refuses navigation during active streaming or compaction, refreshes the finalized projection, and restores transcript tools. This is richer than copying chat bubbles: context, metadata, and the tool surface must agree on the selected branch. It does not restore files changed by those earlier tools. [Context projection][P3] [Settings][P4] [Tree navigation][P5]

## Goose: copy, optional truncation, and provider identity

Goose's ACP `handle_fork_session` validates the effective working directory before copying. It creates another session, optionally truncates conversation history using the `conversationBefore` timestamp, activates the new session with the requested directory and MCP servers, and prepares its agent. Session copying retains settings including provider/model configuration, extensions, recipe values, system-prompt settings, and container configuration. A new session identifier therefore does not imply a fresh environment. [ACP fork][G1] [Copy settings][G2]

The timestamp path deletes messages at or after the cutoff; a separate message-ID truncation method can distinguish messages sharing a timestamp. These boundaries deserve care around tool request/result pairs. This review did not establish that the ACP timestamp cut itself repairs every possible pair or is equivalent to a user-turn boundary. A caller should choose coherent cut points rather than rely on a timestamp alone to express conversation semantics. [Truncation methods][G3]

There is an additional identity boundary: after forking, Goose calls `resume_saved_provider_session`, which finds the latest saved session identifier for the selected provider and invokes `provider.resume`. A copied Goose session may therefore ask an external provider to resume an existing provider session. Whether that provider independently forks, shares state, or rejects the request is provider-specific and was not audited here. New local identity is not proof of new remote identity. [Provider resume][G4]

## Sidekick: scoped branches without assuming a general fork API

The inspected Sidekick implementation establishes branches used for delegation and deliberation, not an arbitrary user-facing “fork this conversation at message X” API. `ContextBuilderService` fetches main-thread messages when `parent_tool_use_id` is absent, or messages scoped by a parent identifier when it is present. Deliberation uses a synthetic branch identifier inside the same conversation for draft and review passes. These are execution and context scopes, not evidence of a cloned top-level conversation. [Scoped context][S1] [Deliberation ownership][S2]

When synthesis incorporates a deliberation branch, `_fold_deliberation_branch` includes draft text as the agent's prior assistant content and review text as attributed user content. Tool traffic does not cross that boundary. This is a deliberate projection that avoids merging unrelated tool-call protocols into the main conversation. It is also different from a full transcript fork or a compaction summary of an abandoned user branch. [Branch folding][S3]

Outpost can resume a wrapped agent's own session, but that external runtime's fork facilities should not automatically be credited to Sidekick's native harness. A current general-purpose conversation-fork contract remains unestablished by this review.

## A fork contract worth specifying

For a new harness, return a fork record containing the new identity, source identity and revision, cut boundary, inherited settings, context-checkpoint reference, and execution-environment choice. Freeze the source revision before assembling the copy so simultaneous appends cannot produce a mixed snapshot. Decide how incomplete tool calls appear, and preserve their uncertain outcomes.

A concrete trace should verify: original history contains a file edit; the fork cuts before that edit; the fork's prompt omits it; both sessions still see the edited file when sharing a workspace. Then repeat with an isolated workspace and verify that only the filesystem snapshot changes that result. Include compaction, duplicate timestamps, cancelled calls, and external-provider resume metadata in the trace.

Summaries deserve provenance too: identify the branch they summarize and keep the original history available for inspection. Finally, do not silently inherit stale approvals merely because a message was copied. Re-evaluate execution policy for the new environment and make the fork's next action explicit. See [compaction](compaction.md), [multi-agent delegation](multiagent.md), and [persistence and recovery](persistence.md) for adjacent state boundaries.

[C1]: https://github.com/openai/codex/blob/8c076942cb70fba8e5c4732c23f1faabc4aed484/codex-rs/core/src/thread_manager.rs#L203-L223
[C2]: https://github.com/openai/codex/blob/8c076942cb70fba8e5c4732c23f1faabc4aed484/codex-rs/core/src/thread_manager.rs#L2700-L2779
[C3]: https://github.com/openai/codex/blob/8c076942cb70fba8e5c4732c23f1faabc4aed484/codex-rs/core/src/thread_manager.rs#L1517-L1662
[C4]: https://github.com/openai/codex/blob/8c076942cb70fba8e5c4732c23f1faabc4aed484/codex-rs/thread-store/src/store.rs#L253-L280
[C5]: https://github.com/openai/codex/blob/8c076942cb70fba8e5c4732c23f1faabc4aed484/codex-rs/core/src/context_manager/history.rs#L964-L980
[P1]: https://github.com/earendil-works/pi/blob/482c1381a00d8d00bc81697e4a980c593db6803e/packages/coding-agent/src/core/session-manager.ts#L1572-L1731
[P2]: https://github.com/earendil-works/pi/blob/482c1381a00d8d00bc81697e4a980c593db6803e/packages/coding-agent/src/core/agent-session-runtime.ts#L262-L352
[P3]: https://github.com/earendil-works/pi/blob/482c1381a00d8d00bc81697e4a980c593db6803e/packages/coding-agent/src/core/session-manager.ts#L469-L513
[P4]: https://github.com/earendil-works/pi/blob/482c1381a00d8d00bc81697e4a980c593db6803e/packages/coding-agent/src/core/session-manager.ts#L418-L433
[P5]: https://github.com/earendil-works/pi/blob/482c1381a00d8d00bc81697e4a980c593db6803e/packages/coding-agent/src/core/agent-session.ts#L3983-L4178
[G1]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose/src/acp/server/fork_session.rs#L5-L83
[G2]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose/src/session/session_manager.rs#L2761-L2807
[G3]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose/src/session/session_manager.rs#L2810-L2845
[G4]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose/src/acp/server.rs#L196-L216
[S1]: https://github.com/datex/sidekick/blob/463d653124e1c2cda46a3f14f01f3d6f384326b1/backend/src/sidekick/services/context_builder_service.py#L455-L515
[S2]: https://github.com/datex/sidekick/blob/463d653124e1c2cda46a3f14f01f3d6f384326b1/backend/src/sidekick_worker/services/deliberation.py#L1-L36
[S3]: https://github.com/datex/sidekick/blob/463d653124e1c2cda46a3f14f01f3d6f384326b1/backend/src/sidekick/services/context_builder_service.py#L1261-L1302
