Two branches, two purposes
Each run creates two Git branches that work in tandem:| Branch | Ref format | Contains |
|---|---|---|
| Run branch | fabro/run/{run_id} | File changes made by agents and commands — the actual work product |
| Metadata branch | refs/fabro/{run_id} | Checkpoint JSON, the workflow graph, a run manifest, and offloaded artifacts |
Run branch commits
After each node finishes, Fabro stages file changes and creates a commit on the run branch. Files matching[checkpoint] exclude_globs patterns (configured in run.toml or server.toml) are excluded from staging:
| Part | Description |
|---|---|
| Subject line | fabro({run_id}): {node_id} ({status}) |
Fabro-Run trailer | The run ID |
Fabro-Completed trailer | Number of completed nodes so far |
Fabro-Checkpoint trailer | SHA of the corresponding commit on the metadata branch |
Fabro-Checkpoint trailer links each run branch commit to its metadata branch commit, so you can navigate from file changes to the full execution state and back.
Metadata branch
The metadata branch (refs/fabro/{run_id}) is an orphan branch that stores structured run data using Git’s object storage directly (via git2). It is initialized at run start with:
manifest.json— Run metadata: run ID, graph name, node/edge counts, base SHA, and branch namegraph.fabro— The workflow DOT source as it was parsed
checkpoint.json— Full execution state (see below)artifacts/*.json— Any offloaded artifact data (large context values over 100KB)nodes/{node_id}/— Per-node execution trace files (prompts, responses, status, diffs — files under 512KB from an allowlist)
What’s in a checkpoint
Thecheckpoint.json captures everything needed to resume a run:
| Field | Description |
|---|---|
timestamp | When the checkpoint was created |
current_node | The node that just completed |
next_node_id | The next node the engine would execute |
completed_nodes | Ordered list of all completed node IDs |
node_retries | How many retry attempts each node has used |
node_outcomes | Full outcome (status, context updates, usage) for each completed node |
context_values | Snapshot of the entire run context |
logs | Internal log entries |
git_commit_sha | SHA of the run branch commit at this checkpoint |
loop_failure_signatures | Failure signature counts for loop detection |
restart_failure_signatures | Failure signature counts across loop-restart edges |
checkpoint.json in the run directory for quick local access.
Worktrees
Fabro uses Git worktrees to isolate workflow runs from your working directory. When a run starts in a clean Git repository:- Fabro records the current HEAD as the base SHA
- Creates a new branch
fabro/run/{run_id}at that SHA - Adds a worktree at
{run_dir}/worktreeon that branch - Changes into the worktree directory for the duration of the run
If the working directory has uncommitted changes, Fabro skips worktree setup and runs in place, logging a warning. Git checkpointing is disabled in this case.
fabro/meta/{run_id} (rather than the local refs/fabro/{run_id} custom ref, since GitHub disallows branch names starting with refs/).
Resuming a run
There are two ways to resume an interrupted run:From a checkpoint file
Resume from acheckpoint.json saved in the run directory:
From a run branch
Resume from the Git branches created during a previous run:refs/fabro/01JKXYZ...), re-attaches a worktree to the existing run branch, and resumes execution. No workflow file argument is needed — everything is recovered from Git.
What happens during resume
What happens during resume
- Fabro reads
checkpoint.jsonfrom the metadata branch - Reads
manifest.jsonandgraph.fabroto reconstruct the workflow - Creates a fresh worktree attached to the existing run branch
- Restores the full context, completed node list, retry counts, and failure signatures
- If the checkpointed node used
fullfidelity, downgrades the first resumed node tosummary:high(since the original conversation thread no longer exists in memory) - Continues execution from
next_node_id
The checkpoint cycle
Here’s the full sequence that runs after every node completes:- Save checkpoint to disk — Write
checkpoint.jsonto the run directory - Write metadata branch — Serialize the checkpoint and any new artifacts to the metadata branch (shadow commit)
- Commit to run branch — Stage all file changes, commit with structured trailers linking to the shadow commit SHA
- Update checkpoint — Re-save
checkpoint.jsonwith thegit_commit_shafield set
Inspecting run history
Because checkpoints are plain Git commits, you can inspect them with standard Git tools:When checkpointing is active
Git checkpointing activates automatically when:- The working directory is a clean Git repository (local and Docker sandboxes)
- The sandbox is Daytona (metadata branch on the host, commits inside the sandbox)
- The working directory has uncommitted changes
- The working directory is not a Git repository
- The run uses
--dry-run