mirror of
https://github.com/shareAI-lab/analysis_claude_code.git
synced 2026-09-22 05:13:48 +08:00
feat: consolidate course into 21 lessons
This commit is contained in:
172
s17_worktree_isolation/README.ja.md
Normal file
172
s17_worktree_isolation/README.ja.md
Normal file
@@ -0,0 +1,172 @@
|
||||
# s17: Worktree Isolation — それぞれのディレクトリ、互いに干渉しない
|
||||
|
||||
[English](README.md) · [中文](README.zh.md) · [日本語](README.ja.md)
|
||||
|
||||
s01 → ... → s15 → s16 → `s17` → [s18](../s18_mcp_plugin/) → s19 → s20 → s21
|
||||
|
||||
> *"それぞれのディレクトリ、互いに干渉しない"* — タスクは目標を管理、worktree はディレクトリを管理、ID で紐付け。
|
||||
>
|
||||
> **Harness 層**: 隔離 — 並列実行のディレクトリ分離。
|
||||
|
||||
---
|
||||
|
||||
## 課題
|
||||
|
||||
s16 では、Alice も Bob も同じディレクトリで作業。Alice のタスクは「認証モジュールのリファクタリング」、Bob のタスクは「UI ログインページのリファクタリング」。
|
||||
|
||||
Alice が `write_file("config.py", ...)` を呼び出し、Bob も `write_file("config.py", ...)` を呼び出す。両者が同じファイルを編集し、互いに上書き。クリーンなロールバックもできない——どの変更が誰のものか区別できない。
|
||||
|
||||
s15-s16 は「誰が何をするか」(タスクシステム)と「どう通信するか」(メッセージバス)を解決したが、「どこで作業するか」は未解決。
|
||||
|
||||
---
|
||||
|
||||
## ソリューション
|
||||
|
||||

|
||||
|
||||
Git worktree を使うと、同じリポジトリ内に複数の独立した作業ディレクトリを作成でき、それぞれが独自のブランチを持つ。Alice は `.worktrees/auth-refactor/` で作業、Bob は `.worktrees/ui-login/` で作業——互いに干渉しない。
|
||||
|
||||
s16 の MessageBus、プロトコル、自動認領を引き継ぐ。本章では次を追加する:
|
||||
|
||||
| 機能 | 目的 |
|
||||
|------|------|
|
||||
| create_worktree | タスク用の独立ディレクトリ + 独立ブランチを作成 |
|
||||
| bind_task_to_worktree | タスクとディレクトリを紐付け(状態は変更しない) |
|
||||
| remove_worktree / keep_worktree | 完了後のクリーンアップまたは保持 |
|
||||
| validate_worktree_name | パストラバーサルと不正文字を拒否 |
|
||||
|
||||
---
|
||||
|
||||
## 仕組み
|
||||
|
||||
### 作成:タスク-Worktree 紐付け
|
||||
|
||||
```python
|
||||
def create_worktree(name: str, task_id: str = "") -> str:
|
||||
validate_worktree_name(name) # [A-Za-z0-9._-]{1,64} のみ許可
|
||||
path = WORKTREES_DIR / name
|
||||
ok, result = run_git(["worktree", "add", str(path), "-b", f"wt/{name}", "HEAD"])
|
||||
if not ok:
|
||||
return f"Git error: {result}"
|
||||
if task_id:
|
||||
bind_task_to_worktree(task_id, name)
|
||||
log_event("create", name, task_id)
|
||||
return f"Worktree '{name}' created at {path}"
|
||||
|
||||
def bind_task_to_worktree(task_id: str, worktree_name: str):
|
||||
task = load_task(task_id)
|
||||
task.worktree = worktree_name # worktree フィールドのみ書き込み
|
||||
save_task(task) # 状態は pending のまま、チームメイトの claim を待つ
|
||||
```
|
||||
|
||||
紐付けルール:1 つのタスクに 1 つの worktree を紐付け。紐付けはタスクの状態を変更しない——タスクは `pending` のままで、チームメイトが認領した時に `in_progress` に進む。これにより Lead は事前にタスクと worktree を作成でき、チームメイトは idle 時に自然に worktree 紐付け済みタスクを認領する。
|
||||
|
||||
### チームメイトツールの cwd 切り替え
|
||||
|
||||
各チームメイトは、現在の worktree パスを記録する `wt_ctx` 辞書を持つ。worktree に紐付いたタスクを認領すると、ランタイムが `wt_ctx` を更新し、そのチームメイトの `bash`、`read_file`、`write_file` は対応する worktree ディレクトリで実行される:
|
||||
|
||||
```python
|
||||
# チームメイトスレッド内部
|
||||
wt_ctx = {"path": None}
|
||||
|
||||
def _run_claim_task(task_id):
|
||||
result = claim_task(task_id, owner=name)
|
||||
if "Claimed" in result:
|
||||
task = load_task(task_id)
|
||||
if task.worktree:
|
||||
wt_ctx["path"] = str(WORKTREES_DIR / task.worktree)
|
||||
return result
|
||||
|
||||
def _run_bash(command):
|
||||
return run_bash(command, cwd=wt_ctx["path"]) # worktree で実行
|
||||
```
|
||||
|
||||
### クリーンアップ:Keep または Remove
|
||||
|
||||
タスク完了後、2 つの選択肢:
|
||||
|
||||
```python
|
||||
def remove_worktree(name: str, discard_changes: bool = False) -> str:
|
||||
# 安全チェック:変更がある場合デフォルトで拒否
|
||||
if not discard_changes:
|
||||
files, commits = _count_worktree_changes(path)
|
||||
if files > 0 or commits > 0:
|
||||
return "未コミットの変更あり。discard_changes=true で強制削除、または keep_worktree で保持"
|
||||
ok, _ = run_git(["worktree", "remove", str(path), "--force"])
|
||||
if not ok:
|
||||
return "削除失敗"
|
||||
run_git(["branch", "-D", f"wt/{name}"])
|
||||
log_event("remove", name)
|
||||
|
||||
def keep_worktree(name: str) -> str:
|
||||
log_event("keep", name)
|
||||
return f"Worktree '{name}' kept for review (branch: wt/{name})"
|
||||
```
|
||||
|
||||
Keep = ブランチを保持し、手動 review 後にマージ。Remove = 未コミット変更がある場合デフォルトで拒否、`discard_changes=true` で確認が必要。タスクの自動 complete はしない——タスク完了はチームメイトの `complete_task` で明示的にトリガー。
|
||||
|
||||
### イベントログ:監査可能
|
||||
|
||||
各ライフサイクル操作はログに記録され、監査に利用:
|
||||
|
||||
```python
|
||||
def log_event(event_type: str, worktree_name: str, task_id: str = ""):
|
||||
event = {"type": event_type, "worktree": worktree_name,
|
||||
"task_id": task_id, "ts": time.time()}
|
||||
# .worktrees/events.jsonl に append
|
||||
```
|
||||
|
||||
イベントタイプは `create`、`remove`、`keep`。ログは手動監査に使い、復元時は `git worktree list` から現在の worktree 一覧を再構築できる。
|
||||
|
||||
### run_git:成功/失敗を返す
|
||||
|
||||
```python
|
||||
def run_git(args: list[str]) -> tuple[bool, str]:
|
||||
r = subprocess.run(["git"] + args, cwd=WORKDIR, ...)
|
||||
return r.returncode == 0, output
|
||||
```
|
||||
|
||||
`create_worktree` と `remove_worktree` は git コマンド成功後のみイベントログに書き込み、ログが実際の状態を反映することを保証。
|
||||
|
||||
---
|
||||
|
||||
## s16 からの変更
|
||||
|
||||
| コンポーネント | 変更前 (s16) | 変更後 (s17) |
|
||||
|--------------|------------|------------|
|
||||
| 作業ディレクトリ | 全 Agent が WORKDIR を共有 | 各タスクが git worktree に紐付け可能 |
|
||||
| タスクデータ | id/subject/status/owner/blockedBy | + worktree フィールド |
|
||||
| チームメイトツール cwd | 常に WORKDIR | worktree 紐付けタスク認領時に自動切り替え |
|
||||
| 新規関数 | — | create_worktree, bind_task_to_worktree, remove_worktree, keep_worktree, validate_worktree_name |
|
||||
| worktree 安全性 | なし | name 検証 + 変更ありの場合削除拒否 |
|
||||
| イベントログ | なし | events.jsonl ライフサイクル監査 |
|
||||
| Lead ツール | チーム・タスクツール | + create_worktree、remove_worktree、keep_worktree |
|
||||
| チームメイトツール | タスク・ファイルツール | ツールは同じ。bash/read/write は認領した worktree の cwd を使う |
|
||||
|
||||
---
|
||||
|
||||
## 試してみる
|
||||
|
||||
```sh
|
||||
cd learn-claude-code
|
||||
python s17_worktree_isolation/code.py
|
||||
```
|
||||
|
||||
以下のプロンプトを試してください:
|
||||
|
||||
`認証モジュールとログインページを並行してリファクタリングし、変更が互いに干渉しないようにしてください。`
|
||||
|
||||
観察ポイント:2 つの worktree の `git status` 出力は異なるブランチを表示しているか?チームメイトが worktree 紐付けタスクを認領後、bash コマンドは worktree ディレクトリで実行されているか?`remove_worktree` は変更がある場合に拒否するか?紐付け後のタスク状態は `pending` のままか?
|
||||
|
||||
---
|
||||
|
||||
## 次の章
|
||||
|
||||
Agent チームが隔離されたワークスペースで自己組織化できるようになった。しかし Agent の能力はツールに制限される——bash、read、write、task...
|
||||
|
||||
もしユーザーが独自のツールを持っていたら?例えば社内 Jira API や独自デプロイシステム?
|
||||
|
||||
s18 MCP Plugin → Agent にプラグインシステムを追加。外部ツールが標準プロトコルで接続、Agent は誰が書いたか知る必要がない。
|
||||
|
||||
|
||||
<!-- translation-sync: zh@v1, en@v1, ja@v1 -->
|
||||
172
s17_worktree_isolation/README.md
Normal file
172
s17_worktree_isolation/README.md
Normal file
@@ -0,0 +1,172 @@
|
||||
# s17: Worktree Isolation — Separate Directories, No Conflicts
|
||||
|
||||
[English](README.md) · [中文](README.zh.md) · [日本語](README.ja.md)
|
||||
|
||||
s01 → ... → s15 → s16 → `s17` → [s18](../s18_mcp_plugin/) → s19 → s20 → s21
|
||||
|
||||
> *"Separate directories, no conflicts"* — Tasks own the goal, worktrees own the directory, bound by ID.
|
||||
>
|
||||
> **Harness Layer**: Isolation — Parallel execution in separate directories.
|
||||
|
||||
---
|
||||
|
||||
## The Problem
|
||||
|
||||
In s16, Alice and Bob both work in the same directory. Alice's task is "refactor auth module", Bob's task is "refactor UI login page".
|
||||
|
||||
Alice calls `write_file("config.py", ...)`. Bob also calls `write_file("config.py", ...)`. Both edit the same file, overwriting each other. And there's no clean rollback — you can't tell whose changes are whose.
|
||||
|
||||
s15-s16 solved "who does what" (task system) and "how to communicate" (message bus), but not "where to work".
|
||||
|
||||
---
|
||||
|
||||
## The Solution
|
||||
|
||||

