Claude Code CLI: Session Branching & Parallel Conversations - Programmatically via CLI in non-interactive mode!
Experiment: Exploring how to fork a single Claude Code conversation into multiple parallel branches using
--session-id,--resume, and--fork-session.
Just like git branch lets you explore multiple code paths from a single commit,
Claude Code's session flags let you fork a conversation at a decision point
and explore multiple directions independently — each branch remembering the
shared context.
┌──► Branch A (session $A)
│
Start ───┼──► Branch B (session $B)
│
└──► Branch C (session $C)
| Flag | Short | Description |
|---|---|---|
--session-id <uuid> |
— | Assign a specific UUID to the session (must be valid UUID) |
--resume <id|name> |
-r |
Resume a previous session by ID or name |
--fork-session |
— | When resuming, create a new session instead of mutating the original |
--continue |
-c |
Resume the most recent session in the current directory |
--model <alias> |
— | Override model for this session (haiku, sonnet, opus) |
-p / --print |
— | Non-interactive "SDK mode": run prompt, print response, exit |
Official docs: CLI Reference · Common Workflows – Resume sessions
From the official docs:
--fork-session— When resuming, create a new session ID instead of reusing the original (use with--resumeor--continue).
Key behaviors:
- Must be combined with
--resumeor--continue— it doesn't work standalone - The fork inherits the full transcript and context window from the parent
- The original session is not modified — it stays at the fork point
- Forked sessions appear grouped under their root in the
/resumepicker - You can assign a deterministic ID to the fork via
--session-id
A naive test (fork → tell branch "you are A" → ask "what are you?" → "A") proves
nothing — it only shows a session remembers its own messages. To actually prove
that --fork-session inherits the parent's context, we need a secret planted
in the trunk that is never repeated to any fork.
- Trunk: Plant a random secret codeword (e.g.
zebra4827) - Fork A/B/C: Ask each to recall the secret — without mentioning it again
- Isolation check: Ask Branch A about Branch B's label (should fail)
- Resumability: Resume a branch and verify it still knows the secret
# === SETUP ===
SECRET="zebra$(shuf -i 1000-9999 -n 1)" # Random secret
Start=$(uuidgen); A=$(uuidgen); B=$(uuidgen); C=$(uuidgen)
# === STEP 1: Plant secret in the trunk ===
claude --model haiku --session-id "$Start" \
-p "The SECRET codeword is: $SECRET. Remember it. Say only: 'Secret received.'"
# === STEP 2: Fork — secret is NOT repeated here ===
claude --model haiku --resume "$Start" --fork-session --session-id "$A" \
-p 'You are Branch A. What is the SECRET codeword from earlier? Reply with ONLY the codeword.'
claude --model haiku --resume "$Start" --fork-session --session-id "$B" \
-p 'You are Branch B. What is the SECRET codeword from earlier? Reply with ONLY the codeword.'
claude --model haiku --resume "$Start" --fork-session --session-id "$C" \
-p 'You are Branch C. What is the SECRET codeword from earlier? Reply with ONLY the codeword.'
# If each fork replies with the correct secret → context inheritance proven!
# The fork prompt never mentioned the secret — it could only know it from the trunk.
# === STEP 3: Verify isolation ===
claude --resume "$A" \
-p 'What label was Branch B assigned? If you do not know, say "unknown".'
# Should say "unknown" — branches can't see each other
# === STEP 4: Resume and verify persistent memory ===
claude --resume "$C" \
-p 'What is the SECRET codeword and your branch label?'
# Should recall both the trunk secret AND its own label- The secret exists only in the trunk's conversation history
- Fork prompts never mention the secret — if a fork knows it, the only
explanation is that
--fork-sessioncopied the trunk's full context - Branch isolation confirms forks don't cross-contaminate
See claude-code-session-branching-experiment.sh
for a copy-paste-ready script that reproduces this experiment.
# Build shared context about a bug
claude --session-id "$BASE" -p "Analyze the auth timeout bug in src/auth.ts. Don't fix yet."
# Fork: try two different fix strategies
claude -r "$BASE" --fork-session --session-id "$FIX_A" -p "Fix using retry with exponential backoff"
claude -r "$BASE" --fork-session --session-id "$FIX_B" -p "Fix using connection pooling"
# Review each
claude -r "$FIX_A"
claude -r "$FIX_B"# Shared analysis
claude --session-id "$BASE" -p "Read the codebase and summarize the architecture."
# Fork N tasks in parallel (background)
claude -r "$BASE" --fork-session -p "Write unit tests for auth module" &
claude -r "$BASE" --fork-session -p "Write unit tests for payment module" &
claude -r "$BASE" --fork-session -p "Write unit tests for user module" &
wait# Start interactive, reach a decision point, then Ctrl+C
claude --session-id "$ROOT"
# ...conversation about refactoring approach...
# Fork interactively to try each approach
claude -r "$ROOT" --fork-session # explore approach 1
claude -r "$ROOT" --fork-session # explore approach 2Sessions are stored locally at:
~/.claude/projects/<cwd-path-encoded>/
Each session is a JSONL transcript. Forked sessions reference their parent,
which is why the /resume picker can group them together.
- Name sessions with
/renameinside interactive mode for easier discovery - Use
--model haikufor cheap/fast exploration branches - Combine with
--output-format jsonfor programmatic pipelines --no-session-persistence(print mode only) to create throwaway branches- Press
Esc+Escinside interactive mode to rewind to earlier messages (creates a fork) - The
/resumepicker groups forked sessions — press→to expand children
- CLI Reference – Claude Code Docs
- Common Workflows – Resume/Fork
- Feature Request: Session Branching #12629
- Reddit: Branch a conversation into a second window
- Managing Multiple Sessions Without Worktrees – GitButler Blog
- 32 Claude Code Tips – Agentic Coding Substack
- Claude Code Definitive Reference – Blake Crosley