Files

7.9 KiB
Raw Permalink Blame History

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 CONFIRMEDdone, хотя 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) и сосредоточиться на системном фиксе навсегда (G2G5 / 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 подрывает само ядро автономности конвейера.