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

11 KiB
Raw Blame History

work_item, stage, author_agent, status, created_at, model_used, escalate
work_item stage author_agent status created_at model_used escalate
ORCH-113 analysis analyst ready-for-review 2026-06-15 claude-opus-4-8 full-cycle

02 — ТЗ (TRZ): ORCH-113 — BUG: job-reaper не должен повторно запускать финализацию deploy-staging, пока жив исходный finalizer

Work Item: ORCH-113 · Repo: orchestrator · Стадия: analysis

ТЗ описывает требования к изменению, выведенные из BRD и фактического кода. Конкретный механизм (heartbeat живости / sub-state finalizing / per-stage grace / ownership-lease на edge-гейты), точные имена символов/колонок/флагов и порядок врезок — архитектурное решение (06-adr), не зона аналитика. Модули-плейсхолдеры ниже выровнены под манифест PIPELINE_DOCS.md.

1. Сводка изменения

Сделать повторную обработку reaper'ом завершившегося-но-ещё-running job на стадии deploy-staging идемпотентной и владеющей состоянием: пока исходный monitor/finalizer жив (или edge-гейты для пары (job, stage) уже исполняются), reaper не должен независимо запускать второй прогон edge- под-гейтов ребра deploy-staging → deploy (security / merge-gate / локальный re-test / coverage / image-freshness) и не должен на этом основании откатывать задачу. Корень — ошибочное допущение Tier-2 finalization-grace (reaper_finalize_grace_s=300), что финализация после штампа finished_at длится «секунды…десятки секунд»; для deploy-staging она длится минуты (полный re-test + coverage

  • rebuild), потому что _try_advance_stage (тяжёлые edge-гейты) выполняется после штампа finished_at и до _finalize_job. Фикс должен дать reaper'у способ отличить «живой, долго финализирующий монитор» от «мёртвого монитора» и обеспечить строгое владение исполнением edge-гейтов.

2. Задействованные модули / пути

Путь Действие
src/job_reaper.py изменить — Tier-2-ветка _reap_job (строки ~197-209), _reap_known_outcome / _reap_exit0 / _gate_driven_advance: ввести проверку живости/владения перед side-effectful re-drive advance_stage для deploy-staging; при живом finalizer'еdefer, не reap
src/agents/launcher.py изменить (вероятно) — _monitor_agent: место/порядок штампа finished_at (строка ~861) относительно _try_advance_stage (строка ~998) и/или эмиссия durable-сигнала «finalizer жив/финализирует» перед запуском тяжёлых edge-гейтов
src/db.py изменить (вероятно) — get_running_jobs (строки ~1337-1367, источник finished_age_s) и/или аддитивная колонка владения/живости (_ensure_column, паттерн tasks.cancelled_at); reap_running_job — без изменения контракта атомарного флипа
src/config.py изменить (вероятно) — kill-switch фикса и/или per-stage/finalize-aware grace; сохранить сквозной инвариант reaper_max_running_s > Σ gate-work + grace (NFR-6)
src/stage_engine.py только чтение/ссылкаadvance_stage ребро deploy-staging (строки ~321-383): эталон того, какие edge-гейты дублируются. Изменение нежелательно; идемпотентность предпочтительно решать на стороне reaper/launcher

Точный набор затронутых модулей и распределение логики (reaper-only vs launcher-сигнал vs db-колонка) архитектор фиксирует в 06-adr. Аналитик фиксирует, что центр тяжести правки — src/job_reaper.py.

3. Функциональные требования

FR-1 — Распознавание живости финализирующего монитора (BR-1, BR-3)

Reaper в Tier-2 для стадии deploy-staging должен распознавать ситуацию «процесс агента завершён, но monitor/finalizer ещё жив и исполняет edge-гейты» и не трактовать её как мёртвый монитор по одному лишь finished_age_s >= reaper_finalize_grace_s. Сигнал живости должен переживать долгую (минуты) финализацию deploy-staging. Инвариант: живой finalizer никогда не reap'ается.

