Skip to content

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_idworktree 字段双向关联。


🔍 核心实现解析

1. 创建任务

先把目标持久化到 .tasks/ 目录,这是 [[11-swarm-coordination|s11]] 的延续。

python
TASKS.create("Implement auth refactor")
# -> .tasks/task_1.json  status=pending  worktree=""

2. 创建 Worktree 并绑定

传入 task_id 自动将任务推进到 in_progress,同时写入两侧状态:

python
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"

绑定逻辑是双向的:

python
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 目录,天然隔离:

python
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 带有"链式反应"——一个调用搞定拆除 + 任务完成 + 事件通知:

python
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

json
{
  "event": "worktree.remove.after",
  "task": {"id": 1, "status": "completed"},
  "worktree": {"name": "auth-refactor", "status": "removed"},
  "ts": 1730000000
}

事件类型:

  • worktree.create.before / .after / .failed
  • worktree.remove.before / .after / .failed
  • worktree.keep
  • task.completed

6. 崩溃恢复

会话记忆是易失的,磁盘状态是持久的。崩溃后从 .tasks/ + .worktrees/index.json 重建现场:

python
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,但双向绑定解决了三个关键问题:

  1. O(1) 查找速度:从 worktree 反查任务不需要遍历任务列表
  2. 强一致性:任何一边的变更立即反映到另一边
  3. 离线恢复:两边各存一份引用,即使 JSON 文件损坏其一,也能从另一边重建

实际工程中这是"最终一致性"的简化版——不依赖分布式共识,在单机场景下足够可靠。


尝试运行

sh
cd learn-claude-code
python agents/s12_worktree_task_isolation.py

推荐 Prompt

试试以下 prompt(英文 prompt 对 LLM 效果更好,也可以用中文):

  1. Create tasks for backend auth and frontend login page, then list tasks.
  2. Create worktree "auth-refactor" for task 1, then bind task 2 to a new worktree "ui-login".
  3. Run "git status --short" in worktree "auth-refactor".
  4. Keep worktree "ui-login", then list worktrees and inspect events.
  5. 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 生命周期管理和会话持久化。

基于 Learn Claude Code 项目改编