Skip to content

Latest commit

 

History

History
288 lines (224 loc) · 8.76 KB

File metadata and controls

288 lines (224 loc) · 8.76 KB

该系列文章基于 github.com/shareAI-lab/learn-claude-code 写就,该仓库以大道至简的风格剖析了Claude Code的核心原理,值得大家学习。由于该仓库是基于Python语言,为方便.NET开发者学习,我已经将代码基于.NET 10的dotnet file 重写,源码已上传至github,源码地址见文末。

v11: Worktree 任务隔离 - 控制面 + 执行面分离

本文是 Learn Claude Code (C# 版) 系列的第十二篇,对应代码文件 v11_worktree_task_isolation.cs

问题:共享工作目录的冲突

v10 让队友能自治地认领任务,但所有智能体仍然共享同一个工作目录:

fe-dev 正在修改 src/App.tsx 的第42行...
be-dev 同时修改 src/App.tsx 的第42行...

→ 冲突!
→ 一个人的改动覆盖另一个人的改动
→ 文件系统没有并发控制

这就像多个开发者直接在同一台机器的同一个文件夹里写代码,不用 Git 分支,不用隔离——灾难是必然的。

解决方案:Git Worktree 隔离

目录隔离,任务 ID 协调——用"任务板(控制面)+ worktree(执行面)"把并行改动从互相污染变成可追踪、可恢复、可收尾。

 控制面 (主仓库)                    执行面 (worktrees)
+------------------+  create    +------------------+
|  .worktrees/     | ────────→  | ../repo-wt-fe/   |
|    index.json    |            |   (独立目录)      |
|    events.jsonl  |            |   branch: wt/fe  |
|                  |            +------------------+
|  .tasks/         |  create    +------------------+
|    task_001.json | ────────→  | ../repo-wt-be/   |
|    task_002.json |            |   (独立目录)      |
+------------------+            |   branch: wt/be  |
                                +------------------+
      任务看板调度                    隔离的代码修改

三个核心模块

1. EventBus:基于事件的通信

替代 JSONL 收件箱,使用全局事件日志:

class EventBus
{
    string eventsFile;  // .worktrees/events.jsonl

    public void Emit(string type, string source, Dictionary<string, object> data)
    {
        var ev = new
        {
            id = Guid.NewGuid().ToString("N")[..8],
            type, source, data,
            timestamp = DateTime.UtcNow.ToString("o")
        };
        // Append-only, 线程安全
        lock (writeLock)
        {
            File.AppendAllText(eventsFile, JsonSerializer.Serialize(ev) + "\n");
        }
    }

    public List<...> Read(int sinceIndex = 0)
    {
        // 从指定位置开始读, 支持增量读取
        return allEvents.Skip(sinceIndex).ToList();
    }
}

2. TaskManager:增强的任务看板

在 v10 基础上增加 worktree 绑定:

class TaskManager
{
    public void BindWorktree(string taskId, string worktreeName)
    {
        var task = Load(taskId);
        task["worktree"] = worktreeName;
        Save(taskId, task);
        eventBus.Emit("task.worktree_bound", "system",
            new() { ["task_id"] = taskId, ["worktree"] = worktreeName });
    }

    public void UnbindWorktree(string taskId)
    {
        var task = Load(taskId);
        task.Remove("worktree");
        Save(taskId, task);
    }
}

3. WorktreeManager:Git Worktree 生命周期

class WorktreeManager
{
    string repoRoot;               // git rev-parse --show-toplevel
    string worktreeBaseDir;        // .worktrees/
    Dictionary<string, WtInfo> index;  // index.json 注册表

    public string Create(string name)
    {
        var branch = $"wt/{name}";
        var absPath = Path.Combine(repoRoot, "..", $"{repoName}-wt-{name}");

        // git worktree add -b wt/<name> <path>
        RunGit($"worktree add -b {branch} {absPath}");

        index[name] = new WtInfo(name, absPath, branch);
        SaveIndex();

        eventBus.Emit("worktree.created", "lead",
            new() { ["name"] = name, ["path"] = absPath });

        return $"Created worktree '{name}' at {absPath}";
    }
}

Worktree 操作全集

操作 说明
worktree_create 创建新的 git worktree(独立分支 + 独立目录)
worktree_list 列出所有已创建的 worktree
worktree_status 查看指定 worktree 的 git 状态
worktree_run 在指定 worktree 目录中执行命令
worktree_remove 删除 worktree 及其分支
worktree_keep 标记 worktree 为"保留"(合并回主分支)

在 Worktree 中执行命令

public string RunInWorktree(string name, string command)
{
    var wt = index[name];

    var proc = Process.Start(new ProcessStartInfo
    {
        FileName = IsWindows ? "cmd.exe" : "/bin/bash",
        Arguments = IsWindows ? $"/c {command}" : $"-c \"{command}\"",
        WorkingDirectory = wt.AbsPath,  // 关键: 切换到 worktree 目录
        RedirectStandardOutput = true
    });
    // ...
}

保留和移除

public string KeepWorktree(string name, string? mergeMessage)
{
    var wt = index[name];
    // git checkout main && git merge wt/<name>
    RunGit($"checkout main");
    RunGit($"merge {wt.Branch} -m \"{mergeMessage ?? $"Merge {name}"}\"");

    wt.Status = "kept";
    SaveIndex();
    return $"Merged {name} into main.";
}

public string RemoveWorktree(string name)
{
    var wt = index[name];
    // git worktree remove <path> && git branch -D wt/<name>
    RunGit($"worktree remove {wt.AbsPath} --force");
    RunGit($"branch -D {wt.Branch}");

    index.Remove(name);
    SaveIndex();
    return $"Removed worktree '{name}'.";
}

工具总览

v11 包含 17 个工具,是系列中最丰富的:

类别 工具
基础 (4) bash, read_file, write_file, edit_file
任务 (5) task_create, task_update, task_list, task_get, task_bind_worktree
Worktree (7) worktree_create, worktree_list, worktree_status, worktree_run, worktree_remove, worktree_keep, worktree_diff
事件 (1) read_events

完整工作流示例

1. Lead 创建任务:
   task_create("实现登录页面")  → task_001

2. 创建隔离 worktree:
   worktree_create("login")    → ../repo-wt-login/

3. 绑定任务到 worktree:
   task_bind_worktree("001", "login")

4. 在隔离目录中工作:
   worktree_run("login", "echo 'working...'")

5. 完成后合并:
   worktree_keep("login", "feat: add login page")

6. 或者丢弃:
   worktree_remove("login")

相对 v10 的变更

组件 之前 (v10) 之后 (v11)
通信模型 JSONL 收件箱 EventBus 全局事件日志
工作隔离 共享目录 git worktree 独立目录
任务绑定 task ↔ worktree 绑定
分支管理 自动 wt/ 前缀分支
工具数 14 (Lead) 17
架构 平铺结构 控制面/执行面分离

设计原理

v11 的关键架构洞察是控制面与执行面分离

  • 控制面(主仓库):任务看板、事件总线、worktree 注册表——管理"做什么"
  • 执行面(worktree 目录):实际代码修改——执行"怎么做"

Git worktree 是天然的隔离机制:

  1. 共享 .git:所有 worktree 共享 object store,不浪费磁盘
  2. 独立工作树:每个 worktree 有自己的 HEAD、index、工作目录
  3. 分支粒度:每个任务一个分支,天然的代码审查单元

这个模式让并行开发从"互相污染"变成"可追踪、可恢复、可收尾"。

运行

dotnet run v11_worktree_task_isolation.cs

可以尝试的提示:

  1. Create a task "Build login page" and a worktree for it
  2. Run "ls" in the login worktree
  3. Create another worktree "api" for backend work
  4. Show me all worktrees and their status
  5. Keep the login worktree (merge back to main)

系列总结

从 v0 到 v11,我们构建了一个完整的智能体系统:

版本 核心概念 一句话总结
v0 Bash 工具 给 LLM 一个终端
v1 Agent Loop while(true) + 工具调用
v2 TODO 工具 工具就是函数
v3 Sub-agent 智能体可以创建智能体
v4 Skills 按需加载领域知识
v5 Context Compact 三层压缩管道
v6 Task System 文件系统持久化任务板
v7 Background Tasks 后台执行不阻塞
v8 Agent Teams JSONL 收件箱通信
v9 Team Protocols request_id 握手 FSM
v10 Autonomous Agents WORK/IDLE 生命周期
v11 Worktree Isolation 控制面/执行面分离

核心循环始终不变:

while (true)
{
    var response = await client.Messages.Create(params);
    if (response.StopReason != StopReason.ToolUse) break;
    // 执行工具, 收集结果, 继续
}

每一步只增加一个概念。这就是 Learn Claude Code 的全部哲学。