Эпилог эпика ORCH-52. Docs/prompts-only: src/**, STAGE_TRANSITIONS, QG_CHECKS, machine-verdict ключи и схема БД не тронуты; frontmatter_validation_strict=False. - FR-1/FR-2: копируемые frontmatter-примеры всех 6 промптов расхардкожены (created_at: <YYYY-MM-DD> / model_used: <resolve ORCH-41> + врезка «не копируй буквально, подставь date +%F и модель из конфига»); литерал claude-opus-4-8 — только справка в таблице полей. - FR-3: имена check_* в промптах сверены с QG_CHECKS — несовпадений нет (закреплено интеграционным тестом TC-03). - FR-4: developer «PR>1500 → разбивай» переформулирован в эскалацию на уровне задач. - FR-5: секция <escalation> у developer/reviewer/tester (после </success_criteria>): back-to:analysis / back-to:dev / REQUEST_CHANGES. - FR-6: deployer — критичные self-hosting-запреты в видной рамке в начале <context>. - FR-7: tester обогащён worktree-путём, smoke serial_gate (ORCH-088), покрытием TC. - FR-8: из reviewer удалена мёртвая строка «тот же экземпляр Developer». - FR-9 (ADR-001 D1): убран ручной git rebase origin/main — свежесть базы держит движок (serial-gate ORCH-088 + auto_rebase_onto_main под merge-lease). - FR-10 (ADR-001 D2): deployer.md оставлен на английском как нормативное исключение. - FR-11: расширен tests/test_agent_prompts_canon.py (ORCH-092 TC-01…TC-08); канон 52d и test_agent_frontmatter_no_model.py зелёные; полный регресс 1278 зелёный. Документация: 6 промптов, CLAUDE.md, docs/architecture/README.md, CHANGELOG.md. Refs: ORCH-092 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
23 KiB
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) — frontmattermodel:удалён как мёртвый, frontmatter описательный; имя модели валидируется форматом^claude-…$перед--model(never-break). ORCH-077 (52d, замыкает эпик 52): тело всех 6 промптов переписано в едином каноне Anthropic (5 обязательных XML-секций в нормативном порядке<context>→<task>→<deliverables>→<constraints>→<output_format>, запреты в формате «❌ X → ✅ Y»,<thinking>у решающих ролей), и каждый промпт добровольно эмитит 6-польную frontmatter-схему 52c (work_item/stage/author_agent/status/created_at/model_used) аддитивно — рядом с machine-verdict ключом, НЕ меняя его имя/регистр/значения (verdict:/result:/staging_status:/deploy_status:/security_status:— байт-в-байт). Это docs/prompts-only изменение:src/**/STAGE_TRANSITIONS/QG_CHECKS/схема БД не тронуты;frontmatter_validation_strictостаётсяFalse(enforcement НЕ включён). Промптcat-ается из worktree в момент запуска → новые промпты вступают в силу на следующем worktree отmainбез прод-рестарта. Анти-регресс — структурные тестыtests/test_agent_prompts_canon.py+ зелёныйtest_agent_frontmatter_no_model.py. Норматив на будущее: новые/изменённые агент-промпты следуют этому канону. Детали —docs/architecture/adr/adr-0021-prompt-canon-anthropic.md. ORCH-092 (эпилог эпика 52, docs/prompts-only): аудит 6 промптов поверх канона — копируемые frontmatter-примеры расхардкожены (created_at: <YYYY-MM-DD>/model_used: <resolve ORCH-41>+ врезка «подставьdate +%F/модель из конфига, не копируй буквально»; литералclaude-opus-4-8— только справка в таблице полей); добавлена секция<escalation>developer/reviewer/tester (после</success_criteria>, порядок 5 секций цел); developer лишён ручногоgit rebase origin/main(свежесть базы — инвариант движка serial-gate ORCH-088 +auto_rebase_onto_mainпод merge-lease; ручной rebase конфликтовал с запретом force-push — ADR-001 D1); tester обогащён worktree-путём + smokeserial_gate+ покрытием каждого TC; из reviewer удалена мёртвая строка «тот же экземпляр Developer». Языковое исключение (нормативно, ADR-001 D2):deployer.mdсознательно остаётся на английском (5 ru + 1 en) как самый safety-critical промпт — НЕ «чинить» язык вслепую; критичные self-hosting-запреты подняты в видную рамку. Verdict-ключи и канон 52d — байт-в-байт; анти-регресс —tests/test_agent_prompts_canon.py(ORCH-092 TC-01…TC-08). Детали —docs/work-items/ORCH-092/06-adr/ADR-001-developer-rebase-and-deployer-language.md. - Очередь задач: собственная (SQLite
jobs,src/queue_worker.py, ORCH-1). ORCH-026:claim_next_jobгейтит задачи с незавершёнными зависимостями (job_deps,NOT EXISTS) без занятия слотаmax_concurrency; декларации/детект циклов — leafsrc/task_deps.py(kill-switchORCH_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-deployDEGRADED→ durable per-repo freeze (repo_freeze,cleared_at IS NULL= активен) + Telegram; снятие — вручнуюPOST /serial-gate/unfreeze?repo=…. Leafsrc/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)
Среды
- prod —
orchestrator(8500), внешний URLhttps://openclaw.mva154.duckdns.org/orchestrator/ - staging —
orchestrator-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/Giteatests/— pytestdocs/— документация, ADR, work-items, operationsscripts/— утилиты (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_mode—bump(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-switchtracker_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-status→analysis → 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(readerread_frontmatter_value— BC; единый парс-примитивparse_frontmatter; writerrender/write_frontmatter; валидатор схемыvalidate_schema/REQUIRED_FIELDS— warning-only по умолчанию, hard-fail только под kill-switchfrontmatter_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 (регистр чувствителен — иначе гейт упадёт ложно).
Правила для агентов
- Перед любым действием прочесть этот файл и
docs/architecture/README.md. - Документация = golden source наравне с кодом. Изменил функционал → обнови доку В ТОМ ЖЕ PR. Архитектурное решение → заведи ADR (формат —
docs/_standards/PIPELINE_DOCS.md§4). Структура номерных доков и шаблоны —docs/_standards/PIPELINE_DOCS.md+docs/_templates/. ОбновиCHANGELOG.md. - Никогда не править артефакты других этапов.
- Никогда не комментировать ТЗ задним числом — если ТЗ не годится, возвращай в Анализ.
- Никогда не закрывать задачу самостоятельно — это делает CI / финальная стадия.
- Reviewer проверяет: обновлена ли документация. Нет → REQUEST_CHANGES. Это включает обзорные доки (ORCH-079, 52f — финал эпика 52): если PR закрывает пункт
README.md«Известные ограничения», но README не обновлён → finding ≥P1 (витрина проекта не должна выдавать решённое за открытое). - Не использовать
--no-verifyбез явного одобрения Owner. - Секреты — только в
.env/.env.stagingна хосте, в гит НЕ коммитятся (канон —.env.example). - Трассировка маркеров (ORCH-078, ORCH-52e): правишь строку/блок с маркером
ORCH-NNN→ ПЕРЕД изменением прочитай егоdocs/work-items/ORCH-NNN/06-adr/и не сломай зафиксированный инвариант; блок с 3+ маркерами → опирайся на сводный сквозной ADR. Стандарт маркеров (формат, размещение, fallback-доступ, анти-археология, каноничное правило чтения) —docs/_standards/TRACEABILITY.md.
⚠️ 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).