Back to blog
Workflows

Why every agent run gets its own git worktree

Branches and stashes share one working tree; clones and containers are heavyweight. A git worktree is the sweet spot — a real isolated checkout that shares the repo, so a failed agent run never touches yours.

main · your working tree specship/wf-…

Hand a coding agent your repository and the first question is: where does it work? If the answer is "in your working tree," you've already lost. A run that edits files in place leaves you with half-applied changes when it fails, a dirty tree you can't reason about, and exactly zero ability to run two experiments at once. Isolation isn't a nice-to-have for agent runs — it's the whole game.

SpecShip's answer is the same one for every workflow run: each run executes in its own git worktree. Your branch, your working tree, your uncommitted changes — untouched, the entire time the agent works.

The four ways to isolate, ranked

"Give the agent a clean place to work" has a few obvious solutions. Most of them are worse than they look.

Branch + stash — fragile and not actually isolated

Switching branches and stashing your changes is the reflex move, and it's the worst one. You're still working in the same directory, so the agent and you are sharing one tree; you can't do anything else while it runs. Stashes are easy to lose, easy to apply to the wrong place, and a failed run leaves you reconstructing what was stashed versus what the agent touched. It feels lightweight and quietly isn't.

git clone — isolated but heavyweight

A fresh clone genuinely isolates. It also copies the entire object store — every blob, every commit — costing disk and minutes per run, and it severs the shared history, so you're reconciling two repos afterward. Fine once; miserable as the per-run default.

Container / VM — isolation by sledgehammer

Spinning up a container or VM per run gives you isolation and then some. But you've taken "the agent needs a clean copy of the code" and answered it with image builds, volume mounts, and a process lifecycle to babysit. It's the right tool for untrusted execution; it's overkill for "let the agent edit some files safely."

The goal isn't maximum isolation. It's enough isolation that a failed run is free to throw away.

Why a worktree is the sweet spot

A git worktree is a second working directory attached to the same repository. It checks out its own branch into its own folder, but it shares the one .git object store underneath. That single property is what makes it the right primitive:

  • Real isolation — a separate directory and branch. The agent's edits, builds, and failed tests live entirely outside your working tree.
  • Cheap and instant — no objects are copied. Creating a worktree is a checkout, not a clone; it's fast even on a large repo.
  • Parallel by default — many worktrees off one repo at once, so several agent runs proceed without colliding.
  • Clean to discard — remove the worktree and its branch and the run is simply gone. Nothing partial ever has to be unwound out of your real tree.
what a run does, in git terms
# spin up an isolated checkout off HEAD
git worktree add ~/.specship/worktrees/wf-a1b2 -b specship/wf-a1b2
# …agent plans, edits, runs tests — all in there…
# approve → merge it; reject → remove it, no trace
git worktree remove ~/.specship/worktrees/wf-a1b2

How SpecShip uses it

Every spec-implement (and spec-fix) run does exactly this. When the workflow starts it creates a worktree at ~/.specship/worktrees/… on a specship/wf-<runId> branch off your current HEAD. The agent drafts a plan, you approve it, and it implements, runs the tests, and asserts the spec links — all inside that worktree. You sit at your own branch the whole time, free to keep working or kick off another run.

Discard is the default, not the exception. Approve a run and its worktree merges; reject or cancel it and the worktree is removed with its branch. An unchanged worktree is auto-cleaned. The reversible path is the one that takes zero effort.

The honest caveat

Worktrees aren't magic. They share the object database, so they're bound to one machine and one repo — they don't isolate the filesystem outside the checkout, the network, or the environment. For running untrusted code you still want a container. But for the actual problem here — letting a trusted agent rewrite your code without ever putting your working tree at risk — a worktree gives you the isolation that matters at a cost low enough to pay on every single run. That's why every run gets one.

#git-worktree#isolation#workflows#safety
SE
Selva E.
Building SpecShip · graph, specs & harness
Works on the parsing, storage, and spec layers of SpecShip. Spends an unreasonable amount of time thinking about edges.