Files
orchestrator/docs/BUGFIXES_2026-06-02_ORCH2.md
Dev Agent 1ebe8afc23 feat(worktree): git worktree per task to isolate shared /repos (ORCH-2 / S-4)
- add src/git_worktree.py: ensure/remove/get_worktree_path
- config: worktrees_dir=/repos/_wt
- launcher: agent runs in per-branch worktree; task-file + commit/push in worktree; no shared checkout
- qg/checks: read artifacts + run make test from worktree (branch arg, backward-compatible)
- webhooks/plane: pass branch into QG dispatch; review fallback from worktree
- webhooks/gitea: keep read-only branch --contains in main clone (documented)
- tests: test_git_worktree.py (isolation) + update test_launcher write-task-file
- docs: ARCHITECTURE worktree section + BUGFIXES_2026-06-02_ORCH2

Preserves B-1/B-2/S-1/S-5 fixes (paths now point at worktree).
2026-06-02 21:12:06 +03:00

5.2 KiB
Raw Blame History

ORCH-2 / S-4 — git worktree per task (изоляция shared /repos)

Дата: 2026-06-02 Ветка: feature/ORCH-2-worktree Источник: AUDIT_2026-06-02.md (SERIOUS S-4), DEV_TASK_ORCH2_WORKTREE.md Исполнитель: Dev (Opus 4.8 Tokenator)

Проблема (S-4)

Все git-операции (launcher.launch cmd, _monitor_agent commit/push, check_tests_local) делали git checkout <branch> в одном общем /repos/<repo>. При двух активных задачах checkout одной перетирал рабочую копию другой → гонки (на ET-009 это дало «два коллектора» и путаницу веток).

Решение

git worktree per branch. Каждая задача (ветка) работает в изолированной рабочей копии:

/repos/<repo>                      ← основной clone (fetch / worktree mgmt / read-only)
/repos/_wt/<repo>/<safe-branch>    ← worktree задачи (рабочая копия агента)

Изменения

Файл Что
src/config.py + worktrees_dir: str = "/repos/_wt"
src/git_worktree.py (новый) _safe, get_worktree_path, ensure_worktree, remove_worktree
src/agents/launcher.py launch(): ветка резолвится заранее → ensure_worktree; cmd = cd <worktree> без git checkout; _write_task_file(repo, branch, ...) пишет в worktree; _monitor_agent commit/push в worktree (checkout убран); чтение 01-questions.md/10-conflict.md из worktree; QG-диспетчер прокидывает branch
src/qg/checks.py _repo_path(repo, branch) helper (worktree если есть, иначе shared); артефакт-чеки получили опциональный branch; check_tests_localensure_worktree + make test в worktree (TODO про S-4 удалён)
src/webhooks/plane.py QG-диспетчер прокидывает branch; review-файл fallback читается из worktree
src/webhooks/gitea.py git branch -r --contains <sha> — подтверждено read-only, оставлено в main clone (+ комментарий)
tests/test_git_worktree.py (новый) покрытие _safe/get_worktree_path/ensure_worktree/remove_worktree + изоляция двух веток (реальные локальные git-репо в tmp, без сети)
tests/test_launcher.py TestWriteTaskFile обновлён под новую сигнатуру (запись в worktree)
docs/ARCHITECTURE.md раздел «Изоляция через git worktree»; убран пункт про shared-checkout гонки

Совместимость с прежними фиксами

  • B-1 (запись task-файла без docker, прямой open()): сохранена — теперь путь = worktree.
  • B-2 (Popen stdout → файл, monitor proc.wait() без зомби): не тронут.
  • S-5 (check_reviewer_verdict — только YAML-frontmatter): не тронут, добавлен лишь worktree-путь.
  • S-1 (check_tests_local — свой make test вместо Gitea CI): сохранён, тесты теперь в worktree.

Обратная совместимость QG-диспетчеризации: артефакт-чеки принимают branch опционально (default None → shared /repos/<repo>), поэтому существующие 2-арг вызовы/тесты не сломаны.

Проверка

# Тесты (в контейнере через образ — хостовый .venv сломан):
IMG=$(docker inspect orchestrator --format '{{.Config.Image}}')
docker run --rm -v /home/slin/repos/orchestrator:/code -w /code --entrypoint python3 $IMG -m pytest tests/ -q
# → 37 passed, 9 failed (pre-existing test_webhooks 401/signature — НЕ относятся к ORCH-2,
#   идентичны baseline на main).

# test_git_worktree.py изолированно → 9 passed.

Тест изоляции (в работающем контейнере)

docker exec orchestrator python3 -c "
import sys; sys.path.insert(0,'/app')
from src.git_worktree import ensure_worktree
import subprocess
p1 = ensure_worktree('enduro-trails','feature/wt-test-A')
p2 = ensure_worktree('enduro-trails','feature/wt-test-B')
b1 = subprocess.run(['git','-C',p1,'branch','--show-current'],capture_output=True,text=True).stdout.strip()
b2 = subprocess.run(['git','-C',p2,'branch','--show-current'],capture_output=True,text=True).stdout.strip()
assert p1!=p2 and b1!=b2, 'NOT ISOLATED'
print('ISOLATION OK', p1, p2, b1, b2)
"

(Результат прогона на сервере — см. ниже / в отчёте Стрим.)

Ограничения / заметки

  • Очередь задач (ORCH-1 / F-2b) не входит в эту задачу.
  • remove_worktree существует, но автоматический вызов при done не подключён (опционально, отдельным шагом).