Skip to content

From One Agent to Many: The Worktree Way

Or: How to Run Multiple Workstreams Without Losing Your Mind


“Just open multiple IDE windows and run multiple agents!”

Sure. Try it. I’ll wait.

You’ll immediately hit conflicts:

Naive parallelism doesn’t work. You need isolation. Real isolation. The kind where your agents can’t even see each other, let alone interfere.


Git worktrees solve the fundamental problem: multiple working directories, one repository.

Worktree Directory Structure
# Main worktree
~/projects/myapp/
├── .git/
├── apps/
└── node_modules/
# Additional worktrees (isolated)
~/projects/myapp-issue-42/
├── apps/
└── node_modules/ # Copy-on-write from main
~/projects/myapp-issue-43/
├── apps/
└── node_modules/ # Copy-on-write from main

Each worktree:

  • Has its own branch (no conflicts)
  • Has its own working directory (no file conflicts)
  • Shares git history (no duplication)
  • Can have its own ports and database

It’s like having multiple parallel universes, except they all share the same timeline and you can merge them back together when you’re done.


Managing worktrees manually is tedious. Like, “I’d rather just do the work myself” tedious. Worktrunk (wt) automates setup with hooks. For more details see the worktrunk github repo.

.config/wt.toml
[post-create]
# Copy environment from main
env = "cp {{ main_worktree_path }}/.env . 2>/dev/null || cp {{ main_worktree_path }}/.env.example .env"
# Configure unique ports and database
config = """
API_PORT={{ branch | hash_port }}
WEB_PORT={{ ('web-' ~ branch) | hash_port }}
DB_NAME="myapp_{{ branch | sanitize }}"
sed -i.bak "s|^PORT=.*|PORT=${API_PORT}|" .env
sed -i.bak "s|^VITE_PORT=.*|VITE_PORT=${WEB_PORT}|" .env
sed -i.bak "s|\\(DATABASE_URL=.*:[0-9]*/\\)[^?]*|\\1${DB_NAME}|" .env
"""
# Copy node_modules (instant on APFS)
deps = """
if cp -c /dev/null /dev/null 2>/dev/null; then
cp -c -r {{ main_worktree_path }}/node_modules . 2>/dev/null || npm install
else
npm install
fi
"""
# Create isolated database
db = "npm run wt:db:create && npm run db:migrate"
[pre-remove]
# Clean up database when removing worktree
db-cleanup = "npm run wt:db:drop 2>/dev/null || true"

What this does:

  1. Copies .env with unique ports (deterministic hash)
  2. Creates isolated database per worktree
  3. Copies node_modules instantly (copy-on-write magic)
  4. Cleans up database when worktree is removed

Shell functions for issue-driven development:

~/.zshrc
# Create worktree and launch agent for an issue
wti() {
local issue_num="$1"
local branch_name="issue-$issue_num"
wt switch -c "$branch_name"
if [ $? -eq 0 ]; then
opencode --prompt "read AGENTS.md and start work on issue $issue_num"
fi
}
# Remove worktree when done
wtr() {
local issue_num="$1"
wt remove "issue-$issue_num" -D
}

Usage:

Terminal window
wti 42 # Create worktree, launch agent for issue #42
wtr 42 # Clean up when done

Two commands. That’s it. Create a fully isolated workspace with its own branch, ports, and database, launch an AI agent with context, and point it at a specific issue.


Terminal Session
# Spin up agents
wti 42 # Agent works on authentication
wti 43 # Agent works on data export
wti 44 # Agent works on UI improvements
# Monitor progress
wt list
# + main ~/projects/myapp
# issue-42 ~/projects/myapp-issue-42 http://localhost:12847
# issue-43 ~/projects/myapp-issue-43 http://localhost:14521
# issue-44 ~/projects/myapp-issue-44 http://localhost:16893
# Review PRs as they complete
# Agent: "Feature #42 ready for review. PR url: ..."
# Ask for changes, improvements
# Agent addresses the points
# Final review
# Tell Agent to finalize
# Cleanup
wtr 42
wtr 43
wtr 44

Result: Three features in no-time, with supervision where it counts.

