post-deploy-monitor fa390dbf7c
All checks were successful
CI / test (push) Successful in 33s
docs(ORCH-021): post-deploy HEALTHY/NONE for ORCH-091
2026-06-10 00:25:52 +03:00

Multi-Agent Orchestrator

См. CLAUDE.md (паспорт проекта) и docs/architecture/README.md (архитектура).

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-staging → 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_ci_green (Gitea CI зелёный на ветке) Auto-advance после developer
review reviewer check_reviewer_verdict (verdict: во frontmatter 12-review.md) Auto-advance после reviewer
testing tester check_tests_passed (test-report.md) Auto-advance после tester
deploy-staging deployer check_staging_status (15-staging-log.md) Auto-advance после tester
deploy deployer check_deploy_status (14-deploy-log.md) Auto-advance после staging
done

API Endpoints

Method Path Описание
GET /health Health check
GET /status Активные задачи (stage != done)
GET /queue Очередь задач (ORCH-1): counts по статусам + max_concurrency + последние 10 jobs
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
├── queue_worker.py      # ORCH-1: фоновый воркер очереди (claim → launch_job)
├── agents/
│   └── launcher.py      # AgentLauncher: launch/launch_job, 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/
├── PRODUCT_VISION.md            # Видение продукта
├── architecture/
│   ├── README.md                # Обзор архитектуры, компоненты, API
│   ├── internals.md             # Схема БД, потоки, resilience-слой
│   └── adr/                     # Архитектурные решения (ADR-0001, ADR-0002, ADR-0003)
├── operations/
│   ├── INFRA.md                 # Топология, порты, env, self-hosting риски
│   ├── DEPLOY_HOOK.md           # Деплой-хук
│   ├── STAGING.md               # Staging-окружение
│   ├── STAGING_CHECK.md         # Проверки staging
│   └── SETUP_WEBHOOKS.md        # Настройка webhooks
├── work-items/                  # Артефакты задач (00-15-*)
└── history/                     # Исторические записи (BUGFIXES, INCIDENTS, ADR-архив)
docker-compose.yml       # Deployment config
Dockerfile               # Python 3.12 + Docker CLI + tini

Запуск

Docker (production)

docker compose up -d --build

Dev

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
ORCH_RUNS_DIR Базовый каталог per-run логов агентов (<runs_dir>/{run_id}.log, ORCH-087) /app/data/runs
ORCH_MAX_CONCURRENCY Сколько jobs воркер запускает параллельно (ORCH-1) 1
ORCH_QUEUE_POLL_INTERVAL Период опроса очереди воркером, сек (ORCH-1) 2.0
ORCH_PREFLIGHT_CACHE_TTL Кэш preflight (CLI/net), сек (ORCH-1 resilience) 45
ORCH_BACKOFF_BASE_SECONDS База exp-backoff для transient (429) 10
ORCH_BACKOFF_MAX_SECONDS Потолок backoff 600
ORCH_TRANSIENT_MAX_ATTEMPTS Ретраи для 429/недоступности 5
ORCH_BREAKER_THRESHOLD transient подряд до открытия breaker 3
ORCH_BREAKER_PAUSE_SECONDS Пауза при открытом breaker 300
ORCH_RECONCILE_ENABLED Kill-switch sweeper потерянных webhook (ORCH-053) true
ORCH_RECONCILE_PLANE_ENABLED Отдельный флаг F-2 (опрос Plane API) true
ORCH_RECONCILE_INTERVAL_S Период фонового прохода reconciler, сек 120
ORCH_RECONCILE_GRACE_DEFAULT_S Порог «застряла» по tasks.updated_at, сек 600
ORCH_RECONCILE_GRACE_OVERRIDES_JSON Per-stage пороги, напр. {"development":300} ""
ORCH_RECONCILE_NOTIFY_UNBLOCK Telegram при разблокировке застрявшей задачи true
ORCH_RECONCILE_SKIP_BLOCKED_ENABLED F-1 Guard 2 (ORCH-060): пропуск задач в Plane-статусе Blocked / Needs Input; false глушит только сетевой Guard 2 (Guard 1 escalated всегда активен) true
ORCH_QG0_TITLE_MAX Верхний лимит длины заголовка QG-0 (вход _qg0_errors); невалидное/пустое значение → дефолт (ORCH-069) 200
ORCH_STOP_STATUS_ENABLED Kill-switch отмены задачи по Plane-статусу STOP + закрытия дыры релонча (ORCH-090); false → поведение 1:1 как до ORCH-090 true
ORCH_STOP_STATUS_REPOS CSV область репо для STOP-отмены; пусто = все репо (ORCH-090) ""

Очередь задач (ORCH-1 / F-2b)

Webhook-хэндлеры больше не спавнят claude-агентов синхронно в процессе uvicorn. Вместо этого они кладут job в персистентную SQLite-таблицу jobs (enqueue_job, мгновенный ответ), а фоновый воркер (src/queue_worker.py) забирает jobs с учётом ORCH_MAX_CONCURRENCY и запускает агента (launch_job, та же Popen-логика, что и раньше).

