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 branchworker/{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:
- Create —
git worktree add .worktrees/{agentId} -b worker/{agentId} - Remove —
git worktree remove .worktrees/{agentId}+ branch cleanup - Merge — fast-forward or three-way merge back to the source branch
- 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 userSDK 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 blockA 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):
worktrees?: {
enabled?: boolean; // default: true
directory?: string; // default: '.worktrees'
symlinkNodeModules?: boolean; // default: false
}Options
| Option | Default | Description |
|---|---|---|
enabled | true | Enable/disable worktree isolation for coders |
directory | '.worktrees' | Directory name for worktrees (relative to repo root) |
symlinkNodeModules | false | Symlink node_modules from main repo into worktrees to save disk space and install time |
Disabling Worktrees
{
"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/alternativeEach 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 branchesCompete 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 worktreesStartup 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
- Run
/worktree cleanupafter sessions — especially after failures that may leave orphan worktrees - Check
/worktree statusif things seem wrong — confirms Git version and active tree counts - Use
symlinkNodeModules: truefor Node.js projects — avoids duplicatenode_modulesin each worktree - Don't manually edit files in
.worktrees/— these directories are managed by the CLI - Add
.worktrees/to.gitignore— worktrees should not be committed