Files
orchestrator/docs/work-items/ORCH-021/02-trz.md

14 KiB
Raw Blame History

ТЗ — ORCH-021: Post-deploy мониторинг прода + авто-rollback

Work Item: ORCH-021 Стадия: analysis → (architecture)

Документ описывает ТРЕБОВАНИЯ к изменениям и НАЗЫВАЕТ задействованные модули. Выбор механизма (стадия vs watchdog vs reserved-agent) и точная реализация — зона архитектора (см. BRD §7). Здесь фиксируется, ЧТО должно измениться и КАКИЕ контракты НЕЛЬЗЯ ломать.

1. Контекст в коде (как есть сейчас)

  • Конвейер заканчивается в src/stages.py: deploy → done, gate check_deploy_status. Терминальный переход deploy → done исполняется в src/stage_engine.py::advance_stage (блок «Terminal sync», set_issue_done, release merge-lease). После этого ничего не наблюдает за продом.
  • scripts/orchestrator-deploy-hook.sh уже умеет:
    • health_check(max_attempts, sleep, label) — опрос http://localhost:$TARGET_PORT/health с проверкой "status":"ok";
    • do_rollback() — retag PREV_IMAGE_FILETARGET_IMAGE + рестарт + пост-rollback health-check; коды возврата 0 (ок) / 1 (нет prev-образа) / 2 (rollback тоже упал);
    • режим --rollback (ручной откат);
    • при обычном деплое сохраняет PREV_IMG в PREV_IMAGE_FILE (.deploy-prev-image-prod для прода, см. settings.deploy_prod_prev_image_file).
  • Self-deploy прода идёт через detached host-процесс: src/self_deploy.py (build_deploy_command, initiate_deploy, sentinel-маркеры под .deploy-state-<repo>/<wi>/, read_result, map_exit_code_to_status).
  • Фоновый daemon-паттерн: src/reconciler.py (threading.Thread(daemon=True) + threading.Event, старт/стоп в src/main.py::lifespan после worker.start() / перед worker.stop(), status() в GET /queue).
  • Reserved-agent (детерминированный no-LLM job) паттерн: deploy-finalizer — перехват в src/agents/launcher.py::launch_job ДО _spawn, исполнение stage_engine.run_deploy_finalizer, отложенная постановка через enqueue_job(..., available_at_delay_s=...).
  • Условность self-hosting: src/qg/checks.py::is_self_hosting_repo, src/self_deploy.py::self_deploy_applies (флаг + CSV-репо; пусто → только orchestrator).
  • Наблюдаемые эндпоинты прода (src/main.py): GET /health, GET /status, GET /queue.
  • API БД: src/db.py::enqueue_job (с available_at_delay_s), get_db, update_task_stage, get_active_tasks_for_reconcile.

2. Требуемые изменения

2.1. Новый leaf-модуль чистой логики наблюдения — src/post_deploy.py (новый)

Контракт never-raise (по образцу self_deploy.py / staging_verdict.py). Чистые, юнит-тестируемые функции:

  • Опрос сигналов: функция, опрашивающая /health и ключевые эндпоинты (/status, /queue) прод-инстанса (base-url из config), возвращающая структуру с результатами (код ответа, ok-флаг, доля 5xx). Сеть/таймаут → консервативный результат, не исключение.
  • Классификация деградации (чистая, без сети): на вход — серия результатов опросов; на выход — вердикт HEALTHY | DEGRADED по порогам (BR-3): ≥ post_deploy_fail_threshold последовательных провалов health ИЛИ доля 5xx выше post_deploy_5xx_threshold на окне. Эта функция — основной предмет юнит-тестов (детерминированная, как compute_staging_verdict в ORCH-061).
  • Решение о реакции (чистая): по (repo, вердикт, политика) → одно из NONE | ROLLBACK | ALERT_ONLY, с учётом self-hosting (BR-5).
  • Запись артефакта результата наблюдения (см. §2.5), best-effort.
  • Условность: хелпер post_deploy_applies(repo) (флаг + CSV-репо, пусто → только self-hosting), по образцу self_deploy_applies / _merge_gate_applies.

2.2. Оркестрация наблюдения (механизм — выбор архитектора)

Требования к механизму (независимо от выбора стадия/watchdog/reserved-agent):

  • запускается ПОСЛЕ перехода deploy → done для применимого репозитория (BR-1);
  • наблюдает окно post_deploy_window_s с интервалом post_deploy_interval_s;
  • restart-safe и идемпотентен (BR-7): состояние наблюдения — в sentinel-файлах (по образцу .deploy-state-<repo>/<wi>/, напр. маркеры monitor-started / monitor-done) ИЛИ через отложенные enqueue_job(available_at_delay_s=...); повторный старт не задваивает наблюдение и не теряет его при рестарте;
  • по итогу вызывает «Решение о реакции» из src/post_deploy.py и исполняет реакцию (§2.3).

Кандидатные точки интеграции (на выбор архитектора, см. BRD §7):

  • хук в stage_engine.advance_stage в блоке next_stage == "done" — арм наблюдения;
  • reserved-agent post-deploy-monitor (расширение launcher.launch_job ДО _spawn, как deploy-finalizer), с само-перепостановкой через available_at_delay_s;
  • отдельный daemon-поток PostDeployWatcher (как Reconciler), старт/стоп в main.lifespan.