|
||||
|
||||
Git worktree lets you create multiple independent working directories in the same repo, each with its own branch. Alice works in `.worktrees/auth-refactor/`, Bob in `.worktrees/ui-login/` — no conflicts.
|
||||
|
||||
Carries forward s16's MessageBus, protocols, and autonomous claiming. This chapter adds:
|
||||
|
||||
| Capability | Purpose |
|
||||
|------------|---------|
|
||||
| create_worktree | Create isolated directory + branch for a task |
|
||||
| bind_task_to_worktree | Bind task and directory (no status change) |
|
||||
| remove_worktree / keep_worktree | Cleanup or preserve after completion |
|
||||
| validate_worktree_name | Reject path traversal and illegal characters |
|
||||
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
### Creation: Task-Worktree Binding
|
||||
|
||||
```python
|
||||
def create_worktree(name: str, task_id: str = "") -> str:
|
||||
validate_worktree_name(name) # Only [A-Za-z0-9._-]{1,64}
|
||||
path = WORKTREES_DIR / name
|
||||
ok, result = run_git(["worktree", "add", str(path), "-b", f"wt/{name}", "HEAD"])
|
||||
if not ok:
|
||||
return f"Git error: {result}"
|
||||
if task_id:
|
||||
bind_task_to_worktree(task_id, name)
|
||||
log_event("create", name, task_id)
|
||||
return f"Worktree '{name}' created at {path}"
|
||||
|
||||
def bind_task_to_worktree(task_id: str, worktree_name: str):
|
||||
task = load_task(task_id)
|
||||
task.worktree = worktree_name # Write worktree field only
|
||||
save_task(task) # Status stays pending, waits for teammate claim
|
||||
```
|
||||
|
||||
Binding rule: one task binds to one worktree. Binding does NOT change task status — the task stays `pending`, and advances to `in_progress` only when a teammate claims it. This way Lead can pre-create tasks and worktrees, and teammates naturally claim worktree-bound tasks during idle.
|
||||
|
||||
### Teammate Tool Cwd Switching
|
||||
|
||||
Each teammate keeps a `wt_ctx` dictionary with its current worktree path. When a teammate claims a task bound to a worktree, the runtime updates `wt_ctx`; that teammate's `bash`, `read_file`, and `write_file` calls then run in the worktree directory:
|
||||
|
||||
```python
|
||||
# Inside teammate thread
|
||||
wt_ctx = {"path": None}
|
||||
|
||||
def _run_claim_task(task_id):
|
||||
result = claim_task(task_id, owner=name)
|
||||
if "Claimed" in result:
|
||||
task = load_task(task_id)
|
||||
if task.worktree:
|
||||
wt_ctx["path"] = str(WORKTREES_DIR / task.worktree)
|
||||
return result
|
||||
|
||||
def _run_bash(command):
|
||||
return run_bash(command, cwd=wt_ctx["path"]) # Execute in worktree
|
||||
```
|
||||
|
||||
### Cleanup: Keep or Remove
|
||||
|
||||
After task completion, two choices:
|
||||
|
||||
```python
|
||||
def remove_worktree(name: str, discard_changes: bool = False) -> str:
|
||||
# Safety check: refuse by default if changes exist
|
||||
if not discard_changes:
|
||||
files, commits = _count_worktree_changes(path)
|
||||
if files > 0 or commits > 0:
|
||||
return "Has uncommitted changes. Use discard_changes=true to force, or keep_worktree"
|
||||
ok, _ = run_git(["worktree", "remove", str(path), "--force"])
|
||||
if not ok:
|
||||
return "Remove failed"
|
||||
run_git(["branch", "-D", f"wt/{name}"])
|
||||
log_event("remove", name)
|
||||
|
||||
def keep_worktree(name: str) -> str:
|
||||
log_event("keep", name)
|
||||
return f"Worktree '{name}' kept for review (branch: wt/{name})"
|
||||
```
|
||||
|
||||
Keep = preserve branch for manual review and merge. Remove = refuse by default if uncommitted changes; requires `discard_changes=true` to confirm. Does NOT auto-complete task — task completion is triggered explicitly by the teammate's `complete_task`.
|
||||
|
||||
### Event Log: Auditable
|
||||
|
||||
Each lifecycle operation writes to a log for auditing:
|
||||
|
||||
```python
|
||||
def log_event(event_type: str, worktree_name: str, task_id: str = ""):
|
||||
event = {"type": event_type, "worktree": worktree_name,
|
||||
"task_id": task_id, "ts": time.time()}
|
||||
# append to .worktrees/events.jsonl
|
||||
```
|
||||
|
||||
Event types are `create`, `remove`, and `keep`. The log supports manual auditing; a recovery flow can rebuild the current set from `git worktree list`.
|
||||
|
||||
### run_git: Returns Success/Failure
|
||||
|
||||
```python
|
||||
def run_git(args: list[str]) -> tuple[bool, str]:
|
||||
r = subprocess.run(["git"] + args, cwd=WORKDIR, ...)
|
||||
return r.returncode == 0, output
|
||||
```
|
||||
|
||||
`create_worktree` and `remove_worktree` only write event logs after successful git commands, ensuring logs reflect actual state.
|
||||
|
||||
---
|
||||
|
||||
## Changes from s16
|
||||
|
||||
| Component | Before (s16) | After (s17) |
|
||||
|-----------|-------------|-------------|
|
||||
| Working directory | All agents share WORKDIR | Each task can bind to a git worktree |
|
||||
| Task data | id/subject/status/owner/blockedBy | + worktree field |
|
||||
| Teammate tool cwd | Always WORKDIR | Auto-switches when claiming worktree-bound task |
|
||||
| New functions | — | create_worktree, bind_task_to_worktree, remove_worktree, keep_worktree, validate_worktree_name |
|
||||
| Worktree safety | None | Name validation + refuse removal with changes |
|
||||
| Event log | None | events.jsonl lifecycle auditing |
|
||||
| Lead tools | Team and task tools | + create_worktree, remove_worktree, keep_worktree |
|
||||
| Teammate tools | Task and file tools | Same tools; bash/read/write use the claimed worktree cwd |
|
||||
|
||||
---
|
||||
|
||||
## Try It
|
||||
|
||||
```sh
|
||||
cd learn-claude-code
|
||||
python s17_worktree_isolation/code.py
|
||||
```
|
||||
|
||||
Try this prompt:
|
||||
|
||||
`Refactor the authentication module and the login page in parallel without letting the changes interfere with each other.`
|
||||
|
||||
What to observe: Do both worktrees show different branches in `git status`? After claiming a worktree-bound task, does the teammate's bash run in the worktree directory? Does `remove_worktree` refuse when there are changes? Is task status still `pending` after binding?
|
||||
|
||||
---
|
||||
|
||||
## What's Next
|
||||
|
||||
Agent teams can now self-organize in isolated workspaces. But Agent capabilities are limited to the tools we wrote — bash, read, write, task...
|
||||
|
||||
What if users already have their own tools? Like an internal Jira API, or a custom deployment system?
|
||||
|
||||
s18 MCP Plugin → Give Agent a plugin system. External tools connect via standard protocol; Agent doesn't need to know who wrote them.
|
||||
|
||||
|
||||
<!-- translation-sync: zh@v1, en@v1, ja@v0 -->
|
||||
172
s17_worktree_isolation/README.zh.md
Normal file
172
s17_worktree_isolation/README.zh.md
Normal file
@@ -0,0 +1,172 @@
|
||||
# s17: Worktree Isolation — 各干各的,互不干扰
|
||||
|
||||
[English](README.md) · [中文](README.zh.md) · [日本語](README.ja.md)
|
||||
|
||||
s01 → ... → s15 → s16 → `s17` → [s18](../s18_mcp_plugin/) → s19 → s20 → s21
|
||||
|
||||
> *"各干各的目录, 互不干扰"* — 任务管目标, worktree 管目录, 按 ID 绑定。
|
||||
>
|
||||
> **Harness 层**: 隔离 — 并行执行的目录隔离。
|
||||
|
||||
---
|
||||
|
||||
## 问题
|
||||
|
||||
s16 中,Alice 和 Bob 都在同一个目录下工作。Alice 的任务是"重构认证模块",Bob 的任务是"重构 UI 登录页"。
|
||||
|
||||
Alice `write_file("config.py", ...)`。Bob 也 `write_file("config.py", ...)`。两个人改同一个文件,互相覆盖。而且无法干净地回滚——分不清哪些改动是谁的。
|
||||
|
||||
s15-s16 解决了"谁干什么"(任务系统)和"怎么通信"(消息总线),但没解决"在哪干"。
|
||||
|
||||
---
|
||||
|
||||
## 解决方案
|
||||
|
||||

