74 KiB
Архитектура 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. - Queue (
src/queue_worker.py, ORCH-1) — персистентная очередь задач (SQLitejobs), atomic claim, max_concurrency, ретраи, restart-safe. ORCH-026:claim_next_jobгейтит задачи с незавершёнными зависимостями (job_deps,NOT EXISTS) без занятия слота; декларации/циклы — leafsrc/task_deps.py. - Job-reaper (
src/job_reaper.py, ORCH-065 — adr-0011) — фоновый daemon-поток (каркасreconciler), стартует/останавливается вmain.lifespan(послеreconciler.start()/ передworker.stop()). Детектирует «мёртвый»running-job без рестарта процесса (Tier-1 мёртвыйjobs.pidпослеreaper_dead_ticksтиков; Tier-2agent_runs.exit_codeзаписан, а job ещёrunning; Tier-3 backstopreaper_max_running_s) и приводит строку к корректному статусу через те же контракты (_try_advance_stage/_finalize_job, gate-driven; exit≠0/неизвестно →attempts<max→queued, иначеfailed+Telegram). Атомарный reap-claim (guardstatus='running') совместим со стартовымrequeue_running_jobs. Тот же поток периодически делает проактивный реклейм stale/dead merge-lease (см. ниже). never-raise; kill-switchORCH_REAPER_ENABLED; снимок вGET /queue(блокreaper). - Reconciler (
src/reconciler.py, ORCH-053 — реализовано, adr-0007) — фоновый daemon-поток (паттернqueue_worker), стартует/останавливается вmain.lifespan(послеworker.start()/ передworker.stop()). Реконсилирует рассинхрон «источник истины ≠ стадия задачи» при потерянном webhook. F-1 gate-side (продвигает застрявшую стадию по локальной БД через штатныйadvance_stage(..., finished_agent=None)), F-2 plane-side (опрос Plane API →handle_*изplane.py), F-3 (БД-fallbacksha→branchвhandle_ci_status). Источник истины — гейт/Plane, не событие; идемпотентность (active-job guard + atomic-claim + grace); kill-switchORCH_RECONCILE_ENABLED.analysisF-1 не трогает (человеческий гейт). F-1 также пропускает escalated (retry≥лимита) и Blocked/Needs-Input задачи (ORCH-060). Наблюдаемость — блокreconcileвGET /queue. - Notifications / Live-tracker (
src/notifications.py, ORCH-042/ORCH-067) — ОДНА live-карточка на задачу (update_task_tracker), обновляется на каждом переходе. РежимORCH_TRACKER_MODE(дефолтbumpс ORCH-067: delete+silent send+repoint внизу чата;edit— правка на месте). Карточка несёт строку Plane-статуса📍 …(оффлайн-ядроplane_status_label+ best-effort live-overlay_live_plane_branch_override, kill-switchORCH_TRACKER_LIVE_STATUS) и кликабельный номер задачи (plane_issue_link/link_for→ ссылка в Plane, fail-safe на сырой номер). Все алерты, упоминающиеwork_item_id, делают номер кликабельным. Контракт всего компонента — never raises; карточка всегда silent. Детали — internals.md §7. - Project Registry (
src/projects.py, ORCH-6) — Plane project id → repo + prefix; фильтрация вебхуков по проекту. - Plane Sync (
src/plane_sync.py) — синхронизация статусов/комментариев в Plane. Резолв статусов проектаget_project_states(ORCH-10) кэширует{logical_key→uuid}per-project; ORCH-068 добавляет в кэш-запись{uuid→group}(для терминал-исключения F-2) и TTLORCH_PLANE_STATES_TTL_S(дефолт 300с;0→ прежний lifetime-кэш) — устаревший набор статусов самозалечивается без рестарта процесса через существующийreload_project_states()(баг кэша после появления нового Plane-статуса). Форма возвратаget_project_statesнеизменна (обратная совместимость).
Конвейер и 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, check_branch_mergeable (ORCH-043), check_staging_image_fresh (ORCH-058), check_security_gate (ORCH-022).
Канон гейтов: машинные вердикты читаются ТОЛЬКО из 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.
Толерантность staging-вердикта к инфра-FAIL (ORCH-061 — design)
Self-hosting зацикливался на deploy-staging: scripts/staging_check.py давал ложный FAILED на C9a/C9b (ветка в sandbox / analyst-job в очереди), вызванный отсутствием sandbox-настроек (bot-аккаунты не члены SANDBOX-проекта), а не регрессом кода → откат deploy-staging → development → петля. ORCH-061 классифицирует проверки suite на REAL (pipeline) и SANDBOX_INFRA (узкий allowlist {C9a, C9b}) и делает вердикт толерантным к инфра-FAIL, сохраняя fail-closed для реальных проверок:
- Чистая логика — leaf-модуль
src/staging_verdict.py(classify_check,compute_staging_verdict, never-raise). Упала хоть одна REAL → FAILED/exit1; упали ТОЛЬКО SANDBOX_INFRA и толерантность вкл → SUCCESS/exit0 (waived); waiver применяется только когда все REAL (вкл. C7/C8) зелёные. scripts/staging_check.pyпомечает проверки категориями, считает вердикт черезstaging_verdict, печатаетINFRA-WAIVED(наблюдаемость).- Kill-switch
staging_infra_tolerance_enabled(envORCH_STAGING_INFRA_TOLERANCE_ENABLED, дефолтtrue, в.env.staging);false→ 1:1 прежнее строгое поведение. check_staging_status/_parse_staging_status/STAGE_TRANSITIONS/ реестрQG_CHECKS— без изменений (новый QG-чек не вводится); условность ORCH-35 и схема БД сохранены.- Инвариант: «no changes to commit» на action-стадиях (
deploy-staging/deploy) не есть недовыполнение — продвижение определяется exit0 + гейт-вердиктом (launcher не откатывает; добавлена observability-строка).
Подробнее: adr-0009, детально — docs/work-items/ORCH-061/06-adr/ADR-001-staging-infra-tolerance.md.
Merge-gate: догон main + re-test + сериализация слияний (ORCH-043)
Детерминированный под-гейт (check_branch_mergeable, без LLM) на ребре deploy-staging → deploy: исполняется ПОСЛЕ check_staging_status и ДО запуска deployer'а, который вливает PR в main (deployer мержит в начале стадии deploy). Стадии (STAGE_TRANSITIONS) НЕ меняются — это «под-гейт» ребра, а не отдельная стадия (триггер — то же событие «staging-deployer завершился»).
Назначение: ветка валидируется относительно того main, из которого создана; параллельная задача могла уйти вперёд → семантический конфликт слияния (зелёная ветка ломает обновлённый main). Merge-gate гарантирует проверку против актуального origin/main перед слиянием:
- Догон: ветка отстаёт (⇔
origin/mainне предок HEAD) →rebase origin/mainв worktree +push --force-with-lease(ТОЛЬКО ветка задачи;main— никогда). Текстовый конфликт →rebase --abort→ откат наdevelopment. - Безусловный pre-merge rebase (ORCH-026, A-2): при
premerge_rebase_always(дефолтTrue, скоупmerge_gate_repos) short-circuitbranch_is_behind_mainпропускается —auto_rebase_onto_mainвызывается всегда под лизом. На актуальной ветке это no-op (rebaseне меняет HEAD,push --force-with-lease→ «Everything up-to-date», CI не триггерится); на отстающей — реальный догон. Детерминированный структурный анти-фантом на уровне планировщика (дополняет рубежи ORCH-073, не заменяет). Kill-switchpremerge_rebase_always=False→ прежнее поведение (ребейз только при behind). - Re-test:
python -m pytest(merge_retest_target, дефолтtests/) в worktree догнанной ветки, тайм-аутmerge_retest_timeout_s. Красный/тайм-аут → откат наdevelopment. - Сериализация (merge-lock): файловый merge-lease на репо (
<repos_dir>/.merge-lease-<repo>.json), живёт от гейта до фактического merge. Acquire неблокирующий (anti-deadlock приmax_concurrency=1): busy → defer (повторная постановка deployer'а наdeploy-stagingс задержкой черезavailable_at), а не откат. Release — на PR-merged вебхуке /deploy→done/ откате / по возрасту (crash-реклейм). Restart-safe; без изменения схемы БД. ORCH-026 (A-1): это окно = «merge → main-updated» (для selfdone⇔ SHA-in-main, ORCH-073) — пока A не вmain, B того же репо получаетmerge-lock busy→ defer. Окно сериализации per-repo НЕ переписывается; кросс-репо параллелизм сохранён (лиз — per-repo файл). - Условность (как ORCH-35): реален для
orchestrator; прочие репо — no-op. Флагиmerge_gate_enabled/merge_gate_repos— поэтапный раскат. Контракт never-raise.
Подробнее: adr-0006, детально — docs/work-items/ORCH-043/06-adr/ADR-001-merge-gate.md.
Безусловный pre-merge rebase + связь с зависимостями задач — adr-0015 (ORCH-026).
Зависимости задач: B ждёт A (ORCH-026, Уровень B)
Плоская очередь ORCH-1 (FIFO по id + available_at + max_concurrency) не выражала логических зависимостей. ORCH-026 вводит декларативные связи «задача B не стартует, пока не готовы её depends-on» — без новой стадии и без изменения STAGE_TRANSITIONS/QG_CHECKS.
- Источник истины планировщика — БД (аддитивная таблица
job_deps(task_id, depends_on_task_id)): claim в горячем цикле обслуживает очередь ВСЕХ проектов и обязан быть offline-устойчив (сетевой Plane на каждый claim = встанет очередь всех проектов). Источник декларации настраиваетсяtask_deps_source = db|plane|hybrid(дефолтdb;plane/hybridчитают Plane relations вhandle_work_item_createdи кэшируют вjob_deps). - Гейт планировщика (
claim_next_job) — условиеNOT EXISTS (job_deps d JOIN tasks t … WHERE d.task_id=j.task_id AND t.stage!='done')приtask_deps_enabled: задача с незавершённой зависимостью не выбирается (агент не запускается, слотmax_concurrencyне занимается). Инертно при пустойjob_deps→ нулевая регрессия; kill-switchtask_deps_enabled=False→ запрос 1:1 как ORCH-1. - Детект дедлоков — DFS-цикл-детектор (leaf
src/task_deps.py::detect_cycle) при вставке связи + backstop вreconciler; цикл →set_issue_blocked+ alert (Telegram/Plane) с перечислением цикла. Поток остальных задач не блокируется. - Видимость — строка «⏳ ждёт ORCH-NNN» в Telegram-карточке (
update_task_tracker, never-raise); PlaneBlocked— на дедлоке (не на нормальном коротком ожидании, чтобы не флаппить). Инвариант «одна карточка на задачу» сохранён. - Совместимость:
reconcilerF-1 пропускает dep-заблокированные задачи (is_task_ready, паттерн ORCH-060);reaperсканирует толькоrunning→ dep-блок остаётсяqueued, не трогается. Зависимости — только intra-repo (v1). - Наблюдаемость: блок
task_depsвGET /queue(заблокированные задачи, держатель merge-lease, defer-счётчики, обнаруженные циклы) — read-only.
Подробнее: adr-0015, детально — docs/work-items/ORCH-026/06-adr/ADR-001-merge-serialization-and-task-deps.md.
Исполняемый самодеплой стадии deploy (ORCH-36)
deploy перестаёт быть «бумажной»: для self-hosting (is_self_hosting_repo) стадия
РЕАЛЬНО деплоит прод (8500) через хост-хук scripts/orchestrator-deploy-hook.sh,
а deploy_status: SUCCESS означает доказанный health-ok, не декларацию LLM. Три фазы
(детерминированно, без LLM в критическом пути self-restart):
- Фаза A (вход в
deploy) — приdeploy_require_manual_approve=trueвместо запуска прод-deployer выставляется approval-pending статус Plane + запрос перевести задачу в статус «Confirm Deploy» (ORCH-059; Plane-коммент + Telegram). Перехват вadvance_stageПОСЛЕcheck_staging_statusи merge-gate. - Фаза B (Plane →
Confirm Deploy, ORCH-059) —advance_stage(deploy, finished_agent=None, confirm_deploy=True)запускает detached host-процесс (ssh + setsid → хук с прод-параметрами + build-once retagSOURCE_IMAGE) и ставит детерминированный finalizer-job; маркерinitiated— идемпотентность. Возврат БЕЗ advance (вердикта ещё нет). ОбычныйApprovedнаdeploy(confirm_deploy=False) — детерминированный no-op (не деплоит и не откатывает). - Фаза C (finalizer) — новый контейнер после рестарта читает sentinel
result(exit-code хука), маппит0→SUCCESS / иначе→FAILED, пишет14-deploy-log.md, вызываетadvance_stage(deploy, finished_agent="deployer")→ существующие контракты:SUCCESS → done,FAILED → откат БАГ-8 на development.
Триггер прод-деплоя = смена статуса Plane на Confirm Deploy (ORCH-059; status-only
verdict model; комментарии не управляют конвейером). Approved остаётся исключительно
человеческим гейтом конвейера и прод-деплой не запускает. На старте — обязательный
ручной approve (флаг true); полный авто — отдельная задача (ORCH-54). Условность как ORCH-35: реально для orchestrator,
прочие репо — прежний синхронный ssh-деплой агентом. Контракты не меняются:
STAGE_TRANSITIONS, реестр QG, check_deploy_status/_parse_deploy_status, БАГ-8,
terminal-sync, merge-gate, exit-code-контракт хука. Restart-safe состояние —
sentinel-файлы (<repos_dir>/.deploy-state-<repo>/<wi>/), без миграции БД.
Подробнее: adr-0007, детально —
docs/work-items/ORCH-036/06-adr/ADR-001-executable-self-deploy.md.
Выделенный статус-триггер прод-деплоя «Confirm Deploy» (ORCH-059 — реализовано)
Перегрузка: один Plane-статус Approved служил И человеческим гейтом BRD на
analysis (check_analysis_approved), И триггером Фазы B прод-деплоя на deploy
— привычный жест approve молча запускал прод-рестарт (групповой self-hosting
риск). ORCH-059 разделяет жесты: вводится отдельный логический статус
confirm_deploy («Confirm Deploy»), который триггерит ТОЛЬКО Фазу B на
deploy; Approved остаётся исключительно гейтом конвейера.
_PLANE_NAME_TO_KEY+="Confirm Deploy" → "confirm_deploy"; в_DEFAULT_STATESключ НЕ добавляется (нет UUID для enduro/fallback) → fail-closed: нет статуса → нет деплоя, безKeyError(доступ через.get).handle_issue_updatedмаршрутизируетConfirm Deploy→handle_confirm_deploy(гардstage=="deploy") →_try_advance_stage(..., confirm_deploy=True).advance_stageполучает kwargconfirm_deploy: bool=False; блок Фазы B (deploy+finished_agent is None+self-hosting) деплоит ТОЛЬКО приconfirm_deploy=True, иначе (обычныйApproved) — no-op (check_deploy_statusне запускается → нет ложного отката БАГ-8).- CTA Фазы A (
_handle_self_deploy_phase_a) просит «Confirm Deploy», не «Approved». - Условность как ORCH-35/36 (только
orchestrator); Фазы A/C,STAGE_TRANSITIONS,QG_CHECKS,check_deploy_status, merge-gate, схема БД — без изменений. - Эксплуатация: в Plane-проекте ORCH создать статус «Confirm Deploy» + сброс кэша
состояний (
docs/work-items/ORCH-059/07-infra-requirements.md).
Детально — docs/work-items/ORCH-059/06-adr/ADR-001-confirm-deploy-status.md
(уточняет/триггер Фазы B относительно adr-0007).
Merge-в-main + пост-деплой верификация как условие done (ORCH-071 — фикс фантомного merge)
Фантомный merge (CRITICAL, постмортем docs/history/LESSONS_2026-06-08_phantom-merge.md):
на self-hosting пути deploy агент deployer НЕ запускается, а фактический merge PR в main
исторически делал ТОЛЬКО он → детерминированный путь
(_handle_self_deploy_phase_b → initiate_deploy → run_deploy_finalizer) не содержал шага
merge-в-main вообще. Detached host-деплой лишь retag'ал образ + рестартил 8500; done
достигался по deploy_status: SUCCESS без верификации main. Зелёный деплой (образ из рабочей
ветки) маскировал отсутствие merge → следующая задача срезала ветку от устаревшего main и
теряла код предшественника (накопительно потеряны ORCH-022/059/066/068). ORCH-071 вводит
детерминированный merge-актор + пост-merge верификацию как под-гейт ребра deploy → done
(симметрично edge-под-гейтам deploy-staging → deploy), только для self-hosting:
- Врезка
_handle_merge_verifyвadvance_stage(current_stage=="deploy"иnext_stage=="done", ПОСЛЕ зелёногоcheck_deploy_status, ДОupdate_task_stage). Гейтит ВСЕ пути кdoneединообразно (run_deploy_finalizerPhase C, reconciler F-1, job-reaper — все идут черезadvance_stage), закрывая дыру обхода merge. - Merge в Phase C (после рестарта), НЕ в Phase B — finalizer restart-surviving (claim воркером нового контейнера, re-drive reaper'ом), merge физически строго ПОСЛЕ рестарта прода → рестарт его не убивает (G3 «шаг, переживающий рестарт»; постмортем-урок №3).
- Merge-актор
merge_gate.merge_pr—pr_already_merged(idempotency no-op повтор) → иначе GiteaPOST /repos/{owner}/{repo}/pulls/{index}/merge. Выбор PR строго поhead.ref==branchИbase.ref=="main". Никогда push/force-push вmain. - Верификатор
merge_gate.verify_merged_to_main(семантика ORCH-073, FR-1): подтверждение — ТОЛЬКОgit merge-base --is-ancestor <validated_sha> origin/main(validated_revision— якорь ORCH-058). PR-флагpr_already_mergedбольше НЕ подтверждает merge (удалён из verify): он понижен до idempotency-guardmerge_prи засчитывает merged PR лишь приhead.ref==branchИbase.ref=="main"(исключает авто docs-PR). Пустой SHA / git-ошибка →False(fail-closed), never-raise. - Регресс-гард целостности
main(ORCH-073, FR-5):merge_gate.check_main_regressionв_handle_merge_verifyПОСЛЕ подтверждённого SHA-в-main и ДОdoneпроверяет, чтоorigin/mainсодержит декларативный набор маркеров ранее-merged задач (MAIN_REGRESSION_MARKERS,git grep -c <marker> origin/main -- <path>> 0). Маркер отсутствует → alert «main regressed» + HOLD (НЕdone, ALERT-only). Fail-open на git-ошибке грепа (регресс — только приcount==0). Kill-switchregression_guard_enabled; non-self → no-op. Набор — append-only константа, значимая задача дописывает свой маркер. - Не подтверждено → alert «deploy succeeded but not merged» (Telegram+Plane) + HOLD
(
set_issue_blocked, задача НЕdone, БЕЗ авто-отката наdevelopment— not-merged есть инфра-дефект, реакция ALERT-only как ORCH-021 self-hosting). Подтверждено → штатныйdeploy → done+merged_to_main: trueво frontmatter14-deploy-log.md(deploy_status:нетронут). - Защита от CHANGELOG-затирания (ORCH-073, FR-4): корневой
.gitattributesсCHANGELOG.md merge=union→ правки## [Unreleased]авто-сливаются приauto_rebase_onto_mainбез конфликта, ветка не откатывается вdevelopmentи не тащит устаревший код-сосед.docs/**под union НЕ ставится (union только для append-only). - Условность как ORCH-35/43/58:
merge_verify_enabled(kill-switch, дефолтtrue) +merge_verify_repos(пусто → только self-hosting); non-self — no-op, merge остаётся заdeployer. never-raise; идемпотентность по SHA-в-main (INV-4, не «любой merged PR»); ручной approve сохранён (Confirm Deploy). - Инварианты:
STAGE_TRANSITIONS,check_deploy_status/_parse_deploy_status, реестрQG_CHECKS(под-гейт — врезка вadvance_stage, НЕ новый зарегистрированный QG), схема БД, БАГ-8, terminal-sync, merge-gate, image-freshness, exit-коды хука — без изменений. Диагностика фантома — runbookdocs/operations/PHANTOM_MERGE_RUNBOOK.md(4 проверки постмортема).
Подробнее: adr-0013 +
adr-0014 (amends 0013 — SHA-в-main как
единственный критерий + регресс-гард, ORCH-073); детально —
docs/work-items/ORCH-071/06-adr/ADR-001-merge-verify-gate.md,
docs/work-items/ORCH-073/06-adr/ADR-001-merge-verify-sha-truth-and-regression-guard.md.
Post-deploy наблюдение прода + реакция на деградацию (ORCH-021 — реализовано)
Конвейер заканчивался на deploy → done и забывал про прод: «успех» = health-check
в момент рестарта (~60с). Класс «зелёный деплой, красный прод» (прецедент ET-8 —
деградация через минуты под трафиком, health 200 ok, фича сломана). ORCH-021 продлевает
ответственность ЗА done: для применимого репо после терминального перехода армится
наблюдение окна post_deploy_window_s (~15 мин) с интервалом post_deploy_interval_s;
деградация фиксируется по детерминированным порогам, при подтверждении — реакция.
Механизм — reserved-agent job post-deploy-monitor (калька deploy-finalizer, НЕ
стадия и НЕ daemon): арм в advance_stage в блоке next_stage == "done"
(post_deploy.arm_monitor, sentinel armed = идемпотентность); тик перехватывается в
launcher.launch_job ДО _spawn → stage_engine.run_post_deploy_monitor (один опрос →
append в series → классификация → перепостановка с задержкой ИЛИ реакция+артефакт+done).
Чистая логика — новый leaf-модуль src/post_deploy.py (never-raise): post_deploy_applies,
probe_signals (/health 200+{"status":"ok"} + доля 5xx на /status,/queue),
classify (HEALTHY|DEGRADED — главный предмет юнит-тестов), decide_action,
sentinel-state, write_post_deploy_log.
- Пороги (BR-3):
DEGRADED⇔≥ post_deploy_fail_thresholdПОСЛЕДОВАТЕЛЬНЫХ провалов health ИЛИ доля 5xx> post_deploy_5xx_threshold; одиночный глюк → HEALTHY (нет ложных откатов). - Реакция: self-hosting (
orchestrator) — ВСЕГДАALERT_ONLY(Telegram+Plane, ручной approve; тик НИКОГДА не откатывает/рестартит прод-контейнер); не-self +post_deploy_auto_rollback=true→ хук--rollback(0→ROLLBACK_OK,1/2→ROLLBACK_FAILED+алерт); дефолт →ALERT_ONLY. - Артефакт
16-post-deploy-log.md(YAML-frontmatterpost_deploy_status/action_taken/…) — машиночитаемо для петли уроков ORCH-8; best-effort. - Наблюдаемость — блок
post_deployвGET /queue(образецreconcile). - Инварианты:
STAGE_TRANSITIONS,QG_CHECKS,check_deploy_status, terminal-sync, merge-gate, exit-коды хука (0/1/2), схема БД — НЕ меняются. Restart-safe (sentinel.post-deploy-state-<repo>/<wi>/+ jobs-очередь). Kill-switchpost_deploy_monitor_enabled, областьpost_deploy_repos(пусто → self-hosting). Условность как ORCH-35/36/43/58.
Подробнее: adr-0010, детально —
docs/work-items/ORCH-021/06-adr/ADR-001-post-deploy-monitor.md.
Свежесть артефакта BUILD-ONCE: провенанс staging-образа (ORCH-058 — реализовано)
BUILD-ONCE retag (ORCH-36) промоутит SOURCE_IMAGE=orchestrator-orchestrator-staging в прод
без rebuild, полагаясь на «staging-образ свеж и провалидирован». Этой гарантии нет:
конвейер нигде не пересобирает staging-образ из провалидированного коммита → retag мог тихо
промоутнуть УСТАРЕВШИЙ образ (инцидент LESSONS_ORCH-036 п.4 — зелёный деплой молча
откатывал прод). ORCH-058 обеспечивает инвариант INV-FRESH двумя слоями (defense in
depth), только для self-hosting:
- A — пересборка (liveness): детерминированный QG-под-чек
check_staging_image_freshна ребреdeploy-staging → deployПОСЛЕ merge-gate и ДО Phase A пересобираетorchestrator-orchestrator-stagingиз worktree валидированного коммита (--build-arg GIT_SHA=<sha>, OCI-лейблorg.opencontainers.image.revision), пересоздаёт 8501 и прогоняетstaging_checkпротив свежего образа → валидируем и промоутим один артефакт. FAIL → откат наdevelopment(как merge-gate). Сборки/recreate — ТОЛЬКО staging. - B — fail-closed guard (safety): хук шагом 2b ПЕРЕД
docker tagсверяет лейблrevisionуSOURCE_IMAGEсEXPECTED_REVISION(пробрасываетbuild_deploy_command). Несовпадение / пустой лейбл / пустой ожидаемый SHA / ошибка inspect →exit 1→ FAILED (БАГ-8 откат), прод не трогается. Делает тихий промоут устаревшего образа структурно невозможным даже при отключённой/проигравшей гонку A.
Якорь «провалидированного коммита» — git rev-parse HEAD worktree ПОСЛЕ merge-gate (один
helper validated_revision питает и штамп A, и EXPECTED_REVISION B). Единый kill-switch
image_freshness_enabled включает A+B как целое (нет «B без A» = вечного fail-fast);
image_freshness_repos (пусто → self-hosting). STAGE_TRANSITIONS, exit-code хука (0/1/2),
check_deploy_status, БАГ-8, merge-gate, схема БД — НЕ меняются (под-гейт ребра + лейбл
образа, без миграций). Подробнее: adr-0008,
детально — docs/work-items/ORCH-058/06-adr/ADR-001-staging-image-provenance.md.
Security-гейт: secret-scanning + dependency audit перед мержем (ORCH-022 — реализовано)
Автономный конвейер вливал ветку в main без проверки на утёкший секрет (ключ/токен/пароль/
приватный ключ) и уязвимую зависимость (CVE); для self-hosting один секрет/CVE через одну
задачу уезжал в общий прод всех проектов (CLAUDE.md §8). ORCH-022 вводит детерминированный
(без LLM) security-гейт как под-гейт ребра deploy-staging → deploy, рядом с merge-gate
(ORCH-043) и image-freshness (ORCH-058), исполняемый ПЕРВЫМ среди edge-под-гейтов
(ДО merge-gate). Паттерн соседей: leaf src/security_gate.py (never-raise) + тонкая обёртка
check_security_gate в QG_CHECKS + врезка _handle_security_gate в advance_stage.
STAGE_TRANSITIONS и схема БД — без изменений.
- Secret-scanning (
gitleaks, offline): сканorigin/main..HEAD; любой секрет вне аллоулиста.gitleaks.toml→ вклад в FAIL. Offline → гарантия «секрет всегда блокирует» не зависит от сети (безусловна). - Dependency audit (
pip-audit, OSV/PyPI): severity ≥security_dep_block_severity(дефолтHIGH) → FAIL; ниже / UNKNOWN → warning. Недоступность фида → fail-open + громкий warning (анти-петля ORCH-061; флагsecurity_dep_audit_fail_closedдля строгого режима). best-effort при доступности фида. - ПЕРВЫМ, ДО merge-gate: дёшево фейлить до дорогих rebase/rebuild; скан ветки ДО rebase
не «обвиняет» задачу в CVE из обновившегося
main; до захвата merge-lease → при FAIL lease освобождать не нужно. - Артефакт
17-security-report.md(YAML-frontmattersecurity_status/secrets_found/deps_blocking/deps_warning/deps_audit_degraded); вердикт читается ТОЛЬКО из frontmatter (гейт пишет → читает обратно черезparse_security_status→ возвращает: единый источник истины), negative-токен авторитетен, битый/нет → fail-closed. - FAIL → откат на
development+ developer-retry (общий_developer_retry_count, cap 3, затемset_issue_blocked+ Telegram);task_descнесёт дословные находки (ORCH-046). - Условность как ORCH-35/43/58:
security_gate_enabled+security_gate_repos(пусто → только self-hosting); never-raise; таймаутsecurity_scan_timeout_s; гейт не деплоит/не рестартит прод. v1 — Python-only; SAST/мульти-стек — follow-up (BR-14).
Подробнее: adr-0012, детально —
docs/work-items/ORCH-022/06-adr/ADR-001-security-gate.md.
Reconciler: реконсиляция потерянных webhook (ORCH-053 — реализовано)
Конвейер продвигается только входящими webhook; потерянное событие (502 на ребилде,
нет ретраев у Plane/Gitea, неразрезолвленный sha→branch) → задача застревает молча
(инцидент ORCH-044). Фоновый поток reconciler периодически (reconcile_interval_s)
находит застрявшие задачи и доигрывает пропущенный переход через те же штатные
гейты/обработчики, что и webhook:
- F-1 gate-side: для задач со
stage∉{done}, без активного job иage(updated_at) ≥ grace_for_stage(stage)— read-only пред-оценка канонического QG; зелёный →stage_engine.advance_stage(..., finished_agent=None); красный → тишина (спам нотификаций структурно невозможен).analysisне реконсилируется. Skip escalated / Blocked / Needs-Input (ORCH-060): ДО оценки гейта F-1 пропускает (молча, без advance/нотификаций) задачи, которые ждут человека — (1) исчерпавшие лимит developer-ретраев (developer_retry_count(task_id) >= MAX_DEVELOPER_RETRIES, детерминированно, без сети — закрывает bounce-петлю ET-013) и (2) в явном Plane-статусе Blocked / Needs Input (Вариант A — запрос Plane API, без миграции БД; never-raise → консервативный skip). Гард retry-count проверяется первым (дёшево, локальный SQL). - F-2 plane-side: опрос Plane API per-project →
handle_status_start/handle_verdictизwebhooks/plane.py(логика не дублируется). ORCH-068 (livelock-fix): (1) задачи в терминальной группе Plane (state.group ∈ {completed, cancelled}, fallback — логические ключиdone/cancelled) исключаются из actionable-выборки per-issue — проектно-независимо, устойчиво к UUID-алиасингу после переименований статусов (ORCH-066); (2)_note_unblock(лог + Telegram +unblocked_total) вызывается ТОЛЬКО при подтверждённом state change (сравнение стадии задачи до/после_dispatch; no-op dispatch → тишина), плюс in-memory дедуп поissue_id→state. Восстанавливает инвариант silence-when-in-sync (AC-9/AC-10). Детали —docs/work-items/ORCH-068/06-adr/ADR-001-reconciler-terminal-exclusion-and-cache-ttl.md. - F-3: усиление
sha→branchвhandle_ci_status(БД-fallback по единственной development-задаче repo; неоднозначность → не резолвим). - F-4 observability: при разблокировке — лог-строка
reconciler: <wi> <stage> разблокирована (потерян webhook)+ Telegram (reconcile_notify_unblock); снимок состояния вGET /queue(блокreconcile). ORCH-068 добавляет в снимок счётчикиskipped_terminal_total(исключённые терминалы) иdeduped_total(подавленные повторные нотификации).
Реализация: src/reconciler.py (daemon-поток по образцу queue_worker), стартует в
main.lifespan после worker.start(), останавливается в finally перед
worker.stop().
Инварианты: источник истины — гейт/Plane, не событие; идемпотентность (active-job
guard + atomic-claim на создании под process-wide Lock + grace + max_concurrency=1);
never-raise на единицу работы; тишина при синхронности; restart-safe; kill-switch
ORCH_RECONCILE_ENABLED (+ ORCH_RECONCILE_PLANE_ENABLED гасит только F-2). Схема БД
и реестры (STAGE_TRANSITIONS/QG_CHECKS) не меняются. Подробнее:
adr-0007, детально — docs/work-items/ORCH-053/06-adr/ADR-001-stuck-task-reconciler.md.
Job-reaper + проактивный реклейм merge-lease (ORCH-065 — design)
Финализация статуса job (done/queued/failed) выполняется ТОЛЬКО в
launcher._monitor_agent → _finalize_job внутри живого процесса. Смерть
monitor-потока/процесса между proc.wait() и _finalize_job (краш, OOM,
self-restart во время deploy) оставляла строку jobs навсегда running; при
max_concurrency=1 одна зомби-строка блокирует claim всех job → встаёт конвейер
ВСЕХ проектов (инциденты 07.06: jobs 236/239/242/254). requeue_running_jobs()
спасал ТОЛЬКО на старте процесса. Симметрично залипал merge-lease (ORCH-043):
реклейм был лениво-по-TTL и только при чужом acquire, liveness держателя по pid
не проверялся. Это последняя ручная точка автономного self-deploy (блокер ORCH-54).
ORCH-065 вводит фоновый watchdog, чтобы смерть процесса/потока на любой стадии НЕ
оставляла навсегда захваченных ресурсов:
- Job-reaper (
src/job_reaper.py) — daemon-поток по образцуreconciler, работает без рестарта. Трёхуровневая liveness: Tier-1 мёртвыйjobs.pid(новая колонка) послеreaper_dead_ticksподряд тиков (анти-ложноположительность — живой долгий агент не реапится); Tier-2agent_runs.exit_codeзаписан, а job ещёrunning— но это окно неоднозначно (живой monitor пишет exit_code ПЕРВЫМ, затем git push/PR/Plane-комментарии), поэтому Tier-2 реапит только после finalization-gracereaper_finalize_grace_s(живой финализирующий monitor НЕ реапится); Tier-3 backstop по потолкуreaper_max_running_s(> max agent_timeout+grace). Действие переиспользует контракты по принципу claim-before-act: для exit0 канонический QG оценивается read-only ПЕРЕД атомарным claim, затем claimdoneПЕРВЫМ и только победитель claim делает_try_advance_stage(advance+enqueue) — проигравший claim (поздний monitor / стартовый requeue) не выполняет побочных эффектов (нет дубль-advance/-enqueue); источник истины — канонический QG, не факт «exit0»; гейт красный или exit≠0/ неизвестно →attempts<max→queued, иначеfailed+Telegram. Атомарный reap-claim (UPDATE ... WHERE id=? AND status='running') совместим со стартовымrequeue_running_jobs(restart-safe, без двойной обработки). - Проактивный реклейм stale/dead lease (функции в
merge_gate.py:pid_alive,reclaim_stale_lease) — на старте (рядом сrequeue_running_jobs) и периодически из тика reaper: освобождает lease, чей держатель мёртв (pid не жив) ИЛИ просрочен (TTLmerge_lock_timeout_s); живой держатель в пределах TTL — НЕ трогать (защита легитимного merge). holder-aware, never-raise, условность как ORCH-43 (merge_gate_repos/self-hosting). - Идемпотентная финализация merge — без новой merge-логики: re-drive через
reaper→
queued→переисполнение стадии / reconciler; дорогие шаги не повторяются (branch_is_behind_main==False); добавлен never-raise guardpr_already_merged(читает состояние PR) — уже слит = no-op. Консультируется самим merge-актором: фактический merge PR вmainделает агентdeployer(в начале стадииdeploy), поэтому wiring — в его промпте.openclaw/agents/deployer.md, который вызываетpr_already_mergedПЕРЕД любым (повторным) merge (AC-11). Чекcheck_branch_mergeableНЕ меняется (AC-13): он на ПЕРВОМ ребреdeploy-staging → deploy, а риск второго merge — на re-drive самой стадииdeploy. - Схема БД: единственное изменение —
jobs.pid INTEGERчерез идемпотентный_ensure_column(live-safe).STAGE_TRANSITIONS,QG_CHECKS,check_*, БАГ-8, exit-коды хука, файл-схема lease — без изменений. - Наблюдаемость: блок
reaperвGET /queue(enabled, interval, last_run_ts, reaped_total, last_reaped, lease_reclaimed_total); каждый reap/lease-reclaim →logger.warning; reap→failedи lease-reclaim → Telegram. - Kill-switch'и:
ORCH_REAPER_ENABLED,ORCH_REAPER_INTERVAL_S,ORCH_REAPER_DEAD_TICKS,ORCH_REAPER_MAX_RUNNING_S,ORCH_REAPER_FINALIZE_GRACE_S,ORCH_LEASE_RECLAIM_ENABLED;false→ строго прежнее поведение.
Подробнее: adr-0011, детально —
docs/work-items/ORCH-065/06-adr/ADR-001-job-reaper-and-lease-reclaim.md.
Осмысленная статусная модель Plane (ORCH-066 — реализовано)
Plane-доска была семантически перегружена: In Progress означал «человек запускает
конвейер», «идёт анализ», «идёт прод-деплой» и «возврат из Needs Input» одновременно.
ORCH-066 наводит порядок по утверждённой Owner модели, меняя только слой B
(Plane-индикация: src/plane_sync.py + точки простановки в src/stage_engine.py/
src/webhooks/plane.py/src/reconciler.py) и не трогая слой A (STAGE_TRANSITIONS,
инвариант). Статус — индикация, не управление (вердикты по-прежнему из YAML-frontmatter):
Backlog → Todo → [To Analyse] → Analysis → [In Review → Approved] → Architecture →
Development → Code-Review → Testing → Awaiting Deploy → [Confirm Deploy] → Deploying →
Monitoring after Deploy → Done
[...] = человеческий вход-триггер; остальное ставит орк.
- 6 новых логических ключей (
to_analyse/analysis/code_review/awaiting_deploy/deploying/monitoring) в_PLANE_NAME_TO_KEY(резолв по имени) +_DEFAULT_STATES.To AnalyseзаменяетIn Progressкак вход-триггер (старт + resume аналитика из Needs Input; fork «старт vs resume» поget_task_by_plane_id+has_active_job_for_task— сохранён). Стадии: analysis→Analysis, review→Code-Review(_STAGE_TO_STATE_KEY). - Self-deploy фазы: Phase A →
Awaiting Deploy(разгружаетIn Review), Phase B →Deploying, Phase C/terminal-sync (self) →Monitoring after Deploy(НЕDoneсразу); post-deploy monitor (ORCH-021): HEALTHY-окно →Done, DEGRADED →Blocked(тик по-прежнему НИКОГДА не рестартит прод — ALERT_ONLY). Не-self репо:deploy → Doneкак сейчас (terminal-sync разводится поpost_deploy.post_deploy_applies). - Fail-closed (project-relative alias-fallback): отсутствующий новый статус в проекте
деградирует на собственный базовый UUID того же проекта (
to_analyse/analysis→in_progress,code_review→review,awaiting_deploy→in_review,deploying→in_progress,monitoring→done) — индикация откатывается к текущей, конвейер не ломается, PATCH валиден даже при частичной конфигурации. Enduro (статусы не создаются) → строго прежнее поведение. Усиленный паттерн ORCH-059 AC-7. - Reconciler: F-2 триггер
in_progress→to_analyse; Guard 2 skip-set расширен активными ожиданиями (awaiting_deploy/deploying/monitoring) с вычитанием базовых рабочих статусов — на enduro (алиасы схлопнуты) нулевой регресс, на orchestrator skip реальных ожиданий (BR-13). - Инварианты:
STAGE_TRANSITIONS,QG_CHECKS,check_deploy_status, exit-коды хука, merge-gate,Confirm Deploy, механизмNeeds Input(analyst-only), схема БД — без изменений. Без нового kill-switch (раскат гейтится созданием Plane-статусов оператором). Инфра-предусловие —docs/work-items/ORCH-066/07-infra-requirements.md.
Подробнее: docs/work-items/ORCH-066/06-adr/ADR-001-plane-status-model.md.
Откаты
- Reviewer REQUEST_CHANGES → откат на
development+ retry (MAX_DEVELOPER_RETRIES = 3). - Tester
check_tests_passedFAIL → откат наdevelopment+ retry. - Deploy / deploy-staging FAILED → откат на
development. - Merge-gate FAIL (конфликт rebase / красный re-test, ORCH-043) → откат на
development+ retry;merge-lock busy→ defer (не откат, dev-retry не тратится). 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); колонкаpid(ORCH-065) — pid агентского процесса для liveness-детекции зомби job-reaper'омjob_deps— декларативные зависимости задач (ORCH-026, Уровень B):(task_id, depends_on_task_id), аддитивная; источник истины планировщика для гейта «B ждёт A»
Изоляция (git worktree, ORCH-2)
Каждая задача исполняется в отдельном git worktree, ветки не пересекаются. Репозитории проектов разделены под /repos/<project>.
API
| Method | Path | Описание |
|---|---|---|
| GET | /health |
health check |
| GET | /status |
активные задачи (stage != done) |
| GET | /queue |
очередь: counts + max_concurrency + resilience + reconcile (ORCH-053) + reaper (ORCH-065) + post_deploy (ORCH-021) + последние 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-07. Обновлять при изменении src/stages.py, src/qg/checks.py, src/main.py. Статусы доработок: ORCH-036 (исполняемый самодеплой deploy, adr-0007) — реализовано; ORCH-043 (merge-gate, adr-0006) — design, ветка feature/ORCH-043; ORCH-053 (reconciler, adr-0007, src/reconciler.py) — реализовано; ORCH-060 (F-1 skip escalated/Blocked/Needs-Input, docs/work-items/ORCH-060/06-adr/ADR-001) — реализовано в ветке feature/ORCH-060 (Guard 1 developer_retry_count>=MAX_DEVELOPER_RETRIES + Guard 2 plane_sync.fetch_issue_state Blocked/Needs-Input, флаг ORCH_RECONCILE_SKIP_BLOCKED_ENABLED); ORCH-058 (провенанс staging-образа: check_staging_image_fresh + staging_check свежего образа + хук-guard, adr-0008) — реализовано в ветке feature/ORCH-058 (обновлять также при изменении src/image_freshness.py, scripts/orchestrator-deploy-hook.sh, Dockerfile); ORCH-061 (толерантность staging-вердикта к инфра-FAIL C9a/C9b, adr-0009, docs/work-items/ORCH-061/06-adr/ADR-001) — реализовано в ветке feature/ORCH-061 (обновлять также при изменении src/staging_verdict.py, scripts/staging_check.py, флаг staging_infra_tolerance_enabled); ORCH-021 (post-deploy наблюдение прода + реакция на деградацию, adr-0010, docs/work-items/ORCH-021/06-adr/ADR-001) — реализовано в ветке feature/ORCH-021-post-deploy-rollback (reserved-agent job post-deploy-monitor: арм в src/stage_engine.py блок next_stage == "done", тик run_post_deploy_monitor + перехват в src/agents/launcher.py ДО _spawn; чистая логика src/post_deploy.py never-raise; флаги post_deploy_* в src/config.py; блок post_deploy в /queue; артефакт 16-post-deploy-log.md; self-hosting всегда ALERT_ONLY — тик не рестартит прод; обновлять также при изменении src/post_deploy.py / арм-блока / launcher-перехвата); ORCH-065 (job-reaper + проактивный реклейм merge-lease + идемпотентная финализация merge, adr-0011, docs/work-items/ORCH-065/06-adr/ADR-001) — реализовано в ветке feature/ORCH-065 (новый daemon-поток src/job_reaper.py + старт/стоп в src/main.py lifespan; колонка jobs.pid через _ensure_column + проставление в src/agents/launcher.py _spawn; функции реклейма lease pid_alive/reclaim_stale_lease + guard pr_already_merged в src/merge_gate.py (консультируется merge-актором — промпт .openclaw/agents/deployer.md); флаги reaper_*/lease_reclaim_* в src/config.py; блок reaper в /queue; обновлять также при изменении этих мест); ORCH-022 (security-гейт: secret-scanning gitleaks + dependency audit pip-audit как под-гейт ребра deploy-staging → deploy ПЕРВЫМ, adr-0012, docs/work-items/ORCH-022/06-adr/ADR-001) — реализовано в ветке feature/ORCH-022-security-secret-scanning (leaf src/security_gate.py never-raise + check_security_gate в src/qg/checks.py QG_CHECKS + врезка _handle_security_gate в src/stage_engine.py блок current_stage == "deploy-staging" ПЕРВОЙ; флаги security_* в src/config.py; gitleaks (pinned) в Dockerfile, pip-audit в requirements.txt, .gitleaks.toml в корне; артефакт 17-security-report.md; обновлять также при изменении этих мест).
Актуально на 2026-06-07. Обновлять при изменении src/stages.py, src/qg/checks.py, src/main.py. Статусы доработок: ORCH-036 (исполняемый самодеплой deploy, adr-0007) — реализовано; ORCH-043 (merge-gate, adr-0006) — design, ветка feature/ORCH-043; ORCH-053 (reconciler, adr-0007, src/reconciler.py) — реализовано; ORCH-060 (F-1 skip escalated/Blocked/Needs-Input, docs/work-items/ORCH-060/06-adr/ADR-001) — реализовано в ветке feature/ORCH-060 (Guard 1 developer_retry_count>=MAX_DEVELOPER_RETRIES + Guard 2 plane_sync.fetch_issue_state Blocked/Needs-Input, флаг ORCH_RECONCILE_SKIP_BLOCKED_ENABLED); ORCH-058 (провенанс staging-образа: check_staging_image_fresh + staging_check свежего образа + хук-guard, adr-0008) — реализовано в ветке feature/ORCH-058 (обновлять также при изменении src/image_freshness.py, scripts/orchestrator-deploy-hook.sh, Dockerfile); ORCH-061 (толерантность staging-вердикта к инфра-FAIL C9a/C9b, adr-0009, docs/work-items/ORCH-061/06-adr/ADR-001) — реализовано в ветке feature/ORCH-061 (обновлять также при изменении src/staging_verdict.py, scripts/staging_check.py, флаг staging_infra_tolerance_enabled); ORCH-021 (post-deploy наблюдение прода + реакция на деградацию, adr-0010, docs/work-items/ORCH-021/06-adr/ADR-001) — реализовано в ветке feature/ORCH-021-post-deploy-rollback (reserved-agent job post-deploy-monitor: арм в src/stage_engine.py блок next_stage == "done", тик run_post_deploy_monitor + перехват в src/agents/launcher.py ДО _spawn; чистая логика src/post_deploy.py never-raise; флаги post_deploy_* в src/config.py; блок post_deploy в /queue; артефакт 16-post-deploy-log.md; self-hosting всегда ALERT_ONLY — тик не рестартит прод; обновлять также при изменении src/post_deploy.py / арм-блока / launcher-перехвата); ORCH-065 (job-reaper + проактивный реклейм merge-lease + идемпотентная финализация merge, adr-0011, docs/work-items/ORCH-065/06-adr/ADR-001) — реализовано в ветке feature/ORCH-065 (новый daemon-поток src/job_reaper.py + старт/стоп в src/main.py lifespan; колонка jobs.pid через _ensure_column + проставление в src/agents/launcher.py _spawn; функции реклейма lease pid_alive/reclaim_stale_lease + guard pr_already_merged в src/merge_gate.py (консультируется merge-актором — промпт .openclaw/agents/deployer.md); флаги reaper_*/lease_reclaim_* в src/config.py; блок reaper в /queue; обновлять также при изменении этих мест); ORCH-059 (выделенный статус-триггер прод-деплоя «Confirm Deploy», ADR docs/work-items/ORCH-059/06-adr/ADR-001) — реализовано в ветке feature/ORCH-059 (маппинг "Confirm Deploy"→"confirm_deploy" в src/plane_sync.py _PLANE_NAME_TO_KEY, НЕ в _DEFAULT_STATES = fail-closed; ветка handle_confirm_deploy + fail-closed .get("confirm_deploy") в src/webhooks/plane.py handle_issue_updated; keyword-only confirm_deploy в src/stage_engine.py advance_stage — Фаза B деплоит ТОЛЬКО при confirm_deploy=True, иначе Approved-на-deploy = no-op; CTA Фазы A просит «Confirm Deploy»; эксплуатация — статус доски «Confirm Deploy» в Plane-проекте ORCH, docs/work-items/ORCH-059/07-infra-requirements.md).
Актуально на 2026-06-07. Обновлять при изменении src/stages.py, src/qg/checks.py, src/main.py. Статусы доработок: ORCH-036 (исполняемый самодеплой deploy, adr-0007) — реализовано; ORCH-043 (merge-gate, adr-0006) — design, ветка feature/ORCH-043; ORCH-053 (reconciler, adr-0007, src/reconciler.py) — реализовано; ORCH-060 (F-1 skip escalated/Blocked/Needs-Input, docs/work-items/ORCH-060/06-adr/ADR-001) — реализовано в ветке feature/ORCH-060 (Guard 1 developer_retry_count>=MAX_DEVELOPER_RETRIES + Guard 2 plane_sync.fetch_issue_state Blocked/Needs-Input, флаг ORCH_RECONCILE_SKIP_BLOCKED_ENABLED); ORCH-058 (провенанс staging-образа: check_staging_image_fresh + staging_check свежего образа + хук-guard, adr-0008) — реализовано в ветке feature/ORCH-058 (обновлять также при изменении src/image_freshness.py, scripts/orchestrator-deploy-hook.sh, Dockerfile); ORCH-061 (толерантность staging-вердикта к инфра-FAIL C9a/C9b, adr-0009, docs/work-items/ORCH-061/06-adr/ADR-001) — реализовано в ветке feature/ORCH-061 (обновлять также при изменении src/staging_verdict.py, scripts/staging_check.py, флаг staging_infra_tolerance_enabled); ORCH-021 (post-deploy наблюдение прода + реакция на деградацию, adr-0010, docs/work-items/ORCH-021/06-adr/ADR-001) — реализовано в ветке feature/ORCH-021-post-deploy-rollback (reserved-agent job post-deploy-monitor: арм в src/stage_engine.py блок next_stage == "done", тик run_post_deploy_monitor + перехват в src/agents/launcher.py ДО _spawn; чистая логика src/post_deploy.py never-raise; флаги post_deploy_* в src/config.py; блок post_deploy в /queue; артефакт 16-post-deploy-log.md; self-hosting всегда ALERT_ONLY — тик не рестартит прод; обновлять также при изменении src/post_deploy.py / арм-блока / launcher-перехвата); ORCH-065 (job-reaper + проактивный реклейм merge-lease + идемпотентная финализация merge, adr-0011, docs/work-items/ORCH-065/06-adr/ADR-001) — реализовано в ветке feature/ORCH-065 (новый daemon-поток src/job_reaper.py + старт/стоп в src/main.py lifespan; колонка jobs.pid через _ensure_column + проставление в src/agents/launcher.py _spawn; функции реклейма lease pid_alive/reclaim_stale_lease + guard pr_already_merged в src/merge_gate.py (консультируется merge-актором — промпт .openclaw/agents/deployer.md); флаги reaper_*/lease_reclaim_* в src/config.py; блок reaper в /queue; обновлять также при изменении этих мест); ORCH-066 (осмысленная статусная модель Plane — слой B, docs/work-items/ORCH-066/06-adr/ADR-001-plane-status-model.md) — реализовано в ветке feature/ORCH-066-plane (только Plane-индикация: новые ключи to_analyse/analysis/code_review/awaiting_deploy/deploying/monitoring в _PLANE_NAME_TO_KEY/_DEFAULT_STATES + project-relative _STATE_ALIAS_FALLBACK в get_project_states + _STAGE_TO_STATE_KEY analysis/review + 5 новых set_issue_* в src/plane_sync.py; триггер in_progress→to_analyse и set_issue_analysis в src/webhooks/plane.py; Phase A→Awaiting Deploy / Phase B→Deploying / terminal-sync split monitoring↔done / post-deploy monitor HEALTHY→Done DEGRADED→Blocked в src/stage_engine.py; F-2 триггер to_analyse + Guard 2 skip-set с вычитанием base_working в src/reconciler.py; STAGE_TRANSITIONS/QG/схема БД НЕ трогаются; без kill-switch — раскат гейтится созданием 6 Plane-статусов оператором, docs/work-items/ORCH-066/07-infra-requirements.md; обновлять при изменении этих мест).
Актуально на 2026-06-07. Обновлять при изменении src/stages.py, src/qg/checks.py, src/main.py. Статусы доработок: ORCH-036 (исполняемый самодеплой deploy, adr-0007) — реализовано; ORCH-043 (merge-gate, adr-0006) — design, ветка feature/ORCH-043; ORCH-053 (reconciler, adr-0007, src/reconciler.py) — реализовано; ORCH-060 (F-1 skip escalated/Blocked/Needs-Input, docs/work-items/ORCH-060/06-adr/ADR-001) — реализовано в ветке feature/ORCH-060 (Guard 1 developer_retry_count>=MAX_DEVELOPER_RETRIES + Guard 2 plane_sync.fetch_issue_state Blocked/Needs-Input, флаг ORCH_RECONCILE_SKIP_BLOCKED_ENABLED); ORCH-058 (провенанс staging-образа: check_staging_image_fresh + staging_check свежего образа + хук-guard, adr-0008) — реализовано в ветке feature/ORCH-058 (обновлять также при изменении src/image_freshness.py, scripts/orchestrator-deploy-hook.sh, Dockerfile); ORCH-061 (толерантность staging-вердикта к инфра-FAIL C9a/C9b, adr-0009, docs/work-items/ORCH-061/06-adr/ADR-001) — реализовано в ветке feature/ORCH-061 (обновлять также при изменении src/staging_verdict.py, scripts/staging_check.py, флаг staging_infra_tolerance_enabled); ORCH-021 (post-deploy наблюдение прода + реакция на деградацию, adr-0010, docs/work-items/ORCH-021/06-adr/ADR-001) — реализовано в ветке feature/ORCH-021-post-deploy-rollback (reserved-agent job post-deploy-monitor: арм в src/stage_engine.py блок next_stage == "done", тик run_post_deploy_monitor + перехват в src/agents/launcher.py ДО _spawn; чистая логика src/post_deploy.py never-raise; флаги post_deploy_* в src/config.py; блок post_deploy в /queue; артефакт 16-post-deploy-log.md; self-hosting всегда ALERT_ONLY — тик не рестартит прод; обновлять также при изменении src/post_deploy.py / арм-блока / launcher-перехвата); ORCH-065 (job-reaper + проактивный реклейм merge-lease + идемпотентная финализация merge, adr-0011, docs/work-items/ORCH-065/06-adr/ADR-001) — реализовано в ветке feature/ORCH-065 (новый daemon-поток src/job_reaper.py + старт/стоп в src/main.py lifespan; колонка jobs.pid через _ensure_column + проставление в src/agents/launcher.py _spawn; функции реклейма lease pid_alive/reclaim_stale_lease + guard pr_already_merged в src/merge_gate.py (консультируется merge-актором — промпт .openclaw/agents/deployer.md); флаги reaper_*/lease_reclaim_* в src/config.py; блок reaper в /queue; обновлять также при изменении этих мест); ORCH-068 (livelock-fix reconciler F-2: терминал-исключение по группе состояния + _note_unblock только при подтверждённом state change + дедуп; TTL _STATES_CACHE, docs/work-items/ORCH-068/06-adr/ADR-001) — реализовано в ветке feature/ORCH-068 (D1 терминал-гард по группе _is_terminal_state + get_project_state_groups в src/plane_sync.py; D2 сравнение стадии до/после _dispatch + дедуп-словарь в src/reconciler.py; TTL-запись _STATES_CACHE + флаг plane_states_ttl_s в src/config.py; счётчики skipped_terminal_total/deduped_total в /queue; обновлять также при изменении src/reconciler.py F-2, src/plane_sync.py get_project_states/get_project_state_groups/_STATES_CACHE).