首页
/ learn-claude-code s12: 基于 git worktree 与任务绑定的目录级隔离(Worktree Task Isolation)

learn-claude-code s12: 基于 git worktree 与任务绑定的目录级隔离(Worktree Task Isolation)

2026-09-04 18:32:39作者:戚魁泉Nursing

本篇技术指南基于 docs/zh/s12-worktree-task-isolation.md 展开,讲解 learn-claude-code 课程第 12 讲(s12)的核心机制:如何为每个任务分配独立的 git worktree 目录,用任务 ID 把「控制面」与「执行面」绑定起来,从而实现多个 Agent 并行工作时的目录隔离与可恢复性。读完本篇,你能掌握 TaskManagerWorktreeManagerEventBus 三个组件的设计意图与调用关系,并能在自己的 Agent 框架中复现「按目录隔离、按任务协调」的并行执行通道。

问题:共享目录让并行任务互相污染

到课程 s11 为止,Agent 已经能自主认领和完成任务,但所有任务共享同一个工作目录。当两个 Agent 同时重构不同模块时,A 修改 config.py,B 也修改 config.py,未提交的改动会互相污染,谁也没法干净地回滚。

任务板只管「做什么」,不管「在哪做」。s12 给出的解法:给每个任务一个独立的 git worktree 目录,用任务 ID 把两边关联起来——任务管目标(tasks manage goals),worktree 管执行上下文(worktrees manage execution context),按 ID 绑定(bound by ID)

总体架构:控制面与执行面的双平面模型

s12 把系统状态划分为两个平面,全部落盘在仓库根目录下:

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             |
+------------------+                +------------------------+
                                    |
                          index.json (worktree registry)
                          events.jsonl (lifecycle log)

State machines:
  Task:     pending -> in_progress -> completed
  Worktree: absent  -> active      -> removed | kept

从源码结构看,这个模型由三个类实现(见 agents/s12_worktree_task_isolation.py):

组件 落盘位置 职责
TaskManager .tasks/task_<id>.json 任务板:创建、查询、更新状态/负责人、绑定 worktree
WorktreeManager .worktrees/index.json + .worktrees/<name>/ 执行面:创建/列出/运行/保留/删除 git worktree,维护生命周期索引
EventBus .worktrees/events.jsonl 只追加(append-only)的生命周期事件流,用于可观测性与崩溃后重建现场

两边的状态机相互独立又通过绑定联动:

  • Taskpending -> in_progress -> completed
  • Worktreeabsent -> active -> removed | kept

绑定发生的那一刻,任务自动从 pending 推进到 in_progress;拆除 worktree 时,可选地把绑定任务推进到 completed

实现详解:从任务创建到收尾的五步闭环

1. 创建任务:先持久化目标

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

从源码看,TaskManager.createL149-L163)写入的 JSON 包含完整字段:idsubjectdescriptionstatus(初始 pending)、ownerworktree(初始空串)、blockedBycreated_atupdated_at。ID 通过扫描 .tasks/task_*.json 取最大编号自增得到(_max_idL128-L135),因此进程重启后 ID 序列不会冲突——磁盘状态是唯一事实来源。

2. 创建 worktree 并按任务 ID 绑定

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"

WorktreeManager.createL284-L335)的完整调用链:

  1. 校验 worktree 名称:正则 [A-Za-z0-9._-]{1,40}_validate_nameL278-L282),防止路径穿越等非法名称;
  2. 检查重名(索引中已存在则报错)与任务存在性;
  3. 发出 worktree.create.before 事件;
  4. 执行 git worktree add -b wt/<name> .worktrees/<name> <base_ref>,其中分支名固定为 wt/ 前缀,base_ref 默认为 HEAD,可传入任意 ref 基于它建分支;
  5. {name, path, branch, task_id, status: "active", created_at} 写入 index.json
  6. 若传了 task_id,调用 tasks.bind_worktree 完成双侧绑定;
  7. 发出 worktree.create.after 事件;任何一步失败则发出 worktree.create.failed 并携带 error 字段。

绑定同时写入两侧状态(bind_worktreeL183-L192):

def bind_worktree(self, task_id: int, worktree: str, owner: str = "") -> str:
    task = self._load(task_id)
    task["worktree"] = worktree
    if owner:
        task["owner"] = owner
    if task["status"] == "pending":
        task["status"] = "in_progress"
    task["updated_at"] = time.time()
    self._save(task)
    return json.dumps(task, indent=2)

