[FEATURE] Conversation Branching — full spec for fork, merge, and tree navigation

Status Open
Maintainer reply None cached
Activity 10 comments · opened Mar 9, 2026

Preflight Checklist

  • [x] I have searched existing issues — this consolidates ideas from #10370 (locked), #12629, #150 (locked), #16276 (locked)
  • [x] This is a single feature request

Summary

Claude Code already has /fork and --fork-session. These create independent sessions from a shared history point. What's missing is the return path — bringing conclusions back, navigating the tree, and managing branches as first-class objects. This would make Claude Code dramatically more useful for exploratory work where you need to try multiple approaches before committing to one.

Business Case

Agentic coding sessions are expensive in time, tokens, and cognitive load. When a user reaches message 40 and wants to try two different approaches, they currently must either:

  1. Pick one and hope — risk wasting a long session if the approach fails
  2. Fork and abandon/fork works, but conclusions from the winning fork can't flow back; the losing fork's lessons are lost
  3. Start over — discard accumulated context entirely

Branching with merge-back solves all three. It turns Claude Code from a linear tool into a tree-structured one, matching how real engineering decisions actually work: explore, evaluate, commit. The closest analogy is git branch + git merge — proven UX that every developer already understands.

What Exists Today

| Capability | Status | Gap |
|-----------|--------|-----|
| Fork from current point | ✅ /fork [name] | Works well |
| Fork on resume | ✅ --fork-session | Works well |
| Rewind current session | ✅ /rewind | Modifies in-place, no branch |
| List/navigate branches | ❌ | /resume shows flat list, no tree |
| Merge conclusions back | ❌ | Must manually copy-paste findings |
| Compare branches | ❌ | No diff between session states |
| Name/tag checkpoints | ❌ | Sessions have IDs, not meaningful names |

Proposed Features (incremental)

Tier 1: Branch awareness (low effort, high value)

/branches or /tree — show the fork tree of the current session lineage.

main (current)
├─ msg 12: fork "try-redis" (15 messages, idle 2h)
│  └─ msg 8: fork "redis-cluster" (3 messages, idle 1h)
├─ msg 28: fork "try-postgres" (22 messages, active)
└─ msg 35: (you are here)

/fork <name> — already exists, but names should be visible in /resume and /branches.

/switch <name|id> — jump to a sibling/child branch. Current session suspends; target session resumes. Equivalent to git checkout <branch>.

Tier 2: Merge-back (medium effort, transformative)

/merge <name|id> — in the current session, inject a summary of another branch's work.

The merge is not replaying tool calls. It's an LLM-generated summary of what the branch accomplished, inserted as a system message. Equivalent to reading a colleague's findings.

/merge try-redis

Inserts into current context:

[Branch "try-redis" merged — 15 messages, 3 file edits]
Summary: Redis approach works for caching but requires sentinel
for HA. Implemented in src/cache.rs. Key finding: latency drops
from 12ms to 0.3ms for repeated queries. Abandoned because
operational complexity too high for single-node deployment.

Merge strategies:

  • summary (default): LLM-generated digest of the branch, injected as context
  • custom: /merge try-redis "Redis works but too complex, use SQLite instead" — user-written summary
  • silent: /merge try-redis --silent — mark as merged for bookkeeping, don't inject anything

Tier 3: Checkpoints and tags (low effort, quality of life)

/checkpoint [name] — bookmark the current conversation state with an optional name. Like a git tag on the message timeline.

/rewind <checkpoint> — rewind to a named checkpoint (currently rewind only goes to auto-created restore points).

This gives users explicit save points before risky operations, without creating a full fork.

Tier 4: Branch comparison (aspirational)

/diff <branch-a> <branch-b> — LLM-generated comparison of two branches' approaches and outcomes.

/diff try-redis try-postgres
Both branches solved the caching problem:
- try-redis: 0.3ms latency, requires sentinel, 3 new dependencies
- try-postgres: 1.2ms latency, uses existing infra, materialized views
Recommendation: try-postgres — simpler operations, acceptable perf.

