Files
wiki/tasks/orchestrator/DEV_TASK_DEPLOY_VERDICT.md
2026-06-04 02:40:01 +03:00

9.2 KiB
Raw Permalink Blame History

DEV TASK — БАГ 8: deploy не имеет QG-гейта → провалившийся деплой уходит в done

Проект: orchestrator | Сервер: slin@82.22.50.71 | Репо: /home/slin/repos/orchestrator | Контейнер: orchestrator (8500) Ветка: fix/deploy-verdict-gate из свежего main (git checkout main && git pull && git checkout -b fix/deploy-verdict-gate). main = a0621b9 (PR #18, перед стартом сделай git pull — мог уехать).

⚠️ ГРАБЛЯ push (ORCH-7): после push git log origin/main..origin/fix/deploy-verdict-gate ДОЛЖЕН показать коммит ДО отчёта «PR готов». Токен Gitea: docker exec orchestrator printenv ORCH_GITEA_TOKEN.

Контекст / дыра (баг 8, подтверждён боевым прогоном ET-011)

deployer (run 71) РЕАЛЬНО провалил деплой (deploy-hook упал на permission denied, контейнер не пересобрался), честно написал Status: FAILED в 14-deploy-log.md и в текстовом отчёте «Exiting with non-zero». НО задача всё равно уехала в done, и фича оказалась нерабочей на проде.

Корень — ДВА места:

  1. src/stages.py:19у стадии deploy "qg": None. Нет проверки выхода вообще (у всех других стадий QG есть). deploy → done безусловно.
  2. exit_code в agent_runs = код процесса LLM-агента (Claude CLI), который ВСЕГДА 0 при успешной LLM-сессии — не отражает реальный результат деплоя. Существующая защита launcher.py:475 if exit_code != 0 and agent == "deployer" поэтому НЕ срабатывает.

Решение (по образцу check_reviewer_verdict, S-5): завести QG check_deploy_status, который читает машинно-читаемое поле из 14-deploy-log.md (как reviewer читает verdict: из 12-review.md). Привязать к стадии deploy. deployer-промпт обязать писать это поле во frontmatter.

Правка 1 — новый QG check_deploy_status в src/qg/checks.py

Добавь функцию ПО ОБРАЗЦУ check_reviewer_verdict (checks.py:210). Образец читает frontmatter verdict: из 12-review.md. Сделай аналог для 14-deploy-log.md, поле deploy_status::

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 (рядом с остальными, ~стр.286): "check_deploy_status": check_deploy_status,.

Правка 2 — привязать QG к стадии deploy в src/stages.py:19

БЫЛО: "deploy": {"next": "done", "agent": None, "qg": None}, СТАЛО: "deploy": {"next": "done", "agent": None, "qg": "check_deploy_status"}, (agent остаётся None — deployer запускается на ВХОДЕ стадии deploy из testing, как сейчас; check_deploy_status гейтит ВЫХОД deploy→done.)

Правка 3 — где вызывается advance deploy→done: убедиться, что QG check_deploy_status проверяется

Найди в src/stage_engine.py / src/agents/launcher.py, где после deployer'а делается переход deploy → done (auto-advance). Сейчас он, видимо, безусловный (qg=None). Нужно: ПЕРЕД update_task_stage(task_id, "done") вызвать check_deploy_status(repo, work_item_id, branch):

  • если True → done (как сейчас) + notify;
  • если FalseНЕ done. Вместо этого: update_task_stage(task_id, "development") + set_issue_blocked(work_item_id) + notify_qg_failure(task_id, "deploy", "check_deploy_status", reason) + Telegram «Deploy FAILED, rolled back to development». (Это дублирует существующий launcher.py:475-495 блок deployer-failure — можно переиспользовать ту же логику, главное чтобы она реально вызывалась на основе ВЕРДИКТА, а не exit_code.)
  • ⚠️ Точное место advance найди сам (grep deploy.*done, update_task_stage.*done, next_stage == "done", _try_advance). НЕ ломай advance для других стадий.

Правка 4 — deployer-промпт .openclaw/agents/deployer.md (в репо enduro-trails, НЕ в orchestrator!)

⚠️ Этот файл — в целевом репо admin/enduro-trails, ветка отдельная. СНАЧАЛА уточни у ассистента нужно ли его трогать в этой задаче или отдельным PR в enduro-trails. Если ассистент скажет править здесь — добавь в инструкцию deployer'а требование писать YAML-frontmatter в начало 14-deploy-log.md:

---
deploy_status: SUCCESS   # или FAILED
version: v0.0.3
---

По умолчанию (если ассистент не уточнил) — НЕ трогай enduro-trails, только orchestrator. Отметь в отчёте, что deployer-промпт enduro-trails требует отдельной правки (добавить frontmatter deploy_status).

Ограничения

  • 🚫 НЕ трогай: другие QG-функции (check_ci_green, check_reviewer_verdict, check_tests_passed, check_tests_local, check_architecture_done, check_analysis_*), review/CI-retry ветки, HMAC/project-filter, nginx, openclaw.json, .env, очередь, PLANE_STATES, conftest.py, status-only, gitea_public_url, стадии кроме deploy.
  • 🚫 НЕ удаляй существующий launcher.py:475 блок (можешь переиспользовать его логику для вердикт-пути).
  • Conventional Commits, один коммит: fix(deploy): gate deploy->done on deployer verdict, not LLM exit code.

Тесты (контейнер)

IMG=$(docker inspect orchestrator --format '{{.Config.Image}}'); docker run --rm -v /home/slin/repos/orchestrator:/code -w /code --entrypoint python3 $IMG -m pytest tests/ -q Добавь в tests/test_qg.py + tests/test_stage_engine.py (pure-logic, БЕЗ TestClient POST — иначе 401 HMAC baseline):

  • check_deploy_status: SUCCESS frontmatter → (True,...); FAILED → (False,...); нет файла → (False,...); нет поля → (False,...). (создай tmp 14-deploy-log.md с фронтматтером, мокни _repo_path/путь).
  • get_qg_for_stage("deploy") == "check_deploy_status".
  • advance: deploy с deploy_status=FAILED → задача НЕ в done (откат в development), deploy_status=SUCCESS → done.
  • Baseline после PR #18: 217 passed + 10 failed (10 off-limits: 9 HMAC/401 + 1 webhook-POST). Итог passed ≥217 + новые. НЕ урони passed.

Отчёт

  • НЕ деплоить, НЕ мержить (мерж + боевой прогон делает ассистент).
  • В отчёте: точный код check_deploy_status, правка stages.py, ГДЕ именно нашёл advance deploy→done и как туда вписал вердикт-проверку, новые тесты, полный вывод pytest. Отметь статус правки 4 (deployer-промпт enduro-trails). Один исполнитель.