|
||||
|
||||
Git worktree 让你在同一仓库中创建多个独立的工作目录,每个有自己的分支。Alice 在 `.worktrees/auth-refactor/` 下工作,Bob 在 `.worktrees/ui-login/` 下工作——互不干扰。
|
||||
|
||||
沿用 s16 的 MessageBus、协议和自治认领机制。本章新增:
|
||||
|
||||
| 能力 | 作用 |
|
||||
|------|------|
|
||||
| create_worktree | 为任务创建独立目录 + 独立分支 |
|
||||
| bind_task_to_worktree | 把任务和工作目录绑定(不改状态) |
|
||||
| remove_worktree / keep_worktree | 完成后清理或保留 |
|
||||
| validate_worktree_name | 拒绝路径穿越和非法字符 |
|
||||
|
||||
---
|
||||
|
||||
## 工作原理
|
||||
|
||||
### 创建:任务-Worktree 绑定
|
||||
|
||||
```python
|
||||
def create_worktree(name: str, task_id: str = "") -> str:
|
||||
validate_worktree_name(name) # 只允许 [A-Za-z0-9._-]{1,64}
|
||||
path = WORKTREES_DIR / name
|
||||
ok, result = run_git(["worktree", "add", str(path), "-b", f"wt/{name}", "HEAD"])
|
||||
if not ok:
|
||||
return f"Git error: {result}"
|
||||
if task_id:
|
||||
bind_task_to_worktree(task_id, name)
|
||||
log_event("create", name, task_id)
|
||||
return f"Worktree '{name}' created at {path}"
|
||||
|
||||
def bind_task_to_worktree(task_id: str, worktree_name: str):
|
||||
task = load_task(task_id)
|
||||
task.worktree = worktree_name # 只写 worktree 字段
|
||||
save_task(task) # 状态保持 pending,等队友 claim
|
||||
```
|
||||
|
||||
绑定规则:一个任务绑定一个 worktree。绑定不改任务状态——任务仍是 `pending`,队友自动认领时才推进到 `in_progress`。这样 Lead 可以提前创建任务和 worktree,队友 idle 时自然认领带 worktree 的任务。
|
||||
|
||||
### 队友工具的 cwd 切换
|
||||
|
||||
每个队友都有一个 `wt_ctx` 字典,用来记录当前 worktree 路径。队友认领绑定了 worktree 的任务后,运行时会更新 `wt_ctx`;该队友的 `bash`、`read_file`、`write_file` 随后都在对应的 worktree 目录下执行:
|
||||
|
||||
```python
|
||||
# 队友线程内部
|
||||
wt_ctx = {"path": None}
|
||||
|
||||
def _run_claim_task(task_id):
|
||||
result = claim_task(task_id, owner=name)
|
||||
if "Claimed" in result:
|
||||
task = load_task(task_id)
|
||||
if task.worktree:
|
||||
wt_ctx["path"] = str(WORKTREES_DIR / task.worktree)
|
||||
return result
|
||||
|
||||
def _run_bash(command):
|
||||
return run_bash(command, cwd=wt_ctx["path"]) # 在 worktree 下执行
|
||||
```
|
||||
|
||||
### 收尾:Keep 还是 Remove
|
||||
|
||||
任务完成后,两个选择:
|
||||
|
||||
```python
|
||||
def remove_worktree(name: str, discard_changes: bool = False) -> str:
|
||||
# 安全检查:有改动时默认拒绝
|
||||
if not discard_changes:
|
||||
files, commits = _count_worktree_changes(path)
|
||||
if files > 0 or commits > 0:
|
||||
return "有未提交改动,使用 discard_changes=true 强制删除,或 keep_worktree 保留"
|
||||
ok, _ = run_git(["worktree", "remove", str(path), "--force"])
|
||||
if not ok:
|
||||
return "删除失败"
|
||||
run_git(["branch", "-D", f"wt/{name}"])
|
||||
log_event("remove", name)
|
||||
|
||||
def keep_worktree(name: str) -> str:
|
||||
log_event("keep", name)
|
||||
return f"Worktree '{name}' kept for review (branch: wt/{name})"
|
||||
```
|
||||
|
||||
Keep = 留着分支,等人工 review 后合并到主分支。Remove = 有改动时默认拒绝,需要 `discard_changes=true` 确认。不自动 complete task——任务完成由队友的 `complete_task` 显式触发。
|
||||
|
||||
### 事件流:可审计
|
||||
|
||||
每次生命周期操作写入日志,方便排查:
|
||||
|
||||
```python
|
||||
def log_event(event_type: str, worktree_name: str, task_id: str = ""):
|
||||
event = {"type": event_type, "worktree": worktree_name,
|
||||
"task_id": task_id, "ts": time.time()}
|
||||
# append to .worktrees/events.jsonl
|
||||
```
|
||||
|
||||
事件类型包括 `create`(创建)、`remove`(删除)和 `keep`(保留)。日志用于人工排查;恢复流程可以通过 `git worktree list` 重建当前 worktree 集合。
|
||||
|
||||
### run_git:返回成功/失败
|
||||
|
||||
```python
|
||||
def run_git(args: list[str]) -> tuple[bool, str]:
|
||||
r = subprocess.run(["git"] + args, cwd=WORKDIR, ...)
|
||||
return r.returncode == 0, output
|
||||
```
|
||||
|
||||
`create_worktree` 和 `remove_worktree` 只在 git 命令成功后才写事件日志,保证日志反映真实状态。
|
||||
|
||||
---
|
||||
|
||||
## 相对 s16 的变更
|
||||
|
||||
| 组件 | 之前 (s16) | 之后 (s17) |
|
||||
|------|-----------|-----------|
|
||||
| 工作目录 | 所有 Agent 共享 WORKDIR | 每个任务可绑定独立 git worktree |
|
||||
| Task 数据 | id/subject/status/owner/blockedBy | + worktree 字段 |
|
||||
| 队友工具 cwd | 始终 WORKDIR | 认领带 worktree 的任务时自动切换 |
|
||||
| 新函数 | — | create_worktree, bind_task_to_worktree, remove_worktree, keep_worktree, validate_worktree_name |
|
||||
| worktree 安全 | 无 | name 校验 + 有改动时拒绝删除 |
|
||||
| 事件日志 | 无 | events.jsonl 生命周期审计 |
|
||||
| Lead 工具 | 团队与任务工具 | + create_worktree、remove_worktree、keep_worktree |
|
||||
| 队友工具 | 任务与文件工具 | 工具不变,bash/read/write 使用已认领任务的 worktree cwd |
|
||||
|
||||
---
|
||||
|
||||
## 试一下
|
||||
|
||||
```sh
|
||||
cd learn-claude-code
|
||||
python s17_worktree_isolation/code.py
|
||||
```
|
||||
|
||||
试试这个 prompt:
|
||||
|
||||
`请并行重构认证模块和登录页面,确保两部分改动不会互相干扰。`
|
||||
|
||||
观察重点:两个 worktree 的 `git status` 输出是否显示不同的分支?队友认领带 worktree 的任务后,bash 命令是否在 worktree 目录下执行?`remove_worktree` 对有改动的 worktree 是否拒绝?`.tasks/` 中的任务在绑定后状态是否仍为 `pending`?
|
||||
|
||||
---
|
||||
|
||||
## 接下来
|
||||
|
||||
Agent 团队能在隔离的工作空间中自组织了。但 Agent 的能力受限于我们给它写的工具——bash、read、write、task...
|
||||
|
||||
如果用户已经有了自己的工具怎么办?比如一个公司内部的 Jira API、一个自建的部署系统?
|
||||
|
||||
s18 MCP Plugin → 给 Agent 装一个插件系统。外部工具通过标准协议接入,Agent 不需要知道它们是谁写的。
|
||||
|
||||
|
||||
<!-- translation-sync: zh@v1, en@v0, ja@v0 -->
|
||||
1178
s17_worktree_isolation/code.py
Normal file
1178
s17_worktree_isolation/code.py
Normal file
File diff suppressed because it is too large
Load Diff
103
s17_worktree_isolation/images/worktree-overview.en.svg
Normal file
103
s17_worktree_isolation/images/worktree-overview.en.svg
Normal file
@@ -0,0 +1,103 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760 450" font-family="system-ui, -apple-system, sans-serif">
|
||||
<defs>
|
||||
<linearGradient id="header" x1="0" y1="0" x2="1" y2="0">
|
||||
<stop offset="0%" stop-color="#1e3a5f"/><stop offset="100%" stop-color="#b45309"/>
|
||||
</linearGradient>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="#555"/>
|
||||
</marker>
|
||||
<marker id="arrow-amber" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="#b45309"/>
|
||||
</marker>
|
||||
<marker id="arrow-green" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="#16a34a"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<rect width="760" height="450" fill="#fafbfc" rx="8"/>
|
||||
|
||||
<!-- Title -->
|
||||
<rect x="0" y="0" width="760" height="44" fill="url(#header)" rx="8"/>
|
||||
<rect x="0" y="36" width="760" height="8" fill="url(#header)"/>
|
||||
<text x="380" y="28" fill="#fff" font-size="15" font-weight="700" text-anchor="middle">Worktree Isolation — Git Worktree + Task-Directory Binding + Event Log</text>
|
||||
|
||||
<!-- Legend -->
|
||||
<rect x="40" y="56" width="12" height="10" rx="2" fill="#f0f4ff" stroke="#2563eb" stroke-width="1"/>
|
||||
<text x="58" y="66" fill="#2563eb" font-size="10" font-weight="600">s16 Preserved</text>
|
||||
<rect x="160" y="56" width="12" height="10" rx="2" fill="#fffbeb" stroke="#b45309" stroke-width="1"/>
|
||||
<text x="178" y="66" fill="#b45309" font-size="10" font-weight="600">s17 New</text>
|
||||
|
||||
<!-- ===== Row 1: Lead Loop (s16 preserved) ===== -->
|
||||
<rect x="20" y="90" width="70" height="40" rx="8" fill="#eef2ff" stroke="#4f46e5" stroke-width="1.5"/>
|
||||
<text x="55" y="114" fill="#4f46e5" font-size="8" font-weight="600" text-anchor="middle">turn</text>
|
||||
|
||||
<line x1="90" y1="110" x2="104" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
|
||||
|
||||
<rect x="107" y="90" width="70" height="40" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
|
||||
<text x="142" y="114" fill="#1e3a5f" font-size="10" font-weight="600" text-anchor="middle">messages</text>
|
||||
|
||||
<line x1="177" y1="110" x2="191" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
|
||||
|
||||
<rect x="194" y="86" width="80" height="48" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
|
||||
<text x="234" y="114" fill="#1e3a5f" font-size="9" font-weight="600" text-anchor="middle">prompt</text>
|
||||
|
||||
<line x1="274" y1="110" x2="288" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
|
||||
|
||||
<rect x="291" y="86" width="70" height="48" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
|
||||
<text x="326" y="114" fill="#1e3a5f" font-size="9" font-weight="600" text-anchor="middle">LLM</text>
|
||||
|
||||
<line x1="361" y1="110" x2="375" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
|
||||
|
||||
<rect x="378" y="76" width="356" height="70" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
|
||||
<text x="556" y="94" fill="#1e3a5f" font-size="10" font-weight="600" text-anchor="middle">TOOL DISPATCH (s16 + s17)</text>
|
||||
<text x="394" y="110" fill="#2563eb" font-size="8">bash · read · write · task(4) · send · inbox</text>
|
||||
<text x="394" y="123" fill="#7c3aed" font-size="8" font-weight="700">request_shutdown · request_plan · review_plan</text>
|
||||
<text x="394" y="136" fill="#b45309" font-size="8" font-weight="700">★ create_worktree · remove_worktree · keep_worktree</text>
|
||||
|
||||
<!-- Loop back -->
|
||||
<path d="M 734 110 L 748 110 L 748 150 L 55 150 L 55 130" fill="none" stroke="#94a3b8" stroke-width="1" marker-end="url(#arrow)" stroke-dasharray="5,4"/>
|
||||
|
||||
<!-- ===== Row 2: Worktree Topology (s17 new) ===== -->
|
||||
<rect x="30" y="172" width="700" height="215" rx="8" fill="#fffbeb" stroke="#b45309" stroke-width="2"/>
|
||||
<text x="380" y="194" fill="#78350f" font-size="11" font-weight="700" text-anchor="middle">Worktree Isolation (s17 new: each task gets its own directory + branch)</text>
|
||||
|
||||
<!-- Main repo box -->
|
||||
<rect x="230" y="208" width="300" height="36" rx="6" fill="#fff" stroke="#b45309" stroke-width="1.5"/>
|
||||
<text x="380" y="231" fill="#78350f" font-size="10" font-weight="600" text-anchor="middle">Main repo (.tasks/ + .worktrees/ + .mailboxes/)</text>
|
||||
|
||||
<!-- Arrow: Main repo → Worktree 1 (Alice) -->
|
||||
<line x1="310" y1="244" x2="178" y2="272" stroke="#b45309" stroke-width="1.5" marker-end="url(#arrow-amber)"/>
|
||||
<text x="200" y="262" fill="#b45309" font-size="7" font-weight="600" transform="rotate(-12 200 262)">create + bind</text>
|
||||
|
||||
<!-- Arrow: Main repo → Worktree 2 (Bob) -->
|
||||
<line x1="450" y1="244" x2="582" y2="272" stroke="#b45309" stroke-width="1.5" marker-end="url(#arrow-amber)"/>
|
||||
<text x="530" y="252" fill="#b45309" font-size="7" font-weight="600" transform="rotate(12 530 252)">create + bind</text>
|
||||
|
||||
<!-- Worktree 1: Alice -->
|
||||
<rect x="50" y="275" width="255" height="78" rx="6" fill="#fff" stroke="#16a34a" stroke-width="1.5"/>
|
||||
<text x="177" y="294" fill="#166534" font-size="10" font-weight="700" text-anchor="middle">Alice: .worktrees/auth/</text>
|
||||
<text x="65" y="310" fill="#374151" font-size="8">branch: wt/auth-refactor</text>
|
||||
<text x="65" y="324" fill="#374151" font-size="8">Task: Refactor auth module</text>
|
||||
<text x="65" y="344" fill="#16a34a" font-size="8" font-weight="600">✓ Isolated, no impact on Bob or main repo</text>
|
||||
|
||||
<!-- Worktree 2: Bob -->
|
||||
<rect x="455" y="275" width="255" height="78" rx="6" fill="#fff" stroke="#16a34a" stroke-width="1.5"/>
|
||||
<text x="582" y="294" fill="#166534" font-size="10" font-weight="700" text-anchor="middle">Bob: .worktrees/ui/</text>
|
||||
<text x="470" y="310" fill="#374151" font-size="8">branch: wt/ui-login</text>
|
||||
<text x="470" y="324" fill="#374151" font-size="8">Task: Refactor UI login page</text>
|
||||
<text x="470" y="344" fill="#16a34a" font-size="8" font-weight="600">✓ Isolated, no impact on Alice or main repo</text>
|
||||
|
||||
<!-- Event log + Lifecycle -->
|
||||
<rect x="50" y="362" width="310" height="20" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
|
||||
<text x="205" y="376" fill="#92400e" font-size="8" text-anchor="middle">Event log: .worktrees/events.jsonl → create / remove / keep</text>
|
||||
|
||||
<rect x="400" y="362" width="310" height="20" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
|
||||
<text x="555" y="376" fill="#92400e" font-size="8" text-anchor="middle">Cleanup: keep (preserve for review) / remove (delete worktree)</text>
|
||||
|
||||
<!-- ===== Row 3: Bottom notes ===== -->
|
||||
<rect x="30" y="400" width="700" height="42" rx="6" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/>
|
||||
<rect x="50" y="412" width="12" height="10" rx="2" fill="#f0f4ff" stroke="#2563eb" stroke-width="1"/>
|
||||
<text x="70" y="422" fill="#475569" font-size="10">s16: scan_unclaimed_tasks + claim_next_task + task_lock</text>
|
||||
<rect x="50" y="426" width="12" height="10" rx="2" fill="#fffbeb" stroke="#b45309" stroke-width="1"/>
|
||||
<text x="70" y="436" fill="#475569" font-size="10">s17: create_worktree + bind_task + remove/keep + events.jsonl</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.0 KiB |
103
s17_worktree_isolation/images/worktree-overview.ja.svg
Normal file
103
s17_worktree_isolation/images/worktree-overview.ja.svg
Normal file
@@ -0,0 +1,103 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760 450" font-family="system-ui, -apple-system, sans-serif">
|
||||
<defs>
|
||||
<linearGradient id="header" x1="0" y1="0" x2="1" y2="0">
|
||||
<stop offset="0%" stop-color="#1e3a5f"/><stop offset="100%" stop-color="#b45309"/>
|
||||
</linearGradient>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="#555"/>
|
||||
</marker>
|
||||
<marker id="arrow-amber" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="#b45309"/>
|
||||
</marker>
|
||||
<marker id="arrow-green" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="#16a34a"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<rect width="760" height="450" fill="#fafbfc" rx="8"/>
|
||||
|
||||
<!-- Title -->
|
||||
<rect x="0" y="0" width="760" height="44" fill="url(#header)" rx="8"/>
|
||||
<rect x="0" y="36" width="760" height="8" fill="url(#header)"/>
|
||||
<text x="380" y="28" fill="#fff" font-size="14" font-weight="700" text-anchor="middle">Worktree Isolation — Git Worktree + タスク・ディレクトリ紐付け + イベントログ</text>
|
||||
|
||||
<!-- Legend -->
|
||||
<rect x="40" y="56" width="12" height="10" rx="2" fill="#f0f4ff" stroke="#2563eb" stroke-width="1"/>
|
||||
<text x="58" y="66" fill="#2563eb" font-size="10" font-weight="600">s16 保持</text>
|
||||
<rect x="130" y="56" width="12" height="10" rx="2" fill="#fffbeb" stroke="#b45309" stroke-width="1"/>
|
||||
<text x="148" y="66" fill="#b45309" font-size="10" font-weight="600">s17 新規</text>
|
||||
|
||||
<!-- ===== Row 1: Lead Loop ===== -->
|
||||
<rect x="20" y="90" width="70" height="40" rx="8" fill="#eef2ff" stroke="#4f46e5" stroke-width="1.5"/>
|
||||
<text x="55" y="114" fill="#4f46e5" font-size="8" font-weight="600" text-anchor="middle">turn</text>
|
||||
|
||||
<line x1="90" y1="110" x2="104" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
|
||||
|
||||
<rect x="107" y="90" width="70" height="40" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
|
||||
<text x="142" y="114" fill="#1e3a5f" font-size="10" font-weight="600" text-anchor="middle">messages</text>
|
||||
|
||||
<line x1="177" y1="110" x2="191" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
|
||||
|
||||
<rect x="194" y="86" width="80" height="48" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
|
||||
<text x="234" y="114" fill="#1e3a5f" font-size="9" font-weight="600" text-anchor="middle">prompt</text>
|
||||
|
||||
<line x1="274" y1="110" x2="288" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
|
||||
|
||||
<rect x="291" y="86" width="70" height="48" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
|
||||
<text x="326" y="114" fill="#1e3a5f" font-size="9" font-weight="600" text-anchor="middle">LLM</text>
|
||||
|
||||
<line x1="361" y1="110" x2="375" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
|
||||
|
||||
<rect x="378" y="76" width="356" height="70" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
|
||||
<text x="556" y="94" fill="#1e3a5f" font-size="10" font-weight="600" text-anchor="middle">TOOL DISPATCH(s16 + s17)</text>
|
||||
<text x="394" y="110" fill="#2563eb" font-size="8">bash · read · write · task(4) · send · inbox</text>
|
||||
<text x="394" y="123" fill="#7c3aed" font-size="8" font-weight="700">request_shutdown · request_plan · review_plan</text>
|
||||
<text x="394" y="136" fill="#b45309" font-size="8" font-weight="700">★ create_worktree · remove_worktree · keep_worktree</text>
|
||||
|
||||
<!-- Loop back -->
|
||||
<path d="M 734 110 L 748 110 L 748 150 L 55 150 L 55 130" fill="none" stroke="#94a3b8" stroke-width="1" marker-end="url(#arrow)" stroke-dasharray="5,4"/>
|
||||
|
||||
<!-- ===== Row 2: Worktree Topology ===== -->
|
||||
<rect x="30" y="172" width="700" height="215" rx="8" fill="#fffbeb" stroke="#b45309" stroke-width="2"/>
|
||||
<text x="380" y="194" fill="#78350f" font-size="11" font-weight="700" text-anchor="middle">Worktree 隔離(s17 新規:各タスクに独立ディレクトリ + 独立ブランチ)</text>
|
||||
|
||||
<!-- Main repo box -->
|
||||
<rect x="230" y="208" width="300" height="36" rx="6" fill="#fff" stroke="#b45309" stroke-width="1.5"/>
|
||||
<text x="380" y="231" fill="#78350f" font-size="10" font-weight="600" text-anchor="middle">メインリポジトリ(.tasks/ + .worktrees/ + .mailboxes/)</text>
|
||||
|
||||
<!-- Arrow: Main repo → Worktree 1 (Alice) -->
|
||||
<line x1="310" y1="244" x2="178" y2="272" stroke="#b45309" stroke-width="1.5" marker-end="url(#arrow-amber)"/>
|
||||
<text x="200" y="262" fill="#b45309" font-size="7" font-weight="600" transform="rotate(-12 200 262)">create + bind</text>
|
||||
|
||||
<!-- Arrow: Main repo → Worktree 2 (Bob) -->
|
||||
<line x1="450" y1="244" x2="582" y2="272" stroke="#b45309" stroke-width="1.5" marker-end="url(#arrow-amber)"/>
|
||||
<text x="530" y="252" fill="#b45309" font-size="7" font-weight="600" transform="rotate(12 530 252)">create + bind</text>
|
||||
|
||||
<!-- Worktree 1: Alice -->
|
||||
<rect x="50" y="275" width="255" height="78" rx="6" fill="#fff" stroke="#16a34a" stroke-width="1.5"/>
|
||||
<text x="177" y="294" fill="#166534" font-size="10" font-weight="700" text-anchor="middle">Alice: .worktrees/auth/</text>
|
||||
<text x="65" y="310" fill="#374151" font-size="8">branch: wt/auth-refactor</text>
|
||||
<text x="65" y="324" fill="#374151" font-size="8">Task: 認証モジュールのリファクタリング</text>
|
||||
<text x="65" y="344" fill="#16a34a" font-size="8" font-weight="600">✓ 隔離、Bob とメインリポジトリに影響なし</text>
|
||||
|
||||
<!-- Worktree 2: Bob -->
|
||||
<rect x="455" y="275" width="255" height="78" rx="6" fill="#fff" stroke="#16a34a" stroke-width="1.5"/>
|
||||
<text x="582" y="294" fill="#166534" font-size="10" font-weight="700" text-anchor="middle">Bob: .worktrees/ui/</text>
|
||||
<text x="470" y="310" fill="#374151" font-size="8">branch: wt/ui-login</text>
|
||||
<text x="470" y="324" fill="#374151" font-size="8">Task: UI ログインページのリファクタリング</text>
|
||||
<text x="470" y="344" fill="#16a34a" font-size="8" font-weight="600">✓ 隔離、Alice とメインリポジトリに影響なし</text>
|
||||
|
||||
<!-- Event log + Lifecycle -->
|
||||
<rect x="50" y="362" width="310" height="20" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
|
||||
<text x="205" y="376" fill="#92400e" font-size="8" text-anchor="middle">イベントログ: .worktrees/events.jsonl → create / remove / keep</text>
|
||||
|
||||
<rect x="400" y="362" width="310" height="20" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
|
||||
<text x="555" y="376" fill="#92400e" font-size="8" text-anchor="middle">片付け: keep(review 用に保持)/ remove(worktree を削除)</text>
|
||||
|
||||
<!-- ===== Row 3: Bottom notes ===== -->
|
||||
<rect x="30" y="400" width="700" height="42" rx="6" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/>
|
||||
<rect x="50" y="412" width="12" height="10" rx="2" fill="#f0f4ff" stroke="#2563eb" stroke-width="1"/>
|
||||
<text x="70" y="422" fill="#475569" font-size="10">s16: scan_unclaimed_tasks + claim_next_task + task_lock</text>
|
||||
<rect x="50" y="426" width="12" height="10" rx="2" fill="#fffbeb" stroke="#b45309" stroke-width="1"/>
|
||||
<text x="70" y="436" fill="#475569" font-size="10">s17: create_worktree + bind_task + remove/keep + events.jsonl</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.2 KiB |
103
s17_worktree_isolation/images/worktree-overview.svg
Normal file
103
s17_worktree_isolation/images/worktree-overview.svg
Normal file
@@ -0,0 +1,103 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760 450" font-family="system-ui, -apple-system, sans-serif">
|
||||
<defs>
|
||||
<linearGradient id="header" x1="0" y1="0" x2="1" y2="0">
|
||||
<stop offset="0%" stop-color="#1e3a5f"/><stop offset="100%" stop-color="#b45309"/>
|
||||
</linearGradient>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="#555"/>
|
||||
</marker>
|
||||
<marker id="arrow-amber" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="#b45309"/>
|
||||
</marker>
|
||||
<marker id="arrow-green" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="#16a34a"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<rect width="760" height="450" fill="#fafbfc" rx="8"/>
|
||||
|
||||
<!-- Title -->
|
||||
<rect x="0" y="0" width="760" height="44" fill="url(#header)" rx="8"/>
|
||||
<rect x="0" y="36" width="760" height="8" fill="url(#header)"/>
|
||||
<text x="380" y="28" fill="#fff" font-size="15" font-weight="700" text-anchor="middle">Worktree Isolation — Git Worktree + 任务-目录绑定 + 事件日志</text>
|
||||
|
||||
<!-- Legend -->
|
||||
<rect x="40" y="56" width="12" height="10" rx="2" fill="#f0f4ff" stroke="#2563eb" stroke-width="1"/>
|
||||
<text x="58" y="66" fill="#2563eb" font-size="10" font-weight="600">s16 保留</text>
|
||||
<rect x="140" y="56" width="12" height="10" rx="2" fill="#fffbeb" stroke="#b45309" stroke-width="1"/>
|
||||
<text x="158" y="66" fill="#b45309" font-size="10" font-weight="600">s17 新增</text>
|
||||
|
||||
<!-- ===== Row 1: Lead Loop (s16 preserved) ===== -->
|
||||
<rect x="20" y="90" width="70" height="40" rx="8" fill="#eef2ff" stroke="#4f46e5" stroke-width="1.5"/>
|
||||
<text x="55" y="114" fill="#4f46e5" font-size="8" font-weight="600" text-anchor="middle">turn</text>
|
||||
|
||||
<line x1="90" y1="110" x2="104" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
|
||||
|
||||
<rect x="107" y="90" width="70" height="40" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
|
||||
<text x="142" y="114" fill="#1e3a5f" font-size="10" font-weight="600" text-anchor="middle">messages</text>
|
||||
|
||||
<line x1="177" y1="110" x2="191" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
|
||||
|
||||
<rect x="194" y="86" width="80" height="48" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
|
||||
<text x="234" y="114" fill="#1e3a5f" font-size="9" font-weight="600" text-anchor="middle">prompt</text>
|
||||
|
||||
<line x1="274" y1="110" x2="288" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
|
||||
|
||||
<rect x="291" y="86" width="70" height="48" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
|
||||
<text x="326" y="114" fill="#1e3a5f" font-size="9" font-weight="600" text-anchor="middle">LLM</text>
|
||||
|
||||
<line x1="361" y1="110" x2="375" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
|
||||
|
||||
<rect x="378" y="76" width="356" height="70" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
|
||||
<text x="556" y="94" fill="#1e3a5f" font-size="10" font-weight="600" text-anchor="middle">TOOL DISPATCH (s16 + s17)</text>
|
||||
<text x="394" y="110" fill="#2563eb" font-size="8">bash · read · write · task(4) · send · inbox</text>
|
||||
<text x="394" y="123" fill="#7c3aed" font-size="8" font-weight="700">request_shutdown · request_plan · review_plan</text>
|
||||
<text x="394" y="136" fill="#b45309" font-size="8" font-weight="700">★ create_worktree · remove_worktree · keep_worktree</text>
|
||||
|
||||
<!-- Loop back -->
|
||||
<path d="M 734 110 L 748 110 L 748 150 L 55 150 L 55 130" fill="none" stroke="#94a3b8" stroke-width="1" marker-end="url(#arrow)" stroke-dasharray="5,4"/>
|
||||
|
||||
<!-- ===== Row 2: Worktree Topology (s17 new) ===== -->
|
||||
<rect x="30" y="172" width="700" height="215" rx="8" fill="#fffbeb" stroke="#b45309" stroke-width="2"/>
|
||||
<text x="380" y="194" fill="#78350f" font-size="11" font-weight="700" text-anchor="middle">Worktree 隔离(s17 新增:每个任务独立目录 + 独立分支)</text>
|
||||
|
||||
<!-- Main repo box -->
|
||||
<rect x="230" y="208" width="300" height="36" rx="6" fill="#fff" stroke="#b45309" stroke-width="1.5"/>
|
||||
<text x="380" y="231" fill="#78350f" font-size="10" font-weight="600" text-anchor="middle">主仓库 (.tasks/ + .worktrees/ + .mailboxes/)</text>
|
||||
|
||||
<!-- Arrow: Main repo → Worktree 1 (Alice) -->
|
||||
<line x1="310" y1="244" x2="178" y2="272" stroke="#b45309" stroke-width="1.5" marker-end="url(#arrow-amber)"/>
|
||||
<text x="200" y="262" fill="#b45309" font-size="7" font-weight="600" transform="rotate(-12 200 262)">create + bind</text>
|
||||
|
||||
<!-- Arrow: Main repo → Worktree 2 (Bob) -->
|
||||
<line x1="450" y1="244" x2="582" y2="272" stroke="#b45309" stroke-width="1.5" marker-end="url(#arrow-amber)"/>
|
||||
<text x="530" y="252" fill="#b45309" font-size="7" font-weight="600" transform="rotate(12 530 252)">create + bind</text>
|
||||
|
||||
<!-- Worktree 1: Alice -->
|
||||
<rect x="50" y="275" width="255" height="78" rx="6" fill="#fff" stroke="#16a34a" stroke-width="1.5"/>
|
||||
<text x="177" y="294" fill="#166534" font-size="10" font-weight="700" text-anchor="middle">Alice: .worktrees/auth/</text>
|
||||
<text x="65" y="310" fill="#374151" font-size="8">branch: wt/auth-refactor</text>
|
||||
<text x="65" y="324" fill="#374151" font-size="8">Task: 重构认证模块</text>
|
||||
<text x="65" y="344" fill="#16a34a" font-size="8" font-weight="600">✓ 隔离,不影响 Bob 和主仓库</text>
|
||||
|
||||
<!-- Worktree 2: Bob -->
|
||||
<rect x="455" y="275" width="255" height="78" rx="6" fill="#fff" stroke="#16a34a" stroke-width="1.5"/>
|
||||
<text x="582" y="294" fill="#166534" font-size="10" font-weight="700" text-anchor="middle">Bob: .worktrees/ui/</text>
|
||||
<text x="470" y="310" fill="#374151" font-size="8">branch: wt/ui-login</text>
|
||||
<text x="470" y="324" fill="#374151" font-size="8">Task: 重构 UI 登录页</text>
|
||||
<text x="470" y="344" fill="#16a34a" font-size="8" font-weight="600">✓ 隔离,不影响 Alice 和主仓库</text>
|
||||
|
||||
<!-- Event log + Lifecycle -->
|
||||
<rect x="50" y="362" width="310" height="20" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
|
||||
<text x="205" y="376" fill="#92400e" font-size="8" text-anchor="middle">事件日志: .worktrees/events.jsonl → create / remove / keep</text>
|
||||
|
||||
<rect x="400" y="362" width="310" height="20" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
|
||||
<text x="555" y="376" fill="#92400e" font-size="8" text-anchor="middle">收尾: keep (保留分支 review) / remove (删除 worktree)</text>
|
||||
|
||||
<!-- ===== Row 3: Bottom notes ===== -->
|
||||
<rect x="30" y="400" width="700" height="42" rx="6" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/>
|
||||
<rect x="50" y="412" width="12" height="10" rx="2" fill="#f0f4ff" stroke="#2563eb" stroke-width="1"/>
|
||||
<text x="70" y="422" fill="#475569" font-size="10">s16: scan_unclaimed_tasks + claim_next_task + task_lock</text>
|
||||
<rect x="50" y="426" width="12" height="10" rx="2" fill="#fffbeb" stroke="#b45309" stroke-width="1"/>
|
||||
<text x="70" y="436" fill="#475569" font-size="10">s17: create_worktree + bind_task + remove/keep + events.jsonl</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.0 KiB |
Reference in New Issue
Block a user