ORCH-110: merge-gate re-test infra-timeout tolerance + tree-kill + re-test contract #132

Merged
admin merged 7 commits from feature/ORCH-110-bug-merge-gate-local-re-test-t into main 2026-06-15 11:05:00 +03:00
Owner

ORCH-110 — merge-gate re-test infra-timeout: устранение ложного отката + утечки процессов

Багфикс инцидента ORCH-109 / PR #129 (bug → escalate full-cycle). Локальный re-test merge-gate (check_branch_mergeable, ребро deploy-staging → deploy) падал по таймауту (516.7s-сюит упёрся в бюджет 600s под CPU-голоданием от осиротевших pytest-процессов) при зелёных CI + tester + staging → маршрутизировался как код-фейл → откат на development + расход developer-retry → каждый retry падал так же → «Merge-gate still failing after 3 developer retries» → ручное вмешательство.

Аддитивно, под 5 независимыми kill-switch, never-raise, скоуп self-hosting. Не тронуты байт-в-байт: STAGE_TRANSITIONS, реестр QG_CHECKS, имя/семантика check_branch_mergeable, machine-verdict-ключи, схема БД. INV-4 (никогда push/force-push main) и запрет рестарта прод-контейнера — соблюдены.

Что сделано (по ADR)

  • D1 — tree-kill (корень утечки): новый stdlib-only leaf src/proc_group.py спавнит re-test/coverage pytest в отдельной группе процессов (start_new_session) и при таймауте убивает всё дерево (os.killpg, каскад SIGTERM→grace→SIGKILL). Используют merge_gate.retest_branch и coverage_gate.measure_coverage. Fallback never-break: subprocess_tree_kill_enabled=False/не-POSIX → прежний subprocess.run.
  • D2/D3 — классификация + маршрутизация: classify_retest_failure различает timeout/red/lock-busy/other; инфра-таймаут → _handle_merge_gate_infra_retry (ограниченный повтор, задача остаётся на deploy-staging, без отката/developer-retry); красный re-test/конфликт → прежний rollback (BR-6). Исчерпание → один инфра-alert.
  • D4 — контракт re-test: пропуск re-test при доказанном no-op rebase (HEAD уже прошёл CI+tester+staging); fail-safe → re-test при любой неопределённости.
  • D5 — бюджет: merge_retest_timeout_s 600 → 900 + валидация; инвариант reaper_max_running_s соблюдён без правки.
  • D6 — наблюдаемость: счётчики + read-only блок merge_gate в GET /queue; regression-guard marker; координация с ORCH-111 без дубля.

Тесты

tests/test_orch110_*.py — TC-01…TC-12 (включая регресс инцидента red-before/green-after). Полный регресс: 1988 passed.

Документация (в том же PR)

docs/architecture/README.md, docs/overview/tech-pipeline.md, CLAUDE.md, CHANGELOG.md, .env.example обновлены.

ADR: docs/work-items/ORCH-110/06-adr/ADR-001-merge-gate-retest-infra-tolerance-and-tree-kill.md, сквозной docs/architecture/adr/adr-0042-merge-gate-retest-infra-tolerance-and-tree-kill.md.

🤖 Generated with Claude Code

