Isolation
The @frites/isolation package (packages/isolation) gives each child agent its own git worktree to edit in, captures the resulting diff, and lands an approved diff on a fresh branch. WorktreeManager implements the WorktreeManagerLike interface the core engine depends on, so the engine has zero git coupling. See the package overview in services/isolation.
The authoritative implementation result is never the child's output text; it is the git diff captured from its worktree. This is what makes the worktree path frites's strongest verification surface.
Worktree lifecycle
Resolve base
resolveBase(repoPath, ref?) first asserts the path is a git repo (assertGitRepo runs git rev-parse --is-inside-work-tree; a non-repo raises an instructive error pointing at git init). It then resolves the base ref (default HEAD) to a concrete SHA with git rev-parse, so every child branches from the same immutable commit.
Create
create(repoPath, runId, agentId, baseSha) runs:
git worktree add --quiet -b frites/run/<runId>/<agentId> .frites/worktrees/<runId>/<agentId> <baseSha>The worktree path lives under .frites/worktrees/<runId>/<agentId> inside the repo (gitignored, local to the repo). The branch is namespaced frites/run/<runId>/<agentId> so it can never collide at the ref level with the apply branch frites/apply/<runId>. Git refs are files, so a branch named frites/<runId> could not coexist with frites/<runId>/<agentId>.
The engine creates worktrees concurrently (one per child) and registers each handle in its shared handles map the instant create returns, so cleanup always covers them on any later throw.
captureDiff
After a child exits, captureDiff(worktreePath) reads the actual change out of git:
git add -A: stage everything, including new files, so the diff is complete.git diff --staged --no-color -- . <excludes>: the unified diff.git diff --staged --name-only -- . <excludes>: the touched-file list.
The excludes (DIFF_EXCLUDES) drop node_modules, dist, and .frites so generated artifacts never pollute candidate diffs. A candidate is usable only when its status is succeeded and it touched at least one file; this captured diff is the candidate's diff and filesTouched.
Seeding the synthesis worktree
applyDiffToWorktree(worktreePath, diff) exists to seed the synthesis worktree from a known-good tree. It runs git apply --3way --index, applying a captured candidate diff into a worktree created from the same base SHA (so the 3-way apply is conflict-free). --index stages the result, and the diff is newline-terminated if needed. This method is optional on the interface; when it is absent or throws, synthesis falls back to fresh-from-base.
Apply to branch
applyToBranch(repoPath, runId, diff) is the one mandatory human gate. It is separate from implementation and lands an approved diff:
Assert the path is a git repo.
Require a clean working tree:
git status --porcelainmust be empty, else it throws asking the user to commit or stash first (frites is about to switch branches).git switch -c frites/apply/<runId>: create and check out a fresh branch.git apply --3way --indexthe diff. If apply fails, it throws with the branch already checked out so the user can resolve manually.
It never touches the user's current branch history, never auto-merges, and never pushes. The MCP frites_apply tool drives this path; with synthesis, a reviewer can pass an explicit candidateId to land a tighter passing child instead of the recommendation. See the safety model for the apply gate's place in the blast-radius controls.
Cleanup assumptions
cleanup(repoPath, handle) tears down one worktree:
--force and prune make cleanup reliable even when a worktree was left dirty or a crash interrupted a run. The engine runs cleanup for every registered handle (children and synthesis alike) inside a single finally via Promise.allSettled, so one failed removal never blocks the others. Because the other passing children's worktrees stay alive on disk until that finally, the synthesizer can reference them read-only during a run.
Related
Isolation service: package overview.
Core engine: how the engine drives the worktree manager.
Safety model: the apply gate and blast-radius controls.
Agents and runners: how children run inside these worktrees.
Last updated