115 lines
10 KiB
Markdown
115 lines
10 KiB
Markdown
# 01 — Business Requirements Document (BRD)
|
||
|
||
**Work Item:** ORCH-043
|
||
**Тема:** Безопасная параллель в одном репо: merge-gate + auto-rebase + re-test
|
||
**Проект:** orchestrator (self-hosting)
|
||
**Автор:** Analyst
|
||
**Дата:** 2026-06-06
|
||
|
||
---
|
||
|
||
## 1. Контекст и проблема
|
||
|
||
Оркестратор ведёт несколько work item **параллельно**, каждый в своём изолированном
|
||
git worktree / ветке (`feature/ORCH-NNN-slug`, ORCH-2/S-4). Все ветки одного проекта
|
||
исходят из общего `origin/main` и в конце конвейера **вливаются обратно в `main`**.
|
||
|
||
Текущий конвейер валидирует ветку **относительно того состояния `main`, из которого
|
||
она была создана**, а не относительно `main` на момент слияния:
|
||
|
||
- `check_ci_green` (стадия `development`) — CI зелёный **на ветке** (Gitea commit status ветки).
|
||
- `check_tests_passed` (стадия `testing`) — вердикт тестировщика по коду **ветки**.
|
||
- На стадии `deploy` ветка вливается в `main` (слияние выполняет deployer-агент,
|
||
см. `src/webhooks/gitea.py` — комментарий про «deployer merges the PR at the START of its run»).
|
||
|
||
**Между «ветка проверена» и «ветка влита» `main` мог уйти вперёд** из-за слияния другой
|
||
параллельной задачи. Возникает **семантический (логический) конфликт слияния**: git
|
||
сливает ветки без текстового конфликта, но объединённый код `main` сломан — тесты,
|
||
которые были зелёными на ветке, на обновлённом `main` падают.
|
||
|
||
### Почему это критично именно здесь (self-hosting)
|
||
Проект ORCH правит инструмент, который СЕЙЧАС работает в проде и обслуживает другие
|
||
проекты (enduro-trails) из одного инстанса с общей БД и общей очередью (см. `CLAUDE.md`,
|
||
`docs/operations/INFRA.md`). Сломанный `main` оркестратора = встал конвейер ВСЕХ проектов.
|
||
Две параллельные ORCH-задачи, каждая «зелёная» по отдельности, при последовательном
|
||
слиянии способны положить прод.
|
||
|
||
### Сценарий-иллюстрация
|
||
1. Задачи A и B ответвлены от `main@C0`.
|
||
2. A проходит конвейер, вливается → `main@C1`.
|
||
3. B тестировалась против `C0`; её CI зелёный относительно `C0`. Git-слияние B в `C1`
|
||
проходит без текстового конфликта, но `C1` содержит изменения A, ломающие B.
|
||
4. `main` становится красным. Конвейер всех проектов деградирует.
|
||
|
||
---
|
||
|
||
## 2. Цель
|
||
|
||
Гарантировать, что ветка вливается в `main` **только если она проверена против
|
||
актуального `origin/main`**. Перед слиянием ветка автоматически догоняет `main`
|
||
(auto-rebase) и **повторно тестируется** (re-test); зелёный результат на актуальном
|
||
`main` — обязательное условие слияния (merge-gate). Слияния в `main` одного репозитория
|
||
**сериализуются**, чтобы окно гонки не воспроизводилось между двумя гейтами.
|
||
|
||
## 3. Заинтересованные стороны
|
||
- **Owner / разработчики** — не хотят красный `main` и ручные разборы конфликтов.
|
||
- **Все проекты на инстансе** — зависят от живого прод-оркестратора.
|
||
- **Агенты конвейера** — получают детерминированный гейт вместо ручной координации.
|
||
|
||
## 4. Объём (Scope)
|
||
|
||
### В объёме
|
||
1. **Merge-gate** — детерминированный гейт перед слиянием в `main`: пропускает
|
||
слияние только если ветка не отстаёт от `origin/main` И повторная проверка зелёная.
|
||
2. **Auto-rebase** — если ветка отстаёт от `origin/main`, автоматически догнать `main`
|
||
(rebase/merge ветки на актуальный `origin/main`) в worktree и запушить результат.
|
||
3. **Re-test** — после auto-rebase повторно прогнать тест-набор на догнанной ветке;
|
||
зелёный результат — условие прохода гейта.
|
||
4. **Сериализация слияний** — в пределах одного репозитория одновременно «догон+слияние»
|
||
выполняет только одна задача (merge-lock), иначе гонка воспроизводится.
|
||
5. **Откаты при неуспехе** — текстовый конфликт rebase ИЛИ красный re-test → возврат
|
||
задачи на `development` (по образцу существующих откатов) с понятным комментарием.
|
||
6. **Конфигурируемость** — пороги/тайм-ауты re-test и поведение гейта вынесены в `settings`.
|
||
|
||
### Вне объёма
|
||
- Изменение логики стадий `analysis` / `architecture` / `review`.
|
||
- Замена самого механизма слияния PR в Gitea (UI/настройки репозитория).
|
||
- Реальные прод-деплои (остаются за `scripts/orchestrator-deploy-hook.sh`).
|
||
- Кросс-репозиторная сериализация (гейт защищает `main` каждого репо отдельно).
|
||
|
||
## 5. Бизнес-требования (BR)
|
||
|
||
| ID | Требование |
|
||
|----|------------|
|
||
| BR-1 | Перед слиянием ветки в `main` оркестратор обязан проверить, что ветка содержит последний `origin/main` (не отстаёт). |
|
||
| BR-2 | Если ветка отстаёт — оркестратор автоматически догоняет её до `origin/main` без участия человека (auto-rebase). |
|
||
| BR-3 | После догона тест-набор повторно прогоняется; слияние разрешено только при зелёном результате (re-test). |
|
||
| BR-4 | Текстовый конфликт при auto-rebase или красный re-test НЕ приводит к слиянию: задача откатывается на `development` для ручного фикса. |
|
||
| BR-5 | В пределах одного репозитория «догон+проверка+слияние» сериализуются: две задачи не могут одновременно пройти merge-gate и влиться. |
|
||
| BR-6 | Гейт детерминированный (Python/гит-команды + код тестов), а не доверие LLM-агенту. |
|
||
| BR-7 | Гейт обязателен минимум для self-hosting репозитория `orchestrator`; применим к любому репо с параллельными задачами. |
|
||
| BR-8 | Все события гейта (догон, re-test, проход/откат) логируются и отражаются комментарием в Plane, без рассинхрона стадий. |
|
||
|
||
## 6. Критерии успеха
|
||
- Воспроизводимый ранее сценарий «две зелёные ветки ломают `main`» более не приводит
|
||
к красному `main`: вторая ветка либо догоняется и проходит re-test, либо откатывается.
|
||
- Прод-контейнер `orchestrator` не перезапускается и не падает в рамках задачи.
|
||
- Реестр гейтов и стадий остаётся консистентным (snapshot-тесты обновлены осознанно).
|
||
|
||
## 7. Риски и ограничения
|
||
- **Гонка между двумя гейтами** — снимается merge-lock (BR-5); без него фикс неполон.
|
||
- **Долгий re-test** — нужен тайм-аут и понятный откат, а не вис задачи.
|
||
- **Force-push догнанной ветки** — допустим только `--force-with-lease` и только по
|
||
own-ветке задачи; никогда по `main`.
|
||
- **Self-hosting** — любые изменения не должны ронять/рестартить прод-оркестратор;
|
||
обязательная страховка стадией `deploy-staging` (порт 8501) сохраняется.
|
||
- Окончательное место встройки в конвейер (новая стадия / гейт существующего перехода /
|
||
шаг перед слиянием) — **решение архитектора** (ADR), BRD фиксирует требуемое поведение.
|
||
|
||
## 8. Связанные артефакты
|
||
- `02-trz.md` — техническое задание (модули, гейт, конфиг, точки встройки).
|
||
- `03-acceptance-criteria.md` — критерии приёмки PASS/FAIL.
|
||
- `04-test-plan.yaml` — план тестов.
|
||
- Контекст кода: `src/qg/checks.py`, `src/stage_engine.py`, `src/git_worktree.py`,
|
||
`src/agents/launcher.py`, `src/webhooks/gitea.py`, `src/stages.py`, `src/config.py`.
|