## ORCH-110 — merge-gate re-test infra-timeout: устранение ложного отката + утечки процессов Багфикс инцидента **ORCH-109 / PR #129** (bug → escalate full-cycle). Локальный re-test merge-gate (`check_branch_mergeable`, ребро `deploy-staging → deploy`) падал по **таймауту** (516.7s-сюит упёрся в бюджет 600s под CPU-голоданием от **осиротевших** pytest-процессов) при зелёных CI + tester + staging → маршрутизировался как **код-фейл** → откат на `development` + расход developer-retry → каждый retry падал так же → «Merge-gate still failing after 3 developer retries» → ручное вмешательство. Аддитивно, под **5 независимыми kill-switch**, never-raise, скоуп self-hosting. **Не тронуты байт-в-байт:** `STAGE_TRANSITIONS`, реестр `QG_CHECKS`, имя/семантика `check_branch_mergeable`, machine-verdict-ключи, схема БД. INV-4 (никогда push/force-push `main`) и запрет рестарта прод-контейнера — соблюдены. ### Что сделано (по ADR) - **D1 — tree-kill (корень утечки):** новый stdlib-only leaf `src/proc_group.py` спавнит re-test/coverage pytest в отдельной группе процессов (`start_new_session`) и при таймауте убивает **всё дерево** (`os.killpg`, каскад SIGTERM→grace→SIGKILL). Используют `merge_gate.retest_branch` и `coverage_gate.measure_coverage`. Fallback never-break: `subprocess_tree_kill_enabled=False`/не-POSIX → прежний `subprocess.run`. - **D2/D3 — классификация + маршрутизация:** `classify_retest_failure` различает `timeout`/`red`/`lock-busy`/`other`; инфра-таймаут → `_handle_merge_gate_infra_retry` (ограниченный повтор, задача остаётся на `deploy-staging`, **без** отката/developer-retry); красный re-test/конфликт → прежний rollback (BR-6). Исчерпание → один инфра-alert. - **D4 — контракт re-test:** пропуск re-test при доказанном no-op rebase (HEAD уже прошёл CI+tester+staging); fail-safe → re-test при любой неопределённости. - **D5 — бюджет:** `merge_retest_timeout_s` 600 → 900 + валидация; инвариант `reaper_max_running_s` соблюдён без правки. - **D6 — наблюдаемость:** счётчики + read-only блок `merge_gate` в `GET /queue`; regression-guard marker; координация с ORCH-111 без дубля. ### Тесты `tests/test_orch110_*.py` — TC-01…TC-12 (включая регресс инцидента red-before/green-after). Полный регресс: **1988 passed**. ### Документация (в том же PR) `docs/architecture/README.md`, `docs/overview/tech-pipeline.md`, `CLAUDE.md`, `CHANGELOG.md`, `.env.example` обновлены. ADR: `docs/work-items/ORCH-110/06-adr/ADR-001-merge-gate-retest-infra-tolerance-and-tree-kill.md`, сквозной `docs/architecture/adr/adr-0042-merge-gate-retest-infra-tolerance-and-tree-kill.md`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
admin added 6 commits 2026-06-15 10:42:41 +03:00
Eliminate the false `deploy-staging -> development` rollback that fired when the
merge-gate local re-test timed out (infra/resource) on a green CI + tester +
staging branch (incident ORCH-109/PR #129: a 516.7s suite blew its 600s budget
under CPU starvation from orphaned pytest processes -> timeout misrouted as a
code fault -> developer-retry loop -> manual gate).

Additive, 5 independent kill-switches, never-raise, self-hosting scope. Untouched
byte-for-byte: STAGE_TRANSITIONS, the QG_CHECKS registry, check_branch_mergeable
name/semantics, machine-verdict keys, the DB schema. INV-4 (never push/force-push
main) and the no-prod-restart rule are preserved.

- D1: new stdlib-only leaf src/proc_group.py runs the spawned re-test/coverage
  pytest in its own process group (start_new_session) and tree-kills the WHOLE
  group on timeout (os.killpg SIGTERM->grace->SIGKILL); used by
  merge_gate.retest_branch and coverage_gate.measure_coverage. No orphan leak.
  Fallback never-break: subprocess_tree_kill_enabled=False / non-POSIX -> the
  prior subprocess.run.
- D2/D3: merge_gate.classify_retest_failure distinguishes timeout/red/lock-busy/
  other; an infra timeout routes to _handle_merge_gate_infra_retry (bounded
  re-queue, task stays on deploy-staging, no rollback / no developer-retry); a
  red re-test / conflict still rolls back (BR-6). Exhaustion -> one infra alert.
- D4: skip the local re-test when the pre-merge rebase was a proven no-op (HEAD
  already CI/tester/staging-validated); fail-safe runs the re-test on any
  uncertainty. Flag merge_retest_skip_when_current_enabled.
- D5: merge_retest_timeout_s 600 -> 900 + _resolve_retest_timeout validation;
  reaper_max_running_s invariant preserved without change.
- D6: in-process counters + read-only merge_gate block in GET /queue; appended
  ("ORCH-110","classify_retest_failure","src/merge_gate.py") to
  MAIN_REGRESSION_MARKERS. Docs (README/internals overview/CLAUDE/CHANGELOG/
  .env.example) updated in the same PR.

Tests: tests/test_orch110_*.py (TC-01..TC-12, incl. the red-before/green-after
incident regression). Full suite green (1988 passed).

Refs: ORCH-110

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tester(ET): auto-commit from tester run_id=690
All checks were successful
CI / test (push) Successful in 4m35s
CI / test (pull_request) Successful in 4m24s
04d5671e1b
admin force-pushed feature/ORCH-110-bug-merge-gate-local-re-test-t from ba20455414 to 04d5671e1b 2026-06-15 10:42:41 +03:00 Compare
admin added 1 commit 2026-06-15 11:04:58 +03:00
deploy(ORCH-036): finalize SUCCESS for ORCH-110
All checks were successful
CI / test (push) Successful in 2m45s
CI / test (pull_request) Successful in 2m26s
f3cd6f4c5a
admin merged commit 28cd204d58 into main 2026-06-15 11:05:00 +03:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: admin/orchestrator#132