Branch a session
Fork at any prior turn so dead ends stay reachable, alternative paths run side by side, and a misdirected steering message no longer means losing the work it sat on top of.
/undo deletes, /branch rewinds
Cantrip stores the conversation as a tree, not a flat list. Every assistant turn carries the id of the turn it replied to, and the active session is just “which leaf is currently live.”
/undo walks back by
removing rows: the messages disappear from history and
the working tree restores from the snapshot before that turn.
Use it when the last turn was wrong and you want the alternate
reality where it never happened.
/branch walks back without removing anything: the
session pointer moves to a prior turn, but every turn that
hung off the old leaf is still in the SQLite store and stays
reachable. Use it when the last turn might have been wrong
or might be salvageable, and you want both options
available later.
Quick reference
| Command | What it does |
|---|---|
/branch |
Fork before the most recent user turn. The typical recovery from a bad steering message. |
/branch <turn-id> |
Move the active head to a specific turn. The turn id comes from /tree. |
/tree |
Render every turn in the session as an indented tree, with an asterisk on the active branch. In the TUI this is an interactive picker; in the CLI it prints to chat. |
cantrip export-transcript <charm> --branch <turn-id> |
Export the conversation path leading to a specific leaf, even if that leaf is no longer the active branch. |
Recover from a bad steering message
You typed “use Path B for this Flask workload” and the agent dutifully started scaffolding a Path B charm even though you meant Path A. Three turns later you realise the mistake.
cantrip> /branch
Forked before turn 14 (your last user message).
Active branch: turn 13 — “Looks good, go ahead.”
The Path B work didn't disappear — it's still on the old
branch. Your next user message extends the active branch
from turn 13. If you ever want to inspect the abandoned
Path B exploration, /tree will show it.
Explore alternatives in parallel
For a charm where the substrate or relation graph has more than one defensible answer, branching lets you walk both paths and compare them rather than picking blind.
- Drive the agent down option A as a normal session.
- Inspect the result, then
/branch <id>back to the design-decision turn. - Steer the agent down option B from the same starting point.
- Use
/treeto see both branches. - Pick one as the active branch; export the other for reference if it had useful artefacts.
cantrip> /tree
Session 4f8a… (charm: my-flask)
* turn 18 — “Run acceptance tests” (Path B branch)
turn 25 — “Add ingress integration” (Path A branch)
Press Enter on a row in the TUI picker to dispatch
/branch <id> for that turn. Escape leaves
the active branch alone.
Export an off-branch path
Branching changes which turns are live, not which turns exist. Every leaf is exportable by id even after the head moves elsewhere:
$ cantrip export-transcript ./my-flask --branch t-25 --format html
Wrote transcript_t-25.html (Path A exploration, 18 turns).
Without --branch, exports follow the currently
active branch, so a forked session exports only the active
path by default.
Snapshots, undo, and branches together
Every user turn takes a working-tree snapshot before it
executes (unless --no-snapshots /
CANTRIP_SNAPSHOTS=false turns it off). Those
snapshots back both /undo and
/branch:
/undorestores the snapshot and deletes the conversation row./branchrestores the snapshot of the target turn and leaves every existing conversation row in place.
The snapshot repo lives at
$XDG_STATE_HOME/cantrip/snapshots/<hash>/,
outside the charm tree, so git clean -fdx on the
charm cannot wipe out your branch history.
Caveats
-
Branching is a session-local feature. Off-branch turns
live in the session's SQLite file
(
.cantrip); a fresh charm clone won't see them until you copy the session over. -
Custom auto-commits (per-turn
commits) sit in the charm's git history regardless of
which branch is active. After a
/branchback, the working tree matches the older snapshot butgit logstill shows the commits from the abandoned branch. Reconcile withgit reset --soft <sha>if you want the repo history to follow the conversation. -
Long-lived sessions accumulate dead branches.
/treestays compact (one line per turn) regardless, but the SQLite file grows. The session export format preserves branches; a fresh session simply starts empty.