S12 Worktree 任务隔离详解笔记
基于
s12_worktree_task_isolation.py源码逐行分析,配合s12-worktree-task-isolation.md设计思路。
一、问题:多个任务共享一个工作目录,互相踩踏
前面 11 章的 agent 都在同一个工作目录下操作。当只有一个 agent 时没问题。但当 s09-s11 引入了多 agent 团队后:
- Alice 在改
utils.py,Bob 也在改utils.py→ 冲突 - Alice 跑了
pip install flask,Bob 跑了pip install django→ 全局环境污染 - Alice 的测试需要 Python 3.10,Bob 需要 3.8 → 不可能同时满足
共享文件系统是多 agent 并行的最大阻碍。每个 agent 需要一个独立的工作空间,但又能共享代码基础。
s12 的解决方案:Git worktree。每个任务分配一个独立的 worktree 目录,agent 在里面为所欲为——修改、装包、跑测试——不影响其他人。完成后,worktree 可以被保留(keep)或删除(remove)。
二、什么是 Git Worktree?
初学者:git worktree允许你从同一个仓库创建多个工作目录,每个目录有自己的分支。它们共享同一个.git目录(节省磁盘空间),但文件系统完全隔离。
主仓库: /project (branch: main) worktree: /project/.worktrees/auth (branch: wt/auth) worktree: /project/.worktrees/api (branch: wt/api)每个 worktree 像一个独立的克隆,但不需要重新 clone 整个仓库。创建 worktree 通常只需要几秒钟。
三、和 s11 相比,多了什么?
| 组件 | s11 | s12 |
|---|---|---|
| 执行隔离 | 无(共享目录) | Git worktree(目录级隔离) |
| 任务绑定 | 无 | task.worktree字段 +bind_worktree() |
| 事件追踪 | 无 | EventBus:生命周期事件日志 |
| 新工具 | idle,claim_task | worktree_create/list/status/run/keep/remove+worktree_events |
| 工具数量 | 14 | 16 |
四、EventBus:可观测性的基础设施
classEventBus:def__init__(self,event_log_path:Path):self.path=event_log_path self.path.parent.mkdir(parents=True,exist_ok=True)defemit(self,event:str,task=None,worktree=None,error=None):payload={"event":event,"ts":time.time(),"task":taskor{},"worktree":worktreeor{},}iferror:payload["error"]=errorwithself.path.open("a")asf:f.write(json.dumps(payload)+"\n")deflist_recent(self,limit:int=20)->str:# 返回最近 N 条事件,用于调试和监控EventBus 记录 worktree 和 task 的完整生命周期:
worktree.create.before → worktree.create.after (或 .failed) worktree.remove.before → worktree.remove.after (或 .failed) worktree.keep task.completed这不是给模型看的——是给开发者和运维工具看的。当系统出现异常(worktree 创建失败、任务卡住),事件日志是回溯问题的第一手资料。s12 是系列中第一个引入面向人类的可观测性的章节。
五、WorktreeManager:Git Worktree 的生命周期
5.1 初始化
classWorktreeManager:def__init__(self,repo_root:Path,tasks:TaskManager,events:EventBus):self.git_available=self._is_git_repo()self.index_path=self.dir/"index.json"index.json是 worktree 的注册表——记录所有 worktree 的名称、路径、分支、关联任务、状态。
5.2create()— 创建 worktree
defcreate(self,name:str,task_id:int=None,base_ref:str="HEAD")->str:self._validate_name(name)ifself._find(name):raiseValueError(f"Worktree '{name}' already exists")path=self.dir/name branch=f"wt/{name}"self.events.emit("worktree.create.before",...)# 核心:执行 git worktree addself._run_git(["worktree","add","-b",branch,str(path),base_ref])# 写入 indexentry={"name":name,"path":str(path),"branch":branch,"task_id":task_id,"status":"active","created_at":time.time()}idx["worktrees"].append(entry)# 绑定到任务iftask_idisnotNone:self.tasks.bind_worktree(task_id,name)self.events.emit("worktree.create.after",...)base_ref默认为HEAD(当前分支的最新提交)。名字校验[A-Za-z0-9._-]{1,40}阻止特殊字符——因为 worktree 名会变成目录名和分支名。
5.3run()— 在 worktree 中执行命令
defrun(self,name:str,command:str)->str:wt=self._find(name)path=Path(wt["path"])r=subprocess.run(command,shell=True,cwd=path,# ← 关键:cwd 指向 worktree 目录capture_output=True,text=True,timeout=300,)return(r.stdout+r.stderr).strip()[:50000]和run_bash的唯一区别:cwd指向 worktree 目录而非主工作目录。agent 可以在 worktree 中装包、改代码、跑测试——一切操作隔离在 worktree 内。
5.4remove()— 删除 worktree
defremove(self,name:str,force:bool=False,complete_task:bool=False):self._run_git(["worktree","remove","--force"ifforceelse"",wt["path"]])ifcomplete_taskandwt.get("task_id")isnotNone:self.tasks.update(task_id,status="completed")self.tasks.unbind_worktree(task_id)idx["worktrees"][...]["status"]="removed"如果complete_task: true,自动把关联任务标记为完成并解绑——一条命令完成"清理 workspace + 完成任务"。
5.5keep()— 保留 worktree
defkeep(self,name:str)->str:idx["worktrees"][...]["status"]="kept"idx["worktrees"][...]["kept_at"]=time.time()不删除 worktree,只在 index 中标记为kept。适合"代码还需要审查"或"先保留以备后续修改"的场景。
六、TaskManager 的扩展
s12 的 TaskManager 在 s07 的基础上新增了worktree字段和bind_worktree/unbind_worktree方法:
defbind_worktree(self,task_id:int,worktree:str,owner:str="")->str:task=self._load(task_id)task["worktree"]=worktreeifowner:task["owner"]=owneriftask["status"]=="pending":task["status"]="in_progress"self._save(task)defunbind_worktree(self,task_id:int)->str:task=self._load(task_id)task["worktree"]=""self._save(task)任务和 worktree 之间是一对一的关系。task.worktree字段记录了"这个任务在哪个隔离环境中执行"。
七、两个 Manager 的协作
Task 创建 → task.status = "pending" ↓ Worktree 创建 → task.bind_worktree(task_id, wt_name) ↓ task.status → "in_progress" Agent 在 worktree 中工作 → worktree_run(wt_name, "pip install flask") ↓ 工作完成 → worktree_remove(name, complete_task=True) ↓ task.status → "completed" task.worktree → ""Tasks 是控制面(管"该做什么"),Worktrees 是执行面(管"在哪里做")。两者的关联通过task.worktree字段 +bind_worktree方法桥接。
八、完整流程走读
场景
用户:“重构认证模块,在隔离环境中进行,完成后清理。”
第 1 轮
Lead 创建任务:task_create("重构认证模块")→ task 12。
第 2 轮
Lead 创建 worktree:worktree_create("auth-refactor", task_id=12)→ 在.worktrees/auth-refactor/创建隔离目录,分支wt/auth-refactor。Task 12 的status自动变为in_progress,worktree设为"auth-refactor"。
第 3-10 轮
Lead 在 worktree 中工作:worktree_run("auth-refactor", "pip install bcrypt")、worktree_run("auth-refactor", "python -m pytest tests/auth/")。所有修改在wt/auth-refactor分支上进行,主工作区完全不受影响。
第 11 轮
Lead 完成工作:worktree_remove("auth-refactor", complete_task=True)→ worktree 被删除,Task 12 自动标记为completed。分支可以后续合并回 main。
如果出错了
worktree_events(limit=10)显示最近 10 条事件:
{"event":"worktree.create.after","worktree":{"name":"auth-refactor","status":"active"}}{"event":"worktree.remove.failed","worktree":{"name":"auth-refactor"},"error":"worktree contains uncommitted changes"}Lead 看到错误,改用worktree_remove("auth-refactor", force=True)强制删除,或用worktree_keep("auth-refactor")保留下来手动处理。
九、设计洞察
9.1 控制面与执行面的分离
Tasks(控制面)回答"做什么",Worktrees(执行面)回答"在哪里做"。两者通过task.worktree字段关联,各自独立演化。这种分离让系统可以:
- 换一种执行隔离方式(容器而非 worktree)而不影响任务管理
- 换一种任务管理方式(Kanban 而非 JSON)而不影响执行隔离
9.2 Git 作为平台
s12 大量依赖 Git(worktree add、worktree remove、rev-parse)。在很多人眼中 Git 是"版本控制工具",但在这里 Git 是文件系统管理平台。git worktree解决了"在同一仓库中创建多个独立工作目录"的问题——这个问题自己实现可能需要上千行代码。
站在巨人的肩膀上,而不是自己造轮子。这是工程中最重要的判断力之一。
9.3 可观测性不是后加的
EventBus 从 worktree 创建的第一步就记录事件。事件覆盖了完整的生命周期:before → after/failed,每条事件带时间戳、关联的 task、关联的 worktree。当系统变得不可预测(异步操作、多线程、外部依赖),事件日志是你理解"发生了什么"的唯一窗口。
9.4 s12 是整个系列的终章——也是新起点
回头看整个系列:
s01: 循环 ← agent 能动了 s02: 工具 ← agent 能操作文件系统了 s03: 计划 ← agent 能追踪进度了 s04: 子 agent ← agent 能委托任务了 s05: 知识 ← agent 能按需学习领域专长了 s06: 压缩 ← agent 能无限工作了 s07: 任务图 ← 状态在对话之外存活了 s08: 后台 ← agent 不等命令了 s09: 团队 ← agent 能合作了 s10: 协议 ← agent 能握手了 s11: 自主 ← agent 自己找工作了 s12: 隔离 ← agent 能在独立空间中工作了s12 的 worktree 隔离让多 agent 并行成为可能——这是从"单机单 agent"到"分布式 agent 网络"的桥梁。虽然这个系列的代码在 s12 结束,但它指向的方向远不止于此。