99 lines
7.9 KiB
Markdown
99 lines
7.9 KiB
Markdown
# 01 — BRD: ORCH-073 — CRIT: эрозия main (код ORCH-067/069 затёрт ребейзами, не доехал)
|
||
|
||
- **Work Item:** ORCH-073
|
||
- **Тип:** BUG CRITICAL — целостность `main`, накопительный регресс/эрозия
|
||
- **Репозиторий:** orchestrator (self-hosting)
|
||
- **Ветка:** `feature/ORCH-073-crit-main-orch-067-069`
|
||
- **Связь:** усиливает/чинит ORCH-071 (merge-verify); НЕ покрыт ORCH-071.
|
||
|
||
## 1. Бизнес-проблема
|
||
|
||
Код успешно «задеплоенных» и переведённых в `done` задач **ORCH-067** (tracker bump,
|
||
Plane-статусы, кликабельные ссылки `plane_issue_link`) и **ORCH-069** (`qg0_title_max`)
|
||
**физически отсутствовал в `origin/main`**, хотя обе прошли весь конвейер, Confirm Deploy,
|
||
merge-verify `CONFIRMED` и стали `done`. В `main` попадали только их **docs-коммиты**
|
||
(staging-log / verdict через отдельные авто docs-PR), но НЕ код feature-веток.
|
||
|
||
Внешнее проявление (нашёл Слава, 08.06): «ссылок на задачу в Plane нет», карточка Telegram
|
||
показывает сырой номер задачи вместо кликабельной ссылки — потому что код ссылок есть в ветке
|
||
ORCH-067, но не в `main`.
|
||
|
||
**Накопительный характер:** каждая новая задача срезает ветку от УСТАРЕВШЕГО `main` и при merge
|
||
тихо (без конфликт-маркеров) затирает код предшественника. Уже потеряны ORCH-067 и ORCH-069;
|
||
без системного фикса теряется код каждой следующей задачи с правкой `CHANGELOG.md`.
|
||
|
||
## 2. Подтверждённый root cause (git-аудит 08.06, не гипотеза)
|
||
|
||
1. **`verify_merged_to_main` подтверждает merge по ложному признаку.**
|
||
`src/merge_gate.py::verify_merged_to_main` возвращает `True`, если выполнено **ЛИБО**
|
||
`pr_already_merged(repo, branch)`, **ЛИБО** `git merge-base --is-ancestor <sha> origin/main`.
|
||
Первая ветка (`pr_already_merged`) и есть дыра.
|
||
2. **`pr_already_merged` засчитывает ЛЮБОЙ merged PR ветки.**
|
||
`src/merge_gate.py::pr_already_merged` делает `GET /pulls?state=all&head=<branch>` и
|
||
возвращает `True`, если **хоть один** PR `merged==True`. У одной ветки несколько PR
|
||
(code-PR + авто docs-PR со staging/deploy-логами). Сливается docs-PR → функция говорит
|
||
«already-merged» → `verify_merged_to_main`=`True` → merge-verify `CONFIRMED` → `done`,
|
||
хотя code-PR НЕ слит. **Ложно-зелёный.**
|
||
3. **CHANGELOG.md-ребейзы — вторичный усилитель.**
|
||
Merge-gate `auto_rebase_onto_main` при конфликте `CHANGELOG.md` откатывает `deploy-staging →
|
||
development`; повторный ребейз ветки от старого `main` несёт устаревшие версии файлов
|
||
(`notifications.py`/`config.py`/`webhooks/plane.py`), которые при merge тихо затирают
|
||
соседний код (фантом-эффект, как в ORCH-071, без конфликт-маркеров).
|
||
|
||
> Уточнение для архитектора: в ТЗ упомянута «инвертированная проверка `merge-base --is-ancestor
|
||
> origin/main HEAD` (merge_gate.py ~76)» — это `branch_is_behind_main` (детектор «ветка
|
||
> свежая»), он корректен для своей цели. Фактический дефект merge-verify — это OR-ветка
|
||
> `pr_already_merged` в `verify_merged_to_main` (строка ~649), которая засчитывает docs-PR.
|
||
|
||
## 3. Состояние на момент анализа (G1)
|
||
|
||
Аудит `origin/main` показал, что **восстановительный PR #76** (`restore(main): re-merge
|
||
ORCH-067 + ORCH-069 (ORCH-073)`) уже вернул код в `main`:
|
||
- `plane_issue_link` присутствует (`src/notifications.py`), `qg0_title_max` присутствует
|
||
(`src/config.py`, `src/webhooks/plane.py`), `verify_merged_to_main` присутствует.
|
||
|
||
Таким образом **G1 (восстановление кода) фактически выполнено** ручным restore-PR. Задача
|
||
ORCH-073 должна **подтвердить и зафиксировать** это в критериях приёмки (AC-1) и сосредоточиться
|
||
на **системном фиксе навсегда** (G2–G5 / FR-1…FR-5), иначе регресс повторится.
|
||
|
||
## 4. Цели (Goals)
|
||
|
||
- **G1.** КОД ORCH-067 и ORCH-069 присутствует в `origin/main` одновременно с ORCH-071
|
||
(подтвердить restore-PR #76, зафиксировать маркеры > 0). Pytest зелёный. Прод задеплоен.
|
||
- **G2 (FR-2/FR-3).** `merge`/`pr_already_merged` различают **code-PR** и **docs-PR** — merge
|
||
засчитывается только за PR с кодом ветки (`base==main`, `head==<feature-branch>`).
|
||
- **G3 (FR-1, ядро).** `verify_merged_to_main` подтверждает merge **ТОЛЬКО** по факту «deployed
|
||
SHA — предок `origin/main`». PR-флаги вспомогательны, не достаточны.
|
||
- **G4 (FR-4).** Защита от CHANGELOG-затирания: `.gitattributes` с `CHANGELOG.md merge=union`
|
||
(+ опц. `docs/*.md merge=union` для append-only).
|
||
- **G5 (FR-5, регресс-гард навсегда).** После деплоя — sanity-проверка целостности `main`:
|
||
deployed SHA в `main` И набор маркеров ранее-merged задач не уменьшился. Откат соседнего кода
|
||
→ alert «main regressed», задача НЕ `done`.
|
||
|
||
## 5. Не-цели (Out of scope)
|
||
|
||
- Не менять Plane / схему БД.
|
||
- Не отменять self-hosting safety (не ронять прод, merge только через PR-API, без force-push в `main`).
|
||
- Не менять ручной гейт `Confirm Deploy`.
|
||
- Не менять поведение merge/verify для non-self репозиториев (enduro-trails) — обратная совместимость.
|
||
|
||
## 6. Инварианты
|
||
|
||
- **INV-1.** never-raise на верификации (alert, не падение).
|
||
- **INV-2.** self-hosting safety: прод не падает; merge только PR-API, без force-push в `main`.
|
||
- **INV-3.** ручной `Confirm Deploy` сохранён.
|
||
- **INV-4.** Идемпотентность: повторный прогон / reaper не делает второй merge; idempotency
|
||
опирается на «SHA-в-main», а не на «любой merged PR».
|
||
- **INV-5.** Обратная совместимость non-self (enduro): поведение merge/verify без изменений.
|
||
|
||
## 7. Заинтересованные стороны
|
||
|
||
- **Owner / Слава** — потребитель (видит кликабельные ссылки в карточке; доверие к merge-verify).
|
||
- **Все проекты на инстансе** (enduro-trails) — общий `main`/очередь/БД; регресс орка = групповой риск.
|
||
|
||
## 8. Срочность
|
||
|
||
КРИТИКАЛ. Без FR-1/FR-4/FR-5 каждая новая задача с правкой `CHANGELOG.md` продолжает терять код
|
||
предшественников (уже потеряны 067, 069). Ложно-зелёный merge-verify подрывает само ядро
|
||
автономности конвейера.
|