2.3. Реакция на деградацию

  • Не-self репозитории / политика auto: вызвать существующий хук в режиме отката (scripts/orchestrator-deploy-hook.sh --rollback с прод-параметрами окружения, как в self_deploy.build_deploy_command, но action=--rollback). Маппинг exit-code хука (0/1/2) в исход переиспользует логику self_deploy.map_exit_code_to_status по смыслу (0 → откат успешен; 1/2 → откат не выполнен/провалился → громкий алерт).
  • Self-hosting (orchestrator) по умолчанию (BR-5): НЕ откатывать автоматически. Сформировать громкий алерт (Telegram + Plane-коммент) и запросить ручной approve отката (по образцу deploy Phase A — статус Plane / Telegram CTA). Откат самого прод-орка, если выполняется, — только через detached host-процесс (нельзя надёжно откатить контейнер, который при этом умирает; переиспользовать механику self_deploy.initiate_deploy).
  • Команда отката для self НЕ должна ронять прод-контейнер в рамках обычного тика наблюдения (CLAUDE.md: не ронять/не рестартить прод-контейнер вне явного действия).

2.4. Конфигурация — src/config.py (расширение Settings)

Добавить (env-префикс ORCH_, дефолты безопасные):

  • post_deploy_monitor_enabled: bool = True — глобальный kill-switch (BR-8).
  • post_deploy_repos: str = "" — CSV применимых репо; пусто → только self-hosting (по образцу self_deploy_repos / merge_gate_repos / image_freshness_repos).
  • post_deploy_window_s: int = 900 — длина окна наблюдения (дефолт ~15 мин, BR-1).
  • post_deploy_interval_s: int = 30 — интервал между опросами.
  • post_deploy_fail_threshold: int = 3 — N последовательных провалов health → DEGRADED.
  • post_deploy_5xx_threshold: float = 0.5 — порог доли 5xx на окне → DEGRADED.
  • post_deploy_auto_rollback: bool = False — глобально разрешён ли авто-откат; при True действует для не-self репо; для self всегда требует approve (BR-5).
  • post_deploy_base_url: str = "http://localhost:8500" — base-url наблюдаемого прода.
  • post_deploy_target параметры отката — переиспользовать существующие deploy_prod_* (service/port/image/prev_image_file), новых дублей не вводить.

2.5. Артефакт задачи — 16-post-deploy-log.md (новый)

В docs/work-items/<plane-id>/. YAML-frontmatter (машиночитаемо, канон гейтов; для будущей петли уроков BR-10):

---
post_deploy_status: HEALTHY | DEGRADED
action_taken: NONE | ROLLBACK_OK | ROLLBACK_FAILED | ALERT_ONLY
work_item: <plane-id>
window_s: <int>
checks_total: <int>
checks_failed: <int>
---

Тело — человекочитаемая сводка опросов. Записывается best-effort (по образцу self_deploy.write_deploy_log); отсутствие файла не должно ничего ронять.

Артефакт 16-post-deploy-log.md добавить в перечень артефактов в CLAUDE.md и таблицу/описание в docs/architecture/README.md (golden-source, в том же PR).

2.6. Наблюдаемость — GET /queue (src/main.py) (BR-9)

Добавить блок post_deploy со снимком состояния (enabled, window, активные наблюдения, последний исход) — по образцу блока reconcile (метод status()).

2.7. Изменения схемы БД

Не требуются. Состояние наблюдения — sentinel-файлы (restart-safe, без миграции, по образцу ORCH-36) и/или отложенные jobs. Если архитектор выберет колонку в tasks для отметки наблюдения — потребуется миграция; предпочтительно избежать (как ORCH-36/53/58).

2.8. Новые QG checks

Не требуются. Наблюдение происходит ПОСЛЕ done и не является gate'ом стадии; реестр QG_CHECKS и STAGE_TRANSITIONS не меняются (если архитектор НЕ выберет вариант «отдельная пост-deploy стадия» — тогда потребуется новая стадия+gate, что надо явно отразить в ADR; по умолчанию предпочтителен вариант без изменения реестров).

3. Инварианты (НЕ ломать)

  • STAGE_TRANSITIONS, реестр QG_CHECKS, контракт check_deploy_status / _parse_deploy_status, момент вердикта deploy_status, БАГ-8 откат, terminal-sync deploy → done, merge-gate, exit-code-контракт хука (0/1/2) — без изменений.
  • Контракт хука: дефолты STAGING-безопасны; прод-параметры приходят только через env.
  • Условность как ORCH-35/36/43/58: реально для orchestrator/listed-repos, прочие — no-op.
  • Never-raise: ошибка в наблюдении не роняет worker / lifespan / конвейер других проектов.
  • Self-hosting: тик наблюдения НИКОГДА не рестартит прод-контейнер сам по себе (BR-5).

4. Задействованные модули (сводка)

Модуль Изменение
src/post_deploy.py новый — чистая логика опроса/классификации/решения/артефакта, never-raise
src/config.py +параметры post_deploy_* (kill-switch, окно, пороги, политика)
src/stage_engine.py и/или src/agents/launcher.py и/или src/main.py арм/исполнение наблюдения (точка — за архитектором)
scripts/orchestrator-deploy-hook.sh переиспользуется (--rollback); правки — только если откат self требует отдельной ветки (за архитектором)
src/main.py блок post_deploy в GET /queue (BR-9); возможный старт daemon в lifespan
docs/work-items/<id>/16-post-deploy-log.md новый артефакт
CLAUDE.md, docs/architecture/README.md, CHANGELOG.md обновить (golden-source, в том же PR)
ADR docs/work-items/ORCH-021/06-adr/ADR-001-*.md (+ возможный сквозной adr/adr-00NN)

5. Артефакты по pipeline, которые должны появиться/обновиться

  • 16-post-deploy-log.md (новый, машиночитаемый frontmatter).
  • Обновлённые CLAUDE.md (перечень артефактов), docs/architecture/README.md (описание пост-деплой наблюдения), CHANGELOG.md.
  • ADR work-item (06-adr/) с зафиксированным выбором механизма и порогов.