--- 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 отсутствуют, документация обновлена, тесты зелёные.