A runnable example of chidori.branch (see
docs/branching-execution.md): an agent
forks itself mid-run into N branches that each explore a strategy from the
same anchored state, then compares the outcomes and picks one.
The agent (agent.ts) does shared "research" once, then forks:
const outcomes = await chidori.branch([
{ label: "outline-first", source: "examples/branching/strategies/outline_first.ts", input: { topic, research } },
{ label: "draft-direct", source: "examples/branching/strategies/draft_direct.ts", input: { topic, research } },
]);
const best = outcomes.filter((o) => o.status === "completed").reduce(pick);Why this beats re-running the whole agent per idea:
- The prefix is paid once. Branches act on the prefix's result (the
parent's VFS plus the explicit
input); they don't re-derive it — so the shared state is byte-identical across branches and the only variable is each branch's code. A controlled experiment, not a stochastic re-roll. - Each branch is its own editable source. Edit
strategies/outline_first.tsand re-run: the branch re-anchors to the same shared state, the other strategy is untouched. - One durable record. The fan-out is recorded as a single
branchcall whose result is the outcomes array.chidori resume examples/branching/agent.ts <run-id> --dir examples/branchingreturns it from the call log without re-running either branch. - Branches nest in the trace. Each branch's host calls carry
parent_seq = the branch call's seq, so an OTLP viewer (tael) shows abranchspan with one subtree per strategy, side by side.
cargo run -- run examples/branching/agent.ts --input '{"topic": "incident postmortem"}'Branch source paths resolve like callAgent paths (relative to the working
directory), so run from the repository root.
With an OTLP endpoint configured the fork is visible live:
OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4317 \
cargo run -- run examples/branching/agent.tsThe strategies are offline-friendly local transforms; swap their bodies for
chidori.prompt(...) calls to compare real model strategies (each branch's
LLM spend is live — cap the fan-out accordingly).
type BranchOutcome = {
label: string;
branchId: string; // <parent run id>-op<branch seq>-branch-<k>
status: "completed" | "paused" | "failed";
output?: Json; // when completed
pendingPrompt?: string; // when paused (e.g. a chidori.input prompt)
error?: string; // when failed
};Nested chidori.branch inside a branch is rejected. Pass
{ concurrency: N } as the second argument to run up to N branches at once
(default 1 — sequential); outcome order always follows variant order.
Each branch persists under the parent run
(.chidori/runs/<run>/branches/op-<seq>/branch-<k>/): its own editable
source.ts, its call log, and the fork-time anchor. That makes a branch
independently operable after the parent has moved on:
# List this run's branches and their states:
chidori branches <run-id> --dir examples/branching
# A branch paused on chidori.input()? Answer it — the branch replays its
# checkpoint with the response and runs to its next outcome:
chidori branch-resume <run-id> <branch-id> --value "blue" --dir examples/branching
# Edit a strategy and re-run ONLY that branch from the same anchored state:
$EDITOR examples/branching/.chidori/runs/<run-id>/branches/op-*/branch-001/source.ts
chidori branch-rerun <run-id> <branch-id> --dir examples/branchingA resumed or re-run branch updates its own store; the parent's recorded outcome is immutable history (compare, don't merge).