Chapter 9 · Worktree Isolation and Parallel Delivery
Chapter goals: Understand why FirstMate坚持 "one clean git worktree per task" isolation model, master the treehouse worktree pool, base-freshness boundaries, and the fail-closed teardown lifecycle.
9.1 Why Worktree Isolation Is Needed
Imagine the無隔离 scenario: you have the firstmate dispatch three crewmates to改 the same repo simultaneously — fixing the login bug, adding dark mode, refactoring utility functions. If they all work directly in your project directory, what happens?
No isolation parallel:
projects/myapp/ ← all crewmates share the同一 work area
├── src/login.ts Crew A is modifying (半成品)
├── src/theme.css Crew B is also改ing here (互相踩踏)
├── src/utils.ts Crew C half-refactors; A's tests can't run
└── ??? Who改 what? git diff is a messUncommitted changes混在一起,无法拆分、无法回滚,甚至无法编译.The收益 of parallel becomes the事故 of parallel instantly.
Git already有 the answer: git worktree. It allows the same repo to checkout multiple work areas on磁盘 simultaneously, each with独立 files and an独立 HEAD, while sharing the同一 .git object store —既隔离又省空间:
# Native git worktree demo: two独立 work areas from the same repo
cd myapp # Main checkout stays on the main branch
git worktree add ../myapp-fix -b fix/login-bug # New worktree + new branch
git worktree add ../myapp-dark -b feat/dark-mode # One more
git worktree list
# /Users/captain/code/myapp abc1234 [main]
# /Users/caoxueling/code/myapp-fix def5678 [fix/login-bug]
# /Users/captain/code/myapp-dark 9abc123 [feat/dark-mode]
cd ../myapp-fix # Mess around here freely; completely不影响 other work areasFirstMate institutionalizes this mechanism: crewmates never故意 touch your project's primary checkout; each task completes in its own干净 worktree**. This is what "Worktrees, not branches in your checkout" means in the架构文档.
9.2 Treehouse: The Worktree Pool
Manually git worktree add per task is太繁琐, and requires handling cleanup, reuse, freshness, etc. FirstMate配套 uses treehouse to集中管理一批干净的 task worktree:
Task-to-worktree mapping:
projects/myapp/ ← your project clone (firstmate read-only; crewmates don't工作 here)
│ fm-spawn.sh
├── treehouse worktree #1 ──→ fm-task1 (ship: fix login bug)
├── treehouse worktree #2 ──→ fm-task2 (scout: investigate性能 regression)
└── treehouse worktree #3 ──→ fm-task3 (ship: dark mode)
Worktree providers per backend:
tmux / herdr / zellij / cmux → treehouse unified供给
orca → Orca自建 worktree (records orca_worktree_id=,
removed via orca worktree rm after常规 teardown checks)Three要点:
- Pooled supply: the tmux, herdr, zellij, cmux four session backends all get worktrees from treehouse; only the experimental Orca backend manages worktree生命周期 itself.
- One task, one tree: each crewmate session binds an专属 worktree; mutuallyinvisible, mutually unpolluted.
- Return after use: after the task ends, the teardown流程 returns the worktree to the pool for reuse.
9.3 Spawn Gates: Two Checks in fm-spawn.sh
Isolation is not靠自觉; it is强制 by scripts. bin/fm-spawn.sh has两道硬性检查 before dispatching any ship or scout task.
Gate 1:真实且独立的 worktree root. Unless the resolved task path is a真实 git worktree root and distinct from the project primary checkout,启动 is refused.
Gate 2: base-freshness boundary. Each全新 ship/scout task's clean worktree must first align to origin's解析出的 default branch latest fetched tip; any unsafe or unverifiable baseline阻止s spawn:
base-freshness flow (handled by fm-spawn.sh):
1. fetch origin,解析 the default branch (origin/HEAD)
2. align the task worktree's base to that fetched tip
3. align succeeds → worker allowed to start
4. unsafe/unverifiable → spawn stops; never开工 with a sick baselineWhy so strict? An outdated baseline means the crewmate develops on旧代码; the PR opens with一堆无谓冲突甚至逻辑错误. "Freshen first, then开工" strangles这类事故 at the起点. The precise refusal机制 is owned by the script header注释; regressioncoverage is in tests/fm-spawn-pool-base-freshen.test.sh.
9.4 Worktree Tangle: The唯一健康判据 Is Branch State
One特殊值得单独讲: the firstmate repo itself canalso be dispatched work (the fleet can调度 crewmates to维护 firstmate itself). In this case, FM_ROOT (firstmate's own运营 checkout) and the crewmate's临时 worktree are both linked worktrees of the same repo — "whether linked" no longer distinguishes健康 from不健康.
The架构文档's判别标准 is branch state:
Healthy topology:
FM_ROOT (primary checkout) → stays on the default branch ✅ healthy
linked worktree / secondmate home → in detached HEAD ✅ healthy
The唯一病态 (worktree tangle):
FM_ROOT → checked out a named non-default branch ❌ tangleOnce a tangle occurs,两处报警:
bin/fm-session-start.shreports aTANGLE:line via bootstrap at session startup;bin/fm-guard.shprints the修复命令 at the next "changeable fleet action".
9.5 Fail-Closed Teardown: Never Lose Unlanded Work
When a task ends, the teardown流程 must decide: can this worktree be销毁? The answer follows a hard原则:
Teardown 判据:
1. 已提交的改动 (landed) → 安全销毁 worktree ✅
2. 未提交的改动 (unlanded) → 拒绝销毁, up to the captain to decide ⚠️
3. scout worktree → 报告存在且 decision inventory 完成后才可丢弃This is Hard Rule 3 in action: never destroy unlanded work without captain authorization. The bin/fm-teardown.sh script implements the landed-work test;绕过拒绝或 --force 都需要船长明确授权.
9.6 Chapter Summary
- Worktree isolation prevents并行事故: each crewmate gets an专属 clean worktree via treehouse;
- Spawn has两道硬门禁:真实独立的 worktree root + base-freshness对齐 origin tip;
- Orca is the only backend that自建 worktrees; others use treehouse统一供给;
- Worktree tangle is detected by分支状态 (FM_ROOT on a named非默认 branch =病态);
- Teardown is fail-closed: unlanded work is拒绝销毁 unless the captain explicitly authorizes.
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. Why does FirstMate insist on one clean git worktree per task?
2. What is the base-freshness gate in fm-spawn.sh?
3. What triggers a "worktree tangle" alert?
4. When can a scout worktree be discarded?
🛠️ Hands-On Exercises
- Run
git worktree listin a test repo; create two worktrees; verify they are独立 and互不干扰; then remove one and observe thepool state. - Simulate a base-freshness failure: create a worktree落后于 origin/main and attempt to spawn; record the拒绝行为. Then fetch to latest and重试;对比 the metadata.
- Trigger a worktree tangle scenario (checkout a named branch in FM_ROOT); observe the TANGLE: line in the session startup digest and the修复命令 from fm-guard.sh.
Isolation understood. Next, Project Modes and Merge Permissions.