9.1 KiB
02 — ТЗ: ORCH-082 (ORCH-81)
Гарантированный идемпотентный код-PR перед merge-verify + наблюдаемость
Машина стадий, реестр
QG_CHECKS, схема БД, exit-коды хука, контрактыcheck_deploy_status/_parse_deploy_status, защита ORCH-073 (SHA-в-main) — НЕ меняются. Изменение — точечная врезка «ensure PR» в под-гейт merge-verify + новый идемпотентный PR-актор вmerge_gate+ структурное логирование.
1. Задействованные модули src/
| Модуль | Роль в задаче | Характер изменения |
|---|---|---|
src/merge_gate.py |
leaf-логика merge-актора (merge_pr, verify_merged_to_main, pr_already_merged) |
+ новый идемпотентный актор ensure_open_pr(repo, branch) -> (status, detail) (never-raise). |
src/stage_engine.py |
под-гейт _handle_merge_verify на ребре deploy → done |
врезка: вызвать ensure_open_pr ПЕРЕД merge_pr; на failed → честный HOLD+alert; логировать исход. |
src/agents/launcher.py |
_ensure_pr (текущий единственный создатель PR) |
усилить наблюдаемость (различать created/existed/failed) — опционально переиспользовать новый актор merge_gate.ensure_open_pr, чтобы создание PR было единым кодом. Поведение «создавать только у developer» НЕ ужесточать без необходимости. |
src/config.py |
флаги | + kill-switch merge_verify_autocreate_pr_enabled (дефолт True), область — та же merge_verify_applies (self-hosting / merge_verify_repos). |
docs/architecture/README.md, CHANGELOG.md |
golden source | обновить (раздел ORCH-071/073 merge-verify — дописать про авто-создание PR). |
Точная сигнатура
ensure_open_pr, имя/дефолт kill-switch и место врезки — за архитектором (ADR). Ниже — функциональные требования к поведению, не финальный дизайн.
2. Функциональные требования
FR-1 — Идемпотентный PR-актор merge_gate.ensure_open_pr(repo, branch)
Возвращает структурированный исход (например ("existed"|"created"|"failed", detail)):
GET …/pulls?state=open→ если есть PR сhead.ref==branchИbase.ref=="main"→("existed", <number>). Фильтр идентиченmerge_pr/ORCH-073 FR-3 — авто-docs-PR (base != main) НЕ считается код-PR.- Иначе
POST …/pulls(head=branch,base=main, заголовок/тело — авто) →201→("created", <number>). - Идемпотентность: если параллельно PR уже создан и Gitea вернёт ошибку «PR exists» —
повторный
GETподтверждает существующий PR и возвращает("existed", …), дубль не плодится (AC-2). - Любая иная ошибка HTTP/parse/сети →
("failed", <reason>). Never-raise.
FR-2 — Врезка в _handle_merge_verify (ребро deploy → done)
Внутри существующего _handle_merge_verify, ПОСЛЕ merge_verify_applies(repo)-гейта и
резолва validated_revision, но ПЕРЕД merge_pr:
- если
merge_verify_autocreate_pr_enabled→ вызватьensure_open_pr(repo, branch); status == "created"|"existed"→ продолжить штатно кmerge_pr→verify_merged_to_main;status == "failed"→ честный HOLD + alert (как сегодняшний not-merged путь:note_not_merged_alert+set_issue_blocked+ Plane-коммент + Telegram; задача остаётся наdeploy, НЕdone, БЕЗ отката на development) с сообщением, отражающим «PR создать не удалось» (а не «PR не влит»).- kill-switch off → текущее поведение 1:1 (никакого создания PR).
FR-3 — Защита ORCH-073 цела (регресс-инвариант)
Создание PR не подменяет проверку слияния. После ensure_open_pr + merge_pr верификация
остаётся только verify_merged_to_main (SHA-в-main, ORCH-073 FR-1) + регресс-гард
(check_main_regression). Если код реально не оказался в main — HOLD сохраняется. Создание PR
лишь устраняет ложный HOLD «no open PR», который конвейер обязан был предотвратить.
FR-4 — Наблюдаемость (G3)
В лог писать однозначный исход на каждом из мест работы с PR:
merge-verify ensure_open_pr -> created PR #N/… -> existed PR #N/… -> failed: <reason>. Сообщение HOLD приfailedобязано отличаться текстом от HOLD «not merged» (оператор должен видеть, что причина — невозможность создать PR, а не невозможность слить уже созданный). Желательно — пометка исхода в14-deploy-log.md(best-effort, frontmatterdeploy_status:нетронут).
FR-5 — Идемпотентность повторного прохода
Повторный заход в merge-verify (reaper / reconciler / повторный approve) при уже существующем
PR → ensure_open_pr возвращает ("existed", …), merge_pr → already-merged/штатно — без
дублей PR и без побочных эффектов (INV-5/AC-9 ORCH-073 сохранены).
3. Изменения API (HTTP / внутренние)
- Внешний HTTP API сервиса — без изменений (новых endpoint нет).
- Исходящие вызовы Gitea: новый
POST /api/v1/repos/{owner}/{repo}/pullsиз контекста merge-verify (тот же вызов, что уже делает_ensure_pr); чтение — существующийGET …/pulls?state=open. - Внутренний контракт
merge_gate: новая публичная функцияensure_open_pr(leaf, never-raise), вызывается изstage_engine._handle_merge_verify(и опционально изlauncher._ensure_pr).
4. Изменения схемы БД
Нет. Состояние идемпотентности выводится из самого Gitea (наличие открытого PR), миграции не требуются. (Согласуется с restart-safe-моделью merge-verify.)
5. Требования к новым QG checks
Новых зарегистрированных QG-checks нет. Это под-гейт-врезка в advance_stage
(_handle_merge_verify), как и сам ORCH-071 merge-verify — не отдельный QG_CHECKS-элемент.
Реестр QG_CHECKS не трогается.
6. Конфигурация / kill-switch
merge_verify_autocreate_pr_enabled: bool = True(envORCH_MERGE_VERIFY_AUTOCREATE_PR_ENABLED).False→ ровно прежнее поведение (нет авто-создания PR; «no open PR» → HOLD как раньше).- Область действия —
merge_gate.merge_verify_applies(repo): реально только для self-hosting /merge_verify_repos; прочие репо — no-op.
7. Артефакты pipeline (создать/обновить)
docs/work-items/ORCH-082/06-adr/ADR-001-*.md— архитектор (root cause G1 + дизайн ensure-PR).12-review.md,13-test-report.md,14/15/16-*— последующие стадии.- Обновить
docs/architecture/README.md(блок ORCH-071/073) иCHANGELOG.md— в ТОМ ЖЕ PR (правило агентов №2/№6).
8. Инварианты (не нарушать)
STAGE_TRANSITIONS,QG_CHECKS, схема БД,check_deploy_status/_parse_deploy_status, exit-коды хука, terminal-sync, merge-gate (ORCH-043), image-freshness (ORCH-058) — без изменений.- Контракт never-raise на всём пути merge-verify (INV-1 ORCH-073).
- Слияние только через PR (
POST /pulls/{index}/merge);mainникогда не push/force-push. - Защита ORCH-073 (SHA-в-main + регресс-гард) приоритетна: при конфликте «создать PR» проигрывает «не дать ложно-зелёный done».