Files
orchestrator/CLAUDE.md
claude-bot 92961d1d32 refactor(frontmatter): unified frontmatter contract + handoff spec (ORCH-52c)
src/frontmatter.py grows from a single-key reader into the full machine
contract: reader (read_frontmatter_value, unchanged), one parse primitive
(parse_frontmatter), writer (render/write_frontmatter), schema validator
(validate_schema/REQUIRED_FIELDS, warning-only by default) and a shared
strip_frontmatter helper. The five verdict gates (check_reviewer_verdict,
_parse_tests_verdict, _parse_deploy_status, _parse_staging_status,
parse_security_status) now read through the single parse_frontmatter point
instead of duplicated ad-hoc YAML logic; review_parse._strip_frontmatter and
security_gate.extract_security_findings reuse the shared helper.

Strictly backward compatible + never-raise: STAGE_TRANSITIONS, the QG_CHECKS
composition, verdict semantics (incl. ORCH-047 three-field tester + negative
token priority), reason-strings and worktree->origin/main fallback are 1:1.
The schema validator never influences a gate verdict by default; hard-fail is
reserved behind the frontmatter_validation_strict kill-switch (default False).

New formal handoff spec docs/_standards/HANDOFF_PROTOCOL.md ("stage -> required
output" + required frontmatter schema), aligned 1:1 with PIPELINE_DOCS.md.

Tests: test_frontmatter.py (TC-01..07), test_qg_verdicts.py (TC-08..15),
test_security_gate.py (TC-12), test_stages_invariants.py (TC-16). Full
tests/ green (1212).

Refs: ORCH-076

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 14:14:30 +03:00

19 KiB
Raw Blame History

CLAUDE.md — паспорт проекта orchestrator

TL;DR

Мульти-агентный оркестратор разработки. FastAPI-сервис: принимает webhooks от Plane и Gitea, ведёт задачи по конвейеру стадий через Quality Gates, запускает Claude CLI агентов (analyst → architect → developer → reviewer → tester → deployer) на каждой стадии. Оркестратор дорабатывает в том числе сам себя (self-hosting).

Стек

  • Backend: FastAPI + uvicorn (Python 3.12)
  • БД: SQLite (src/db.py)
  • Агенты: Claude CLI (ORCH_CLAUDE_BIN), по одному промпту на роль в .openclaw/agents/. ORCH-74: модель/эффорт агента берутся ТОЛЬКО из config (resolve_agent_model/resolve_agent_effort, ORCH-41) — frontmatter model: удалён как мёртвый, frontmatter описательный; имя модели валидируется форматом ^claude-…$ перед --model (never-break).
  • Очередь задач: собственная (SQLite jobs, src/queue_worker.py, ORCH-1). ORCH-026: claim_next_job гейтит задачи с незавершёнными зависимостями (job_deps, NOT EXISTS) без занятия слота max_concurrency; декларации/детект циклов — leaf src/task_deps.py (kill-switch ORCH_TASK_DEPS_ENABLED). Сериализация мержа одного репо — безусловный pre-merge rebase под merge-lease (ORCH_PREMERGE_REBASE_ALWAYS). ORCH-088 (serial gate, Этап 1): новая задача репо не входит в analysis (analyst-job не выбирается, ветка не режется), пока в репо есть более ранняя незавершённая задача (t2.id < jobs.task_id, FIFO) ИЛИ репо заморожен (repo_freeze). Срез ветки отложен со start_pipeline на момент claim analyst-job (launcher._materialize_deferred_branch) — база = свежий origin/main с кодом предшественника (анти-stale-base). Post-deploy DEGRADED → durable per-repo freeze (repo_freeze, cleared_at IS NULL = активен) + Telegram; снятие — вручную POST /serial-gate/unfreeze?repo=…. Leaf src/serial_gate.py (claim — fail-OPEN, freeze — fail-CLOSED); флаги ORCH_SERIAL_GATE_ENABLED (kill-switch), ORCH_SERIAL_GATE_REPOS (CSV; пусто = все репо), ORCH_SERIAL_GATE_FREEZE_ENABLED. Блок serial_gate в GET /queue. STAGE_TRANSITIONS/QG_CHECKS не тронуты.
  • Контейнеризация: Docker + Compose
  • CI/CD: Gitea Actions (.gitea/workflows/)
  • Деплой: docker compose на mva154

Команды

  • uvicorn src.main:app --reload --port 8500 — поднять локально (dev)
  • pytest tests/ -q — все тесты
  • docker compose up -d --build — прод
  • docker compose --profile staging up -d orchestrator-staging — staging-песочница (8501)

Среды

  • prodorchestrator (8500), внешний URL https://openclaw.mva154.duckdns.org/orchestrator/
  • stagingorchestrator-staging (8501), изолированная БД (./data/staging), только sandbox-проект

Структура

  • src/ — приложение (main, config, db, stages, stage_engine, queue_worker, projects, usage)
  • src/agents/launcher.py — запуск Claude CLI агентов
  • src/qg/checks.py — Quality Gate проверки
  • src/webhooks/ — приём вебхуков Plane/Gitea
  • tests/ — pytest
  • docs/ — документация, ADR, work-items, operations
  • scripts/ — утилиты (staging_check.py, orchestrator-deploy-hook.sh)

Конвейер (кратко; детали — docs/architecture/README.md)

created → analysis → architecture → development → review → testing → deploy-staging → deploy → done
                          ↑                          │
                          └──── REQUEST_CHANGES ──────┘  (откат на development, max 3)

Статусная модель Plane (ORCH-066) — индикация ≠ управление

Статусы Plane — это слой B (индикация), отдельный от слоя A (машина стадий) src/stages.py::STAGE_TRANSITIONS. Plane показывает наблюдателю осмысленную картину (Backlog → Todo → Analysis → Architecture → Development → Code-Review → Testing → Awaiting Deploy → Deploying → Monitoring after Deploy → Done + человеческие гейты In Review/Approved, Confirm Deploy), но НИКОГДА не управляет конвейером. Маппинг и сеттеры — src/plane_sync.py (6 новых ключей: to_analyse/analysis/code_review/awaiting_deploy/deploying/monitoring), с project-relative alias-fallback: на частично сконфигурированном проекте новый ключ деградирует на базовый UUID ТОГО ЖЕ проекта (нулевая регрессия для enduro-trails). Детали — docs/architecture/README.md.

Нотификации / Telegram live-tracker (ORCH-042/066/067/087)

Каждая задача = одна карточка в Telegram (src/notifications.py). Поведение карточки:

  • Дефолт tracker_modebump (ORCH-067; edit доступен через ORCH_TRACKER_MODE=edit). bump на каждом обновлении удаляет старую карточку и шлёт свежую вниз чата (тихо), edit редактирует на месте. Инвариант «одна карточка на задачу» — в обоих режимах.
  • Зачистка сирот (ORCH-087): bump ведёт авторитетный леджер ВСЕХ созданных карточек (таблица tracker_messages, deleted_at IS NULL = жива) и на каждом обновлении удаляет ВСЕ незакрытые mid, а не только скаляр tracker_message_id (он сохранён как указатель на текущую карточку, BC). Это устраняет класс «замёрзшая сирота» (старая карточка с заголовком ранней стадии, потерявшая ссылку при гонке/delete-fail+send-ok). Новый mid пишется в леджер ТОЛЬКО при успешном send (BR-6); transient-delete остаётся незакрытым для ретрая; «already gone»/>48ч (_DELETE_GONE_MARKERS) → закрывается. Остаточная гонка самозалечивается за один bump. Known-limitation: Telegram 48ч (сироты старше неудаляемы).
  • Эффорт в строке стадии (ORCH-087): колонка agent_runs.effort стампится фактическим resolve_agent_effort в launcher._spawn (CLI его в result-JSON не возвращает); строка рендерится · {model} · {effort} (developer=xhigh, tester/deployer=medium, прочие=high); пустой/исторический effort → суффикс опускается.
  • Честное итоговое время (ORCH-087): done-строка = три независимых подписанных метрики ⏱️ Агенты {Σ agent_runs} · твоё {review~cap} · общее с ожиданием {wall} (раньше Всего {wall} читалось как сумма, которой не является). «Твоё» ограничено tracker_brd_review_cap_s (ORCH_TRACKER_BRD_REVIEW_CAP_S, дефолт 2ч; маркер ~ при отсечке аномального застоя).
  • Статус-строка карточки (📍 <status_label>) показывает текущий Plane-статус по модели ORCH-066 (plane_status_label). Оффлайн-ядро (stage → статус, In Review из brd-clock) работает всегда без сети; best-effort live-overlay (kill-switch tracker_live_status, TTL-кэш, короткий таймаут) лишь дорисовывает ветки, неотличимые offline (Needs Input / Blocked / Rejected / Cancelled / Confirm Deploy / Deploying / Monitoring) и никогда не блокирует конвейер.
  • Кликабельный номер задачи (plane_issue_link) — ORCH-NNN в карточке И во всех уведомлениях (notify_*, alert'ы стадий) рендерится как <a href=…> на issue в Plane; fail-safe → просто html.escape(номер), если ссылку построить нельзя. Никогда не падает.
  • Без link-preview (ORCH-080): оба примитива (send_telegram/edit_telegram) шлют payload с disable_web_page_preview: True — баннер Plane («Modern project management») под кликабельной ссылкой ORCH-NNN больше не разворачивается ни в карточке (bump/edit), ни в notify/alert-сообщениях. parse_mode: HTML сохранён → ссылка остаётся кликабельной.
  • Транспорт (send_telegram/edit_telegram/delete_telegram), disable_notification (карточка тихая, пингуют только alert-хелперы), схема БД — не трогаются.

Авто-режим по лейблам: autoApprove + autoDeploy (ORCH-089)

Конвейер имеет два человеческих гейта, тормозящих пакетный автономный прогон (эпик ORCH-088): гейт BRD (analysis: ручной Approved) и гейт прод-деплоя (deploy Phase A: ручной Confirm Deploy, ORCH-059). ORCH-089 снимает только эти два человеческих решения — выборочно (лейбл Plane на задаче), декларативно, обратимо, не трогая ни одной технической проверки. Инвариант: авто-режим снимает лишь ожидание человеческого сигнала; STAGE_TRANSITIONS/QG_CHECKS/check_*/схема БД — не трогаются. Аддитивно: leaf src/labels.py (never-raise) + две точечные врезки.

  • autoApprove → врезка в stage_engine._handle_analysis_approved_flow (ветка files_ok): set_issue_approved (индикация) + лог/Telegram/Plane-коммент + advance_stage(..., finished_agent=None)тот же путь, что человеческий Approved (approved-via-statusanalysis → architecture + mark_brd_review_ended).
  • autoDeploy → врезка в stage_engine._handle_self_deploy_phase_a после advance на deploy + clear_state: лог/Telegram/Plane-коммент + _handle_self_deploy_phase_b (маркер INITIATED, статус Deploying, finalizer). Пропускаются лишь индикативно-человеческие шаги. BR-5 структурно: Phase A достигается только после зелёных под-гейтов ребра deploy-staging → deploy (security → merge-gate → image-freshness → staging) → autoDeploy физически не деплоит сломанное.
  • Чтение лейбловplane_sync.fetch_issue_labels (None при ошибке ≠ []) + get_project_labels ({normalized_name→uuid}, TTL-кэш); сопоставление по нормализованному имени (strip().casefold()), неоднозначность → «нет лейбла». Источник истины — Plane API, не payload вебхука. Новый сеттер set_issue_approved.
  • Флаги (config.py): auto_label_enabled (kill-switch), auto_approve_label/ auto_deploy_label, auto_label_repos (CSV; пусто → self-hosting only), auto_label_states_ttl_s. applies(repo) (локальный) проверяется ПЕРВЫМ; has_label (сеть) — только при applies==True → при выключенном флаге нулевой сетевой оверхед.
  • Fail-safe (never auto): любая ошибка/недоступность Plane/неоднозначность → «нет авто» → ручной гейт (never-raise). Прозрачность: лог + Telegram + Plane-коммент + live-карточка; блок auto_labels в GET /queue. Инфра-предусловие: создать лейблы autoApprove/autoDeploy в Plane-проекте ORCH (их отсутствие = ручной режим, fail-safe). Детали — docs/work-items/ORCH-089/06-adr/ADR-001-auto-label-gates.md, docs/architecture/adr/adr-0018-auto-label-gates.md.

Конвенции

  • Conventional Commits (feat:, fix:, docs:, refactor:, test:)
  • Ветки: feature/ORCH-NNN-slug, fix/ORCH-NNN-slug
  • ADR per work-item: docs/work-items/<plane-id>/06-adr/ADR-NNN-slug.md
  • Global ADR (сквозные решения): docs/architecture/adr/adr-NNNN-slug.md
  • Work items: docs/work-items/<plane-id>/
  • Машинные вердикты Quality Gate — строго YAML-frontmatter (verdict:, deploy_status:, staging_status:, security_status:), никогда проза. ORCH-52c (ORCH-076): парсинг frontmatter сведён к единому контракту src/frontmatter.py (reader read_frontmatter_value — BC; единый парс-примитив parse_frontmatter; writer render/write_frontmatter; валидатор схемы validate_schema/REQUIRED_FIELDS — warning-only по умолчанию, hard-fail только под kill-switch frontmatter_validation_strict, дефолт False). Пять вердикт-парсеров (check_reviewer_verdict, _parse_tests_verdict, _parse_deploy_status, _parse_staging_status, parse_security_status) читают через ОДНУ точку парсинга; семантика вердиктов и STAGE_TRANSITIONS/состав QG_CHECKS — 1:1. Формальная спека «стадия → обязательный выход» + обязательная frontmatter-схема — docs/_standards/HANDOFF_PROTOCOL.md

Артефакты задачи (docs/work-items/<plane-id>/)

00-business-request.md, 01-brd.md, 02-trz.md, 03-acceptance-criteria.md, 04-test-plan.yaml, 06-adr/ADR-NNN-slug.md, 07-infra-requirements.md, 08-data-requirements.md, 10-tech-risks.md, 12-review.md, 13-test-report.md, 14-deploy-log.md, 15-staging-log.md, 16-post-deploy-log.md (post-deploy наблюдение, ORCH-021), 17-security-report.md (security-гейт: security_status:/secrets/deps, ORCH-022).

Стандарт документов (ORCH-075, ORCH-52b): структура каждого дока, карта «стадия→агент→документ→гейт→machine-key» и конвенция ADR-naming зафиксированы в docs/_standards/PIPELINE_DOCS.md (golden source); копируемые скелеты — в docs/_templates/. Перед написанием номерного дока бери скелет из docs/_templates/ и не меняй имя machine-key frontmatter (регистр чувствителен — иначе гейт упадёт ложно).

Правила для агентов

  1. Перед любым действием прочесть этот файл и docs/architecture/README.md.
  2. Документация = golden source наравне с кодом. Изменил функционал → обнови доку В ТОМ ЖЕ PR. Архитектурное решение → заведи ADR (формат — docs/_standards/PIPELINE_DOCS.md §4). Структура номерных доков и шаблоны — docs/_standards/PIPELINE_DOCS.md + docs/_templates/. Обнови CHANGELOG.md.
  3. Никогда не править артефакты других этапов.
  4. Никогда не комментировать ТЗ задним числом — если ТЗ не годится, возвращай в Анализ.
  5. Никогда не закрывать задачу самостоятельно — это делает CI / финальная стадия.
  6. Reviewer проверяет: обновлена ли документация. Нет → REQUEST_CHANGES.
  7. Не использовать --no-verify без явного одобрения Owner.
  8. Секреты — только в .env/.env.staging на хосте, в гит НЕ коммитятся (канон — .env.example).

⚠️ Self-hosting — оркестратор правит САМ СЕБЯ

Задачи проекта ORCH меняют инструмент, который СЕЙЧАС работает в продакшене и обслуживает ДРУГИЕ проекты (enduro-trails) из ОДНОГО инстанса с ОБЩЕЙ БД и общей очередью.

  • НЕ перезапускать / не ронять прод-контейнер orchestrator в рамках задачи — встанет конвейер всех проектов.
  • Любой деплой/рестарт self = групповой риск. Детали и топология — docs/operations/INFRA.md.
  • Стадия deploy-staging (порт 8501) — обязательная страховка перед прод-деплоем орка.
  • Прод-деплой орка запускается ТОЛЬКО переводом задачи на стадии deploy в выделенный Plane-статус «Confirm Deploy» (ORCH-059). Статус Approved — человеческий гейт конвейера и прод-деплой НЕ запускает (на deploy — no-op). Это разделяет «одобрить артефакт» и «выкатить в прод», чтобы привычный approve не ронял прод случайным кликом.

Паспорт проекта orchestrator. Поддерживается агентами при каждой доработке. Изолирован: описывает только этот проект (канон per-repo, см. ORCH-9).