Implementation Notes

Data model

The JSONL session format already has parentUuid linking messages and isSidechain for subagents. Fork metadata could extend this:

// In session metadata (or a sibling .meta.json)
{
  "sessionId": "abc123",
  "forkOf": "parent456",       // null for root sessions
  "forkAtMessage": "msg789",   // message UUID where fork occurred
  "forkName": "try-redis",     // user-provided name
  "mergedInto": ["parent456"], // sessions this was merged back into
  "checkpoints": [
    {"name": "before-refactor", "messageId": "msg012", "created": "..."}
  ]
}

Context budget

Merge summaries should be treated like compacted context — they compress a branch's work into a few hundred tokens. This is strictly cheaper than the alternative (user manually copy-pasting findings, which wastes context on formatting and repetition).

Git integration (optional, not required)

Branches could optionally map to git branches (as #150 proposed), but this is not required for the core feature. Conversation branching is useful even when not writing code — for research, planning, debugging. Tying it to git would limit the feature unnecessarily.

Prior Art

  • #10370: Comprehensive proposal for chat branching with selective merge-back (18 👍, detailed UX mockups, storage format spec). Locked.
  • #12629: Request for in-session /fork UI (15 👍). Open.
  • #150: Conversation forks as git branches (locked).
  • #16276: Fork from specific message (closed as duplicate).
  • ChatGPT: Has conversation branching in the web UI (edit a message → creates a branch), but no merge-back.
  • Cursor: Has checkpoint/restore but no branching.
  • agent-deck (community): Wraps --fork-session in tmux for parallel sessions.

Summary

The primitives exist (/fork, --fork-session, session JSONL with parent links). The missing pieces are branch awareness (see the tree), merge-back (bring conclusions home), and naming (human-meaningful labels). Tiers 1-2 would make Claude Code meaningfully better for any session longer than 20 messages. Tier 1 alone would be a significant improvement with relatively low implementation cost.

---

Addendum: Cross-session context sharing (the git remote analogy)

The branching model above covers forks within a single session lineage. But there's an equally important use case: sending context between unrelated concurrent sessions.

Real workflow

I'm working on two related projects simultaneously:

  • Session A: valdivia-analysis/ — computational geometry, GPU kernels, benchmarking
  • Session B: vga-paper/ — the academic paper describing Session A's algorithm

Session A discovers that p=10 HLL precision gives R²=0.996 at 3.6x less memory than p=12. Session B needs this finding for the results section. Today I copy-paste between terminals. What I want:

# In Session A (valdivia-analysis)
/push vga-paper "p=10 is the sweet spot: R²=0.996 IHH, R²=0.982 MD, median err 1.25%/0.44%, 4.1s BFS. 3.6x less memory than p=12."

# In Session B (vga-paper) — receives it as injected context
[From session "valdivia-analysis"]: p=10 is the sweet spot: R²=0.996 IHH...

This is git remote for conversations

| Git | Conversation equivalent |
|-----|------------------------|
| git remote add | Register another session as a peer |
| git push | Send a finding/conclusion to a peer session |
| git pull | Request a summary of a peer session's recent work |
| git fetch | Check what's new in a peer without injecting it |

Why this matters beyond single-user workflows

The same primitive enables multi-user agent collaboration:

  • My Claude session pushes a question to a colleague's Claude session
  • A CI/CD agent pushes test results to a developer's active session
  • A research agent pushes findings to a coding agent working on implementation

This connects to #32504 and #24798 but frames it differently: those issues focus on orchestration (coordinator directing workers). The git-remote model is peer-to-peer — any session can push to any other, no coordinator required. It's the difference between a CI pipeline and distributed version control.

Proposed commands

  • /push <session-name|id> "message" — send context to another session
  • /pull <session-name|id> — request an LLM-generated summary of another session's recent work
  • /peers — list sessions that have been registered as peers (or auto-discover by project proximity)
  • /inbox — view messages pushed to this session from peers

Messages arrive asynchronously. The receiving session sees them on next user interaction (like checking email), not as interrupts. This keeps sessions autonomous while enabling information flow.

View original on GitHub ↗

10 Comments

stiege · 5 months ago

This issue consolidates ideas from several related proposals that were auto-closed and locked:

  • #10370 (@AlexZan) — chat branching with selective merge-back
  • #12629 (@jonkhler) — in-session fork UI
  • #150 (@adamavenir) — conversation forks as git branches
  • #16276 (@jdxbla) — fork from specific message
  • #24798 (@hmcg001) / #32504 (@SecludedCorner) — inter-session communication

Also builds on work by @asheshgoplani (agent-deck fork implementation) and @smconner (search-term consolidation on #10370).

If this captures what you were after, a 👍 on the issue would help signal demand. Feedback welcome — especially on the merge-back semantics (Tier 2) and cross-session push/pull (the git-remote addendum).

cc @NickNick @tbrebant @wxtry @kyryl-lebedin @marcindulak

SecludedCorner · 5 months ago

Great consolidation, @stiege — especially the Addendum section with the git remote analogy.

We're running a multi-team coordinator workflow that maps directly to your cross-session push/pull proposal:

  • Research team session: 24 AI agents (#0–#23) doing architectural research and voting on design decisions
  • Dev team session: 7 agents running SOPs (Standard/Release/Hotfix/DOC/Simulation)
  • Coordinator session (me): bridges the two teams — generates master letters, transfers deliverables, maintains shared memory

Today the coordinator manually transfers files between teams via shared directories and updates memory files. The pain points:

  1. No session discovery — I can't see if the research or dev session is still running
  2. No push capability — when dev finishes a build, research doesn't know until I manually relay it
  3. File-based mailbox is fragile — no delivery confirmation, no read receipts, sessions don't notice new files without polling

Your proposed /push + /inbox would eliminate our entire manual relay workflow. Specifically:

# Coordinator pushes research output to dev session
/push dev-team "Cycle 02-8 research complete. 7 decisions passed. Plan32 spec ready in share/research_team_suggestion/cycle02-8/"

# Dev session checks inbox on next interaction
/inbox
→ [From coordinator]: Cycle 02-8 research complete...

The peer-to-peer model (vs. coordinator-directed) is key — sometimes the dev team needs to push findings directly to research without going through the coordinator.

+1 on the full proposal. Tiers 1-2 are valuable, but the Addendum is what would be transformative for multi-agent workflows.

unthingable · 5 months ago

Use case: retroactive fork with selective compaction

Related: #28716 (summarize before here)

There's a variant that sits between Tier 1 and Tier 2: retroactive branch-and-compact. The scenario:

You're 60 messages in and realize messages 35–60 went down a tangent. You want to:

  1. Fork at message 35
  2. The new session gets messages 35–60 verbatim (full fidelity)
  3. Messages 1–34 get compacted into a summary as the session's preamble

The result is a clean, focused session for the tangent — with enough compressed context to remain coherent, but without dragging along the full weight of the earlier conversation.

This differs from /fork today (which copies everything up to the fork point verbatim) and from /compact (which compresses the whole session indiscriminately). It's the combination: fork + selective compaction at the branch point.

The git analogy would be git checkout -b tangent && git rebase --squash main~34 — take the recent commits as-is, squash the old history.

yurukusa · 5 months ago

A hook-based approach can implement basic conversation branching via checkpoints:

PROMPT=$(cat | jq -r '.userPrompt // empty' 2>/dev/null)
BRANCH_DIR="$HOME/.claude/branches"
mkdir -p "$BRANCH_DIR"
if echo "$PROMPT" | grep -qiE '(/branch|save checkpoint|fork here)'; then
    NAME=$(echo "$PROMPT" | sed 's/.*\(branch\|checkpoint\|fork\)\s*//' | head -c 30 | tr ' ' '-')
    [ -z "$NAME" ] && NAME="checkpoint-$(date +%s)"
    CHECKPOINT="$BRANCH_DIR/$NAME"
    mkdir -p "$CHECKPOINT"
    git stash create > "$CHECKPOINT/git-stash" 2>/dev/null
    git diff --stat > "$CHECKPOINT/changes.txt" 2>/dev/null
    git log --oneline -5 > "$CHECKPOINT/history.txt" 2>/dev/null
    echo "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$CHECKPOINT/timestamp"
    jq -n --arg n "$NAME" '{"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":"Checkpoint saved: " + $n + ". Use /branch restore <name> to return to this state."}}'
fi
if echo "$PROMPT" | grep -qiE '/branch restore'; then
    NAME=$(echo "$PROMPT" | sed 's/.*restore\s*//' | tr ' ' '-')
    CHECKPOINT="$BRANCH_DIR/$NAME"
    if [ -d "$CHECKPOINT" ]; then
        STASH=$(cat "$CHECKPOINT/git-stash" 2>/dev/null)
        [ -n "$STASH" ] && git stash apply "$STASH" 2>/dev/null
        jq -n --arg n "$NAME" '{"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":"Restored checkpoint: " + $n}}'
    fi
fi
if echo "$PROMPT" | grep -qiE '/branch list'; then
    LIST=$(ls "$BRANCH_DIR" 2>/dev/null | while read d; do echo "  $d ($(cat "$BRANCH_DIR/$d/timestamp" 2>/dev/null))"; done)
    jq -n --arg l "$LIST" '{"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":"Saved checkpoints:\n" + $l}}'
fi
exit 0

This gives you basic /branch, /branch restore, and /branch list commands via hooks.

HyeonuPark · 5 months ago
Disclaimer: The comment below is generated by claude code. The idea is mine, the words and the research is not. But the entire content is under my responsibility.

Prefix cache reuse makes fork/merge nearly free - here's why

Building on the discussion here and in related issues (#10370, #18840, #28415, #12790), I want to add an

The three-way dilemma today

As emernic noted in #12790, the current options are all unsatisfying:

| Mode | Sees parent context? | Prefix cache hit? | Intermediates pollute parent? |
|---|---|---|---|
| Inline (default) | Yes | Yes | Yes (permanent) |
| Agent / context: fork | No | No (fresh prompt) | No |
| Workarounds (log replay, etc.) | Partial | No | Varies |

Why branching is cheaper than you'd expect

The Anthropic API caches KV states for matching prompt prefixes. A branched execution that copies the parent's message array as its prefix would hit the cache automatically - cached tokens are ~90% cheaper and near-instant. The branch diverges only at the append point (the scoped task + tool calls), so the entire shared prefix is served from cache.

This means fork/merge isn't just a convenience - it's cheaper than the current subagent approach, which rebuilds its entire prompt from scratch every turn with zero cache benefit.

For a session at 100k tokens with a skill producing 20k tokens of intermediate output:

  • Inline: 100k (cached) + 20k permanently added. Stale intermediates compete with useful context at compaction time.
  • Subagent: Fresh prompt every turn, no cache reuse. Clean parent, but expensive and context-blind.
  • Branched: 100k (cached) + 20k in branch only. Parent gets back a small result. Next turn: ~100.5k with full cache hit.

For N parallel branches from the same point (the scenario in #28415), all N share the same cached prefix - the redundant ~50k x (N-1) token cost disappears entirely.

Implementation: harness-level only, no API changes

The harness already manages the message array:

  1. Fork: Copy the current message array. Append the scoped task prompt.
  2. Execute: Send to the API normally. Shared prefix hits cache. Tool calls append to the copy only.
  3. Merge: Insert the final assistant message (or structured return value) into the original array. Discard the copy.

This could surface as:

  • context: branch in skill frontmatter (a third option alongside inline and fork, as Crazytieguy suggested the parent should control)
  • branch_from_current: true on the Agent tool
  • A merge-back path for the existing /fork command (completing the gap identified in this issue)

Relation to the tier structure

This supports the tiered approach proposed here. Prefix cache reuse makes Tier 1/2 forks cheap enough to be casual - you wouldn't need to think twice about branching for a quick investigation, because the cost is essentially "only pay for the new work, not for re-reading the conversation."

pietrosperoni · 4 months ago

Great spec. One thing I'd add to Tier 1 that would cover a very common workflow:

/branch "name" --from N — Create a named branch from N prompts ago, without leaving the current session.

Use case: You're deep in a technical architecture discussion and it naturally forks into business/legal implications. You realize 3 prompts later that you want both threads. Today this
requires: /branch (only from current point) → exit → picker → rename → re-enter → /rewind. By then you've lost your flow.

The proposed command would:

  1. Create the branch from the specified history point
  2. Name it immediately (no need to exit and use the picker)
  3. Stay in the current session (you switch to the new branch when you're ready via /switch)

Related: there's currently no way to see the current session ID from inside a session (/session-info or similar), which makes managing parallel branches very hard.

[dictated to me by my claude code after we discussed it, Pietro]

LiuShiyuMath · 3 months ago

Hi Alex,

Your #32631 spec stuck with me — modeling conversation branching as git branch + fork → evaluate → merge-back, with a tree to navigate the rest. Cleaner than how Claude Code thinks about it today.

I noticed swarm-mcp too — you're already running parallel Claude agents in Docker. So you've probably hit the thing I'm chasing: when several branches finish overnight, deciding which one is actually safe to merge back is its own job, separate from getting the work done.

One genuine question, no pitch: when you've got multiple parallel agent outputs to merge, do you enjoy going through each one yourself — reading the diff, working out why it changed what it did — or would you rather just have a fast, trustworthy read on which branch is merge-safe and skip the deep-dive?

I'm async-only and a few timezones off, so no call — a one-line reply is plenty.

For context on why I'm asking: I'm building the thing that sits after the agent finishes. The agent does the work overnight; in the morning you get a short report that tells you in a few minutes whether it's safe to merge, instead of reading all of it line by line.

Best,
Shiyu

m13v · 3 months ago

the thing I keep hitting with fork/merge proposals like this: the blocker isn't the merge command, it's that the parent session keeps auto-compacting underneath you. you fork at message 40, explore a branch or two deep, and by the time you want to merge findings back the parent's context already got summarized out from under you, so there's nothing faithful left to merge into. tree-structured work only holds if every node keeps its full context independently and survives a restart. the parentUuid plumbing handles lineage fine, but lineage isn't the same as preserved context per node. separate those two and merge-back gets a lot more tractable.

coolcorexix · 2 months ago

For Tier 1/3 (branch from a past message + checkpoint navigation), there's something working today: hit Ctrl-B in ccfind (claude-grep) on any session row to fork that session at any past user message — creates a new session file in Claude Code's native /btw-compatible format. No merge-back yet (Tier 2), but the branch-from-checkpoint UX is functional.

brew install coolcorexix/tap/claude-grep

— happy for this to be superseded by a native impl, just sharing as existence proof.

KCSAbeywickrama · 1 month ago

I just created this simple tool: branch-graph, as a workaround for the following gap mentioned in this issue until it's implemented natively.

| Capability | Status | Gap |
|------------|--------|-----|
| List/navigate branches | ❌ | /resume shows a flat list instead of a branch tree |

The branch-graph UI looks like the following, making it much easier to navigate between branches.

Branches in my-project

1  ● a4f3d8c2  Draft initial README outline
2  ● 9b6e1f47  Design payment retry logic
3  ├─● c8a2b91d  stripe-webhooks
4  │  ├─● 5e7f3a06  Add signature verification for Stripe webhooks
5  │  └─● 1d4c9b82  Handle partial refunds and capture expiration (most recent)
6  └─● 7f2e8c15  paypal-webhooks
...