> your AI agent picks dependencies from memory; give it dated facts — try starlog.dev ↗ vet your agent's deps ↗ vibe-coding is fine. vibe-importing isn’t. — try starlog.dev ↗ vibe-importing isn’t fine ↗ your agent has never seen your private packages — try starlog.dev ↗ facts for private packages ↗ a linter for the dependencies your AI agent picks — try starlog.dev ↗ a linter for agent deps ↗

Back to Articles

Shepherd: Git-Like Forking for Agent Execution Traces

[ View on GitHub ]

Shepherd: Git-Like Forking for Agent Execution Traces

Hook

What if agent execution wasn't a black box, but a Git repository you could fork, rewind, and merge? Shepherd treats every agent run as an immutable trace with native copy-on-write semantics.

Context

If you've tried building agents that supervise other agents, you've hit the rollback problem. Your meta-agent explores a decision tree, running worker agents down different branches to find optimal strategies. But checkpointing each branch with Docker is glacially slow—docker commit takes seconds, serializing entire filesystems for each fork. LangChain and AutoGPT give you observability hooks, but no native way to snapshot an execution mid-flight, fork it into parallel universes, and replay with 95% of expensive LLM cache intact.

This gap is particularly painful for Monte Carlo Tree Search (MCTS) over agent behaviors, where you're exploring thousands of execution branches. Researchers building agent self-play systems or meta-learning frameworks resort to fragile hacks: manual Git checkpoints between steps, custom serialization of agent state, or just eating the Docker overhead. Shepherd emerged from this frustration, purpose-built for one use case: making agent execution a first-class data structure that meta-agents can manipulate with Git-like primitives.

Technical Insight

Trace Output

Sandboxed Execution

Type annotations

May[GitRepo, RW]

Native policies

Enforces grants

Writes intercepted

Baseline reference

Diff vs baseline

Logs & decisions

Fork/replay

User commits

5x speedup

Task Definition

(Python signature)

Permission Compiler

Syscall Jail

(Seatbelt/Landlock)

Agent Process

Shadow Workspace

(.shepherd/traces/{run_id})

Git Snapshots

(before state)

Changeset Delta

Execution Metadata

KV Cache

(reusable states)

Real Workspace

System architecture — auto-generated

Shepherd's core trick is treating the filesystem as the execution trace substrate. When you define a task, you write a signature-only Python function where type annotations encode permission grants:

from shepherd import task, May, GitRepo, ReadWrite, ReadOnly

@task
def refactor_codebase(
    repo: May[GitRepo, ReadWrite],
    docs: May[GitRepo, ReadOnly]
) -> str:
    """
    Refactor the repository following best practices,
    consulting documentation but not modifying it.
    """

The function body is optional—even empty. The signature itself is the contract. May[GitRepo, ReadWrite] isn't just a type hint; Shepherd's compiler translates it into a native syscall jail policy. On macOS, it generates Seatbelt profiles. On Linux with Landlock support, it creates kernel-enforced LSM rules. Before the agent executes, the runtime locks down filesystem access at the OS layer: the repo binding gets read-write to its workspace root, docs gets read-only, and everything else is denied.

When the agent runs, Shepherd intercepts writes using a 'retained output' layer—a shadow workspace that captures filesystem changes without touching the real directory. Imagine Git's staging area as a runtime construct. The agent thinks it's writing to repo/src/main.py, but those bytes land in .shepherd/traces/{run_id}/output/src/main.py. The original workspace stays pristine until you explicitly commit the trace.

Here's where copy-on-write becomes powerful. Say your meta-agent is doing beam search over refactoring strategies:

from shepherd import Shepherd

shep = Shepherd()
base_run = shep.execute(refactor_codebase, repo=my_repo, docs=stdlib_docs)

# Fork the execution into 3 parallel explorations
branches = [
    shep.fork(base_run, temperature=0.3),  # Conservative
    shep.fork(base_run, temperature=0.7),  # Balanced  
    shep.fork(base_run, temperature=1.2),  # Aggressive
]

for branch in branches:
    branch.replay(continue_from=base_run.last_decision)

Each fork() creates a new trace backed by Git references. Instead of copying gigabytes, Shepherd links to the parent's object store and reuses cached KV states from the base run's LLM interactions. When replay() continues from a decision point, it loads the workspace snapshot via Git checkout semantics—hardlinks for unchanged files, copy-on-write for modifications. This is why it's 5x faster than Docker: no tarball serialization, no filesystem scanning, just pointer updates.

The permission model's coarseness is deliberate. You grant whole-repository access, not file-level ACLs, because Shepherd targets rapid prototyping with untrusted agents. The syscall jail prevents obvious catastrophes (writing to /etc/passwd), while the retained output layer ensures you review changes before committing. It's not paranoid sandboxing; it's optimistic execution with a safety net.

The architecture couples tightly to Git's object model. Each trace stores:

  • A Git ref pointing to the 'before' workspace state
  • The retained output tree (uncommitted changes)
  • Execution metadata (LLM calls, decision points, KV cache keys)
  • Parent trace references for fork provenance

When a meta-agent decides to commit a branch, Shepherd merges the retained output into the workspace and updates the Git ref. When it reverts, it discards the output tree and resets to the parent ref. The entire execution history becomes a DAG you can traverse, diff, and manipulate with familiar Git semantics.

Gotcha

The repository-level permission granularity is a deal-breaker if you need surgical access control. You can't express 'write to src/ but not src/config/' or 'read this API key file but nothing else in secrets/'. Shepherd grants or denies entire directory trees, making it unsuitable for agents that need nuanced permissions within a codebase. If your threat model includes agents tampering with specific files, you'll need external validation logic.

Syscall jail enforcement is brittle outside development environments. macOS Seatbelt works reliably, but Linux Landlock requires kernel 5.13+ and falls back to container-based isolation otherwise. In practice, this means production deployments on most cloud infrastructure need Docker anyway, negating Shepherd's speed advantage—you're back to wrapping the entire runtime in a container to get reliable sandboxing. The framework also assumes filesystem operations are the execution trace. Network calls, database writes, or API interactions happen outside the retained output layer. If your agent queries an external service, that side effect isn't captured or reversible. Replaying a run re-executes those calls with potentially different results, breaking the determinism guarantee.

Verdict

Use if: You're building multi-agent systems where a supervisor performs tree search over worker executions (MCTS-based agent training, evolutionary prompt optimization, hierarchical task decomposition with rollback). The copy-on-write forking genuinely solves the Docker checkpoint bottleneck for research workloads that need to explore hundreds of execution branches rapidly. Also compelling if you're prototyping agents on untrusted codebases and want permission enforcement without manually writing sandbox policies—the signature-as-ACL pattern is elegant for dynamic agent synthesis. Skip if: You need fine-grained permissions beyond repository-level grants, deploy agents in production cloud environments without custom kernel support, or care about non-filesystem side effects like API calls. Also skip for standard single-shot agent workflows—the trace recording overhead buys nothing unless you're actively forking and replaying runs. If you're just chaining LLM calls with observability, stick with LangChain.