注意两个细节:绑定只会把 pending 推进为 in_progress,不会把 completed 任务「复活」;owner 参数可选,用于同时标记认领者。反向操作 unbind_worktreeL194-L199)只清空 worktree 字段,不改变任务状态,留给拆除流程决定。

3. 在隔离目录中执行命令

subprocess.run(command, shell=True, cwd=worktree_path,
               capture_output=True, text=True, timeout=300)

worktree_run 工具背后的 WorktreeManager.runL368-L392)在隔离执行上做了三层防护:

  • 危险命令黑名单rm -rf /sudoshutdownreboot> /dev/ 命中即拒绝,返回 Error: Dangerous command blocked
  • 索引校验:worktree 必须在 index.json 中登记且路径实际存在,否则返回 Unknown / path missing 错误,杜绝「未登记目录」被当作合法执行通道;
  • 资源上限timeout=300 秒,超时返回 Error: Timeout (300s);输出合并 stdout/stderr 后截断到 50000 字符,避免撑爆上下文。

worktree_status 则直接在目标 worktree 中执行 git status --short --branchL351-L366),工作区干净时返回 Clean worktree

4. 收尾:keep 或 remove 二选一

  • worktree_keep(name)——把索引中该条目标记为 kept 并记录 kept_at,目录保留供后续使用(L448-L471),同时发出 worktree.keep 事件;
  • worktree_remove(name, force=False, complete_task=False)——删除目录,可选完成绑定任务,一次调用搞定拆除 + 完成(L394-L446):
def remove(self, name: str, force: bool = False, complete_task: bool = False) -> str:
    ...
    args = ["worktree", "remove"]
    if force:
        args.append("--force")
    args.append(wt["path"])
    self._run_git(args)

    if complete_task and wt.get("task_id") is not None:
        task_id = wt["task_id"]
        before = json.loads(self.tasks.get(task_id))
        self.tasks.update(task_id, status="completed")
        self.tasks.unbind_worktree(task_id)
        self.events.emit("task.completed", task={...}, worktree={"name": name})

    # index.json 中该条目标记为 status="removed" 并记录 removed_at

从源码结构看,remove 的执行顺序是「先 git 拆除、再任务收尾、最后更新索引」:拆除前发 worktree.remove.before,成功后索引条目保留在 index.json 中(不删除记录,只把状态改为 removed 并加 removed_at),再发 worktree.remove.after;任何异常走 worktree.remove.failed 分支并携带错误信息后重新抛出。force=True 会附加 --force 参数,用于强制移除有未提交改动的 worktree。

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/failedworktree.remove.before/after/failedworktree.keeptask.completed

EventBusL83-L118)的 emit 用追加模式写入,payload 固定包含 eventts(时间戳)、taskworktree 四个字段,失败场景额外带 error。查询工具 worktree_eventslist_recentlimit 被夹在 [1, 200] 区间(默认 20),逐行解析 JSONL,单行解析失败不会中断整个列表,而是降级为 {"event": "parse_error", "raw": ...} 条目——这是对「日志文件也可能损坏」的防御式设计。

恢复模型是明确的:会话记忆是易失的,磁盘状态是持久的。崩溃后,Agent 可以从 .tasks/ 下所有任务 JSON + .worktrees/index.json 重建完整现场:哪些任务在进行中、各自的 worktree 在哪里、哪些已经保留或拆除、事件流走到哪一步。

工具清单:Agent 看到的完整能力面

TOOL_HANDLERSL536-L553)把上述组件暴露为 12 个任务/worktree 工具,外加 4 个基础文件工具(bashread_filewrite_fileedit_file),供 LLM 在 agent loop 中调用:

