跳转到内容

Isolation 与 Worktree

当多个 agent 同时操作同一代码库时,需要 isolation 来避免相互影响。Claude Code 以 git worktree 作为主要的 isolation 机制——每个 agent 获得仓库自己的工作副本,共享相同的 git 历史,但拥有独立的文件系统。

其他方案及其被排除的原因:

方案问题
同一目录并发 agent 间的文件冲突
完整克隆占用磁盘空间大、速度慢、历史分离
Docker/容器重量级、配置复杂、非普遍可用
Git worktree✅ 轻量级、共享历史、独立文件

Git worktree 是 git 的原生特性(git worktree add),可创建链接到同一仓库的新工作目录。各 agent 可以独立修改文件、创建分支和提交,互不影响。

Agent 定义在 BaseAgentDefinition 中支持两种 isolation 模式:

src/tools/AgentTool/loadAgentsDir.ts
isolation?: 'worktree' | 'remote'
模式可用范围描述
worktree所有用户本地磁盘上的 git worktree
remote仅 Anthropic 内部在 Claude Code Remote(CCR)中远程执行

remote 模式在解析时受限:

// src/tools/AgentTool/loadAgentsDir.ts — AgentJsonSchema
isolation: (process.env.USER_TYPE === 'ant'
? z.enum(['worktree', 'remote'])
: z.enum(['worktree'])
).optional(),

外部用户只能使用 worktree。在 agent 定义中设置 remote 将导致验证失败。

sequenceDiagram
    participant L as Leader Agent
    participant W as Worktree System
    participant G as Git
    participant A as Agent (in worktree)

    L->>W: Spawn agent with isolation: 'worktree'
    W->>G: git worktree add <path> <branch>
    G-->>W: Worktree created at <path>
    W->>A: Start agent with cwd = worktree path
    Note over A: Agent works independently<br/>in its own working copy
    A->>A: Read, Edit, Bash, Commit...
    A-->>L: Report results
    L->>W: Cleanup / Kill agent
    W->>G: git worktree remove --force <path>
    G-->>W: Worktree removed

每个 team 成员可以拥有自己的 worktree,存储在 team 配置中:

// teamHelpers.ts 中的 TeamMember
type TeamMember = {
agentId: string
worktreePath?: string // 该成员的 worktree 路径
// ...
}

team 解散或成员被终止时,worktree 必须被清理。destroyWorktree() 函数会谨慎处理这一过程:

// src/utils/swarm/teamHelpers.ts — destroyWorktree(概念性代码)
async function destroyWorktree(worktreePath: string): Promise<void> {
// 1. 读取 .git 文件以找到主仓库
// (worktree 的 .git 是文件而非目录:"gitdir: /path/to/main/.git/worktrees/...")
const gitContent = await readFile(join(worktreePath, '.git'), 'utf-8')
// 2. 从 gitdir 引用中提取主仓库路径
const mainRepoPath = resolveMainRepo(gitContent)
// 3. 使用 git worktree remove 进行干净移除
try {
await exec(`git worktree remove --force ${worktreePath}`, { cwd: mainRepoPath })
} catch {
// 4. 回退:强制删除目录
await exec(`rm -rf ${worktreePath}`)
}
}

当 fork mechanism 在隔离的 worktree 中创建子进程时,会向子进程的 context 注入一条特殊说明:

src/tools/AgentTool/forkSubagent.ts
export function buildWorktreeNotice(parentCwd: string, worktreeCwd: string): string {
return `You've inherited the conversation context above from a parent agent
working in ${parentCwd}. You are operating in an isolated git worktree at
${worktreeCwd} — same repository, same relative file structure, separate
working copy.
Paths in the inherited context refer to the parent's working directory;
translate them to your worktree root.
Re-read files before editing if the parent may have modified them since
they appear in the context.
Your changes stay in this worktree and will not affect the parent's files.`
}

这条说明解决了三个关键问题:

  1. 路径转换 — 继承的路径指向父级目录,而非 worktree
  2. 过时 context — 父级 context 中读取的文件可能已发生变化
  3. isolation 保证 — 修改不会影响父级的工作副本

清理 team 时,必须先销毁所有 worktree,再删除目录:

flowchart TD
    A[cleanupTeamDirectories<br/>teamName] --> B[Read team config.json]
    B --> C[For each member<br/>with worktreePath]
    C --> D[destroyWorktree<br/>member.worktreePath]
    D --> E{Success?}
    E -->|yes| F[Continue]
    E -->|no| G[Log error, continue]
    F --> H[Remove task directories<br/>for each member]
    H --> I[Delete team config file]

session 级清理(cleanupSessionTeams)在 session 结束时运行,确保不留下孤立的 worktree:

// 清理函数查找该 session 的所有 team 文件并销毁其 worktree
async function cleanupSessionTeams(sessionId: string): Promise<void> {
const teamFiles = await findTeamFilesForSession(sessionId)
for (const teamFile of teamFiles) {
await cleanupTeamDirectories(teamFile.name)
}
}

worktree 系统还支持独立 session(不仅限于 team 成员)。src/utils/worktree.ts 中的 getCurrentWorktreeSession() 追踪当前 session 的 worktree 元数据:

// 在 prompts.ts 中用于 statusLine agent 的输入
type WorktreeSession = {
name: string // worktree 名称/slug
path: string // worktree 的完整路径
branch?: string // git 分支
original_cwd: string // 进入 worktree 前的目录
original_branch?: string
}

这些元数据流入状态栏配置,让用户能看到当前所在的 worktree 并快速返回。

Worktree 路径经过验证以防止路径穿越:

  • 路径必须为绝对路径
  • 路径不能逃逸项目根目录
  • 读取 worktree 中的 .git 文件以验证其指向合法仓库

src/memdir/paths.ts 中的 validateMemoryPath() 函数展示了系统中广泛使用的路径验证模式——拒绝相对路径、根路径、null 字节和 UNC 路径。