Преимущества:

  • Рестарт-safe. При старте jobs со статусом running возвращаются в queued (queue-recovery в lifespan) — работа не теряется.
  • Лимит параллелизма. Воркер не превышает ORCH_MAX_CONCURRENCY.
  • Ретраи. Упавший job (exit≠0) ретраится пока attempts < max_attempts, потом failed + Telegram-нотификация.

Статусы job: queued → running → done | failed; cancelled — терминальный исход STOP-отмены (ORCH-090), нигде не реквью'ится. Наблюдаемость — через GET /queue.

Отмена задачи: статус STOP (ORCH-090)

Перевод задачи в выделенный Plane-статус STOP отменяет её: оркестратор останавливает активного агента (graceful SIGTERM-каскад), снимает все job'ы (терминальный cancelled, без авто-requeue), удаляет worktree и рабочую ветку в Gitea (никогда main, без force-push), сбрасывает прогресс в durable-терминал tasks.stage='cancelled' и тумбстонит натуральные ключи (#cancelled-<id>), чтобы повторный «To Analyse» создал задачу с нуля. Docs-артефакты (01..17) сохраняются. STOP во время критичного шага merge/deploy — откладывается до его честного завершения (никакого half-merge / рестарта прода). Параллельно закрыта «дыра релонча»: ручной перевод в промежуточный рабочий статус больше не релончит агента — единственный вход к запуску пайплайна остаётся «To Analyse» (релонч агента сменой статуса разрешён только на стадии analysis — владельце Needs Input). Всё под kill-switch ORCH_STOP_STATUS_ENABLED, аддитивно, never-raise. Наблюдаемость — блок stop в GET /queue. Деталь — docs/work-items/ ORCH-090/06-adr/ADR-001-stop-cancel-task.md + сквозной docs/architecture/adr/adr-0026-stop-cancel-task.md.

Инфра-предусловие: на доске Plane проекта ORCH создать статус «STOP» с группой cancelled. До создания статуса фича в fail-safe (нет UUID → ветка STOP не активируется).

Resilience-слой: дешёвый preflight (CLI/net, кэш, без токенов) гейтит claim; 429/overload детектится по логу (transient vs permanent), transient ретраится с exp-backoff (available_at, Retry-After); circuit breaker паузит воркер после N transient подряд. Подробности: docs/history/ORCH-1_JOB_QUEUE.md.

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):

    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. Проверь резолв:

    docker exec orchestrator python3 -c "from src.projects import get_project_by_plane_id as g; print(g('<новый-uuid>'))"
    

Поля name опционально (по умолчанию = repo). Подробности — docs/architecture/internals.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 → Gitea CI статус; ORCH-045: авторитетный гейт развития (development → review) — check_ci_green читает статус ветки с polling-retry (устраняет гонку «pending сразу после push»)

Тесты

pytest tests/ -v

Известные ограничения

Реально открытые ограничения (сверено с кодом, ORCH-079):

  1. Telegram 48h — карточки-сироты старше 48 часов неудаляемы (лимит Telegram Bot API); зачистка сирот самозалечивает только свежие (ORCH-087).
  2. Зависимости задач — только intra-repo (v1)job_deps выражают связи в пределах одного репозитория; кросс-репо зависимости пока не поддержаны (ORCH-026).
  3. Пакетный автоном — Этап 1 — per-repo serial gate сериализует задачи одного репо (ORCH-088); полный пакетный автономный прогон «1020 задач за ночь» — в развитии (эпик ORCH-088).

Закрыто (история)

Пункты, ранее значившиеся ограничениями, закрыты кодом — оставлены как трассировка:

  • Single-task / shared /repos checkout → git worktree per task (ensure_worktree) + serial-gate (ORCH-088) + task-deps (ORCH-026).
  • In-process daemon-потоки → персистентная очередь задач (SQLite jobs, src/queue_worker.py), restart-safe (ORCH-1).
  • Gitea CI не настроен → активный гейт стадии developmentcheck_ci_green (src/qg/checks.py); check_tests_local помечен DEPRECATED.
  • No retry on API errors → exp-backoff + circuit breaker в queue_worker.py (ORCH_BACKOFF_* / ORCH_BREAKER_* / ORCH_TRANSIENT_MAX_ATTEMPTS) + retry-loop в check_ci_green (ORCH-1 resilience / ORCH-045).
  • Plane sync — маппинг issue ID → зрелый src/plane_sync.py (find_issue_id, fetch_issue_sequence_id) со статус-моделью и TTL-самозалечиванием (ORCH-010 / 066 / 068).
  • Tester timeout — Playwright e2e → orchestrator является pytest-сервисом (Playwright неприменим); реальный механизм — конфигурируемый watchdog (agent_timeout_seconds, ORCH-7).
Description
Агентская разработка — методика и workflow Claude Code CLI + OpenClaw
Readme 12 MiB
Languages
Python 99.3%
Shell 0.5%
Dockerfile 0.2%