Files
orchestrator/docs/overview/tech-pipeline.md
claude-bot 651b9af7c3 fix(merge-gate): tolerate re-test infra-timeout + tree-kill spawned pytest
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>
2026-06-15 10:42:34 +03:00

8.4 KiB
Raw Blame History

Блок 2. Конвейер: стадии, гейты, маршруты

Источник истины — карта переходов STAGE_TRANSITIONS в src/stages.py и реестр гейтов QG_CHECKS в src/qg/checks.py; перечень ниже сверяется с кодом структурным тестом (tests/test_system_docs.py). Норматив структуры доков конвейера — PIPELINE_DOCS.

Схема конвейера

created → analysis → architecture → development → review → testing → deploy-staging → deploy → done
                          ↑                          │
                          └──── REQUEST_CHANGES ─────┘   (откат на доработку, max 3)

Плюс системный сток cancelled — терминальное состояние отменённой задачи (кнопка STOP, см. ниже). Это не ребро конвейера, а равноправный done сток: попасть в него можно с любой стадии, выйти — нельзя.

Стадии и гейты выхода

Гейт выхода (exit-гейт) — машинная проверка, без которой задача не покидает стадию:

Стадия Кто работает Гейт выхода (имя в реестре) Что проверяет
created вход конвейера (вебхук Plane)
analysis analyst check_analysis_approved пакет аналитики полон И постановка одобрена человеком
architecture architect check_architecture_done ADR / инфра-требования зафиксированы
development developer check_ci_green CI на ветке задачи зелёный
review reviewer check_reviewer_verdict машинный вердикт ревью: APPROVED
testing tester check_tests_passed машинный вердикт тестера: PASS
deploy-staging deployer check_staging_status репетиция выкладки на песочнице успешна
deploy deployer / finalizer check_deploy_status прод-выкладка реально успешна
done терминал
cancelled терминал (сток отмены)

Под-гейты деплойного ребра — врезки, не стадии

На переходе deploy-staging → deploy исполняются четыре под-гейта в нормативном порядке (security → merge → coverage → image-freshness):

  1. check_security_gate — секреты/зависимости, вердикт из security-отчёта;
  2. check_branch_mergeable — merge-gate: ветка догнана до свежего main (под merge-lease) и мержабельна;
  3. check_coverage_gate — покрытие тестами не ниже базовой линии/порога (baseline-ratchet);
  4. check_staging_image_fresh — staging-образ собран из актуального кода.

Это врезки в переход, а не стадии: они не появляются в карте STAGE_TRANSITIONS, а исполняются stage engine'ом внутри ребра. Провал любого из них — откат на доработку. Исключение (ORCH-110): инфра-таймаут локального re-test merge-gate (а не детерминированный красный тест) — это транзиент, а не дефект кода → ограниченный повтор + отдельный инфра-alert, без отката на доработку и без расхода developer-retry (красный re-test/конфликт по-прежнему откатывают). На ребре deploy → done аналогичная врезка merge-verify подтверждает, что код задачи реально слит в main (слияние — только через PR-API Gitea, см. интеграции).

Откаты

REQUEST_CHANGES от ревьюера, проваленные тесты или красный под-гейт возвращают задачу на стадию разработки с дословным перечнем замечаний. Лимит — 3 попытки подряд, дальше задача останавливается и требует человека (анти-петля).

Человеческие гейты и их снятие авто-лейблами

В штатном прогоне человек принимает ровно два решения:

  • Одобрение постановки (на analysis): перевод задачи в статус Approved пропускает её дальше;
  • Подтверждение прод-выкладки (на deploy): отдельный статус Confirm Deploy — чтобы привычный «approve» не выкатывал прод случайным кликом.

Оба решения можно снять декларативно — лейблами autoApprove / autoDeploy на задаче (пакетный авто-режим). Снимается только ожидание человеческого сигнала: ни одна техническая проверка не пропускается, autoDeploy физически не может выкатить непрошедшее под-гейты.

Багфикс-маршрут

Задача с меткой Bug едет коротким путём: облегчённая аналитика (но полный пакет документов) и пропуск стадии architecture — из аналитики сразу в разработку. Срезается только аналитика/проектирование: все гейты исполняются без изменений. Сложный баг эскалируется обратно в полный цикл.

Последовательность внутри репозитория (serial gate)

Новая задача репозитория не входит в работу, пока не завершена более ранняя (FIFO): ветка каждой задачи срезается от свежего main, уже содержащего код предшественника. Деградация прода после выкладки замораживает репозиторий (freeze) до ручного разбора — следующие задачи ждут.

Отмена: STOP → cancelled

Перевод задачи в статус STOP останавливает агента, снимает job'ы с очереди, удаляет рабочую ветку и worktree и переводит задачу в cancelled. Если задача в необратимой фазе (идёт слияние/выкладка) — отмена откладывается и применяется после честного завершения шага. STOP никогда не трогает main и прод-контейнер.

Статусная модель Plane: индикация ≠ управление

Статусы в Plane — слой индикации: они показывают человеку осмысленную картину хода задачи, но никогда не управляют конвейером (машина стадий — только STAGE_TRANSITIONS). Управляющих статусов ровно три: запуск в работу, Approved/Confirm Deploy (человеческие гейты) и STOP (отмена). Полная карта статусов — в инженерном справочнике.


Кто работает на каждой стадии и что сдаёт — агенты; как гейты читают вердикты — качество и безопасность.