How to fork Codex and OpenCode sessions (from UI/TUI, or from your phone)
A practical how-to for branching a session at the right point (fork-from-message vs fork-latest), plus what happens to context after the fork.
- CLI
- 0.1.0
- Preview ref
- f36aa45
Forking is just branching.
You keep the original session intact, and you create a child session that starts from an earlier point.
This guide is a straightforward how-to for forking Codex and OpenCode sessions:
- from their own UI/TUI (when supported)
- and from a phone/browser UI (Happier is one option)
The only thing to get right is where you fork from.
TL;DR
- Decide what you want to branch from:
- Fork latest (branch from “right now”)
- Fork from message (branch earlier)
- In Codex:
- forking is most reliable from the latest session state
- In OpenCode:
- server-native fork-from-message is possible when you’re using an OpenCode server backend
- If you want a consistent fork UI across devices, Happier is one option: it exposes “Fork from this message” inside the transcript and creates a child session automatically.
What “fork” means (in practice)
A fork is not a copy/paste.
A fork is:
- a new session id
- with ancestor context linked
- and a new message stream from the fork point onward
The original session stays unchanged.
Fork latest vs fork from message (how to choose)
Fork latest
Use this when:
- you like the current direction
- you just want to try an alternative approach in parallel
Fork from message
Use this when:
- you can point to the exact moment where you want to diverge
- you want to redo an instruction cleanly
Option A: fork inside Codex
Codex support varies by surface, but the workflow is usually some version of:
- fork the current session (fork-latest)
- continue the forked session as a new branch
When Codex fork-latest is the right move
Fork-latest is great when:
- you want an alternative implementation path
- you want to compare two approaches
- you’re about to do a risky change and want a safe branch
When you actually want fork-from-message
Fork-from-message is the "I want to rewind" move:
- you want to redo an instruction cleanly
- you want to diverge before the session started going off-track
If your Codex surface doesn’t support fork-from-message, the common fallback is replay:
- create a new session
- seed it with enough context to behave like the original
(That’s why a good “fork UX” always includes a replay path.)
Option B: fork inside OpenCode
OpenCode has a strong client/server story.
If you’re using an OpenCode server backend, server-native fork-from-message can be available.
A simple OpenCode decision rule
- If you know the exact message you want to branch from → fork-from-message
- Otherwise → fork-latest
If fork-from-message isn’t available in your current OpenCode surface, the replay fallback applies here too.
Option C: fork from your phone (Happier)
If you want a consistent fork UI from phone/browser/desktop, Happier is one option.
Here’s what it looks like (implementation-backed):
Fork from a specific message
This is the most useful fork, and it’s the one most tools make surprisingly hard.
In Happier:
- Open the session transcript.
- Find the message you want to branch from.
- Choose Fork from this message.
Two details that matter:
- The fork button only appears when that message can be used as a fork point.
- Happier forks by sequence number (
forkPoint: { type: 'seq', upToSeqInclusive }) so the fork point is stable.
A small UX detail I really like
If you fork on a committed user message, Happier treats it like “redo this instruction cleanly”:
- it forks before the user message
- it restores the user message text into the child composer as an editable draft
So you can:
- tweak the wording
- add one missing constraint
- resend it cleanly
Without rewriting the whole setup.
(Implementation-backed: this is resolveForkFromMessageSemantics producing restoredDraftText, which is then written into the child session draft.)
Fork latest (branch from now)
If you don’t care about an exact fork point, fork-latest is still useful.
In practice it’s the “parallel thread starting now” move.
What happens to context in the child session
After the fork:
- ancestor messages are visible as read-only context
- a divider row is inserted (“Forked from …”)
- you can open the parent at the cutoff point
(Implementation-backed: ancestor messages are annotated via injectForkContextRows, and the divider row is rendered by ForkDividerRow.)
What you’ll see after you fork
In the child transcript, Happier injects a divider row:
- “Forked from …”
- with a button to open the parent at the cutoff point
Under the hood, injectForkContextRows annotates ancestor messages as read-only context, and ForkDividerRow provides the “open parent” deep link.
Troubleshooting
I don’t see a “Fork from this message” button
Common causes:
- the session/provider doesn’t support fork-from-message in this surface
- you’re on a message type that isn’t eligible as a fork point
If you can’t fork-from-message, use fork-latest (or a replay/new session approach).
My forked session feels like it lost context
This usually means the fork mechanism fell back to replay with a smaller seed than you expected.
Two fixes:
- fork earlier (include more ancestor context)
- ask for a summary checkpoint in the parent before forking
I want to redo one instruction, not create a whole new branch
Fork-from-message + restored draft text is exactly for this. Fork before the user message, edit the restored draft, and resend.
FAQ
Does forking delete the original session?
No. Forking creates a child session and keeps the original intact.
When should I fork instead of just sending a correction?
If the correction changes the whole direction (or you’re about to do something risky), fork.
If it’s a small clarification, just send a follow-up.
Common mistakes
- forking too late (you really wanted an earlier fork point)
- using fork-latest when you needed fork-from-message
- broadcasting a fork decision without telling teammates what changed