130 lines
11 KiB
Markdown
130 lines
11 KiB
Markdown
# 02 — ТЗ: ORCH-073 — системный фикс эрозии main + восстановление кода 067/069
|
||
|
||
> ТЗ описывает ТРЕБУЕМОЕ ПОВЕДЕНИЕ и точки изменения. Выбор конкретного дизайна
|
||
> (где именно резать docs-PR от code-PR, формат набора регресс-маркеров) — за архитектором (`06-adr`).
|
||
> Запрещено комментировать ТЗ задним числом: если требование не годится — вернуть в Анализ.
|
||
|
||
## 1. Задействованные модули `src/`
|
||
|
||
| Модуль | Роль в фиксе | FR |
|
||
| --- | --- | --- |
|
||
| `src/merge_gate.py` | `verify_merged_to_main`, `pr_already_merged`, `merge_pr`, новый регресс-гард | FR-1, FR-2, FR-3, FR-5 |
|
||
| `src/stage_engine.py` | `_handle_merge_verify` (под-гейт `deploy → done`) — точка вызова FR-1/FR-5 | FR-1, FR-5 |
|
||
| `src/config.py` | (опц.) настройки регресс-гарда: kill-switch + набор маркеров/таймаут | FR-5 |
|
||
| `.gitattributes` (корень репо, новый) | `CHANGELOG.md merge=union` (+ опц. `docs/*.md merge=union`) | FR-4 |
|
||
| `docs/architecture/README.md` | раздел merge-verify — обновить под новую семантику | AC-8 |
|
||
| `CHANGELOG.md` | запись Unreleased | AC-8 |
|
||
| `docs/work-items/ORCH-073/06-adr/` | ADR на новую семантику merge-verify + регресс-гард | AC-8 |
|
||
|
||
## 2. Требуемые изменения по коду
|
||
|
||
### FR-1 (G3, ядро) — `verify_merged_to_main` чинит семантику
|
||
**Текущее (баг):** `src/merge_gate.py::verify_merged_to_main(repo, branch, sha)` возвращает `True`,
|
||
если `pr_already_merged(...)` **ИЛИ** `git merge-base --is-ancestor <sha> origin/main`.
|
||
OR-ветка `pr_already_merged` засчитывает docs-PR → ложно-зелёный.
|
||
|
||
**Требование:** подтверждение merge — **ТОЛЬКО** прямой факт «deployed commit является предком
|
||
`origin/main`»:
|
||
- после `git fetch origin main` выполнить `git merge-base --is-ancestor <deployed_sha> origin/main`;
|
||
- `rc==0` → `True` (код в main), иначе → `False`.
|
||
- `pr_already_merged` **НЕ может быть единственным/достаточным** условием `True`. Допустимо
|
||
оставить PR-флаг только как **вспомогательный** сигнал (idempotency / диагностика), но он НЕ
|
||
должен подтверждать merge при отсутствии SHA в main.
|
||
- Пустой `sha` → неопределённо → `False` (fail-closed: alert + HOLD), как сейчас.
|
||
- never-raise: любая git/HTTP-ошибка → `False` (INV-1).
|
||
|
||
### FR-2 (G2) — `pr_already_merged` различает code-PR и docs-PR
|
||
**Текущее (баг):** `src/merge_gate.py::pr_already_merged` возвращает `True` за ЛЮБОЙ
|
||
`merged==True` PR из `GET /pulls?state=all&head=<branch>` — включая авто docs-PR.
|
||
|
||
**Требование (на выбор архитектора, предпочтителен вариант «б»):**
|
||
- **(а)** засчитывать merged только для PR, реально несущего код ветки: `base.ref==main`
|
||
И `head.ref==<feature-branch>` (исключить docs/* ветки и docs-only PR); **или**
|
||
- **(б, предпочтительно)** понизить роль `pr_already_merged` до **idempotency-guard**: единственный
|
||
критерий «merged/done» — SHA-предок-`main` (FR-1); PR-флаги вспомогательны.
|
||
- Поведение для non-self репо (enduro) не меняется (INV-5).
|
||
- never-raise → `False` (консервативно).
|
||
|
||
### FR-3 (G2) — `merge_pr` реально сливает code-ветку
|
||
**Требование:** `src/merge_gate.py::merge_pr` мержит ИМЕННО feature-PR с кодом (`base==main`,
|
||
`head==<feature-branch>`), а не полагается на docs-PR. После merge — обязательная верификация
|
||
по FR-1 (SHA в main) как единственный источник истины. Merge только через Gitea PR-merge API,
|
||
никогда push/force-push в `main` (INV-2).
|
||
|
||
### FR-5 (G3 регресс-гард, защита навсегда) — sanity-проверка целостности main
|
||
**Требование:** перед фиксацией `done` (в `_handle_merge_verify`, ПОСЛЕ зелёного
|
||
`check_deploy_status`, до `update_task_stage`):
|
||
1. Подтвердить FR-1 (deployed SHA — предок `origin/main`).
|
||
2. (опц., по дизайну) Проверить, что в `origin/main` присутствует **набор маркеров** ключевых
|
||
функций недавно-merged задач (regression marker set) — merge не уменьшил его.
|
||
3. При откате соседнего кода / отсутствии маркера → **alert** «main regressed: code of <prev
|
||
tasks> missing» (Telegram + Plane), задача **НЕ `done`** (HOLD), как ветка not-merged в ORCH-071.
|
||
- Реакция — **ALERT-only + HOLD**, без авто-отката на `development` (это инфра-дефект, не код-фолт).
|
||
- never-raise (INV-1); kill-switch (как `merge_verify_enabled`); условность только для self-hosting
|
||
/ `merge_verify_repos` (INV-5).
|
||
- Набор маркеров — конфигурируемый/декларативный (например, в `src/config.py` или рядом), чтобы
|
||
следующие задачи могли его расширять. Точный формат — за архитектором.
|
||
|
||
### FR-4 (G2/G4 корень) — `.gitattributes` с `merge=union`
|
||
**Требование:** в корне репо завести `.gitattributes`:
|
||
```
|
||
CHANGELOG.md merge=union
|
||
# опционально для append-only документов:
|
||
# docs/**/*.md merge=union # ВНИМАНИЕ: union НЕ годится для файлов, где правки
|
||
# переписывают строки — применять только к append-only
|
||
```
|
||
- `merge=union` встроен в git (драйвер по умолчанию), доп. конфиг хоста не требуется — но
|
||
проверить, что атрибут реально применяется в worktree агентов (`git check-attr merge CHANGELOG.md`).
|
||
- Эффект: при `auto_rebase_onto_main` правки `## [Unreleased]` авто-сливаются (обе записи
|
||
сохраняются) без конфликта → ветка не откатывается в `development` и не затирает соседний код.
|
||
|
||
## 3. Изменения API
|
||
|
||
- **Внешних HTTP API оркестратора (`src/main.py` endpoints) НЕ менять.**
|
||
- Внутренние сигнатуры:
|
||
- `verify_merged_to_main(repo, branch, sha) -> bool` — семантика меняется, сигнатура сохраняется.
|
||
- `pr_already_merged(repo, branch) -> bool` — семантика/назначение уточняется.
|
||
- `merge_pr(repo, branch) -> tuple[bool, str]` — поведение уточняется (фильтр code-PR).
|
||
- (опц.) новая функция регресс-гарда в `merge_gate.py` — `tuple[bool, str]`/`bool`, never-raise.
|
||
- `GET /queue` `merge_verify_status()` — допустимо дополнить счётчиком регресс-алертов (read-only,
|
||
не источник истины).
|
||
- Внешние вызовы Gitea — те же эндпоинты (`/pulls`, `/pulls/{index}/merge`).
|
||
|
||
## 4. Изменения схемы БД
|
||
|
||
- **НЕТ.** Схема БД (`src/db.py`) не трогается (Не-цель). Регресс-гард опирается на git/`origin/main`,
|
||
не на новые таблицы.
|
||
|
||
## 5. Требования к новым/изменённым QG checks
|
||
|
||
- **Новых зарегистрированных QG-checks не вводить.** Логика остаётся **под-гейтом** в
|
||
`advance_stage` (`_handle_merge_verify`), как ORCH-071 — не новый элемент реестра `QG_CHECKS`.
|
||
- Реестр `QG_CHECKS`, `check_deploy_status`, `_parse_deploy_status`, merge-gate
|
||
(`check_branch_mergeable`), image-freshness — **без изменений**.
|
||
|
||
## 6. Конфигурация (`src/config.py` / `.env.example`)
|
||
|
||
- Существующие `merge_verify_enabled` (kill-switch, дефолт `true`), `merge_verify_repos` (пусто →
|
||
только self-hosting), `merge_pr_timeout_s`, `merge_verify_timeout_s` — переиспользовать.
|
||
- (опц., по дизайну) новые: kill-switch регресс-гарда и декларация набора маркеров. Дефолты —
|
||
безопасные (для non-self — no-op). Любой новый ключ задокументировать в `.env.example`.
|
||
|
||
## 7. Артефакты pipeline, которые должны быть созданы/обновлены
|
||
|
||
- `docs/work-items/ORCH-073/06-adr/ADR-001-*.md` — решение по новой семантике merge-verify
|
||
(FR-1/FR-2/FR-3) + регресс-гард (FR-5) + `.gitattributes` (FR-4).
|
||
- `docs/architecture/README.md` — обновить раздел «Merge-в-main + пост-деплой верификация»
|
||
(ORCH-071) под FR-1 (SHA как единственный критерий) и добавить регресс-гард FR-5.
|
||
- `CHANGELOG.md` — запись в `## [Unreleased]`.
|
||
- `docs/work-items/ORCH-073/10-tech-risks.md`, `12-review.md`, `13-test-report.md`,
|
||
`14-deploy-log.md`, `15-staging-log.md` — по ходу конвейера.
|
||
- `04-test-plan.yaml` (этот пакет) — реализовать тесты в `tests/`.
|
||
|
||
## 8. Аудит G4 (зафиксировать в ADR / 06-adr)
|
||
|
||
Зафиксировать подтверждённую причину docs-only merge: у feature-ветки 067/069 в `main` попадали
|
||
только авто docs-PR (staging-log / deploy-log / CLAUDE.md / CHANGELOG), а code-PR не сливался,
|
||
при этом `pr_already_merged` засчитывал docs-PR → merge-verify ложно `CONFIRMED` → `done`.
|
||
Корень устранён FR-1+FR-2+FR-3. Восстановление кода (G1) уже выполнено restore-PR #76 —
|
||
подтвердить маркеры в `origin/main` (AC-1).
|