Files
orchestrator/docs/work-items/ORCH-071/01-brd.md

54 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# BRD — ORCH-071: Фантомный merge — деплой без слияния в main
## 1. Контекст и тип
- **Тип:** BUG CRITICAL (целостность `main` / надёжность деплоя, self-hosting).
- **Обнаружено:** Слава + Стрим, 2026-06-08, при разборе «ORCH-067 не подхватился».
- **Постмортем:** `docs/history/LESSONS_2026-06-08_phantom-merge.md`.
- **Подозрение на регресс:** ORCH-065 (idempotent merge / lease-reclaim) — последний честный merge (PR#66).
- **Связано:** восстановление текущего `main` ведётся ОТДЕЛЬНО (ветка `integ/restore-main-2026-06-08`); эта задача — ROOT-FIX, чтобы фантом не повторялся.
## 2. Проблема (бизнес-формулировка)
Self-deploy (Phase B) для self-hosting репо `orchestrator` собирает прод-образ из ВЕТКИ задачи и рапортует `finalize SUCCESS` + post-deploy `HEALTHY`, **но git-merge ветки в `main` НЕ происходит**. PR остаётся `open`. Следующая задача срезает свою ветку от устаревшего `main` → теряет код незалитых предшественников.
Накопительно потеряны в `main`: **ORCH-022, 059, 066, 068** (PR#67/68/69/70 — open). Последний реально слитый — ORCH-065 (PR#66).
## 3. Подтверждённый root cause (по результатам код-аудита)
Гипотеза A постмортема подтверждена аудитом кода ветки:
1. **В `src/` НЕТ кода, выполняющего merge PR в `main`** (`grep` по `pulls/.../merge`, `/merge`, `merge_pr` — 0 совпадений). Фактический merge выполняет ТОЛЬКО LLM-агент `deployer` через Bash в начале стадии `deploy` (см. `.openclaw/agents/deployer.md`).
2. Для self-hosting (`orchestrator`) стадия `deploy` оркеструется **детерминированным кодом** (`stage_engine._handle_self_deploy_phase_b``self_deploy.initiate_deploy` → finalizer `run_deploy_finalizer`), и агент `deployer` **НЕ запускается** (так предписывает `deployer.md`). Detached host-процесс делает retag staging-образа на прод-тег + рестарт 8500. **Ни одна фаза A/B/C не вызывает merge ветки в `main`.**
3. `run_deploy_finalizer` маппит exit-code хука `0→SUCCESS`, пишет `14-deploy-log.md` и вызывает `advance_stage(..., finished_agent="deployer")`. Гейт `check_deploy_status` читает только `deploy_status:` из артефакта → `SUCCESS → done`. **Состояние `main` нигде не верифицируется.**
Итог: для self-hosting путь `deploy` структурно НЕ содержит шага merge-в-main, а `done` достигается исключительно по deploy-маркеру. «Зелёный» деплой + здоровый прод (образ из рабочей ветки) маскируют отсутствие merge — сигнала о проблеме нет, пока следующая задача не потеряет код предшественника.
Вторичный фактор (усиливает риск даже если merge добавить наивно): Phase B **рестартит прод-контейнер**, поэтому любой держатель merge-lease / незавершённый git-шаг внутри процесса умирает до завершения merge (урок №3 постмортема).
## 4. Бизнес-цели
| ID | Цель |
|----|------|
| **G1** | Деплой ВЕРИФИЦИРУЕТ, что задеплоенный commit реально влит в `main` ПОСЛЕ деплоя (deployed SHA — предок `origin/main` ИЛИ `PR.merged==true`). Иначе — alert, задача НЕ `done`. |
| **G2** | Задача → `done` ТОЛЬКО при подтверждённом merge (`PR.merged==true`); маркеров `finalize`/`post-deploy` недостаточно. |
| **G3** | Merge в `main` завершается и подтверждается ДО рестарта прод-контейнера, ЛИБО merge вынесен в шаг, переживающий рестарт (паттерн `requeue_running_jobs` для merge-в-main). |
| **G4** | Диагностический runbook (4 проверки из постмортема) — в `docs/operations`. |
## 5. Не-цели
- Не менять source-of-truth (Plane), схему БД.
- Не отменять self-hosting safety (no auto-rollback / no-restart-others) — наоборот, усилить верификацией.
- Восстановление текущего `main` (долив 022/059/066/068) — ОТДЕЛЬНАЯ ветка `integ/restore-main-2026-06-08`, вне scope.
## 6. Инварианты (обязательны к соблюдению)
| ID | Инвариант |
|----|-----------|
| **INV-1** | **never-raise** на шаге верификации — при ошибке шлётся alert, не падение процесса/конвейера. |
| **INV-2** | self-hosting safety: верификация НЕ рестартит и НЕ роняет прод-контейнер `orchestrator` (8500), не трогает другие проекты. |
| **INV-3** | Ручной approve прод-деплоя (триггер «Confirm Deploy», ORCH-059) сохранён — новая логика не вводит авто-деплой. |
| **INV-4** | Никогда не делать force-push / прямой push в `main`; merge только через PR-merge API Gitea (как у deployer-агента сегодня). |
| **INV-5** | Идемпотентность: повторный прогон (re-drive/reaper/двойной webhook) не делает второй merge и не ломает контракты (опора на `pr_already_merged`, ORCH-065). |
## 7. Заинтересованные стороны
- **Owner** — одобряет прод-деплой («Confirm Deploy»), получает alert при «deployed but not merged».
- **Все проекты на инстансе** (enduro-trails) — косвенно: целостность `main` орка влияет на инструмент, обслуживающий их из общей БД/очереди.
## 8. Критерий успеха (бизнес-уровень)
После доработки невозможно состояние «задача `done` + прод задеплоен, а PR `open` / commit не в `main`»: либо merge подтверждён и задача `done`, либо задача НЕ `done` и поднят alert «deploy succeeded but not merged». Воспроизведение исходного сценария на staging показывает, что `main` реально получает commit.