Files
orchestrator/docs/history/LESSONS_2026-06-08_confirm-deploy-deadtrigger.md

4.9 KiB
Raw Permalink Blame History

Lessons Learned — 2026-06-08: статус Confirm Deploy не триггерит Phase B (мёртвый триггер)

Контекст

ORCH-066 ввела новую статусную модель Plane, включая человекочитаемый статус Confirm Deploy для прод-деплойного approve-gate (self-deploy Phase B). Орк сам выставляет задачу в Awaiting Deploy / Confirm Deploy через set_issue_awaiting_deploy() и т.п.

Инцидент (2026-06-08, первый реальный прод-self-deploy — ORCH-068)

Слава нажал статус Confirm Deploy в Plane, ожидая запуск прод-деплоя. Орк ответил no pipeline action и НИЧЕГО не запустил. Прод-деплой стартовал только после ручного перевода в Approved.

Root cause

Диспетчер статусов handle_issue_status (src/webhooks/plane.py ~158-166) слушает РОВНО три состояния:

if   new_state == proj_states["to_analyse"]: await handle_status_start(...)
elif new_state == proj_states["approved"]:   await handle_verdict(..., approved=True)
elif new_state == proj_states["rejected"]:   await handle_verdict(..., approved=False)
else: logger.info("... no pipeline action")

Phase B (прод-деплой) триггерится в _try_advance_stage (src/stage_engine.py ~215-224) при current_stage == "deploy" and finished_agent is None — то есть ТОЛЬКО когда пришёл вебхук Approved. Статус Confirm Deploy в эту тройку НЕ входит → ветка else → no-op.

ORCH-066 добавила статус как МЕТКУ (запись), но не подключила обратный путь (чтение/триггер). Классическая дыра: протестировали, что орк правильно СТАВИТ статус, но не протестировали, что нажатие этого статуса человеком РЕАЛЬНО запускает действие.

Почему не поймали тестирование/ревью

  1. Не в scope ORCH-068. ORCH-068 чинит reconciler (BRD §6 N1-N3 явно: не трогать диспетчер статусов / Phase B). Тестер прогнал TC-01..13 — все про reconciler/terminal-статусы. Ревьюер смотрел diff reconciler.py/plane_sync.py. Корректно — это дефект ORCH-066, не 068.
  2. Дыра ORCH-066. Её тесты, видимо, проверяли запись статусов, а не обратный триггер.
  3. Staging не покрывает прод-путь. Phase A (staging-деплой) автоматический, ручной Confirm Deploy живёт ТОЛЬКО на прод-пути, который на staging не гоняется. Поэтому всплыло лишь на первом реальном прод-деплое.

Уроки

  1. Тестировать обратный путь статусов, не только запись. Для каждого статуса, который человек может нажать, нужен тест «нажатие → ожидаемое pipeline-действие». Запись (орк ставит статус) и чтение (орк реагирует на статус) — два разных контракта.
  2. Прод-only пути (ручной Confirm Deploy) нуждаются в явном тесте/чеклисте. Staging их не ловит by design. Любой approve-gate, доступный человеку, обязан иметь регресс-тест на триггер.
  3. Новый статус = подключить В ОБЕ стороны. При добавлении статуса в модель — сразу проверить, что диспетчер handle_issue_status его слушает (если он actionable), а не только что орк его выставляет.
  4. UX-консистентность: статус, названный действием («Confirm Deploy»), обязан выполнять это действие. Иначе оператор жмёт интуитивную кнопку, а система молчит → потеря доверия к автономности.

Фикс

Заведена ORCH-070: подключить Confirm Deploy (или его actionable-эквивалент) к триггеру Phase B в handle_issue_status, + регресс-тест на обратный путь статусов прод-деплоя. Source-of-truth и существующий Approved-путь не ломать (обратная совместимость).