Files
orchestrator/docs/ARCHITECTURE.md

218 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Архитектура 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:
```bash
claude.exe --print --system-prompt --allowedTools Read,Write,Edit,Bash
```
Каждый запуск:
1. Записывает run в DB (agent_runs)
2. Запускает subprocess. **stdout/stderr перенаправляются СРАЗУ в файл `/app/data/runs/{id}.log` на уровне ОС** (Popen `stdout=log_fh`). Никакого PIPE в памяти оркестратора → нет PIPE-deadlock, нет потока-читателя, нет зомби (B-2).
3. Стартует **watchdog thread** (timeout 30 мин → SIGKILL по pid)
4. Стартует **monitor thread**: `proc.wait()` (гарантированный reap → реальный exit_code в БД) → закрывает log_fh → git commit/push → auto-advance
### 5. Auto-advance (`launcher._try_advance_stage`)
После успешного завершения агента:
1. Определяет текущую стадию задачи
2. Проверяет QG для выхода из стадии
3. Если QG пройден — продвигает стадию
4. Запускает следующего агента (если определён)
Примечание: переход `review → testing` использует `check_reviewer_verdict` (читается из frontmatter `12-review.md`); `development → review``check_tests_local` (оркестратор сам прогоняет тесты, не зависит от Gitea CI).
### 6. Review Bounce
При REQUEST_CHANGES:
1. Считает количество developer runs для задачи
2. Если < MAX_DEV_RETRIES (3) — откатывает в development, перезапускает developer
3. Если >= MAX_DEV_RETRIES — эскалация (логирование + уведомление)
## Database Schema
```sql
-- Задачи
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
```yaml
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**: `--print` mode (весь вывод в 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 `/repos` checkout (гонки при параллельных задачах).** Все агенты и
`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` на хосте.