From 77c38ed8dd54ff21f1fe28b4dc379b3e2ceae16c Mon Sep 17 00:00:00 2001 From: Stream Date: Thu, 4 Jun 2026 02:50:01 +0300 Subject: [PATCH] auto-sync: 2026-06-04 02:50:01 --- .../dev-2026-06-03-deploy-verdict-gate.md | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 tasks/orchestrator/reports/dev-2026-06-03-deploy-verdict-gate.md diff --git a/tasks/orchestrator/reports/dev-2026-06-03-deploy-verdict-gate.md b/tasks/orchestrator/reports/dev-2026-06-03-deploy-verdict-gate.md new file mode 100644 index 0000000..59c10c0 --- /dev/null +++ b/tasks/orchestrator/reports/dev-2026-06-03-deploy-verdict-gate.md @@ -0,0 +1,135 @@ +# Dev Report: БАГ 8 — deploy verdict gate +Дата: 2026-06-03 +Статус: DONE + +## Задача +Завести QG `check_deploy_status` по образцу `check_reviewer_verdict`, гейтить deploy→done +на машинном вердикте deployer'а (`deploy_status:` из `14-deploy-log.md`), а не на exit_code +LLM-процесса (всегда 0). PR в main, НЕ мержить/деплоить. + +## Сделано +- [x] Ветка `fix/deploy-verdict-gate` из свежего main (a0621b9, PR#18) +- [x] Правка 1: `check_deploy_status` + регистрация в QG_CHECKS +- [x] Правка 2: `stages.py` deploy qg None → check_deploy_status +- [x] Правка 3: вердикт-проверка в advance deploy→done +- [x] Тесты: +7 (test_qg) +3 (test_stage_engine), все зелёные +- [x] Commit + push, remote проверен (ORCH-7), PR #19 +- [x] Правка 4: НЕ трогал (enduro-trails) — см. ниже + +## Изменённые файлы +- `src/qg/checks.py` — новая функция `check_deploy_status` + запись в QG_CHECKS +- `src/stages.py` — deploy `"qg": None` → `"qg": "check_deploy_status"` +- `src/stage_engine.py` — новая deployer-ветка в `_handle_qg_failure_rollbacks` +- `tests/test_qg.py` — class TestCheckDeployStatus (7 тестов) +- `tests/test_stage_engine.py` — class TestDeployVerdict (3 теста) + +## Правка 1 — точный код check_deploy_status (src/qg/checks.py) +```python +def check_deploy_status(repo: str, work_item_id: str, branch: str | None = None) -> tuple[bool, str]: + """ + БАГ 8 fix: gate the deploy -> done transition on the deployer's machine-readable + verdict in 14-deploy-log.md frontmatter, NOT on the LLM process exit code + (which is always 0 on a successful agent session even when the deploy failed). + + Mirrors check_reviewer_verdict (S-5): reads ONLY `deploy_status:` from YAML + frontmatter. Returns: + (True, ...) -> deploy_status: SUCCESS + (False, ...) -> deploy_status: FAILED, missing field, or no frontmatter + """ + import yaml + repo_path = _repo_path(repo, branch) + log_path = os.path.join(repo_path, f"docs/work-items/{work_item_id}/14-deploy-log.md") + + if not os.path.isfile(log_path): + return False, "Deploy log not found (14-deploy-log.md)" + try: + with open(log_path, "r") as f: + content = f.read() + status = None + if content.startswith("---"): + parts = content.split("---", 2) + if len(parts) >= 3: + try: + fm = yaml.safe_load(parts[1]) or {} + except yaml.YAMLError as e: + return False, f"Invalid YAML frontmatter in deploy log: {e}" + status = str(fm.get("deploy_status", "")).upper().strip() + if status == "SUCCESS": + return True, "Deploy status: SUCCESS" + if status == "FAILED": + return False, "Deploy status: FAILED" + return False, f"No machine-readable deploy_status in frontmatter (got: {status!r})" + except OSError as e: + return False, f"Error reading deploy log: {e}" +``` +Регистрация в QG_CHECKS: `"check_deploy_status": check_deploy_status,` (после check_tests_local). + +## Правка 2 — stages.py +`"deploy": {"next": "done", "agent": None, "qg": None}` → +`"deploy": {"next": "done", "agent": None, "qg": "check_deploy_status"}` + +## Правка 3 — ГДЕ нашёл advance deploy→done и как вписал вердикт +Auto-advance унифицирован в `src/stage_engine.py::advance_stage` (ORCH-4/M-3 merge). +launcher.py:_try_advance_stage (стр.648) — тонкий wrapper, который грузит task по (repo,branch) +и вызывает `advance_stage(current_stage=, finished_agent=agent)`. Когда deployer +завершается, current_stage="deploy", finished_agent="deployer". + +В `advance_stage` QG уже выполняется ГЕНЕРИЧЕСКИ: +- `qg_name = get_qg_for_stage("deploy")` теперь = "check_deploy_status"; +- `_run_qg` → ветка «everything else» → `check_deploy_status(repo, work_item_id, branch)` (сигнатура совпала); +- SUCCESS (True) → блок Advance: update_task_stage(done)+notify (агент не запускается, agent=None); +- FAILED (False) → вызывается `_handle_qg_failure_rollbacks(...)`. + +В `_handle_qg_failure_rollbacks` НЕ было ветки для deployer → ничего не происходило. Добавил +новую ветку (после architect-ветки, в конце функции), триггер по ВЕРДИКТУ не exit_code: +```python + if agent == "deployer" and qg_name == "check_deploy_status": + update_task_stage(task_id, "development") + notify_stage_change(task_id, current_stage, "development") + plane_notify_stage(work_item_id, current_stage, "development") + result.rolled_back_to = "development" + set_issue_blocked(work_item_id) + notify_qg_failure(task_id, "deploy", "check_deploy_status", reason) + plane_add_comment(work_item_id, "❌ Deploy FAILED (...). Rolled back ...", author="deployer") + send_telegram("🚨 {wid}: Deploy FAILED (...). Rolled back to development. Needs fix.") + result.alerted = True + logger.error(...) +``` +launcher.py:475 блок (exit_code-based deployer-failure) НЕ удалён — оставлен как был; он просто +не срабатывает (exit_code=0), а реальный гейт теперь по вердикту. Advance других стадий не тронут. + +## Новые тесты +test_qg.py / TestCheckDeployStatus (через tmp 14-deploy-log.md + fixture с monkeypatch repos_dir): +- SUCCESS frontmatter → True; FAILED → False; нет файла → False; нет поля → False; + prose-only без frontmatter → False; get_qg_for_stage("deploy")=="check_deploy_status"; + зарегистрирована в QG_CHECKS. +test_stage_engine.py / TestDeployVerdict (pure-logic, БЕЗ TestClient POST): +- FAILED вердикт → НЕ done, откат в development, alerted, set_issue_blocked, send_telegram; +- нет лога → откат в development; SUCCESS → done, агент не запущен, jobs пусто. + +## Результат — полный вывод pytest (контейнер) +`docker run --rm -v ... --entrypoint python3 orchestrator-orchestrator -m pytest tests/ -q` +→ **10 failed, 227 passed** +- passed: 217 (baseline PR#18) + 10 новых = 227 ✓ (не уронил) +- 10 failed — ровно baseline off-limits (9 HMAC/401 в test_webhooks + 1 webhook-POST + test_plane_webhook_event_logged). Их не трогал. +Целевой прогон новых: `pytest TestCheckDeployStatus TestDeployVerdict -v` → 10 passed. + +## Статус правки 4 (deployer-промпт enduro-trails) +НЕ трогал (по умолчанию — только orchestrator). **Требует отдельной правки в репо +admin/enduro-trails**: `.openclaw/agents/deployer.md` обязать deployer'а писать YAML-frontmatter +в начало `14-deploy-log.md`: +``` +--- +deploy_status: SUCCESS # или FAILED +version: vX.Y.Z +--- +``` +Без этого frontmatter check_deploy_status вернёт False (нет поля) → задача откатится в +development. То есть гейт fail-safe, но для прохождения SUCCESS промпт enduro-trails надо обновить. + +## Push / PR (ORCH-7 проверен) +- Коммит: `e4a9c48 fix(deploy): gate deploy->done on deployer verdict, not LLM exit code` +- `git log origin/main..origin/fix/deploy-verdict-gate` → e4a9c48 (remote содержит коммит ✓) +- PR #19: https://git.mva154.duckdns.org/admin/orchestrator/pulls/19 (fix/deploy-verdict-gate → main) +- НЕ мержил, НЕ деплоил — мерж + боевой прогон делает ассистент.