fix(merge_gate): retry transient Gitea merge errors (405/5xx) + already-in-main guard (ORCH-093) #104

Merged
admin merged 7 commits from feature/ORCH-093-bug-merge-gitea-405-5xx-hold-p into main 2026-06-09 22:51:43 +03:00
Owner

ORCH-093 — merge-актор ретраит транзиентные ошибки Gitea + гард «ветка уже в main»

Чинит инцидент ORCH-063: self-deploy прошёл, staging OK, PR был open+mergeable, конфликтов не было, но POST /pulls/{n}/merge вернул HTTP 405 "Please try again later" (Gitea пересчитывал mergeable сразу после пуша). One-shot merge_pr мгновенно вернул False → защита ORCH-071/081 удержала задачу на deploy + потребовала ручной домерж; повторный прогон финализатора плодил мусорный пустой PR.

Изменения (аддитивно, never-raise, под существующими kill-switch'ами)

  • merge_pr retry-loop (D1/D2): ретраится только мутирующий POST …/merge с экспоненциальным backoff (min(base*2^(i-1), max), суммарный сон (N-1)*max ≤ 10 с). Классификатор _classify_merge_response: транзиент (ретрай) — 405/408/5xx/таймаут/сетевая + 409/422 при mergeable==True; терминал (быстрый честный False, защита ORCH-071/073 как прежде) — 403/404/реальный конфликт (mergeable==False). mergeable==None/недоступно → дефолт-транзиент (fail-OPEN-в-ретрай).
  • ensure_open_pr гард already-in-main (D3): leaf _branch_fully_in_main (git merge-base --is-ancestor HEAD origin/main) → ветка целиком в main → исход "already-in-main" без создания пустого PR; git-ошибка → fail-OPEN на create-путь.
  • _handle_merge_verify (D4): already-in-main пропускает merge_pr и отдаёт авторитетному SHA-в-main довести до done (НЕ HOLD).
  • Конфиг (D5): ORCH_MERGE_RETRY_* (merge_retry_enabled kill-switch → one-shot; max_attempts=3, backoff_base_s=2, backoff_max_s=5) + .env.example.

Инварианты

INV-4 (мерж только через Gitea PR-merge API, никогда push/force-push в main), never-raise, STAGE_TRANSITIONS/QG_CHECKS/схема БД — сохранены 1:1. Защита ORCH-071/081 «deploy succeeded but not merged» — без изменений.

Тесты

tests/test_merge_gate.py TC-01..12, tests/test_config.py TC-13, tests/test_merge_verify.py TC-14..16; обновлён tests/test_orch082_ensure_pr.py. Полный регресс pytest tests/ -q зелёный (1389 passed).

Документация

README (merge-verify раздел), CLAUDE.md, CHANGELOG, .env.example — в том же PR. ADR: docs/work-items/ORCH-093/06-adr/ADR-001-*, сквозной docs/architecture/adr/adr-0027-*.

Refs: ORCH-093

🤖 Generated with Claude Code

## ORCH-093 — merge-актор ретраит транзиентные ошибки Gitea + гард «ветка уже в `main`» Чинит инцидент **ORCH-063**: self-deploy прошёл, staging OK, PR был `open`+`mergeable`, конфликтов не было, но `POST /pulls/{n}/merge` вернул `HTTP 405 "Please try again later"` (Gitea пересчитывал `mergeable` сразу после пуша). One-shot `merge_pr` мгновенно вернул `False` → защита ORCH-071/081 удержала задачу на `deploy` + потребовала ручной домерж; повторный прогон финализатора плодил мусорный пустой PR. ### Изменения (аддитивно, never-raise, под существующими kill-switch'ами) - **`merge_pr` retry-loop (D1/D2):** ретраится **только** мутирующий `POST …/merge` с экспоненциальным backoff (`min(base*2^(i-1), max)`, суммарный сон `(N-1)*max ≤ 10 с`). Классификатор `_classify_merge_response`: транзиент (ретрай) — `405`/`408`/`5xx`/таймаут/сетевая + `409`/`422` при `mergeable==True`; терминал (быстрый честный `False`, защита ORCH-071/073 как прежде) — `403`/`404`/реальный конфликт (`mergeable==False`). `mergeable==None`/недоступно → дефолт-транзиент (fail-OPEN-в-ретрай). - **`ensure_open_pr` гард already-in-main (D3):** leaf `_branch_fully_in_main` (`git merge-base --is-ancestor HEAD origin/main`) → ветка целиком в `main` → исход `"already-in-main"` без создания пустого PR; git-ошибка → fail-OPEN на create-путь. - **`_handle_merge_verify` (D4):** `already-in-main` пропускает `merge_pr` и отдаёт авторитетному SHA-в-main довести до `done` (НЕ HOLD). - **Конфиг (D5):** `ORCH_MERGE_RETRY_*` (`merge_retry_enabled` kill-switch → one-shot; `max_attempts=3`, `backoff_base_s=2`, `backoff_max_s=5`) + `.env.example`. ### Инварианты INV-4 (мерж только через Gitea PR-merge API, никогда push/force-push в `main`), never-raise, `STAGE_TRANSITIONS`/`QG_CHECKS`/схема БД — **сохранены 1:1**. Защита ORCH-071/081 «deploy succeeded but not merged» — без изменений. ### Тесты `tests/test_merge_gate.py` TC-01..12, `tests/test_config.py` TC-13, `tests/test_merge_verify.py` TC-14..16; обновлён `tests/test_orch082_ensure_pr.py`. Полный регресс `pytest tests/ -q` зелёный (**1389 passed**). ### Документация README (merge-verify раздел), CLAUDE.md, CHANGELOG, .env.example — в том же PR. ADR: `docs/work-items/ORCH-093/06-adr/ADR-001-*`, сквозной `docs/architecture/adr/adr-0027-*`. Refs: ORCH-093 🤖 Generated with [Claude Code](https://claude.com/claude-code)
admin added 6 commits 2026-06-09 22:47:22 +03:00
merge_pr now wraps ONLY the mutating POST /pulls/{n}/merge in a bounded
exponential-backoff retry-loop on TRANSIENT outcomes (405 "try again later",
408, any 5xx, network/timeout, and 409|422 while the PR is still mergeable);
TERMINAL outcomes (403/404/real conflict via mergeable==False) -> fast honest
False, so the ORCH-071/081 not-merged HOLD backstop is unchanged. Fixes the
ORCH-063 false HOLD + manual re-merge on Gitea's post-push mergeability hiccup.

ensure_open_pr gains an "already fully in main" guard (_branch_fully_in_main,
git merge-base --is-ancestor HEAD origin/main) BEFORE creating a PR -> new
"already-in-main" outcome avoids the garbage empty PR on a re-driven finalizer;
_handle_merge_verify skips merge_pr on that outcome and lets the authoritative
SHA-in-main check confirm -> done (not a HOLD). git error of the guard fails
OPEN to the create path.

New ORCH_MERGE_RETRY_* settings (kill-switch merge_retry_enabled -> one-shot,
max_attempts=3, backoff base=2/max=5). INV-4 (merge only via Gitea PR-merge API,
never push/force-push main), never-raise, STAGE_TRANSITIONS/QG_CHECKS/DB schema
unchanged. Docs (README merge-verify section, CLAUDE.md, CHANGELOG, .env.example)
updated in the same PR. Tests: test_merge_gate.py TC-01..12, test_config.py
TC-13, test_merge_verify.py TC-14..16; full suite green (1389).

Refs: ORCH-093

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tester(ET): auto-commit from tester run_id=516
All checks were successful
CI / test (push) Successful in 42s
CI / test (pull_request) Successful in 39s
7863932012
admin force-pushed feature/ORCH-093-bug-merge-gitea-405-5xx-hold-p from 11d19d2a6d to 7863932012 2026-06-09 22:47:22 +03:00 Compare
admin merged commit fbedd0485b into main 2026-06-09 22:51:43 +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#104