13 KiB
work_item, stage, author_agent, status, created_at, model_used, escalate
| work_item | stage | author_agent | status | created_at | model_used | escalate |
|---|---|---|---|---|---|---|
| ORCH-110 | analysis | analyst | ready-for-review | 2026-06-15 | claude-opus-4-8 | full-cycle |
02 — ТЗ (TRZ): ORCH-110 — merge-gate local re-test timeout: устранение ложного отката + утечки процессов
Work Item: ORCH-110 · Repo: orchestrator · Стадия: analysis
ТЗ описывает конкретные требования к реализации, выведенные из BRD и фактического кода. Архитектурное обоснование, выбор вариантов и контракт merge-gate — задача архитектора (06-adr, основание
escalate: full-cycle). Здесь — поведение/контракты/инварианты и привязка к модулям, НЕ выбор механизма.
1. Сводка изменения
Устранить ложный откат deploy-staging → development, возникающий когда локальный re-test merge-gate
падает по таймауту (инфра/ресурс), при зелёном tester PASS и зелёном CI. Изменение бьёт по двум
корням и одному контракту: (1) утечка осиротевших pytest-процессов из оркестратор-спавненных
прогонов re-test/coverage (источник CPU-голодания) → гарантировать tree-kill дерева подпроцесса при
таймауте/kill; (2) классификация инфра-таймаута как транзиента (повтор/defer/инфра-alert), а не
код-фейла (откат + расход developer-retry); (3) контракт необходимости локального re-test
относительно зелёного CI и состояния branch vs origin/main. Сопутствующе — согласование бюджета
re-test с реальным временем сюита. Всё — аддитивно, под kill-switch, never-raise, скоуп self-hosting,
с сохранением исходной защиты merge-gate от семантического конфликта (красный re-test по-прежнему
откатывает).
2. Задействованные модули / пути
| Путь | Действие |
|---|---|
src/merge_gate.py |
изменить — retest_branch: жизненный цикл подпроцесса (tree-kill при таймауте/kill); классификация исхода «timeout» как транзиента (контракт возврата) |
src/coverage_gate.py |
изменить — measure_coverage: тот же tree-kill при таймауте (сиблинг-источник утечки, BR-3) |
src/qg/checks.py |
изменить — check_branch_mergeable: различать «timeout/infra» от «red re-test» в возвращаемом контракте (без смены имени/семантики зарегистрированного check_*) |
src/stage_engine.py |
изменить — _handle_merge_gate / маршрутизация исхода: инфра-таймаут → defer/повтор/инфра-alert (по образцу _handle_merge_gate_defer), НЕ _handle_merge_gate_rollback; красный re-test → прежний rollback |
src/config.py |
изменить — флаг(и) толерантности к инфра-таймауту + (опц.) согласование merge_retest_timeout_s; уважить сквозные инварианты merge_lock_timeout_s / reaper_max_running_s / coverage_run_timeout_s |
docs/architecture/README.md, CLAUDE.md, CHANGELOG.md |
обновить — описание поведения merge-gate re-test (golden source наравне с кодом) |
tests/test_* |
создать — покрытие по 04-test-plan.yaml |
Точный набор новых символов/флагов и механизм tree-kill (process-group
start_new_session+killpg, либо иной) — решение архитектора. ТЗ фиксирует что должно выполняться, не как.
3. Функциональные требования
FR-1 — Толерантность к инфра-таймауту re-test (нет ложного отката) [BR-1, BR-2]
Когда merge-gate локальный re-test завершается специфически по таймауту (а не детерминированно
красным результатом), исход ДОЛЖЕН классифицироваться как транзиент/инфра, не код-фейл. Путь
восстановления НЕ ДОЛЖЕН быть тем же _handle_merge_gate_rollback (откат на development + инкремент
developer-retry), который при зелёных CI/tester ведёт к «Manual intervention needed». Допустимая
реакция (выбор — архитектор): ограниченный повтор re-test и/или defer (по образцу существующего
_handle_merge_gate_defer для merge-lock busy) и/или отдельный инфра-alert. Прецеденты толерантности
к инфра: ORCH-061 (staging infra tolerance), ORCH-093 (transient vs terminal классификация merge-POST).
FR-2 — Tree-kill оркестратор-спавненных тест-процессов [BR-3]
merge_gate.retest_branch и coverage_gate.measure_coverage ДОЛЖНЫ гарантировать, что при таймауте
(а также при любом kill/прерывании прогона) завершается всё дерево подпроцесса pytest, включая
внуков, а не только прямой потомок. После таймаута ни один оркестратор-спавненный pytest-процесс не
должен оставаться живым и грузить CPU. Контракт возврата retest_branch
((False, "re-test timeout after <T>s")) сохраняется; меняется лишь побочный эффект — отсутствие
утечки. Существующий каскад launcher SIGTERM→grace→SIGKILL (stop_process) — образец на уровне
агентов; для этих subprocess-прогонов требуется эквивалентная гарантия на уровне группы процессов.
FR-3 — Согласованность бюджета re-test [BR-4, NFR-6]
Бюджет merge_retest_timeout_s ДОЛЖЕН иметь достаточный запас над фактическим временем полного сюита
(наблюдаемо: 600s бюджет vs 516.70s факт ≈ 16%). Бюджет остаётся конфигурируемым; при его изменении
ДОЛЖНЫ соблюдаться сквозные инварианты: reaper_max_running_s > max(agent_timeout, бюджеты) + grace
(ORCH-065/109) и согласование с merge_lock_timeout_s (TTL merge-lease держится на время re-test).
Малформный/непозитивный конфиг → безопасный дефолт + WARNING (never-break).
FR-4 — Контракт необходимости локального re-test [BR-5, BR-6]
Merge-gate ДОЛЖЕН различать риск-кейсы и применять re-валидацию пропорционально реальному риску слияния:
- ветка реально отстала от уехавшего
origin/mainи ребейзнута → семантический риск → re-test оправдан (текущая цель ORCH-043 сохраняется); - ветка уже актуальна / rebase — no-op, и CI по этому самому HEAD зелёный → локальный полный
re-test пере-проверяет ровно подтверждённый CI коммит и не должен быть единственной точкой ложного
отказа.
Конкретный контракт (например: пропуск re-test при «не-behind + зелёный CI по HEAD», сокращённый
scope, доверие SHA, подтверждённому CI, и т. п.) — выбор архитектора в ADR (ядро запрошенного
баг-карточкой «анализа контрактов merge-gate»). Инвариант BR-6: детерминированно красный
re-test (реальный сбой теста) обязан и далее откатывать на
development— послабление применяется ТОЛЬКО к таймауту/инфра.
FR-5 — Сохранение инвариантов и kill-switch [NFR-1, NFR-2, NFR-3, NFR-4]
Изменение аддитивно: STAGE_TRANSITIONS / реестр QG_CHECKS / семантика check_* / machine-verdict
ключи / схема БД — без изменений; merge-gate остаётся под-гейтом-врезкой, не новой стадией/QG. Под
kill-switch: выключенный флаг → байт-в-байт прежнее поведение (таймаут → откат). Скоуп self-hosting
(orchestrator); enduro — no-op. never-raise; INV-4 (никогда push/force-push main; merge только
через Gitea PR API) и запрет рестарта прод-контейнера — соблюдены.
FR-6 — Наблюдаемость и ограниченность [BR-7, NFR-5]
Состояние «инфра-таймаут» ДОЛЖНО логироваться, уведомляться в Telegram (кликабельный номер задачи) и
быть видимым read-only (например, расширение блока merge/merge_verify в GET /queue), отличимо от
код-фейл-отката. Любой повтор/defer строго ограничен (число попыток + суммарное время); исчерпание →
инфра-alert (не «developer must fix»). Координация с ORCH-111 (proc_blocking) — без дубля:
ORCH-110 предотвращает/толерирует, ORCH-111 наблюдает.
4. Изменения API
Новых обязательных эндпоинтов не требуется. Допустимо (when-applicable, на усмотрение
архитектора) read-only расширение существующего снимка GET /queue (блок merge-gate) полями
наблюдаемости инфра-таймаута/повторов. Никаких новых управляющих эндпоинтов.
5. Изменения схемы БД
Нет. Счётчики повторов/defer — по образцу существующих (_merge_defer_count /
_developer_retry_count поверх jobs/agent_runs) либо in-memory/sentinel; новые таблицы/колонки не
вводятся (NFR-1).
6. Требования к новым/изменённым QG checks
Нет нового зарегистрированного QG. check_branch_mergeable остаётся в реестре QG_CHECKS с тем же
именем и семантикой PASS/FAIL; меняется лишь различение причины FAIL (timeout/infra vs red) в
возвращаемом reason и маршрутизация исхода во врезке _handle_merge_gate (advance_stage).
STAGE_TRANSITIONS и состав QG_CHECKS — байт-в-байт.
7. Совместимость / регресс
- Обратная совместимость: kill-switch off → поведение байт-в-байт как до ORCH-110 (таймаут →
rollback на
development), включая текст alert'ов. - Область раската: self-hosting
orchestrator(как ORCH-035/043/058/071); прочие репо — no-op, путь LLM-deployer/прежний merge не затронут. - Обратимость: чисто аддитивная логика под флагом; откат = выключить флаг.
- Self-hosting: без рестарта прод-контейнера; merge только через Gitea PR API; никаких операций с
main(INV-4). - Анти-регресс целей merge-gate: красный re-test → прежний rollback (BR-6); защита от семантического конфликта/фантомного merge (ORCH-043/071/073) — не ослаблена.
- Трассировка маркеров (ORCH-078): правки в
merge_gate.py/coverage_gate.py/qg/checks.pyзатрагивают блоки с маркерами ORCH-043/071/073/093/027/065/109 — перед изменением сверить их06-adrи не сломать зафиксированные инварианты (lease, never-raise, fail-open/closed, бюджеты).