Long-running agents accumulate state that no transcript captures. A coding agent at step 10 holds edited files, a running dev server, installed packages, and a warm prompt cache. When it misreads a traceback and rewrites a file that was already correct, neither available recovery path is cheap: patching forward grows the context and the token bill, and restarting from step one re-pays every model and tool call while reproducing nothing exactly, because runs are non-deterministic. Jumping back to step eight is the option engineers actually want, and it is the one existing runtimes cannot offer. Git versions files, not a live process or a cache. Researchers at Northeastern University and Stanford University have released Shepherd, a Python runtime substrate that records an agent run as a Git-like trace of typed events, so any past state can be forked and replayed. The research team reports forks 5× faster than Docker and over 95% prompt-cache reuse on replay.
Is it deployable?
Yes but it is available in early alpha and not ready for production. Shepherd is MIT-licensed and installable with pip install shepherd-ai from PyPI. It needs Python 3.11+. OS-level grant enforcement runs on macOS (Seatbelt) and Linux (Landlock, in a privileged container).
- Industries: Software engineering and DevOps, AI infrastructure and agent-platform vendors, quantitative finance research, security tooling and offensive-security research, and data engineering. The common trait is not the vertical. It is long-horizon agent runs against heavy sandbox state, where a failed run is expensive to redo.
- Applications: Live supervision of coding agents, with a meta-agent reverting a bad write before it commits. Automated recovery from a wrong tool call, without a full restart. Branching exploration over candidate agent strategies, compared side by side. Rollout generation for reinforcement learning, forking at selected turns.
What Shepherd changes
Shepherd is a Python substrate that records an agent’s execution as a first-class object. Every agent-environment interaction becomes a typed event in a Git-like execution trace. Core operations are formalized as functions and mechanized in Lean.
Each interaction is effectively a commit. Unlike Git, the commit covers the agent process and the filesystem together, copy-on-write. A branch therefore carries live state, not just files. Returning to an earlier point is a single fork from that commit.
The research team reports that Shepherd forks the agent process and its filesystem 5× faster than Docker. Because the prompt prefix through the branch point is unchanged, replay achieves over 95% prompt-cache reuse.
The documentation organizes the framework around four concepts: tasks, effects, runs, and workspaces. A task is a typed function whose body the model fills in, so the signature is the contract. An effect is every crossing of the task boundary, and it can be watched, answered, or refused. A run is the durable record of those crossings.
Permissions are declared in the signature. A May[GitRepo, ReadOnly] binding is compiled to that run’s writable roots and enforced at the native syscall jail.
Forking enables higher-order agents that observe a trace and intervene before a bad write commits. The research team demonstrates three applications:
- In runtime intervention, a live supervisor raised pair-coding pass rates on CooperBench from 28.8% to 54.7%.
- In counterfactual meta-optimization, branching exploration beat baselines across four benchmarks by up to 11 points, while cutting wall-clock time by up to 58%.
- In Tree-RL training, forking rollouts at selected turns improved TerminalBench-2 from 34.2% to 39.4%.
Shepherd: replaying an agent run from a commit, not a transcript
Click any step on the rail to fork the run from that point. Northeastern University & Stanford University, arXiv:2605.10913.
Execution trace — step 9 misreads a traceback
Main branch
Forked branch
Why a message log is not enough
A transcript records what the agent said and which tools it called. It does not record the live process, the dev server, the installed packages, or the prompt cache. Shepherd records every agent–environment interaction as a typed event, so a commit covers the agent process and the filesystem together, copy-on-write.
Rewind to step 8 — what actually comes back
Transcript + git checkout
Message historyrestored
Source filesrestored
Running processstill at step 9
Dev server / portsstale
Installed packagesdrifted
Prompt cachecold
Shepherd fork from commit 8
Message historyrestored
Source filesrestored
Running processforked
Dev server / portsforked
Installed packagesforked
Prompt cache>95% reused
Prompt-cache reuse figure is reported in the paper abstract for replay after a fork.
The boundary that is not reversible
Forking undoes filesystem and process state inside the sandbox. It does not undo the outside world. A sent email, a real charge, or a third-party write stays sent. Shepherd’s answer is refusal rather than rollback: every crossing is an effect that a handler in scope can watch, answer, or refuse before it happens.
Recovery strategy — move the slider to set where the run breaks
Break at step
9
Illustrative model, not paper measurements. It assumes a restart re-pays every step and re-prefills the context, while a fork re-pays only the steps after the branch point and reuses the cached prefix. The two verified anchors are the reported fork speed and the reported replay cache reuse.
Reported in the paper
CooperBench pair-coding pass rateno supervisor
+ Tree-RL fork training
5×
Faster fork of agent process + filesystem than Docker
>95%
Prompt-cache reuse on replay after a fork
+11 pts
Peak gain from branching exploration across four benchmarks
−58%
Peak wall-clock reduction from counterfactual meta-optimization
‘+(i===BREAK?’break’:’c’+i)+’
‘;
d.onclick=function(){pick(+this.dataset.i)};
R.appendChild(d);
}
}
function pick(i){
sel=i;draw();
F.innerHTML=”;FL.style.display=’block’;
var sp=document.createElement(‘div’);sp.className=”spacer”;
sp.style.width=((i-1)*41)+’px’;F.appendChild(sp);
var n=0;
var t=setInterval(function(){
n++;if(n>3){clearInterval(t);return}
var e=document.createElement(‘div’);e.className=”fdot”;e.textContent=i+n;F.appendChild(e);
},170);
M.innerHTML=i
:’Commit ‘+i+’ is at or after the bad write. Forking here inherits the broken file. Pick a commit before step ‘+BREAK+’.’;
}
draw();
M.innerHTML=’Steps 1–8 are correct. At step ‘+BREAK+’ the agent misreads a traceback and rewrites a file that was fine. Click a commit to fork from it.’;
var tabs=document.querySelectorAll(‘#mtp-shep-x .tab’),panes={a:’pa’,b:’pb’,c:’pc’,d:’pd’};
tabs.forEach(function(t){t.onclick=function(){
tabs.forEach(function(x){x.classList.remove(‘on’)});this.classList.add(‘on’);
for(var k in panes)document.getElementById(panes[k]).classList.remove(‘on’);
document.getElementById(panes[this.dataset.p]).classList.add(‘on’);
if(this.dataset.p===’d’)bars();
}});
function bars(){
document.querySelectorAll(‘#mtp-shep-x .fill’).forEach(function(f){
f.style.width=”0″;setTimeout(function(){f.style.width=(+f.dataset.w*1.6)+’%’},60);
});
}
var S=document.getElementById(‘sl’);
function sim(){
var n=+S.value;document.getElementById(‘slv’).textContent=n;
var patch=n*1.9+14, restart=n*1.0+4, fork=(n-8>0?n-8:1)*0.9+1.2;
if(restart
