Lesson 12: Worktree 任务隔离
"各干各的目录,互不干扰" —— 任务管目标,worktree 管目录,按 ID 绑定。
Harness 层: 目录隔离 —— 永不碰撞的并行执行通道。
概述
上一讲 [[09-multi-agent-swarm|s09 多智能体协作]] 和 [[11-swarm-coordination|s11 蜂群协调]] 解决了"怎么分配任务"的问题——多智能体可以认领任务、协同工作。但所有任务仍然共享同一个工作目录。
两个智能体同时重构不同模块:Agent A 改 config.py,Agent B 也改 config.py,未提交的改动互相覆盖,谁也回滚不了。
核心洞察: [[07-task-board|s07 任务板]] 管"做什么",不管"在哪做"。缺少的是一层物理隔离。
Worktree 任务隔离就是这个缺失的拼图——给每个任务一个独立的 git worktree 目录,用任务 ID 双向绑定。
为什么需要 Worktree
共享目录的问题
| 问题 | 表现 |
|---|---|
| 文件冲突 | 两个 Agent 修改同一文件 |
| 状态污染 | 未提交的改动互相干扰 |
| 回滚困难 | 分不清哪些改动属于哪个任务 |
| 并行受限 | 只能串行执行,无法充分利用多 Agent |
Worktree 的优势
Git worktree 允许从同一个仓库检出多个工作目录,每个目录有自己的:
- 独立的工作区文件
- 独立的暂存区(index)
- 独立的 HEAD 引用(通过分支隔离)
变更天然隔离,互不感知。
架构设计
双平面模型
Control plane (.tasks/) Execution plane (.worktrees/)
+------------------+ +------------------------+
| task_1.json | | auth-refactor/ |
| status: in_progress <------> branch: wt/auth-refactor
| worktree: "auth-refactor" | task_id: 1 |
+------------------+ +------------------------+
| task_2.json | | ui-login/ |
| status: pending <------> branch: wt/ui-login
| worktree: "ui-login" | task_id: 2 |
+------------------+ +------------------------+
|
.worktrees/index.json (worktree registry)
.worktrees/events.jsonl (lifecycle log)状态机
- Task:
pending → in_progress → completed - Worktree:
absent → active → removed | kept
两侧通过 task_id 和 worktree 字段双向关联。
🔍 核心实现解析
1. 创建任务
先把目标持久化到 .tasks/ 目录,这是 [[11-swarm-coordination|s11]] 的延续。
TASKS.create("Implement auth refactor")
# -> .tasks/task_1.json status=pending worktree=""2. 创建 Worktree 并绑定
传入 task_id 自动将任务推进到 in_progress,同时写入两侧状态:
WORKTREES.create("auth-refactor", task_id=1)
# -> git worktree add -b wt/auth-refactor .worktrees/auth-refactor HEAD
# -> index.json gets new entry
# -> task_1.json gets worktree="auth-refactor", status="in_progress"绑定逻辑是双向的:
def bind_worktree(self, task_id, worktree):
task = self._load(task_id)
task["worktree"] = worktree
if task["status"] == "pending":
task["status"] = "in_progress"
self._save(task)3. 在隔离目录中执行
所有命令的 cwd 指向 worktree 目录,天然隔离:
subprocess.run(command, shell=True, cwd=worktree_path,
capture_output=True, text=True, timeout=300)每个 Agent 看到的都是自己的文件系统快照,改动不影响其他 worktree。
4. 收尾:两种策略
| 操作 | 效果 | 典型场景 |
|---|---|---|
worktree_keep(name) | 保留目录 | 长期分支、WIP 需要保留现场 |
worktree_remove(name, complete_task=True) | 删除目录 + 完成任务 | PR 合并后清理 |
remove 带有"链式反应"——一个调用搞定拆除 + 任务完成 + 事件通知:
def remove(self, name, force=False, complete_task=False):
self._run_git(["worktree", "remove", wt["path"]])
if complete_task and wt.get("task_id") is not None:
self.tasks.update(wt["task_id"], status="completed")
self.tasks.unbind_worktree(wt["task_id"])
self.events.emit("task.completed", ...)5. 事件流
每个生命周期步骤写入 .worktrees/events.jsonl:
{
"event": "worktree.remove.after",
"task": {"id": 1, "status": "completed"},
"worktree": {"name": "auth-refactor", "status": "removed"},
"ts": 1730000000
}事件类型:
worktree.create.before/.after/.failedworktree.remove.before/.after/.failedworktree.keeptask.completed
6. 崩溃恢复
会话记忆是易失的,磁盘状态是持久的。崩溃后从 .tasks/ + .worktrees/index.json 重建现场:
def restore(self):
tasks = self.tasks.list()
active = self.list()
for wt in active:
task_id = wt.get("task_id")
task = next((t for t in tasks if t["id"] == task_id), None)
if task:
task["status"] = "in_progress" # 恢复为进行中相对 s11 的变更一览
| 组件 | s11(之前) | s12(之后) |
|---|---|---|
| 协调 | 任务板(owner/status) | 任务板 + worktree 显式绑定 |
| 执行范围 | 共享目录 | 每个任务独立目录 |
| 可恢复性 | 仅任务状态 | 任务状态 + worktree 索引 |
| 收尾 | 任务完成 | 任务完成 + 显式 keep/remove |
| 生命周期可见性 | 隐式日志 | .worktrees/events.jsonl 显式事件流 |
🔍 深入理解:绑定为什么是双向的
这个设计看似多余——任务本来就可以通过 task_id 查到 worktree,但双向绑定解决了三个关键问题:
- O(1) 查找速度:从 worktree 反查任务不需要遍历任务列表
- 强一致性:任何一边的变更立即反映到另一边
- 离线恢复:两边各存一份引用,即使 JSON 文件损坏其一,也能从另一边重建
实际工程中这是"最终一致性"的简化版——不依赖分布式共识,在单机场景下足够可靠。
尝试运行
cd learn-claude-code
python agents/s12_worktree_task_isolation.py推荐 Prompt
试试以下 prompt(英文 prompt 对 LLM 效果更好,也可以用中文):
Create tasks for backend auth and frontend login page, then list tasks.Create worktree "auth-refactor" for task 1, then bind task 2 to a new worktree "ui-login".Run "git status --short" in worktree "auth-refactor".Keep worktree "ui-login", then list worktrees and inspect events.Remove worktree "auth-refactor" with complete_task=true, then list tasks/worktrees/events.
设计原则
显式优于隐式
Worktree 的创建、绑定、保持、删除都有显式调用。没有隐式的副作用或魔法清理。这使得:
- Agent 行为可预测
- 日志可审计
- 崩溃后可恢复
简单状态机
只有 absent → active → removed | kept 三种状态。简单到 Agent 不需要文档也能用对。
链式反应与可组合性
remove 自动触发任务完成和事件通知,但不是硬编码——你仍然可以分别调用。这种"默认联动,可独立控制"的设计给了 Agent 灵活性。
小结
Worktree 任务隔离是 Harness 工程中最朴素的并行化策略——不依赖锁、不依赖分布式协调、不依赖复杂的调度算法,只用 git 的原生能力就解决了"文件冲突"这个本质问题。
- [[07-task-board|s07 任务板]] 提供了"做什么"的语义
- [[09-multi-agent-swarm|s09 多智能体]] 和 [[11-swarm-coordination|s11 蜂群协调]] 提供了"谁来做"的机制
- s12 提供了"在哪做"的答案——物理隔离,让并行成为可能
下一讲将在此基础上构建更高级的 Agent 生命周期管理和会话持久化。