docs(orchestrator): adopt enduro doc canon + CLAUDE.md + ADR (ORCH-9)
All checks were successful
CI / test (pull_request) Successful in 9s
All checks were successful
CI / test (pull_request) Successful in 9s
This commit is contained in:
77
docs/architecture/README.md
Normal file
77
docs/architecture/README.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Архитектура Orchestrator
|
||||
|
||||
## Обзор
|
||||
Мульти-агентный оркестратор разработки. Принимает webhooks от Plane (управление задачами) и Gitea (git-события), ведёт задачи по конвейеру стадий через Quality Gates, на каждой стадии запускает Claude CLI агента. Поддерживает несколько проектов (multi-repo) и self-hosting (дорабатывает сам себя).
|
||||
|
||||
## Компоненты
|
||||
- **Webhook Receivers** (`src/webhooks/plane.py`, `gitea.py`) — приём событий, HMAC-проверка, дедупликация (`_dedup.py`). Роуты: `POST /webhook/plane`, `POST /webhook/gitea`.
|
||||
- **State Machine** (`src/stages.py`) — `STAGE_TRANSITIONS`: переходы, агент и QG каждой стадии. Хелперы: `get_next_stage`, `get_agent_for_stage`, `get_qg_for_stage`, `get_previous_stage`.
|
||||
- **Stage Engine** (`src/stage_engine.py`) — исполнение переходов, диспетчеризация QG (`_run_qg`), откаты, синхронизация с Plane.
|
||||
- **Quality Gates** (`src/qg/checks.py`) — проверки выхода со стадии, реестр `QG_CHECKS`.
|
||||
- **Agent Launcher** (`src/agents/launcher.py`) — запуск Claude CLI агентов в изолированном git worktree, мониторинг, auto-advance.
|
||||
- **Queue** (`src/queue_worker.py`, ORCH-1) — персистентная очередь задач (SQLite `jobs`), atomic claim, max_concurrency, ретраи, restart-safe.
|
||||
- **Project Registry** (`src/projects.py`, ORCH-6) — Plane project id → repo + prefix; фильтрация вебхуков по проекту.
|
||||
- **Plane Sync** (`src/plane_sync.py`) — синхронизация статусов/комментариев в Plane.
|
||||
|
||||
## Конвейер и Quality Gates
|
||||
|
||||
```
|
||||
created → analysis → architecture → development → review → testing → deploy-staging → deploy → done
|
||||
↑ │
|
||||
└──── REQUEST_CHANGES ──────┘ (откат на development, max 3 retries)
|
||||
```
|
||||
|
||||
| Стадия | Агент (выход) | Quality Gate | Артефакт |
|
||||
|--------|---------------|--------------|----------|
|
||||
| created | analyst | — | — |
|
||||
| analysis | architect | `check_analysis_approved` | 01-brd / 02-trz / 03-acceptance-criteria / 04-test-plan.yaml |
|
||||
| architecture | developer | `check_architecture_done` | 06-adr/ |
|
||||
| development | reviewer | `check_ci_green` | код + PR |
|
||||
| review | tester | `check_reviewer_verdict` | 12-review.md (`verdict:`) |
|
||||
| testing | deployer | `check_tests_passed` | 13-test-report.md |
|
||||
| deploy-staging | deployer | `check_staging_status` | 15-staging-log.md (`staging_status:`) |
|
||||
| deploy | — | `check_deploy_status` | 14-deploy-log.md (`deploy_status:`) |
|
||||
| done | — | — | — |
|
||||
|
||||
**Реестр QG** (`QG_CHECKS`): check_analysis_approved, check_analysis_complete, check_architecture_done, check_ci_green, check_review_approved, check_tests_passed, check_reviewer_verdict, check_tests_local, check_deploy_status, check_staging_status.
|
||||
|
||||
**Канон гейтов:** машинные вердикты читаются ТОЛЬКО из YAML-frontmatter, никогда из прозы. Лог-файлы мержатся в `origin/main` отдельным PR; гейт читает из `origin/main`.
|
||||
|
||||
### Условный staging-гейт (ORCH-35)
|
||||
`check_staging_status` реален только для self-hosting (`is_self_hosting_repo(repo)` → `orchestrator`); для остальных проектов → no-op `(True, "Staging gate N/A")`. Для orchestrator парсит `staging_status:` из `15-staging-log.md`; FAILED → откат на `development`. Подробнее: [ADR-0003](adr/adr-0003-staging-gate.md).
|
||||
|
||||
## Откаты
|
||||
- Reviewer REQUEST_CHANGES → откат на `development` + retry (`MAX_DEVELOPER_RETRIES = 3`).
|
||||
- Tester `check_tests_passed` FAIL → откат на `development` + retry.
|
||||
- Deploy / deploy-staging FAILED → откат на `development`.
|
||||
- `get_previous_stage` использует порядок ключей `STAGE_TRANSITIONS`.
|
||||
|
||||
## База данных (SQLite)
|
||||
- `events` — входящие вебхуки (дедуп)
|
||||
- `tasks` — задачи и их стадии
|
||||
- `agent_runs` — запуски агентов (run_id, usage, cost)
|
||||
- `jobs` — очередь задач (ORCH-1)
|
||||
|
||||
## Изоляция (git worktree, ORCH-2)
|
||||
Каждая задача исполняется в отдельном git worktree, ветки не пересекаются. Репозитории проектов разделены под `/repos/<project>`.
|
||||
|
||||
## API
|
||||
| Method | Path | Описание |
|
||||
|--------|------|----------|
|
||||
| GET | `/health` | health check |
|
||||
| GET | `/status` | активные задачи (stage != done) |
|
||||
| GET | `/queue` | очередь: counts + max_concurrency + последние jobs |
|
||||
| POST | `/webhook/plane` | Plane webhook |
|
||||
| POST | `/webhook/gitea` | Gitea webhook (push, PR, CI status) |
|
||||
|
||||
## Деплой и эксплуатация
|
||||
Топология, контейнеры, порты, env-карта, self-hosting риски — [docs/operations/INFRA.md](../operations/INFRA.md). Деплой-хук — [DEPLOY_HOOK.md](../operations/DEPLOY_HOOK.md). Staging — [STAGING.md](../operations/STAGING.md).
|
||||
|
||||
## ADR
|
||||
Сквозные архитектурные решения — [adr/](adr/). Per-work-item решения — `docs/work-items/<id>/06-adr/`.
|
||||
|
||||
## Детали реализации
|
||||
Схема БД, потоки данных, resilience-слой, детали Dockerfile — [internals.md](internals.md).
|
||||
|
||||
---
|
||||
*Актуально на 2026-06-05 (main `f1b3146`). Обновлять при изменении src/stages.py, src/qg/checks.py, src/main.py.*
|
||||
15
docs/architecture/adr/README.md
Normal file
15
docs/architecture/adr/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Architecture Decision Records
|
||||
|
||||
Индекс сквозных (cross-cutting) ADR проекта orchestrator.
|
||||
Per-work-item решения живут в `docs/work-items/<id>/06-adr/ADR-NNN-slug.md`.
|
||||
|
||||
| # | Решение | Статус | Дата | Источник |
|
||||
|---|---------|--------|------|----------|
|
||||
| adr-0001 | Реестр проектов (multi-repo) | accepted | 2026-06-02 | ORCH-6 |
|
||||
| adr-0002 | Очередь задач вместо in-process потоков | accepted | 2026-06-03 | ORCH-1 |
|
||||
| adr-0003 | Условный staging-гейт перед прод-деплоем | accepted | 2026-06-05 | ORCH-35 |
|
||||
|
||||
## Формат
|
||||
**Контекст → Решение → Альтернативы → Последствия → Связи.** Статус: proposed / accepted / superseded.
|
||||
Принятый ADR не меняется — новое решение заводится отдельным файлом со ссылкой `supersedes adr-XXXX`.
|
||||
Новые ADR добавляет архитектор при принятии решения (см. `CLAUDE.md` → Конвенции).
|
||||
23
docs/architecture/adr/adr-0001-multi-repo-registry.md
Normal file
23
docs/architecture/adr/adr-0001-multi-repo-registry.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# adr-0001: Реестр проектов (multi-repo)
|
||||
|
||||
- **Статус:** accepted
|
||||
- **Дата:** 2026-06-02
|
||||
- **Задача:** ORCH-6
|
||||
|
||||
## Контекст
|
||||
Инцидент 2026-06-02: Plane-вебхук слушал весь воркспейс и хардкодил `repo = settings.default_repo` (enduro-trails). Задачи ЛЮБОГО проекта сливались в один репо с одним префиксом (ET). Нужна изоляция по проектам.
|
||||
|
||||
## Решение
|
||||
Введён реестр `src/projects.py`: `ProjectConfig` (frozen dataclass) связывает `plane_project_id` → `repo` + `work_item_prefix` + `name`. Источник правды — env `ORCH_PROJECTS_JSON`; при пустом/невалидном — встроенный дефолт (`enduro-trails`/ET, `orchestrator`/ORCH). Позволяет: фильтровать вебхуки по проекту (неизвестный → ignore), резолвить gitea-репо + префикс, роутить Plane-синк в свой проект задачи.
|
||||
|
||||
## Альтернативы
|
||||
- Один репо на всё — отклонён (источник инцидента).
|
||||
- Хардкод маппинга в коде — отклонён в пользу env-конфигурируемого реестра с безопасным дефолтом.
|
||||
|
||||
## Последствия
|
||||
- Изоляция проектов на уровне вебхуков и роутинга.
|
||||
- Парсер устойчив: битый элемент скипается, пустой результат → дефолт.
|
||||
- Основа для `is_self_hosting_repo` (adr-0003).
|
||||
|
||||
## Связи
|
||||
adr-0003 (условный гейт опирается на repo из реестра).
|
||||
23
docs/architecture/adr/adr-0002-job-queue.md
Normal file
23
docs/architecture/adr/adr-0002-job-queue.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# adr-0002: Очередь задач вместо in-process потоков
|
||||
|
||||
- **Статус:** accepted
|
||||
- **Дата:** 2026-06-03
|
||||
- **Задача:** ORCH-1 (F-2b)
|
||||
|
||||
## Контекст
|
||||
Ранняя версия запускала стадии конвейера в in-process daemon-потоках. Проблемы: не переживало рестарт (задачи терялись), нет контроля параллелизма, нет ретраев, нет наблюдаемости.
|
||||
|
||||
## Решение
|
||||
Введена персистентная очередь задач (`src/queue_worker.py` + таблица `jobs` в SQLite): atomic claim задачи воркером, `max_concurrency`, ретраи при сбое, restart-safe (running-задачи реквестятся при старте), эндпоинт `GET /queue`.
|
||||
|
||||
## Альтернативы
|
||||
- In-process потоки — отклонены (не restart-safe).
|
||||
- Внешний брокер (Redis/RabbitMQ) — избыточно для текущего масштаба; SQLite-очередь проще и без новых зависимостей.
|
||||
|
||||
## Последствия
|
||||
- Конвейер переживает рестарт контейнера.
|
||||
- Контроль параллелизма и наблюдаемость через `/queue`.
|
||||
- ⚠️ Очередь общая на все проекты прод-инстанса — фактор группового риска при self-hosting (см. `docs/operations/INFRA.md`).
|
||||
|
||||
## Связи
|
||||
adr-0001 (реестр проектов), INFRA.md (общая очередь при self-hosting).
|
||||
27
docs/architecture/adr/adr-0003-staging-gate.md
Normal file
27
docs/architecture/adr/adr-0003-staging-gate.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# adr-0003: Условный staging-гейт перед прод-деплоем
|
||||
|
||||
- **Статус:** accepted
|
||||
- **Дата:** 2026-06-05
|
||||
- **Задача:** ORCH-35
|
||||
|
||||
## Контекст
|
||||
Оркестратор дорабатывает сам себя (self-hosting). Раньше стадия `deploy` имела «бумажный» вердикт: deployer-агент писал `deploy_status: SUCCESS`, но реального прогона на изолированной среде не было. Нужен предохранитель: прод-деплой орка не должен происходить, пока изменения не проверены на живой staging-среде. При этом другие проекты (enduro-trails) staging-инфры не имеют.
|
||||
|
||||
## Решение
|
||||
Добавлена промежуточная стадия `deploy-staging` между `testing` и `deploy`: `testing → deploy-staging → deploy → done`.
|
||||
- deployer гоняет `scripts/staging_check.py --base-url http://localhost:8501` и пишет `staging_status: SUCCESS|FAILED` в `15-staging-log.md`.
|
||||
- Quality Gate `check_staging_status` парсит вердикт (только YAML-frontmatter).
|
||||
- **Гейт условный:** `is_self_hosting_repo(repo)` → реальная проверка только для `orchestrator`; для остальных проектов гейт = no-op `(True, "Staging gate N/A")`.
|
||||
- FAILED → откат на `development`.
|
||||
|
||||
## Альтернативы
|
||||
- Глобальный гейт для всех проектов — отклонён: у enduro нет staging-инстанса, задачи застревали бы на откате.
|
||||
- Деплой реально дёргает хост-хук прямо здесь — отложен в ORCH-36 (Вариант B).
|
||||
|
||||
## Последствия
|
||||
- Прод-деплой орка недостижим, пока staging-гейт не зелёный.
|
||||
- Другие проекты не затронуты (no-op).
|
||||
- Реальный docker-деплой через хук пока НЕ выполняется (вердикт «бумажный», но подкреплён прогоном сьюта). Исполняемый деплой — ORCH-36.
|
||||
|
||||
## Связи
|
||||
adr-0001 (реестр проектов — основа `is_self_hosting_repo`), ORCH-34 (deploy-hook + rollback), ORCH-36 (исполняемый самодеплой).
|
||||
@@ -58,7 +58,8 @@ STAGE_TRANSITIONS = {
|
||||
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)
|
||||
testing: → deploy-staging (agent: deployer, QG: check_tests_passed)
|
||||
deploy-staging: → deploy (agent: deployer, QG: check_staging_status)
|
||||
deploy: → done (agent: None, QG: None)
|
||||
}
|
||||
```
|
||||
@@ -189,8 +190,10 @@ services:
|
||||
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
|
||||
15. Auto-advance: testing → deploy-staging (QG check_tests_passed → PASS)
|
||||
16. Deployer: runs staging checks → writes 15-staging-log.md (staging_status: SUCCESS)
|
||||
17. Auto-advance: deploy-staging → deploy (QG check_staging_status → PASS)
|
||||
18. PR merge → Gitea PR webhook: action=closed, merged=true → done
|
||||
```
|
||||
|
||||
### Review bounce path
|
||||
96
docs/operations/INFRA.md
Normal file
96
docs/operations/INFRA.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# INFRA.md — инфраструктура и эксплуатация оркестратора
|
||||
|
||||
> RUNBOOK. Топология, контейнеры, порты, переменные окружения, границы.
|
||||
> **Секреты тут НЕ хранятся** — только дескрипторы. Реальные значения — в `.env` на хосте.
|
||||
|
||||
## Топология
|
||||
|
||||
```
|
||||
host: mva154 (slin@82.22.50.71), network_mode: host
|
||||
┌──────────────────────────────────────────────────────────────────────┐
|
||||
│ orchestrator (PROD) :8500 env_file .env │
|
||||
│ БД: ./data/orchestrator.db (обслуживает ВСЕ прод-проекты) │
|
||||
│ │
|
||||
│ orchestrator-staging (STAGING) :8501 env_file .env.staging │
|
||||
│ БД: ./data/staging/orchestrator.db (изолирована, только sandbox) │
|
||||
│ profile: staging — НЕ стартует обычным `docker compose up` │
|
||||
└──────────────────────────────────────────────────────────────────────┘
|
||||
│ webhooks │ git
|
||||
▼ ▼
|
||||
Plane (ag_proj) Gitea (localhost:3000)
|
||||
/repos/<project> ← общий каталог репозиториев (host: /home/slin/repos)
|
||||
```
|
||||
|
||||
## Контейнеры
|
||||
|
||||
| Контейнер | Роль | Порт | env_file | БД (хост) | Старт |
|
||||
|-----------|------|------|----------|-----------|-------|
|
||||
| `orchestrator` | прод | 8500 | `.env` | `./data/orchestrator.db` | `docker compose up -d` |
|
||||
| `orchestrator-staging` | staging / песочница | 8501 | `.env.staging` | `./data/staging/orchestrator.db` | `docker compose --profile staging up -d orchestrator-staging` |
|
||||
|
||||
Оба: `network_mode: host`, `init: true` (tini как PID 1 — reaping зомби, B-2), `restart: unless-stopped`.
|
||||
|
||||
### Тома (volumes)
|
||||
- `./data` → `/app/data` (БД; у staging — `./data/staging`)
|
||||
- `/home/slin/repos` → `/repos` (рабочие репозитории проектов)
|
||||
- `/var/run/docker.sock` (для docker-операций деплоя)
|
||||
- claude-code, node, `~/.claude*` (CLI агентов, ro)
|
||||
- `~/.orchestrator-ssh` → `/root/.ssh` (ro, деплой по ssh)
|
||||
|
||||
## Переменные окружения (карта; значения — в `.env`)
|
||||
|
||||
| Переменная | Назначение |
|
||||
|-----------|-----------|
|
||||
| `ORCH_PLANE_API_URL` / `_TOKEN` / `_WORKSPACE_SLUG` | доступ к Plane API |
|
||||
| `ORCH_PLANE_WEBHOOK_SECRET` | HMAC-проверка вебхуков Plane |
|
||||
| `ORCH_GITEA_URL` / `_TOKEN` / `_WEBHOOK_SECRET` | доступ к Gitea + HMAC |
|
||||
| `ORCH_CLAUDE_BIN` | путь к claude CLI |
|
||||
| `ORCH_REPOS_DIR` / `ORCH_HOST_REPOS_DIR` | каталог репозиториев (в контейнере / на хосте) |
|
||||
| `ORCH_DB_PATH` | путь к SQLite БД |
|
||||
| `ORCH_PROJECTS_JSON` | реестр проектов (Plane id → repo + prefix); пусто → дефолт из `src/projects.py` |
|
||||
| `DEPLOY_SSH_USER` / `_HOST` / `DEPLOY_HOOK_SCRIPT` | параметры деплой-хука |
|
||||
|
||||
**Секреты — только в `.env` / `.env.staging` на хосте, в гит НЕ коммитятся.** Канон — `.env.example`, `.env.staging.example`.
|
||||
|
||||
## Реестр проектов (`src/projects.py`, ORCH-6)
|
||||
Связывает Plane project id → gitea repo + work-item prefix. Источник: `ORCH_PROJECTS_JSON`, fallback — встроенный дефолт. Прод видит: `enduro-trails` (ET), `orchestrator` (ORCH). Staging видит ТОЛЬКО `orchestrator-sandbox` (SANDBOX) — изоляция.
|
||||
|
||||
## ⚠️ Self-hosting — оркестратор дорабатывает САМ СЕБЯ
|
||||
|
||||
**Факт:** прод-инстанс `orchestrator` (8500) — ОДИН на ВСЕ прод-проекты (enduro-trails + orchestrator), с ОБЩЕЙ БД `./data/orchestrator.db` и общей очередью задач (ORCH-1).
|
||||
|
||||
**Следствие — групповой риск:** когда орк выполняет задачу из проекта ORCH (дорабатывает себя), он бежит в том же инстансе, что обслуживает enduro-trails.
|
||||
- Рестарт / падение прод-контейнера орк-задачей → конвейер ВСЕХ проектов встаёт.
|
||||
- Кривой self-деплой (ORCH-36, Вариант B) → лежат все проекты сразу.
|
||||
- Общая очередь → орк-задача занимает concurrency-слоты других проектов.
|
||||
|
||||
**Что изолировано (безопасно):**
|
||||
- Staging (8501) — отдельная БД (`./data/staging`), отдельный реестр (`ORCH_PROJECTS_JSON` = только sandbox). Прод-проекты не видит.
|
||||
- Репозитории разделены, изоляция веток через git worktree (ORCH-2).
|
||||
|
||||
**Страховки:**
|
||||
- Стадия `deploy-staging` (порт 8501) — обязательный гейт перед прод-деплоем орка. Прод-деплой недостижим, пока staging-гейт не зелёный (см. `STAGING.md`, ORCH-35). Гейт условный: реален только для self-hosting (repo=orchestrator), для остальных проектов — no-op.
|
||||
|
||||
**Правила для агентов при задачах ORCH:**
|
||||
1. НЕ перезапускать / не ронять прод-контейнер `orchestrator` в рамках задачи.
|
||||
2. Все проверки деплоя — на staging (8501), боевой 8500 не трогать.
|
||||
3. Деплой self — только через хук с health-check + авто-rollback (`DEPLOY_HOOK.md`).
|
||||
|
||||
## Эксплуатация (быстрые команды)
|
||||
```bash
|
||||
# статус
|
||||
docker ps --filter name=orchestrator
|
||||
curl -s http://localhost:8500/health
|
||||
curl -s http://localhost:8500/status # активные задачи
|
||||
curl -s http://localhost:8500/queue # очередь
|
||||
|
||||
# поднять staging-песочницу
|
||||
docker compose --profile staging up -d orchestrator-staging
|
||||
curl -s http://localhost:8501/health
|
||||
|
||||
# логи
|
||||
docker logs --tail 100 orchestrator
|
||||
```
|
||||
|
||||
---
|
||||
*RUNBOOK 2026-06-05. Обновлять при изменении топологии/портов/переменных. См. CONTRIBUTING.md §8.*
|
||||
Reference in New Issue
Block a user