Files
orchestrator/docs/architecture/adr/adr-0027-merge-actor-transient-retry-and-already-in-main.md

6.2 KiB
Raw Permalink Blame History

work_item, stage, author_agent, status, created_at, model_used
work_item stage author_agent status created_at model_used
ORCH-093 architecture architect proposed 2026-06-09 claude-opus-4-8

adr-0027: Merge-актор — ретрай транзиентных ошибок Gitea + гард «ветка уже в main»

Сквозной (cross-cutting) ADR. Амендмент к adr-0013 (merge-verify под-гейт), adr-0014 (SHA-в-main как источник истины) и adr-0016 (гарантированный код-PR). Детальное решение задачи — docs/work-items/ORCH-093/06-adr/ADR-001-merge-transient-retry-and-already-in-main-guard.md.

Регистрируется как сквозной, т.к. правит блок merge-актора с 3+ маркерами (ORCH-071, ORCH-073, ORCH-082) — анти-археология маркеров (docs/_standards/TRACEABILITY.md): сводный ADR агрегирует эволюцию вместо перечисления work item в коде.

Статус

Proposed

Контекст

Детерминированный merge-актор merge-verify под-гейта (deploy → done, self-hosting) состоит из ensure_open_prmerge_prverify_merged_to_main (src/merge_gate.py). Инцидент ORCH-063 (09.06) вскрыл два дефекта, оба сверены по коду прода:

  1. merge_prone-shot: POST /pulls/{index}/merge, любой не-200/201 → мгновенный False. Транзиентная икота Gitea (405 "Please try again later" при пересчёте mergeable сразу после пуша; 5xx; таймаут) → ложный HOLD защиты ORCH-071/073 → ручной домерж.
  2. ensure_open_pr — после ручного мержа код-PR closed, открытый не найден → создаёт новый пустой PR на ветке, уже целиком в main.

Защита ORCH-071/073 («deploy succeeded but not merged») корректна и сохраняется; задача снижает лишь ложные срабатывания на транзиентах и устраняет мусорные PR. Это блокер автономного прогона (эпик ORCH-088).

Решение

Аддитивно, без правки STAGE_TRANSITIONS / QG_CHECKS / схемы БД; INV-4 (мерж только через Gitea PR-merge API; никогда push/force-push в main) и never-raise сохранены.

  • Ретрай-loop вокруг POST …/merge (только мутирующий вызов) до merge_retry_max_attempts (дефолт 3) с экспоненциальным backoff и потолком (base 2, max 5; суммарно ≤10 с). Классификатор транзиент (405/408/5xx/таймаут/сетевое; 409/422 при mergeable==True; mergeable==None → транзиент-по-дефолту в рамках бюджета) vs терминал (403/404; 409/422 при mergeable==False) — по коду ответа и полю mergeable (GET /pulls/{index}). Терминал → быстрый честный False (защита ORCH-071/073 — как прежде). Образец — check_ci_green (attempt i/N) + transient-breaker агентов.
  • Гард already-in-main в ensure_open_pr: перед созданием PR — git merge-base --is-ancestor <branch> origin/main (rc==0 → ветка целиком в main) → новый исход "already-in-main", PR не создаётся; git-ошибка/ambiguous → fail-OPEN на текущий create-путь (гард не должен превратить икоту git в ложный no-op мержа). _handle_merge_verify трактует "already-in-main" как «мержить нечего» → пропуск merge_pr → авторитетный SHA-в-main (verify_merged_to_main, ADR-0014) доводит до done без мусорного PR.
  • Конфиг: merge_retry_enabled (kill-switch; False → one-shot, нулевая регрессия), merge_retry_max_attempts, merge_retry_backoff_base_s, merge_retry_backoff_max_s (env ORCH_MERGE_RETRY_*). Гард already-in-main — без отдельного флага (накрыт существующим merge_verify_autocreate_pr_enabled).

Объём раската — реально только self-hosting (merge_verify_applies); на прочих репо мерж делает LLM-deployer → изменение нейтрально.

Последствия

  • + Транзиент Gitea переживается автоматически → нет ложного HOLD / ручного домержа в автономном конвейере; нет мусорных пустых PR; повтор финализатора идемпотентен.
  • + Реальный конфликт → быстрый честный HOLD; защита ORCH-071/073 и SHA-в-main (ADR-0014) — авторитетны и неизменны.
  • Дефолт mergeable==None → transient может добавить ≤10 с до HOLD на реальном конфликте (бюджет жёстко ограничен); один лишний GET /pulls/{index} в редком ambiguous-кейсе.
  • Откат: ORCH_MERGE_RETRY_ENABLED=false → one-shot; ORCH_MERGE_VERIFY_AUTOCREATE_PR_ENABLED=false → отключает врезку ensure_open_pr с гардом. Полный откат — revert PR.

Ссылки

  • Детальный ADR: docs/work-items/ORCH-093/06-adr/ADR-001-merge-transient-retry-and-already-in-main-guard.md
  • Лехатая: adr-0006, adr-0013, adr-0014, adr-0016
  • Код: src/merge_gate.py, src/stage_engine.py::_handle_merge_verify, src/config.py