64 lines
5.2 KiB
Markdown
64 lines
5.2 KiB
Markdown
# adr-0013: Merge-в-main + пост-деплой верификация как условие `done` (фикс фантомного merge)
|
||
|
||
- **Статус:** accepted
|
||
- **Дата:** 2026-06-08
|
||
- **Задача:** ORCH-071 (CRITICAL bug)
|
||
- **Детальный ADR:** `docs/work-items/ORCH-071/06-adr/ADR-001-merge-verify-gate.md`
|
||
- **Постмортем:** `docs/history/LESSONS_2026-06-08_phantom-merge.md`
|
||
|
||
## Контекст
|
||
Для self-hosting репо `orchestrator` стадия `deploy` идёт детерминированным путём
|
||
(`_handle_self_deploy_phase_b → initiate_deploy → run_deploy_finalizer`), а LLM-агент
|
||
`deployer` НЕ запускается. Фактический merge PR в `main` исторически делал **только**
|
||
агент `deployer` → на self-hosting пути **нет шага merge-в-main вообще**. Detached
|
||
host-деплой лишь retag'ает образ + рестартит 8500; `done` достигается по
|
||
`deploy_status: SUCCESS` без верификации `main`. «Зелёный» деплой (образ из рабочей
|
||
ветки) маскирует отсутствие merge → следующая задача срезает ветку от устаревшего `main`
|
||
и теряет код предшественника. Накопительно потеряны ORCH-022/059/066/068. Вторичный
|
||
фактор: Phase B рестартит прод → merge внутри живого процесса гонялся бы с рестартом
|
||
(урок №3).
|
||
|
||
## Решение
|
||
Детерминированный **merge-актор + пост-merge верификация** как **под-гейт ребра
|
||
`deploy → done`**, врезанный в единственную функцию перехода `advance_stage` (симметрично
|
||
edge-под-гейтам security/merge-gate/image-freshness). `STAGE_TRANSITIONS`,
|
||
`check_deploy_status`/`_parse_deploy_status`, реестр `QG_CHECKS`, схема БД — **не меняются**.
|
||
|
||
- **Врезка `_handle_merge_verify` в `advance_stage`** (`current_stage=="deploy"` и
|
||
`next_stage=="done"`, ПОСЛЕ зелёного `check_deploy_status`, ДО `update_task_stage`).
|
||
Гейтит **ВСЕ** пути к `done` единообразно: `run_deploy_finalizer` (Phase C), reconciler
|
||
F-1, job-reaper — все идут через `advance_stage`. Закрывает дыру: reconciler F-1 иначе
|
||
протолкнул бы `done` в обход merge.
|
||
- **Merge в Phase C (после рестарта), НЕ в Phase B.** Phase C finalizer —
|
||
restart-surviving (reserved-job `deploy-finalizer`, claim воркером нового контейнера,
|
||
re-drive reaper'ом). Merge физически строго ПОСЛЕ рестарта → рестарт его не убивает
|
||
(G3 вторым вариантом — «шаг, переживающий рестарт»).
|
||
- **Merge-актор `merge_gate.merge_pr`** — `pr_already_merged` (no-op повтор, ORCH-065) →
|
||
иначе Gitea `POST /repos/{owner}/{repo}/pulls/{index}/merge`. Никогда push/force-push в
|
||
`main`. never-raise.
|
||
- **Верификатор `merge_gate.verify_merged_to_main`** — `PR.merged==true` ИЛИ
|
||
`git merge-base --is-ancestor <validated_sha> origin/main`. never-raise → `False`
|
||
(«не подтверждено»).
|
||
- **Не подтверждено → alert «deploy succeeded but not merged» (Telegram+Plane) + HOLD**
|
||
(`set_issue_blocked`, задача НЕ `done`, БЕЗ авто-отката на `development` — not-merged
|
||
есть инфра-дефект, реакция ALERT-only как ORCH-021 self-hosting). Подтверждено →
|
||
штатный `deploy → done` (терминал-sync / post-deploy monitor как сегодня) +
|
||
`merged_to_main: true` во frontmatter `14-deploy-log.md` (наблюдаемость, `deploy_status:`
|
||
нетронут).
|
||
- **Идемпотентность (INV-5):** `pr_already_merged` перед merge; verify зелёный для
|
||
уже-слитого PR; повтор без дубль-merge/ложного отката.
|
||
- **Условность (как ORCH-35/43/58):** `merge_verify_enabled` (kill-switch, дефолт `true`) +
|
||
`merge_verify_repos` (пусто → только self-hosting). Non-self репо — no-op, merge остаётся
|
||
за агентом `deployer`.
|
||
|
||
## Инварианты
|
||
never-raise на verify/merge (ошибка → alert, не падение конвейера); не рестартить/не ронять
|
||
прод 8500; ручной approve прод-деплоя сохранён (`Confirm Deploy`, ORCH-059); только PR-merge
|
||
API Gitea; restart-safe (sentinel + jobs, без миграции БД).
|
||
|
||
## Последствия
|
||
Невозможно «`done` + прод задеплоен, а PR `open`». Минусы: при недоступной Gitea verify
|
||
консервативно `False` → возможен ложный HOLD+alert (снимается повтором; fail-closed для
|
||
`done` приоритетен); HOLD требует ручного вмешательства. Диагностика фантома — runbook
|
||
`docs/operations/PHANTOM_MERGE_RUNBOOK.md` (G4).
|