# DEV TASK: закрыть вторую дверь баг-8 — gitea PR-merged обходит deploy-QG ## Контекст / проблема БАГ 8 (deploy без QG) был починен в `stage_engine.advance_stage` через `check_deploy_status` (читает `deploy_status:` из frontmatter `14-deploy-log.md`). Но есть **вторая дверь**: обработчик Gitea-вебхука `pull_request` при `action=closed & merged=true` **слепо** переводит задачу в `done` для ЛЮБОЙ стадии, минуя QG. Боевой инцидент (ET-012, task 30): - deployer мержит PR в начале работы → прилетает webhook `closed+merged` → `gitea.py` мгновенно ставит `done` (за 33 сек), ПОКА deployer ещё работает 6 минут. - deployer затем пишет `deploy_status: FAILED` — но поздно, задача уже `done`. - `check_deploy_status` **не вызывается вообще** → фейк-done вернулся через merge-вебхук. ## Файл `src/webhooks/gitea.py` — единственный файл с изменениями кода. ## Текущий код (конец обработчика `pull_request`, примерно стр. 336-339) ```python elif action == "closed" and pr.get("merged", False): update_task_stage(task_id, "done") notify_stage_change(task_id, current_stage, "done") logger.info(f"Task {task_id}: PR merged, stage → done") ``` ## Что нужно Merge PR **не должен** напрямую ставить `done`, когда `current_stage == "deploy"`. Для deploy финальный переход deploy→done обязан проходить через `check_deploy_status` (вердикт deployer'а), как и задумано баг-8 фиксом. Для всех ОСТАЛЬНЫХ стадий — поведение merge сохранить как есть (прямой `done` — это легитимный путь, когда фичу мержат не из конвейера). ### Требуемая логика ```python elif action == "closed" and pr.get("merged", False): # БАГ 8 (вторая дверь): для стадии deploy финальный done гейтится # вердиктом deployer'а (check_deploy_status), а НЕ фактом merge PR. # deployer мержит PR в начале работы — merge != успешный деплой. if current_stage == "deploy": logger.info( f"Task {task_id}: PR merged at deploy stage — done gated by " f"deployer verdict (check_deploy_status), not merge event. Ignoring merge-driven done." ) return update_task_stage(task_id, "done") notify_stage_change(task_id, current_stage, "done") logger.info(f"Task {task_id}: PR merged, stage → done") ``` **Обоснование игнора (а не вызова advance_stage):** на deploy стадии deployer-job уже запущен через очередь (`enqueue_job` на входе стадии). Когда он завершится, launcher вызовет `advance_stage`, который выполнит `check_deploy_status` и сам решит done/rollback. Дублировать advance_stage из merge-вебхука НЕ нужно — это создаст гонку. Merge-вебхук на deploy просто **молча игнорируется** (done решает только вердикт). ## ОГРАНИЧЕНИЯ (НЕ трогать) - НЕ менять ветки `action == "reviewed"` (APPROVED / REQUEST_CHANGES) — они работают. - НЕ менять HMAC, project-filter (`get_project_by_repo`), импорты кроме необходимого. - НЕ трогать `stage_engine.py`, `launcher.py`, `checks.py`, `stages.py`, `conftest.py`. - НЕ трогать nginx, openclaw.json, .env, queue, PLANE_STATES, gitea_public_url. - Сохранить блок launcher.py:475. ## Тесты - Добавить тест(ы) в существующий тест-файл вебхуков gitea (найти где тестируется `pull_request` merged — вероятно `tests/.../test_gitea_webhook*.py` или аналог). - Кейсы: 1. `closed+merged` при `current_stage="deploy"` → задача НЕ переходит в `done` (остаётся на deploy), `update_task_stage(...,"done")` НЕ вызван. 2. `closed+merged` при `current_stage` НЕ deploy (например `review` или иной валидный) → задача переходит в `done` (регрессия не сломана). - Запустить полный `pytest`. Baseline: **227 passed + 10 failed** (10 failed = off-limits: 9 HMAC/401 + 1 webhook-POST — их НЕ чинить, они и должны падать). Новые тесты должны дать **229+ passed, те же 10 failed**. ## Процесс сдачи - Ветка от актуального main orchestrator: `fix/gitea-merge-deploy-gate`. - Прямой push в main запрещён (pre-receive hook) → только через PR в Gitea. - Gitea токен: `docker exec orchestrator printenv ORCH_GITEA_TOKEN`. - Репо на проде: `/home/slin/repos/orchestrator` (slin@82.22.50.71, пароль в .env). - В отчёте: commit-хеш, номер PR, вывод pytest (passed/failed), diff gitea.py. - Отчёт положить в `tasks/orchestrator/reports/dev-2026-06-04-gitea-merge-gate.md`. ## Verify (после мержа PR) 1. Перезапустить контейнер orchestrator (подхватить новый код). 2. Прогнать любую задачу до deploy ИЛИ юнит-проверкой убедиться: merge-вебхук на deploy не ставит done; done ставится только после deployer verdict через check_deploy_status.