How to Build an AI Agent
How to Build an AI Agent
s12

Worktree + Изоляция задач

Коллаборация

Isolate by Directory

694 LOC16 инструментовComposable worktree lifecycle + event stream over a shared task board
Each works in its own directory; tasks manage goals, worktrees manage directories, bound by ID

s01 > s02 > s03 > s04 > s05 > s06 | s07 > s08 > s09 > s10 > s11 > [ s12 ]

"Каждый работает в своём каталоге, без помех" -- задачи управляют целями, worktrees управляют каталогами, связаны по ID.

Проблема

В s11 агенты могут забирать и завершать задачи автономно. Но каждая задача выполняется в общем каталоге. Два агента, рефакторящие разные модули одновременно, столкнутся: агент A модифицирует config.py, агент B модифицирует config.py, неустановленные изменения смешиваются, и никто не может чисто откатиться.

Доска задач отслеживает что делать, но не имеет мнения где это делать. Решение: дать каждой задаче свой собственный git worktree. Задачи управляют целями, worktrees управляют контекстом выполнения. Связать по ID задачи.

Решение

План управления (.tasks/)             План выполнения (.worktrees/)
+------------------+                +------------------------+
| task_1.json      |                | auth-refactor/         |
|   status: in_progress  <------>   branch: wt/auth-refactor
|   worktree: "auth-refactor"   |   task_id: 1             |
+------------------+                +------------------------+
| task_2.json      |                | ui-login/              |
|   status: pending    <------>     branch: wt/ui-login
|   worktree: "ui-login"       |   task_id: 2             |
+------------------+                +------------------------+
                                    |
                          index.json (worktree registry)
                          events.jsonl (lifecycle log)

Машины состояний:
  Задача:     pending -> in_progress -> completed
  Worktree:   absent  -> active      -> removed | kept

Как Это Работает

  1. Создать задачу. Сначала персистировать цель.
TASKS.create("Implement auth refactor")
# -> .tasks/task_1.json  status=pending  worktree=""
  1. Создать worktree и связать с задачей. Передача task_id автоматически переводит задачу в in_progress.
WORKTREES.create("auth-refactor", task_id=1)
# -> git worktree add -b wt/auth-refactor .worktrees/auth-refactor HEAD
# -> index.json gets new entry, task_1.json gets worktree="auth-refactor"

Связь записывает состояние с обеих сторон:

def bind_worktree(self, task_id, worktree):
    task = self._load(task_id)
    task["worktree"] = worktree
    if task["status"] == "pending":
        task["status"] = "in_progress"
    self._save(task)
  1. Выполнять команды в worktree. cwd указывает на изолированный каталог.
subprocess.run(command, shell=True, cwd=worktree_path,
               capture_output=True, text=True, timeout=300)
  1. Завершить. Два выбора:
    • worktree_keep(name) -- сохранить каталог для последующей работы.
    • worktree_remove(name, complete_task=True) -- удалить каталог, завершить связанную задачу, отправить событие. Один вызов обрабатывает демонтаж + завершение.
def remove(self, name, force=False, complete_task=False):
    self._run_git(["worktree", "remove", wt["path"]])
    if complete_task and wt.get("task_id") is not None:
        self.tasks.update(wt["task_id"], status="completed")
        self.tasks.unbind_worktree(wt["task_id"])
        self.events.emit("task.completed", ...)
  1. Поток событий. Каждый этап жизненного цикла отправляет в .worktrees/events.jsonl:
{
  "event": "worktree.remove.after",
  "task": { "id": 1, "status": "completed" },
  "worktree": { "name": "auth-refactor", "status": "removed" },
  "ts": 1730000000
}

События: worktree.create.before/after/failed, worktree.remove.before/after/failed, worktree.keep, task.completed.

После сбоя состояние восстанавливается из .tasks/ + .worktrees/index.json на диске. Память разговора непостоянна; состояние файла долговечно.

Что Изменилось с s11

КомпонентДо (s11)После (s12)
КоординацияДоска задач (owner/статус)Доска задач + явная связь worktree
Область выполненияОбщий каталогИзолированный каталог на задачу
ВосстановлениеТолько статус задачиСтатус задачи + индекс worktree
ДемонтажЗавершение задачиЗавершение задачи + явный keep/remove
Видимость циклаНеявная в логахЯвные события в .worktrees/events.jsonl

Попробуйте

python agents/s12_worktree_task_isolation.py
  1. Создать задачи для backend auth и страницы frontend login, затем перечислить задачи.
  2. Создать worktree "auth-refactor" для задачи 1, затем связать задачу 2 с новым worktree "ui-login".
  3. Выполнить "git status --short" в worktree "auth-refactor".
  4. Сохранить worktree "ui-login", затем перечислить worktrees и просмотреть события.
  5. Удалить worktree "auth-refactor" с complete_task=true, затем перечислить задачи/worktrees/события.