11 KiB
11 KiB
Архитектура Orchestrator
Обзор
Orchestrator — event-driven FastAPI сервис, который управляет жизненным циклом задач разработки через мульти-агентный пайплайн. Каждая задача проходит через фиксированные стадии, на каждой из которых работает специализированный Claude CLI агент.
Компоненты
1. Webhook Receivers
Plane Webhook (src/webhooks/plane.py)
- Принимает
work_item.created— создаёт задачу в DB, запускает analyst - Принимает
work_item.updated— синхронизация статусов
Gitea Webhook (src/webhooks/gitea.py)
- push — проверяет наличие артефактов (docs/, src/), продвигает стадию
- pull_request* (wildcard) — обрабатывает review approved/rejected, PR merge
- status — CI green/failure, продвигает development → review
2. State Machine (src/stages.py)
Линейный пайплайн с одним возможным откатом (review → development):
STAGE_TRANSITIONS = {
created: → analysis (agent: None)
analysis: → architecture (agent: architect, QG: check_analysis_approved)
architecture: → development (agent: developer, QG: check_architecture_done)
development: → review (agent: reviewer, QG: check_tests_local)
review: → testing (agent: tester, QG: check_reviewer_verdict)
testing: → deploy (agent: deployer, QG: check_tests_passed)
deploy: → done (agent: None, QG: None)
}
3. Quality Gates (src/qg/checks.py)
| Check | Метод проверки |
|---|---|
| check_analysis_approved | Filesystem: 4 файла + :approved: comment в Plane |
| check_architecture_done | Filesystem: ADR dir или infra-requirements.md |
| check_tests_local | Оркестратор сам гоняет make test в /repos/<repo> (judge по exit-code). Заменил check_ci_green: Gitea CI не сконфигурирован. |
| check_reviewer_verdict | Filesystem: читает verdict: APPROVED|REQUEST_CHANGES из YAML-frontmatter 12-review.md (только машиночитаемое поле, не подстроки в тексте) |
| check_tests_passed | Filesystem: test-report.md содержит "PASS" |
| check_ci_green | (legacy) Gitea API: GET /commits/{branch}/status — больше не используется как QG развития |
| check_review_approved | (legacy) Gitea API: GET /pulls/{n}/reviews — не используется в STAGE_TRANSITIONS |
4. Agent Launcher (src/agents/launcher.py)
Запускает Claude CLI как subprocess:
claude.exe --print --system-prompt --allowedTools Read,Write,Edit,Bash
Каждый запуск:
- Записывает run в DB (agent_runs)
- Запускает subprocess. stdout/stderr перенаправляются СРАЗУ в файл
/app/data/runs/{id}.logна уровне ОС (Popenstdout=log_fh). Никакого PIPE в памяти оркестратора → нет PIPE-deadlock, нет потока-читателя, нет зомби (B-2). - Стартует watchdog thread (timeout 30 мин → SIGKILL по pid)
- Стартует monitor thread:
proc.wait()(гарантированный reap → реальный exit_code в БД) → закрывает log_fh → git commit/push → auto-advance
5. Auto-advance (launcher._try_advance_stage)
После успешного завершения агента:
- Определяет текущую стадию задачи
- Проверяет QG для выхода из стадии
- Если QG пройден — продвигает стадию
- Запускает следующего агента (если определён)
Примечание: переход review → testing использует check_reviewer_verdict (читается из frontmatter 12-review.md); development → review — check_tests_local (оркестратор сам прогоняет тесты, не зависит от Gitea CI).
6. Review Bounce
При REQUEST_CHANGES:
- Считает количество developer runs для задачи
- Если < MAX_DEV_RETRIES (3) — откатывает в development, перезапускает developer
- Если >= MAX_DEV_RETRIES — эскалация (логирование + уведомление)
Database Schema
-- Задачи
CREATE TABLE tasks (
id INTEGER PRIMARY KEY,
work_item_id TEXT, -- Plane issue identifier (e.g. "ET-006")
plane_issue_id TEXT, -- Plane UUID
repo TEXT,
branch TEXT,
stage TEXT DEFAULT 'created',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Запуски агентов
CREATE TABLE agent_runs (
id INTEGER PRIMARY KEY,
task_id INTEGER REFERENCES tasks(id),
agent TEXT, -- analyst/architect/developer/reviewer/tester
started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
finished_at TIMESTAMP,
exit_code INTEGER,
output_path TEXT -- /app/data/runs/{id}.log
);
-- Сырые события
CREATE TABLE events (
id INTEGER PRIMARY KEY,
source TEXT, -- plane/gitea
event_type TEXT,
payload TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Deployment
Docker Compose
services:
orchestrator:
build: .
container_name: orchestrator
restart: unless-stopped
network_mode: host
volumes:
- ./data:/app/data # SQLite + logs
- /home/slin/repos:/repos # Git repositories
- /var/run/docker.sock:/var/run/docker.sock # Docker CLI
- claude-code:/opt/claude-code:ro # Claude CLI binary
- /home/slin/.claude:/home/slin/.claude # Claude config
env_file: .env
group_add: ["999"] # docker group
Dockerfile
- Base: python:3.12-slim
- Docker CLI (sibling containers)
- tini как PID 1 (proper zombie reaping)
git config --global safe.directory '*'- ENTRYPOINT: tini → uvicorn
Потоки данных
Happy path (ET-006 пример)
1. Plane webhook: work_item.created → task created, analyst launched
2. Analyst: пишет BRD/TRZ/AC/TestPlan → git push docs/
3. Plane comment :approved: → QG check_analysis_approved → PASS
4. Auto-advance: analysis → architecture, architect launched
5. Architect: пишет ADR, infra-requirements → git push docs/
6. Gitea push webhook: ADR detected → QG check_architecture_done → PASS
7. Auto-advance: architecture → development, developer launched
8. Developer: пишет код src/ + tests/ → git push, creates PR
9. Gitea status webhook: CI green → QG check_ci_green → PASS
10. Auto-advance: development → review, reviewer launched
11. Reviewer: оставляет review (APPROVED или REQUEST_CHANGES)
12. Gitea PR webhook: review event → QG check_review_approved → PASS
13. Advance: review → testing, tester launched
14. Tester: прогоняет тесты, пишет test-report.md → git push
15. Auto-advance: testing → deploy (QG check_tests_passed → PASS)
16. PR merge → Gitea PR webhook: action=closed, merged=true → done
Review bounce path
11. Reviewer: REQUEST_CHANGES
12. Gitea PR webhook: review_state=REQUEST_CHANGES, stage=review
13. Rollback: review → development, developer relaunched (attempt N/3)
14. Developer: фиксит замечания → git push
15. CI green → development → review, reviewer relaunched
16. Reviewer: APPROVED → continue happy path
Resilience
| Механизм | Описание |
|---|---|
| Watchdog | Каждый агент: timeout 30 мин → SIGKILL + exit_code=-9 |
| safe.directory | git операции работают в любой директории |
| Max retries | Developer: max 3 попытки, затем эскалация |
| Zombie-free | stdout идёт сразу в файл + monitor proc.wait() → процесс всегда reap'нут (B-2) |
| Orphan recovery | При старте: orphan-run'ы (finished_at IS NULL, старше 35 мин) помечаются exit=-1 с per-run warning + Telegram-уведомлением «нужна ручная проверка» (M-1) |
Агенты
Каждый агент — Claude CLI с:
- System prompt:
.openclaw/agents/{role}.md(в репозитории) - Task file:
.task-{suffix}.md— генерируется orchestrator прямой записью в смонтированный volume/repos/<repo>/(B-1, без docker). В.gitignoreрепозитория проекта (рантайм-артефакт, не коммитится). - Tools: Read, Write, Edit, Bash
- Output:
--printmode (весь вывод в stdout после завершения)
| Агент | Артефакты | Время (типичное) |
|---|---|---|
| analyst | BRD, TRZ, AC, TestPlan | 5-10 мин |
| architect | ADR, infra-requirements, tech-risks | 5-10 мин |
| developer | src/, tests/, PR | 15-30 мин |
| reviewer | review report, PR review | 3-5 мин |
| tester | test-report.md, e2e results | 10-25 мин |
| deployer | merge PR + SSH deploy-hook + smoke | 5-10 мин |
Известные ограничения
- Shared
/reposcheckout (гонки при параллельных задачах). Все агенты иcheck_tests_localделаютgit checkoutв одном/repos/<repo>. При двух одновременно активных задачах checkout одной перетрёт рабочую копию другой. Пока приемлемо (задачи идут последовательно). Исправление — git worktree per task/branch (запланировано отдельной задачей S-4). - In-process daemon-потоки. Агенты запускаются в daemon-потоках uvicorn. При рестарте uvicorn запущенные агенты осиротевают → ловит orphan-recovery (M-1). Целевая архитектура — очередь задач (F-2b, отдельно).
- Gitea CI не настроен. QG развития теперь локальный (
check_tests_local); Gitea CI-статусы не являются authoritative и не блокируют pipeline. - Docker внутри контейнера orchestrator НЕДОСТУПЕН. Деплой идёт только через
SSH-хук
enduro-deploy-hook.shна хосте.