Files
orchestrator/README.md
Dev Agent ca81f38330 docs: document multi-repo registry + ORCH-6 bugfix and incident
ORCH-6: ARCHITECTURE.md gets a project-registry section; README explains
how to add a project via ORCH_PROJECTS_JSON; BUGFIXES_2026-06-03.md
records the fix and links the 2026-06-02 webhook autorun incident.
2026-06-02 22:30:51 +03:00

189 lines
10 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.
# Multi-Agent Orchestrator
FastAPI-сервис для оркестрации мульти-агентного пайплайна разработки. Принимает webhooks от Plane и Gitea, управляет жизненным циклом задач через Quality Gates, запускает Claude CLI агентов на каждой стадии.
## Архитектура
```
Plane (task mgmt) ──webhook──┐
├──► Orchestrator (FastAPI) ──► Quality Gates ──► Agent Launcher
Gitea (git events) ─webhook──┘ │ │
▼ ▼
SQLite DB Claude CLI
(events, tasks, (analyst, architect,
agent_runs) developer, reviewer, tester)
```
## Стадии пайплайна
```
created → analysis → architecture → development → review → testing → deploy → done
↑ │
└─── REQUEST_CHANGES ─┘ (max 3 retries)
```
| Стадия | Агент | Quality Gate (выход) | Триггер перехода |
|--------|-------|---------------------|------------------|
| created | — | — | Plane webhook (work_item.created) |
| analysis | analyst | Файлы BRD/TRZ/AC/TestPlan | Push docs/ |
| architecture | architect | ADR или infra-requirements | Push docs/ |
| development | developer | check_tests_local (орк сам гоняет `make test`) | Auto-advance после developer |
| review | reviewer | check_reviewer_verdict (`verdict:` во frontmatter 12-review.md) | Auto-advance после reviewer |
| testing | tester | Test report с PASS | Auto-advance после tester |
| deploy | deployer | — | SSH deploy-hook |
| done | — | — | — |
## API Endpoints
| Method | Path | Описание |
|--------|------|----------|
| GET | `/health` | Health check |
| GET | `/status` | Активные задачи (stage != done) |
| POST | `/webhook/plane` | Plane webhook receiver |
| POST | `/webhook/gitea` | Gitea webhook receiver |
## Структура проекта
```
src/
├── main.py # FastAPI app, lifespan (orphan recovery)
├── config.py # Pydantic settings (env vars)
├── db.py # SQLite: init, get_db, update_task_stage
├── stages.py # State machine (transitions, agents, QG)
├── notifications.py # Уведомления (логирование)
├── plane_sync.py # Синхронизация статусов с Plane API
├── agents/
│ └── launcher.py # AgentLauncher: launch, monitor, watchdog, auto-advance
├── webhooks/
│ ├── plane.py # Plane webhook handler
│ └── gitea.py # Gitea webhook handler (push, PR, CI status)
└── qg/
└── checks.py # Quality Gate checks (filesystem + Gitea API)
data/
├── orchestrator.db # SQLite database
└── runs/ # Agent output logs ({run_id}.log)
docs/
├── ARCHITECTURE.md # Подробная архитектура
├── LESSONS_ET006.md # Lessons learned из ET-006
├── BUGFIXES_2026-05-21.md # Багфиксы
└── SETUP_WEBHOOKS.md # Настройка webhooks
docker-compose.yml # Deployment config
Dockerfile # Python 3.12 + Docker CLI + tini
```
## Запуск
### Docker (production)
```bash
docker compose up -d --build
```
### Dev
```bash
pip install -r requirements.txt
uvicorn src.main:app --reload --port 8500
```
## Конфигурация
Все переменные с префиксом `ORCH_`:
| Переменная | Описание | Default |
|-----------|----------|---------|
| `ORCH_PLANE_API_URL` | Plane API URL | `http://localhost:8091` |
| `ORCH_PLANE_API_TOKEN` | Plane API token | — |
| `ORCH_PLANE_WEBHOOK_SECRET` | Webhook secret | — |
| `ORCH_PLANE_WORKSPACE_SLUG` | Workspace slug | — |
| `ORCH_PLANE_PROJECT_ID` | Project UUID | — |
| `ORCH_GITEA_URL` | Gitea URL | `http://localhost:3000` |
| `ORCH_GITEA_TOKEN` | Gitea API token | — |
| `ORCH_GITEA_WEBHOOK_SECRET` | Gitea webhook secret | — |
| `ORCH_GITEA_OWNER` | Gitea repo owner | `admin` |
| `ORCH_DEFAULT_REPO` | Default repository (fallback) | `enduro-trails` |
| `ORCH_PROJECTS_JSON` | Multi-repo реестр (JSON-массив, ORCH-6) | `""` → дефолт в `src/projects.py` |
| `ORCH_CLAUDE_BIN` | Путь к Claude CLI | `/opt/claude-code/bin/claude.exe` |
| `ORCH_REPOS_DIR` | Repos dir (container) | `/repos` |
| `ORCH_HOST_REPOS_DIR` | Repos dir (host) | `/home/slin/repos` |
| `ORCH_DB_PATH` | SQLite path | `/app/data/orchestrator.db` |
## Multi-repo: реестр проектов (ORCH-6)
Оркестратор обслуживает несколько репозиториев через реестр проектов
(`src/projects.py`), ключ = **Plane project id**. Plane-webhook фильтрует события
по проекту (неизвестный проект → `ignored`) и резолвит `repo` / `work_item_prefix` /
Plane-проект из маппинга.
По умолчанию (если `ORCH_PROJECTS_JSON` пуст) зарегистрированы два проекта:
| Проект | Plane project id | repo | prefix |
|--------|------------------|------|--------|
| enduro-trails | `7a79f0a9-5278-49cd-9007-9a338f238f9c` | `enduro-trails` | `ET` |
| orchestrator | `8da6aa25-a60e-44d6-a1e2-d8ae59aa7d6a` | `orchestrator` | `ORCH` |
### Как добавить новый проект
1. Убедись, что gitea-репо уже клонировано в `/repos/<repo>` (авто-clone — отдельно).
2. Узнай Plane project uuid (из URL проекта в Plane или через Plane API).
3. Добавь запись в `ORCH_PROJECTS_JSON` в `.env` (JSON-массив). **Важно:** если
задаёшь `ORCH_PROJECTS_JSON`, он полностью заменяет дефолт — перечисли **все**
нужные проекты (включая enduro-trails и orchestrator):
```bash
ORCH_PROJECTS_JSON='[
{"plane_project_id":"7a79f0a9-5278-49cd-9007-9a338f238f9c","repo":"enduro-trails","work_item_prefix":"ET","name":"enduro-trails"},
{"plane_project_id":"8da6aa25-a60e-44d6-a1e2-d8ae59aa7d6a","repo":"orchestrator","work_item_prefix":"ORCH","name":"orchestrator"},
{"plane_project_id":"<новый-uuid>","repo":"<новый-repo>","work_item_prefix":"<PREFIX>","name":"<имя>"}
]'
```
4. Пересобери: `docker compose up -d --build`.
5. Проверь резолв:
```bash
docker exec orchestrator python3 -c "from src.projects import get_project_by_plane_id as g; print(g('<новый-uuid>'))"
```
Поля `name` опционально (по умолчанию = `repo`). Подробности — `docs/ARCHITECTURE.md`.
## Ключевые механизмы
### Auto-advance
После успешного завершения агента (exit_code=0), `_try_advance_stage()` проверяет QG и автоматически продвигает задачу + запускает следующего агента.
### Review bounce
При REQUEST_CHANGES от reviewer задача откатывается в development, developer перезапускается (до 3 попыток). При исчерпании — эскалация.
### Orphan recovery (M-1)
При старте контейнера каждый run с `finished_at IS NULL` старше 35 минут помечается exit_code=-1, логируется per-run warning и отправляется Telegram-уведомление «нужна ручная проверка/перезапуск» (не молча).
### Запись task-файлов (B-1)
Task-файлы `.task-*.md` пишутся **прямой записью в смонтированный volume `/repos/<repo>/`** (без docker). При ошибке записи — RuntimeError (не молчит). В `.gitignore` проекта.
### Логи агентов (B-2)
stdout/stderr агента перенаправляются СРАЗУ в `/app/data/runs/{id}.log` на уровне ОС (без PIPE). monitor-поток делает `proc.wait()` → реальный exit_code, нет зомби.
### Watchdog
Каждый агент имеет timeout 30 минут. При превышении — SIGKILL + запись exit_code=-9.
### Event routing
Gitea events роутятся по типу:
- `push` → проверка файлов, advance architecture/development
- `pull_request*` (wildcard) → review approved/rejected, PR merge
- `status` → (legacy) Gitea CI; С-1: больше не authoritative, `failure` логируется на debug и не блокирует/не алертит (QG развития = локальный `check_tests_local`)
## Тесты
```bash
pytest tests/ -v
```
## Известные ограничения
1. **Single-task / shared `/repos` checkout** — одновременно безопасно обрабатывается одна задача: все агенты и `check_tests_local` делают `git checkout` в одном `/repos/<repo>` → гонки при параллельных задачах. Исправление — git worktree per task (S-4, отдельно).
2. **Plane sync** — маппинг issue ID может быть некорректным (P3, в работе)
3. **In-process daemon-потоки** — агенты живут в потоках uvicorn; при рестарте ловит orphan-recovery. Целевое — очередь задач (F-2b)
4. **Gitea CI не настроен** — тесты гоняет сам оркестратор локально
3. **Tester timeout** — e2e тесты с Playwright могут занимать >25 мин на тяжёлых фичах
4. **No retry on API errors** — httpx вызовы к Gitea/Plane без retry logic