Skip to content

Git Worktree Isolation

Agents Fleet uses Git worktrees to give coder workers isolated file system environments for safe parallel editing.

Overview

When the coordinator spawns a coder worker, it automatically gets its own Git worktree:

  • Coder workers → isolated worktree at .worktrees/{agentId}/ on branch worker/{agentId}
  • Non-coder workers (explorer, reviewer, tester) → share the main working directory

This ensures multiple coders can edit files simultaneously without conflicts during execution. Changes are merged back to the main branch when the worker completes.

Key Features

  • Automatic creation — worktrees are created on worker spawn, no user action needed
  • Auto-merge on completion — successful workers have their changes merged back
  • Conflict detection — merge conflicts are caught and handled gracefully
  • Worktree preservation on failure — failed workers' worktrees are kept for inspection
  • Max 2 merge resolution attempts — prevents infinite conflict loops (DoD-31/32)

How It Works

WorktreeManager

The WorktreeManager is responsible for the lifecycle of Git worktrees:

  1. Creategit worktree add .worktrees/{agentId} -b worker/{agentId}
  2. Removegit worktree remove .worktrees/{agentId} + branch cleanup
  3. Merge — fast-forward or three-way merge back to the source branch
  4. List — enumerate all active worktrees

FleetManager Integration

The FleetManager hooks into worker lifecycle events:

Worker Spawn
  → WorktreeManager.create(agentId)
  → SDK session receives worktree path as workingDirectory
  → Worker operates in isolated directory

Worker Complete (success)
  → WorktreeManager.merge(agentId)
  → Worktree removed on successful merge
  → Branch cleaned up

Worker Complete (failure)
  → Worktree preserved for inspection
  → Warning logged for user

SDK Working Directory

The SDK's workingDirectory parameter passes the worktree path to the worker session. This means the worker's file operations (read, write, edit, bash) all operate within the isolated worktree.

Merge Conflict Flow

When a merge conflict is detected:

Merge Attempt
  → Conflict detected
  → Abort merge (git merge --abort)
  → Preserve worktree for manual resolution
  → Block the task (mark as blocked)
  → Notify coordinator of conflict

Coordinator may retry (up to 2 attempts)
  → If still conflicted after 2 attempts → permanent block

A maximum of 2 merge resolution attempts are allowed before the task is permanently blocked (DoD-31/32). This prevents infinite conflict resolution loops.

Commands

/worktree list

Show all active worktrees:

🌳 Active Worktrees
────────────────────
Type     Path                          Branch
worker   .worktrees/worker-1/          worker/worker-1
worker   .worktrees/worker-3/          worker/worker-3
compete  .worktrees/compete-pragmatic/ compete/pragmatic

/worktree cleanup

Remove all worker worktrees and their branches:

🧹 Cleaned up 3 worker worktrees

/worktree cleanup --all

Remove all worktrees including /compete worktrees:

🧹 Cleaned up 3 worker worktrees + 2 compete worktrees

/worktree status

Show worktree system status:

🌳 Worktree Status
──────────────────
Git Version:    2.43.0 ✅
Worker Trees:   2 active
Compete Trees:  1 active
Directory:      .worktrees/

Configuration

Worktree settings in your fleet config (~/.fleet/config.json or project .fleet/config.json):

typescript
worktrees?: {
  enabled?: boolean;           // default: true
  directory?: string;          // default: '.worktrees'
  symlinkNodeModules?: boolean; // default: false
}

Options

OptionDefaultDescription
enabledtrueEnable/disable worktree isolation for coders
directory'.worktrees'Directory name for worktrees (relative to repo root)
symlinkNodeModulesfalseSymlink node_modules from main repo into worktrees to save disk space and install time

Disabling Worktrees

json
{
  "worktrees": {
    "enabled": false
  }
}

When disabled, all workers (including coders) share the main working directory. This is simpler but risks file conflicts during parallel editing.

/compete Integration

The /compete command uses the WorktreeManager with the prefix compete:

/compete "Implement caching layer"

This creates 3 parallel worktrees:

.worktrees/compete-pragmatic/    → branch: compete/pragmatic
.worktrees/compete-clean/        → branch: compete/clean
.worktrees/compete-alternative/  → branch: compete/alternative

Each implementation runs in full isolation. After judging, the winning implementation is merged:

/compete pick pragmatic
  → Merges compete/pragmatic into main branch
  → Cleans up all compete worktrees and branches

Compete Lifecycle

/compete <description>
  → Create 3 worktrees (pragmatic, clean, alternative)
  → Spawn 3 coder workers in parallel
  → Workers implement independently

/compete judge
  → Spawn reviewers to rate each implementation
  → Scores: correctness, quality, testing, performance, simplicity, integration

/compete pick <name>
  → Merge winning branch
  → Remove all compete worktrees

/compete cleanup
  → Force-remove all compete worktrees

Startup Checks

Git Version Requirement

Worktrees require Git ≥ 2.15. On startup, the CLI checks your Git version:

  • ≥ 2.15 → worktrees enabled normally
  • < 2.15 → warning displayed, worktrees disabled for the session
⚠️  Git 2.12.0 detected. Worktrees require Git ≥ 2.15.
    Worktree isolation disabled. Coders will share the main directory.

Orphan Worktree Detection

On startup, the CLI scans for leftover worktrees from previous sessions:

⚠️  Found 2 orphan worktrees from a previous session:
    .worktrees/worker-5/
    .worktrees/worker-8/
    Run /worktree cleanup to remove them.

This helps keep your repository clean after crashes or interrupted sessions.

Best Practices

  1. Run /worktree cleanup after sessions — especially after failures that may leave orphan worktrees
  2. Check /worktree status if things seem wrong — confirms Git version and active tree counts
  3. Use symlinkNodeModules: true for Node.js projects — avoids duplicate node_modules in each worktree
  4. Don't manually edit files in .worktrees/ — these directories are managed by the CLI
  5. Add .worktrees/ to .gitignore — worktrees should not be committed