AI Agent 也会「抢地盘」?文件冲突怎么解
🎯 学习目标
- 任务所有权等于文件系统隔离吗?文件锁能解决吗?
- Git Worktree 提供什么?和文件锁的区别?
- 为什么不能把 worktree 塞进 Task schema?三条独立状态线是什么?
- Binding 的状态机(reserved/active/kept/needs_review/removed)?
- 有未提交改动/未集成提交/Git检查失败时,删除会怎样?
- 工具执行前,可信工作目录如何解析到当前 claim 对应的 Worktree?
🧠 核心概念
1 · 文件锁不是答案 ▸
文件锁只能保证「同一时刻一个人写」,不能给两项并行工作提供独立版本:
Alice 获得 config.ts 锁并写入
-> Alice 释放锁
-> Bob 获得锁并写入
-> Alice 的结果仍被 Bob 覆盖
锁把并行写变成有序覆盖,没解决「两个候选改动如何分别保存、审查和集成」。Git Worktree 提供物理目录隔离,同时复用同一个 Git 对象库。
2 · Git Worktree 结构 ▸
repository-root/
├── .git/
├── .agent_tutorial/
│ ├── tasks.sqlite3
│ └── worktrees/
│ ├── alice/ branch: wt/alice
│ └── bob/ branch: wt/bob
└── source files integration ref 所在主工作目录
Alice 和 Bob 可同时改 config.ts,但修改位于不同目录和不同分支。之后由明确 review/merge 流程决定哪一份进入集成引用。
3 · 不能把 worktree 塞进 Task:三条独立状态线 ▸
| 状态线 | 回答的问题 | 典型状态 |
|---|---|---|
| Project Task | 工作是否未开始/执行中/已完成 | pending→in_progress→completed |
| Task Claim | 当前谁在租期内拥有执行权 | active/expired/cleared |
| Worktree Binding | 任务对应的隔离目录处于什么生命周期 | reserved/active/kept/needs_review/removed |
4 · Binding 状态机 ▸
- reserved:binding 已在 SQLite 落库,但 Git 目录还没建成功——一条意图记录;git worktree add 失败就停在这里,任务仍 pending,自动认领捡不到它。
- active:Git 目录和分支都已回读确认(读回 branch --show-current 证明),这条 binding 才可被认领和路由。顺序不能反:先落库再调 Git,孤儿副作用比多余状态记录严重得多。
- kept:任务完成并已集成,worktree 保留(可后续删除)。
- needs_review:有未提交改动/未集成提交/Git检查失败,删除请求变成 needs_review 并带 review_reason,目录和提交不被强制丢弃。
- removed:clean worktree 已安全删除;binding 行留在库里当审计记录。
每次工具执行前,可信工作目录解析到当前 claim 对应的 Worktree:write_file 的 path 在 active binding 时解析到 wt/alice/,不是主目录。解析由 ToolContextProvider.resolve() 完成,排在 schema 校验之后、Hook 与权限之前——Hook 看到的就是最终执行上下文;解析失败报成对 tool_context_error,handler 零调用。不用 process.chdir();executionScope 是每回合新建的冻结对象,用 WeakMap 映射到 claim token,生命周期正好一轮;失效 claim 必须显式失败,绝不静默回落主目录——只有完全没声明 claim 才用主目录。
5 · 删除的安全边界 ▸
完成 A 并把 wt/alice 集成进明确引用后,clean Worktree 可安全删除。但 B 有未提交改动、未集成提交或 Git 检查失败时,删除请求变成 needs_review:
集成判定用 branch tip 是否已包含在指定 integration_ref 里(merge-base --is-ancestor),不用 branch --merged;integration_ref 先 rev-parse --verify 解析成不可变 40 位 commit 再建 Worktree。git status 非零退出(出错)与空串(干净)必须区别对待,Git 报错不泄露 stderr。已是 kept/needs_review 的不重复迁移。validateRepository 要求 workspace 正好是仓库根,子目录不行。