fix(merge_gate): retry transient Gitea merge errors (405/5xx) + already-in-main guard (ORCH-093) #104
Reference in New Issue
Block a user
Delete Branch "feature/ORCH-093-bug-merge-gitea-405-5xx-hold-p"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
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-shotmerge_prмгновенно вернулFalse→ защита ORCH-071/081 удержала задачу наdeploy+ потребовала ручной домерж; повторный прогон финализатора плодил мусорный пустой PR.Изменения (аддитивно, never-raise, под существующими kill-switch'ами)
merge_prretry-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).ORCH_MERGE_RETRY_*(merge_retry_enabledkill-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.pyTC-01..12,tests/test_config.pyTC-13,tests/test_merge_verify.pyTC-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
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>11d19d2a6dto7863932012