ORCH-044 closes two blind spots that let a single de-authenticated agent
stall the shared queue for all projects:
P1 — preflight auth gate. `claude --version` answers even when logged out,
so version-only preflight was blind to auth. Adds a token-free, network-free
check of <AGENT_HOME>/.claude/.credentials.json: missing/unreadable/no-oauth
or an expired `claudeAiOauth.expiresAt` (epoch ms, vs now + skew) => preflight
FAIL; absent expiry => OK (no false positives). Result is cached on the same
preflight_cache_ttl. Post-factum safety net: launcher detects auth markers
("not logged in" / "/login" / "unauthorized" / 401) in the run log and resets
the preflight cache so the next tick re-evaluates auth. Auth failure is a gate,
not a transient — it does not spin the circuit breaker. Emergency toggle
ORCH_PREFLIGHT_CHECK_AUTH=false restores version-only behaviour.
P3 — empty log / no result-JSON => job failed. exit_code==0 with an empty or
JSON-less run log no longer counts as success: a separate result_ok flag gates
stage advance + usage comments, fires a Telegram alert, and routes the job
through the normal transient/permanent failure path (exit_code integrity in
agent_runs preserved).
Scope: P2 (--effort) is intentionally excluded and tracked in ORCH-50.
New settings: ORCH_PREFLIGHT_CHECK_AUTH, ORCH_CLAUDE_CREDENTIALS_PATH,
ORCH_AUTH_EXPIRY_SKEW_SECONDS. Docs updated (INFRA.md, internals.md, CHANGELOG).
Refs: ORCH-044
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Архитектура 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. - Review/Test Parsers (
src/review_parse.py, ORCH-046) — defensive-извлечение дословного must-fix текста из артефактов для встраивания вtask_descзаворота:extract_review_findings(P0/P1 из12-review.md),extract_test_failures(фрагмент тела13-test-report.md). Контракт «never raise»: любая ошибка →"". - Quality Gates (
src/qg/checks.py) — проверки выхода со стадии, реестрQG_CHECKS. - Agent Launcher (
src/agents/launcher.py) — запуск Claude CLI агентов в изолированном git worktree, мониторинг, auto-advance. Валидация результата (ORCH-044):exit_code==0считается успехом только если run-лог непустой и содержит валидный result-JSON; пустой/невалидный результат ⇒ jobfailed/retry + алерт, без авто-advance и «успешного» коммента. - Preflight (
src/preflight.py, ORCH-1/ORCH-044) — дешёвый token-free гейт клейма:os.path.exists(bin)+claude --version+ проверка авторизации (чтение<AGENT_HOME>/.claude/.credentials.jsonи валидностиclaudeAiOauth.expiresAt; постфактум-маркерNot logged in). Кешируется наpreflight_cache_ttl. Подробнее: ADR work-item ORCH-044. - Queue (
src/queue_worker.py, ORCH-1) — персистентная очередь задач (SQLitejobs), atomic claim, max_concurrency, ретраи, restart-safe. Не клеймит job приpreflight=fail(в т.ч. auth-fail). - 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.
Откаты
- Reviewer REQUEST_CHANGES → откат на
development+ retry (MAX_DEVELOPER_RETRIES = 3). - Tester
check_tests_passedFAIL → откат наdevelopment+ retry. - Deploy / deploy-staging FAILED → откат на
development. get_previous_stageиспользует порядок ключейSTAGE_TRANSITIONS.
Обогащение task_desc при заворотах (ORCH-046)
При откате на development task_desc (попадает в .task-dev.md developer-агента) несёт дословный must-fix текст, а не только ссылку — чтобы агент видел суть претензий сразу и не повторял ту же ошибку:
- reviewer REQUEST_CHANGES → дословные пункты P0/P1 из секции
## Findingsфайла12-review.md(extract_review_findings); - tester
check_tests_passedFAIL →reasonгейта + фрагмент тела13-test-report.md(приоритет:## Вывод pytest→ FAIL-строки## Результаты→## Итог;extract_test_failures).
Ссылка на полный файл-артефакт сохраняется всегда («Полный контекст»). Парсеры src/review_parse.py — defensive (never-raise); при отсутствующем/битом артефакте task_desc graceful-фоллбэк на прежнюю ссылку-строку, последовательность отката и retry-счётчик не меняются (ADR docs/work-items/ORCH-046/06-adr/ADR-001-embed-findings-in-task-desc.md).
Plane Sync: единый status-коммент агентов (ORCH-016)
Все агенты (analyst / architect / developer / reviewer / tester / deployer) пишут финальный коммент через один хелпер usage.build_status_comment(...) (ADR docs/work-items/ORCH-016/06-adr/ADR-001-unified-status-comment.md). Формат HTML, разделители <br>:
{ICON} {RoleName} — {описание стадии}
[Verdict|Status: VALUE] # reviewer/tester/deployer, из YAML-frontmatter артефакта
[Длительность: 4m 12s] # явный duration_s от launcher, либо fallback из agent_runs
<b>Документы:</b><ul><li><a href="…">label</a></li>…</ul>
[<sub>8.5M in / 45.8k out · $7.29</sub>] # тех-хвост usage; опускается при нулях
- Длительность считается launcher'ом (
_monitor_agent) и пробрасывается в_post_usage_comments; для analyst (коммент строится вstage_engine) используется DB-фоллбэкusage.get_agent_duration(task_id, agent). - Vердикт-парсер —
src/frontmatter.read_frontmatter_value(...)(defensive, никогда не raise). Машинные ключи: reviewer →verdict:(12-review.md); testing-гейтcheck_tests_passed(13-test-report.md) → любое из трёх равноправных:result:(канон промпта тестера),verdict:,status:(ORCH-047, ADR-001); deployer →deploy_status:(14-deploy-log.md),staging_status:(15-staging-log.md). Negative-токен в любом поле авторитетен (перебивает positive). - Формат коммента не меняет реестр гейтов и стадий; коммент — отображение, не управление.
База данных (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. Деплой-хук — DEPLOY_HOOK.md. Staging — STAGING.md.
ADR
Сквозные архитектурные решения — adr/. Per-work-item решения — docs/work-items/<id>/06-adr/.
Детали реализации
Схема БД, потоки данных, resilience-слой, детали Dockerfile — internals.md.
Актуально на 2026-06-05 (main f1b3146). Обновлять при изменении src/stages.py, src/qg/checks.py, src/main.py.