FR-2 — Идемпотентность и строгое владение edge-гейтами (BR-2)

Для пары (job, stage) на deploy-staging тяжёлый прогон edge-гейтов (security / merge-gate / локальный re-test / coverage / image-freshness) исполняется не более одного раза одновременно: актор, не владеющий состоянием, не запускает второй advance_stage/re-test/merge-gate. Текущий атомарный claim-before-act (reap_running_job ... WHERE status='running') защищает только флип строки job — требование расширяет защиту на side-effectful исполнение edge-гейтов.

FR-3 — Согласование grace/бюджета (BR-3, NFR-6)

Длительность finalization-grace (или заменяющий её сигнал живости) должна покрывать фактический wall-clock финализации deploy-staging = Σ(security + merge re-test merge_retest_timeout_s=900 + coverage + image rebuild). Любая правка grace/таймаутов сохраняет сквозной инвариант ORCH-065/109/110: reaper_max_running_s (5400) > Σ(deploy-staging gate-work) + grace.

FR-4 — Сохранение добивания мёртвого finalizer'а (BR-4)

Реально мёртвый monitor/finalizer на deploy-staging (краш посреди финализации, сигнал живости отсутствует/протух) по-прежнему добивается reaper'ом за ограниченное время по существующему контракту (retry в пределах бюджета, иначе failed + Telegram; Tier-3 backstop как крайний предохранитель). Reaper не становится no-op для deploy-staging.

FR-5 — Отсутствие расхождения состояния (BR-5)

Исключить одновременный ложный откат deploy-staging → development и успешное завершение deploy → done для одной задачи. После согласования у задачи — единственное консистентное терминальное/стадийное состояние; ложный developer-retry не инкрементируется. Под-гейт merge-verify (deploy → done, ORCH-071) остаётся единственным choke-point'ом в done — он не ослабляется.

4. Изменения API

Новых публичных эндпоинтов не требуется. Допустима аддитивная read-only наблюдаемость в GET /queue (например, отметка «deploy-staging finalize in-progress / deferred-by-liveness» в блоке reaper) — без изменения существующих ключей ответа.

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

Возможна аддитивная колонка владения/живости finalizer'а (например, durable-таймштамп «finalizing heartbeat» или owner-токен на job/agent_run), вводимая идемпотентно через _ensure_column (паттерн tasks.cancelled_at / tasks.track). Существующие таблицы/колонки/индексы и их семантика — без изменений. Точная необходимость и форма — за архитектором (06-adr / 08-data-requirements).

6. Требования к новым/изменённым QG checks

Нет. QG_CHECKS и любой check_* (включая check_staging_status, check_branch_mergeable, check_coverage_gate, check_security_gate, check_staging_image_fresh, check_deploy_status) — не трогаются ни по составу, ни по семантике, ни по machine-verdict ключам. Багфикс — свойство страховочного демона/финализатора, не Quality Gate.

7. Совместимость / регресс

  • Kill-switch: фикс под выделенным флагом; False → строго прежнее поведение reaper (нулевая регрессия).
  • Область: поведение для не-deploy-staging стадий и путь добивания мёртвого монитора — сохранены; self-hosting-only/скоуп репо согласовать с существующими leaf-паттернами (если вводится per-repo разрез).
  • Обратимость: раскат обратим выключением флага (+ откатом значений grace/таймаутов к дефолтам).
  • Never-raise / restart-safe / self-hosting (NFR-1/3/5): любой сбой нового пути живости/владения деградирует безопасно (reaper-тик продолжается); новый durable-маркер восстановим после рестарта; фикс не рестартит прод и не пушит main.
  • Сквозной инвариант (NFR-6): reaper_max_running_s > Σ gate-work + grace сохранён.
  • Анти-регресс: структурный тест-гард (см. 04-test-plan.yaml), полный tests/ остаётся зелёным.