6.2 KiB
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_pr → merge_pr → verify_merged_to_main (src/merge_gate.py). Инцидент ORCH-063
(09.06) вскрыл два дефекта, оба сверены по коду прода:
merge_pr— one-shot:POST /pulls/{index}/merge, любой не-200/201→ мгновенныйFalse. Транзиентная икота Gitea (405 "Please try again later"при пересчётеmergeableсразу после пуша;5xx; таймаут) → ложный HOLD защиты ORCH-071/073 → ручной домерж.ensure_open_pr— после ручного мержа код-PRclosed, открытый не найден → создаёт новый пустой 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(envORCH_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.