工具 参数 说明
task_create subject(必填), description 在共享任务板上创建任务,返回完整任务 JSON
task_list 列出所有任务,含状态标记 [ ]/[>]/[x]、owner 与 worktree 绑定
task_get task_id 按 ID 查询任务详情
task_update task_id(必填), status 枚举, owner 更新状态(pending/in_progress/completed,非法值直接报错)或负责人
task_bind_worktree task_id, worktree(必填), owner 手动把任务绑定到 worktree 名称(通常由 worktree_create 自动完成)
worktree_create name(必填), task_id, base_ref(默认 HEAD 创建 git worktree 并可选绑定任务
worktree_list 列出 index.json 中登记的 worktree 及状态
worktree_status name 在指定 worktree 中执行 git status --short --branch
worktree_run name, command(必填) 在指定 worktree 目录中执行 shell 命令(300s 超时)
worktree_keep name 标记 worktree 为 kept,目录保留
worktree_remove name(必填), force, complete_task 拆除 worktree,可选完成任务
worktree_events limit(默认 20) 查看最近的生命周期事件

系统提示词(L73-L79)直接把这层「harness 思想」注入 Agent:「Use task + worktree tools for multi-task work. For parallel or risky changes: create tasks, allocate worktree lanes, run commands in those lanes, then choose keep/remove for closeout. Use worktree_events when you need lifecycle visibility.」 这正是本课程的定位——目录隔离是一条「永不碰撞的并行执行通道」(parallel execution lanes that never collide)。

相对 s11 的变更

组件 之前 (s11) 之后 (s12)
协调 任务板 (owner/status) 任务板 + worktree 显式绑定
执行范围 共享目录 每个任务独立目录
可恢复性 仅任务状态 任务状态 + worktree 索引
收尾 任务完成 任务完成 + 显式 keep/remove
生命周期可见性 隐式日志 .worktrees/events.jsonl 显式事件流

运行方式与实操 Prompt

依赖见 requirements.txtanthropic>=0.25.0python-dotenv>=1.0.0pyyaml>=6.0),并通过 .env 提供 MODEL_IDANTHROPIC_BASE_URL 等环境变量(脚本用 load_dotenv(override=True) 加载)。程序启动时会用 git rev-parse --show-toplevel 探测仓库根目录(detect_repo_rootL53-L68),worktree 目录、任务板、事件流都锚定在仓库根下;若当前不在 git 仓库内,会打印提示且所有 worktree_* 工具返回错误(git 不可用时 _run_git 直接抛 RuntimeError)。

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

启动后进入交互式 REPL(s12 >> 提示符,输入 q/exit 退出)。试试这些 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.

这五个 prompt 恰好覆盖完整生命周期:建任务 → 建 worktree 并绑定 → 隔离目录内执行 → keep 保留 → remove + complete_task 拆除收尾,配合 worktree_events 可以逐步核对事件流的推进。

延伸:s13 中该机制的强化方向

从仓库后续章节与测试看,worktree 隔离在 s13(Agent Teams)中被进一步深化为团队协作能力。测试文件 tests/test_agent_teams_runtime.py 中出现了一系列更严格的生命周期语义,可以推断团队版实现了本讲未覆盖的防护,例如:

  • 默认拒绝拆除脏 worktree(test_remove_worktree_refuses_dirty_checkout_by_default),需要显式 discard_changes=True 才可强制拆除;
  • 被 gitignore 的文件也视为未提交数据,同样阻止默认拆除(test_remove_worktree_treats_ignored_files_as_uncommitted_data);
  • 名称含 ../ 等非法 worktree 永远不会成为可认领的分配(test_invalid_or_unregistered_worktree_never_becomes_claimable);
  • git worktree add 失败时回滚绑定,任务上不会残留幽灵 worktree 字段。

这说明 s12 建立的「索引 + 事件 + 双侧绑定」骨架是可扩展的:s12 关注单 Agent 视角的隔离通道,后续章节在同一数据模型上叠加了团队认领、脏状态保护等语义。

小结

s12 的核心设计可以压缩成三句话:

  1. 任务管目标,worktree 管目录,按 ID 绑定——控制面(.tasks/)与执行面(.worktrees/)通过 task_id 双向引用,任一侧都可单独恢复;
  2. 磁盘状态是持久的,会话记忆是易失的——崩溃恢复不依赖 Agent 的上下文,只依赖 .tasks/.worktrees/index.json 加事件流;
  3. 隔离是目录级的,协调是任务级的——每个任务一条互不碰撞的执行通道,keep/remove 显式收尾,events.jsonl 提供完整的生命周期可见性。

对想在自己的 Agent 框架中引入并行目录隔离的读者,agents/s12_worktree_task_isolation.py 提供了约 800 行、单文件、无外部框架依赖的完整参照实现:三个核心类(TaskManager / WorktreeManager / EventBus)+ 12 个工具定义 + 标准 agent loop,可直接裁剪复用。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.79 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
988
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384