You’re not writing most of the code. You’re reviewing end results. You’re making architectural decisions. You’re doing the parts of the job that actually require a human brain. Orchestrating.


Tools like the Ralph Wiggum loop run agents indefinitely until completion. The parallel worktree approach is different:

Fire and ForgetIssue-Driven Worktrees
Single long-running agentMultiple scoped agents
No isolationFull isolation (branch, ports, DB)
Completion = agent decidesCompletion = PR reviewed
Hard to monitorEasy to check wt list
Hard to abortJust wtr <issue>
No review until endSub-agent review per PR

Each agent is scoped (one issue, one branch, one PR), isolated (can’t interfere with others), reviewable (work visible in PR before merge), and disposable (easy to abort and clean up).


API_PORT={{ branch | hash_port }}

This hashes the branch name to generate a port between 10000-19999.

Why this matters:

  • Same branch always gets same ports. Predictable.
  • No collisions between worktrees. Ever.
  • Predictable URLs for bookmarking.
  • No manual configuration.

Each worktree gets its own PostgreSQL database:

Database URLs
# Main worktree
DATABASE_URL=postgresql://localhost:5432/myapp
# Issue 42 worktree
DATABASE_URL=postgresql://localhost:5432/myapp_issue-42

This is the thing that makes parallel agents actually work. Without database isolation, you’re just running multiple agents that randomly break each other’s tests. With it: no test interference, clean state per worktree, automatic cleanup on removal.


The fastest install is no install.

Copy-on-Write Commands
# macOS APFS
cp -c -r main/node_modules .
# Linux btrfs/xfs
cp --reflink=auto -r main/node_modules .

Copy-on-write means instant “copy” (just metadata), no disk space until files modified, and 500MB of node_modules in less than 1 second. Without it, each worktree needs 30-60 seconds for npm install.


Practical limits per machine:

  • ~500MB disk per worktree (node_modules)
  • ~200MB RAM per dev server
  • ~100MB RAM per database
  • 2 ports per worktree

Human attention limits:

  • 1-2 agents: Easy, minimal supervision
  • 3-4 agents: Optimal balance
  • 5-6 agents: Requires more monitoring
  • 7+: Diminishing returns

Terminal window
lsof -i :12847
kill -9 <PID>

Something’s squatting on your port. Evict it.

Terminal window
npm run wt:db:create
npm run db:migrate

Database doesn’t exist or isn’t migrated. Create it. Migrate it. Move on.

Terminal window
wt list
wt remove issue-42
wt switch -c issue-42

Clean up the old one, create a fresh one.

AGENTS.md should include clear boundaries. Agents need to know: “You are working in a git worktree. The human orchestrator manages worktree creation/removal. Your job is feature implementation. DO implement features, write tests, create PRs. DO NOT create worktrees, remove worktrees, switch branches.”


Terminal window
brew install worktrunk/tap/wt
# Create .config/wt.toml
wt switch -c test-feature
# Verify ports, database work
wt remove

Get comfortable with the basics. Make sure everything works.

Terminal window
wti 42 # Simple bug fix
wti 43 # Documentation update
# Monitor both, get comfortable

Start small. Two agents on simple tasks. Get used to the rhythm.

Terminal window
wti 44 # New feature
wti 45 # Refactoring
wti 46 # Tests
# Practice monitoring and unblocking

Add a third. Or a fourth. Find your personal limit.

  • Add shell functions (wti, wtr)
  • Refine AGENTS.md worktree section
  • Find your optimal parallel count

Parallel agent execution changes how you work:

Before: Implement features sequentially, wait for each to complete, context-switch constantly, lose half your productivity to “where was I again?”

After: Orchestrate multiple features simultaneously, review in batches, maintain focus, actually use your brain for the hard problems

The key enablers: git worktrees for isolation, Worktrunk for automation, deterministic ports, isolated databases, issue-driven workflow, and clear agent boundaries in AGENTS.md.

Result: 3-4x throughput with maintained quality.

The future isn’t one developer, one feature at a time. It’s one developer orchestrating multiple AI agents, each on isolated features, all in parallel.


See Effective AI-Augmented Development for the full framework, or Case Study: Self-Correcting Test Isolation for guardrails in action.