ワークツリー分離
タスクごとの git worktree を使用し、イベントストリームによる完全なコード分離を実現する。
学ぶこと
2つのエージェントが同時に同じファイルを編集すると、一方が負けます。変更が上書きされるか、両方の変更がまとまって一貫性のないものになります。ワークツリー分離は、各エージェントに独自の完全な作業ディレクトリを与えることでこれを排除します。
このセッションが終わるまでに、以下のことを理解できるようになります:
- 共有作業ディレクトリがマルチエージェントワークフローを破綻させる理由
- git worktree がエージェントごとの分離を提供する方法
- ワークツリーのライフサイクル:作成、作業、マージまたは破棄
- ワークツリーの活動を監視するためのイベントストリーム
- 未使用のワークツリーの自動クリーンアップ
問題
同じリポジトリで並列に作業する2つのエージェント:
AGENT A: Adding authentication AGENT B: Adding logging
Creates src/middleware/auth.ts Creates src/middleware/logger.ts
Modifies src/app.ts ←──── CONFLICT ────→ Modifies src/app.ts
Modifies src/routes/index.ts Modifies src/config.ts
両方が src/app.ts を変更します。分離がなければ、3つの結果が考えられます:
1. LAST WRITE WINS
Agent A writes app.ts with auth import
Agent B writes app.ts with logger import → auth import lost
2. READ-BEFORE-WRITE RACE
Both read the same version of app.ts
Both write based on that version → second write overwrites the first
3. MERGE CONFLICT
Both stage changes → git conflict markers → manual resolution needed
どれも許容できません。根本原因:複数のエージェントが単一の作業ディレクトリを共有していること。
仕組み
Git Worktree:1つのリポジトリ、複数のディレクトリ
Git worktree は、同じリポジトリから追加の作業ディレクトリを作成します。それぞれが独自のブランチとファイルを持ちますが、git 履歴は共有されます。
MAIN REPOSITORY WORKTREE: AGENT A
/project/ /project/.worktrees/agent-a/
├── .git/ ◀── shared ──▶ (branch: feat/auth)
├── src/ ├── src/ ← independent copy
├── tests/ ├── tests/
└── package.json └── package.json
WORKTREE: AGENT B
/project/.worktrees/agent-b/
(branch: feat/logging)
├── src/ ← independent copy
├── tests/
└── package.json
エージェント A とエージェント B は両方とも src/app.ts を変更できます — それぞれが自分のブランチ上に独自のコピーを持っています。
ワークツリー分離によるエージェントのスポーン
親が分離付きで子をスポーンする場合、シーケンスは以下の通りです:
Parent Agent
│
├──▶ 1. CREATE git worktree add .worktrees/task-42 -b task-42
│ → new directory + branch created (fast, uses hardlinks)
│
├──▶ 2. SET CWD Child agent works in .worktrees/task-42/
│ Agent sees a normal project — doesn't know it's a worktree
│
├──▶ 3. WORK Child reads, writes, tests — all local to worktree
│ No impact on main directory or other worktrees
│
├──▶ 4. COMPLETE Child commits to branch task-42
│ Returns: { worktree_path, branch, files_changed }
│
└──▶ 5. DECIDE Parent merges branch → or discards worktree
子エージェントは特別な認識を必要としません。その視点からは、通常のディレクトリで作業しています。分離はワーカーにとって透過的です。
完全なライフサイクル
┌─────────────────────────────────────────────────────┐
│ CREATE │
│ git worktree add <path> -b <branch> │
│ Fast: hardlinks, not full copy │
│ │ │
│ ▼ │
│ WORK │
│ Agent operates in worktree directory │
│ All reads/writes isolated. Can run tests locally. │
│ │ │
│ ▼ │
│ EVALUATE │
│ git diff --stat → changes exist? │
│ ┌──────┴──────┐ │
│ ▼ ▼ │
│ MERGE DISCARD │
│ Merge branch Remove worktree │
│ into main Delete branch │
│ └──────┬──────┘ │
│ ▼ │
│ CLEANUP │
│ git worktree prune │
│ Remove directory, clean git references │
└─────────────────────────────────────────────────────┘
イベントストリーム:活動の監視
親はイベントを通じてワークツリーの活動を監視します。分離を壊すことなく:
Parent Agent (orchestrator)
│
├── AGENT A (worktree: feat/auth)
│ ├── event: file_created src/middleware/auth.ts
│ ├── event: file_modified src/app.ts
│ ├── event: test_run 3 passed, 0 failed
│ └── event: task_complete files_changed: 3
│
├── AGENT B (worktree: feat/logging)
│ ├── event: file_created src/middleware/logger.ts
│ ├── event: file_modified src/app.ts
│ └── event: task_complete files_changed: 3
│
└── MERGE DECISION
Both modified src/app.ts on different branches.
Git merge handles it if changes are on different lines.
If conflict: parent resolves with full context of both tasks.
イベントは子から親への一方向のみ流れます。親は干渉することなく観察します — 子が作業中にワークツリーを変更すると一貫性が壊れます。
自動クリーンアップ
変更のないワークツリーは自動的にクリーンアップされます:
1. Worktree created for exploration task
2. Agent reads 20 files, concludes "no changes needed"
3. git diff --stat → empty
4. Automatic: git worktree remove + git branch -d
5. No branch, no directory, no mess
これにより、多くの並列エージェントを伴う長いセッションでのワークツリーの蓄積を防ぎます。
キーインサイト
ワークツリー分離は、真の並列開発を可能にするものです。フィーチャーブランチと同じ概念ですが、エージェントレベルで適用されます。
より深いインサイト:ワークツリーは調整モデルを変えます。分離がなければ、エージェントはリアルタイムで調整する必要があります(「app.ts に触らないで、編集中だから」)。分離があれば、エージェントは自由に作業し、コンフリクトはマージ時に一度だけ解決されます。これは悲観的ロックではなく楽観的並行性です — エージェントが互いをブロックしないため、よりスケールします。
実践例
2つのエージェントが並列で機能を実装し、マージする例:
STEP 1: Create worktrees
──────────────────────────────────
git worktree add .worktrees/auth -b feat/auth
git worktree add .worktrees/logging -b feat/logging
STEP 2: Agents work in parallel
──────────────────────────────────
Agent A (in .worktrees/auth/):
Creates src/middleware/auth.ts
Modifies src/app.ts:
+ import { authMiddleware } from './middleware/auth'
+ app.use(authMiddleware)
Runs tests → pass
Commits: "feat: add authentication middleware"
Agent B (in .worktrees/logging/): ← simultaneous
Creates src/middleware/logger.ts
Modifies src/app.ts:
+ import { logger } from './middleware/logger'
+ app.use(logger)
Runs tests → pass
Commits: "feat: add request logging"
STEP 3: Merge results
──────────────────────────────────
git checkout main
git merge feat/auth → clean merge
git merge feat/logging → auto-merge (different lines of app.ts)
Final app.ts has BOTH imports. No manual conflict resolution.
STEP 4: Cleanup
──────────────────────────────────
git worktree remove .worktrees/auth
git worktree remove .worktrees/logging
マージコンフリクトの処理
変更が重なった場合、親エージェントがコンテキストを持って解決します:
Agent A adds line 15: app.use(authMiddleware)
Agent B adds line 15: app.use(logger)
Parent reads conflict → understands both intents → resolves:
app.use(logger) ← logger first (every request)
app.use(authMiddleware) ← auth second (validates credentials)
分離戦略の比較
No Isolation: instant setup, zero safety → single agent only
Worktree: fast setup (hardlinks), full file isolation → parallel agents
Full Clone: slow (copies repo), full isolation → untrusted agents
Container: slowest (OS-level), maximum isolation → untrusted code
Worktree hits the sweet spot: strong isolation with minimal overhead.
変更点
| 共有作業ディレクトリ | ワークツリー分離 |
|---|---|
| エージェントが互いを上書き | 各エージェントが独自のディレクトリを持つ |
| 作業中のコンフリクト | コンフリクトはマージ時に解決 |
| ファイルアクセスの調整が必要 | エージェントが独立して作業 |
| 衝突回避のため逐次的 | 真の並列実行 |
| 単一ブランチ | エージェントごとに1ブランチ |
次のセッション
これでモジュール2:マルチエージェントシステムは完了です。タスクグラフ、バックグラウンドタスク、チームコミュニケーション、プロトコル、自律エージェント、ワークツリー分離を理解しました。
モジュール3では、Claude Code を設定し拡張するシステムに移ります。セッション13はコントロールプロトコルから始まります。外部ツールが Claude Code に構造化されたコマンドを送信しレスポンスを受信する方法、IDE 統合やプログラマティック制御を可能にする仕組みについて学びます。