109 lines
9.1 KiB
Markdown
109 lines
9.1 KiB
Markdown
# 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)`):
|
||
1. `GET …/pulls?state=open` → если есть PR с **`head.ref==branch` И `base.ref=="main"`** →
|
||
`("existed", <number>)`. **Фильтр идентичен `merge_pr`/ORCH-073 FR-3** — авто-docs-PR
|
||
(`base != main`) НЕ считается код-PR.
|
||
2. Иначе `POST …/pulls` (`head=branch`, `base=main`, заголовок/тело — авто) → `201` →
|
||
`("created", <number>)`.
|
||
3. Идемпотентность: если параллельно PR уже создан и Gitea вернёт ошибку «PR exists» —
|
||
повторный `GET` подтверждает существующий PR и возвращает `("existed", …)`, **дубль не
|
||
плодится** (AC-2).
|
||
4. Любая иная ошибка 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, frontmatter `deploy_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` (env `ORCH_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».
|