---
type: review
work_item_id: ORCH-082
verdict: APPROVED
version: 1
---

# Review ORCH-082 — Гарантированный идемпотентный код-PR перед merge-verify

## Summary
Изменение закрывает отсутствующий инвариант «к моменту merge-verify у ветки есть открытый
код-PR» (root cause ложного HOLD «no open PR» на деплое ORCH-074). Реализовано строго аддитивно,
по дизайну ADR-001: новый идемпотентный leaf-актор `merge_gate.ensure_open_pr`, точечная врезка в
`stage_engine._handle_merge_verify` ПЕРЕД `merge_pr`, distinguishable HOLD-helper
`_hold_pr_create_failed`, делегирование `launcher._ensure_pr` в единый актор, kill-switch
`merge_verify_autocreate_pr_enabled`. Защита ORCH-073 (SHA-в-main + регресс-гард) не ослаблена и
остаётся приоритетной. Машина стадий, `QG_CHECKS`, схема БД, контракты деплоя — не тронуты.

Все 4 оси проверки пройдены:
- **ТЗ (02-trz.md):** FR-1..FR-5 реализованы — идемпотентный актор с фильтром
  `head==branch & base=="main"`, врезка после `validated_revision` и до `merge_pr`, честный HOLD
  на `failed`, защита ORCH-073 цела, идемпотентность повторного прохода.
- **AC (03-acceptance-criteria.md):** AC-1..AC-11 покрыты. Root cause задокументирован в ADR
  (R-A структурный + R-C проксимальный для ORCH-074); идемпотентность/existed (TC-02, TC-05);
  autocreate до merge_pr (TC-06); защита ORCH-073 (TC-07); логи различают исход (TC-12); фильтр
  base==main (TC-03); never-raise (TC-04, TC-08); kill-switch off (TC-09); условность non-self
  (TC-10); инварианты + документация; pytest зелёный.
- **ADR:** реализация 1:1 соответствует Р-1..Р-5 ADR-001; не нарушает глобальные adr-0013/0014
  (амендмент adr-0016 корректно зарегистрирован).
- **Качество кода:** never-raise соблюдён (все внешние вызовы в try/except), docstrings на
  публичных функциях, тесты содержательные (мок Gitea HTTP + интеграционные на под-гейт, не
  тривиальные). Секреты не хардкодятся (token из settings). `main` не push/force-push.

`pytest tests/ -q` → **1046 passed**. Целевые наборы (`test_orch082_ensure_pr.py`,
`test_orch082_merge_verify_autocreate.py`, `test_merge_verify.py`) — зелёные.

## Findings

### P0 — Blocker
- нет

### P1 — Must fix
- нет

### P2 — Should fix
- нет

### P3 — Nice-to-have
- [ ] Поведенческое уточнение `launcher._ensure_pr`: после делегирования в `ensure_open_pr`
  developer-путь теперь требует `base=="main"` (раньше принимался любой открытый PR с
  `head==branch`). Это корректное усиление (выравнивание с `merge_pr`) и для штатного потока
  PR всегда создаётся на `main` — регресса нет; зафиксировано для истории, действий не требует.

## Документация
Документация обновлена в том же PR — соответствие правилу №2/№6 CLAUDE.md:
- `docs/architecture/README.md` — добавлен раздел ORCH-082 в блок merge-verify (строки 209-240).
- `CHANGELOG.md` — запись в `## [Unreleased]`.
- `.env.example` — `ORCH_MERGE_VERIFY_AUTOCREATE_PR_ENABLED=true` + комментарий.
- `docs/architecture/adr/adr-0016-ensure-open-pr-before-merge-verify.md` — сквозной ADR (амендмент
  adr-0013/0014), зарегистрирован в `docs/architecture/adr/README.md` (макс. номер → 0016).
- `docs/work-items/ORCH-082/06-adr/ADR-001-*.md` — детальный ADR (root cause + дизайн).
- API сервиса не менялось (новых endpoint нет), конфиг-флаг отражён в `.env.example`. Все
  изменения `src/` (merge_gate, stage_engine, launcher, config) задокументированы.

**Вердикт: APPROVED** — P0/P1 отсутствуют, документация обновлена, тесты зелёные.
