146 lines
14 KiB
Markdown
146 lines
14 KiB
Markdown
# 01 — BRD: ORCH-088 — Пакетный автономный режим (Этап 1: serial e2e)
|
||
|
||
Work Item: **ORCH-088**
|
||
Repo: **orchestrator** (self-hosting)
|
||
Стадия: analysis
|
||
Заказчик: Слава
|
||
Тип: ЭПИК — Этап 1 (минимальный, без параллелизма)
|
||
|
||
> ⚠️ **Скоп зафиксирован Владельцем 09.06.** Реализуется ТОЛЬКО serial e2e (FR-1…FR-5).
|
||
> Фазовый режим A/B/C, merge-очередь FIFO, pre-merge rebase и зависимость от ORCH-83 —
|
||
> **ОТМЕНЕНЫ, не реализовывать.**
|
||
|
||
---
|
||
|
||
## 1. Бизнес-контекст и проблема
|
||
|
||
### 1.1. Цель эпика
|
||
Дать оркестратору **масштаб автономности**: накидать вечером 10–20 задач и получить к утру
|
||
последовательно проведённый через весь конвейер (analysis → … → deploy → done) пакет — без
|
||
ручного запуска каждой задачи и без взаимного повреждения веток.
|
||
|
||
### 1.2. Корневая проблема — «stale-анализ» (логический, а не код-затирание)
|
||
Конвейер создаёт ветку задачи от `main`. Если задача **N+1** входит в анализ, пока задача **N**
|
||
ещё **не влита в `main`**, то ветка N+1 срезается от **устаревшего** `main` (без кода N). Результат:
|
||
- семантически устаревшая база разработки;
|
||
- риск потери/переоткрытия уже сделанного в N (накопительные потери прецедента — постмортем
|
||
фантомного merge, см. CLAUDE.md / ORCH-071);
|
||
- ручной разбор конфликтов утром вместо готового пакета.
|
||
|
||
Физическое **код-затирание** при параллельном merge уже закрыто (ORCH-026 auto_rebase + merge-lease).
|
||
ORCH-088 закрывает **логический** разрыв: гарантирует, что каждая следующая задача стартует от
|
||
`main`, **уже содержащего все предыдущие завершённые задачи репо**.
|
||
|
||
### 1.3. Почему сериализация именно «от АНАЛИЗА», а не «от merge»
|
||
Ветка срезается в самом начале — на входе в анализ (`start_pipeline` создаёт ветку в Gitea, далее
|
||
worktree). Если допустить параллельный анализ N и N+1, ветка N+1 уже срезана от старого `main` —
|
||
поздняя сериализация на merge проблему не лечит. Поэтому gate ставится на **входе новой задачи в
|
||
анализ**: новая задача не начинает анализ (и не режет ветку), пока в репо есть незавершённая задача.
|
||
|
||
### 1.4. Установленные факты (проверено, не изобретать)
|
||
- **Plane API v1:** bulk-операций НЕТ; issue-relation НЕТ → зависимости/очередь оркестратор хранит
|
||
**у себя** (gate в планировщике/claim по локальной БД), не в Plane.
|
||
- **Уже есть (переиспользовать):** `max_concurrency=1`; ORCH-026 auto_rebase_onto_main +
|
||
force-with-lease + merge-lease; персистентная очередь ORCH-1 (таблица `jobs`, atomic claim,
|
||
restart-safe); ORCH-021 post-deploy monitor (для self — всегда `ALERT_ONLY`, db-стадия `done`
|
||
достигается ДО окна мониторинга — ORCH-071/066).
|
||
|
||
### 1.5. Решения Владельца (09.06) — приняты как требования
|
||
| # | Решение |
|
||
|---|---------|
|
||
| D-1 | Serial e2e подтверждён. BRD появляются **по одному** — осознанный размен: надёжность > батч-просмотр BRD. |
|
||
| D-2 | Сигнал «задача завершена» = **успешный прод-деплой** (`stage = done` после прод-деплоя). НЕ merge, НЕ staging. |
|
||
| D-3 | Мониторинг (~15 мин) **НЕ ждём**: gate N+1 открывается по `stage = done`, не по завершению окна мониторинга. |
|
||
| D-4 | Auto-rollback прода во время мониторинга → **заморозить gate + алерт**; следующая НЕ стартует до ручного снятия. |
|
||
| D-5 | Зависимость ORCH-088 ← ORCH-83 **убрана** — запускается независимо. |
|
||
|
||
---
|
||
|
||
## 2. Объём (scope)
|
||
|
||
### 2.1. В объёме (Этап 1)
|
||
- **FR-1 — Serial gate (per-repo):** новая задача не входит в `analysis` (не режет ветку, не
|
||
запускает analyst), пока в том же репо есть незавершённая задача (`stage < done`).
|
||
- **FR-2 — Очередь e2e:** накиданные задачи становятся в очередь и обрабатываются **строго по одной**
|
||
end-to-end (от анализа до прод-деплоя).
|
||
- **FR-3 — Per-repo изоляция:** сериализация действует **внутри одного репо**; разные репо
|
||
(`orchestrator`, `enduro-trails`) идут **параллельно** (независимые `main`).
|
||
- **FR-4 — Restart-safe:** активная задача и состояние gate определяются по **БД** (не in-memory) —
|
||
переживают рестарт оркестратора.
|
||
- **FR-5 — Rollback-freeze:** auto-rollback / деградация прода → gate репо **заморожен** + Telegram-
|
||
алерт; следующая задача не стартует до **ручного** снятия заморозки.
|
||
|
||
### 2.2. Вне объёма (явно, не делать)
|
||
- Merge-очередь FIFO; pre-merge rebase как отдельная фича; фазовый режим A/B/C; любая координация
|
||
**параллелизма** задач внутри одного репо.
|
||
- Изменение `STAGE_TRANSITIONS`, реестра `QG_CHECKS`, новых стадий конвейера.
|
||
- Зависимость от ORCH-83.
|
||
|
||
---
|
||
|
||
## 3. Заинтересованные стороны
|
||
- **Владелец/оператор (Слава):** накидывает пакет вечером, разбирает заморозку при сбое, читает
|
||
алерты, снимает freeze вручную.
|
||
- **Self-hosting прод (`orchestrator`):** обслуживает enduro-trails из того же инстанса — нельзя
|
||
ронять/блокировать конвейер enduro (FR-3).
|
||
|
||
---
|
||
|
||
## 4. Бизнес-требования (BR)
|
||
|
||
| ID | Требование | Связь |
|
||
|----|------------|-------|
|
||
| BR-1 | Пока в репо есть задача со `stage < done`, любая **другая** задача того же репо не начинает анализ — ждёт в очереди. | FR-1, AC-1 |
|
||
| BR-2 | Как только активная задача достигла `stage = done` (после прод-деплоя), следующая задача того же репо **автоматически** стартует анализ. | FR-1/FR-2, AC-2, D-2 |
|
||
| BR-3 | Ветка новой задачи срезается от `main`, **уже содержащего все ранее завершённые задачи репо** — нет stale-base. Branch не создаётся раньше, чем предшественник завершён. | FR-1, AC-6, §1.2 |
|
||
| BR-4 | Сериализация — строго per-repo; задачи разных репо идут параллельно, gate одного репо не влияет на другой. | FR-3, AC-4 |
|
||
| BR-5 | Активная задача и факт заморозки определяются из БД; после рестарта оркестратора gate ведёт себя идентично (не «забывает» активную задачу и не «теряет» freeze). | FR-4, AC-3 |
|
||
| BR-6 | Auto-rollback/деградация прода (post-deploy) → per-repo freeze + Telegram-алерт; следующая задача не стартует до ручного снятия freeze. | FR-5, AC-5, D-4 |
|
||
| BR-7 | Мониторинг прода (~15 мин) gate **не ждёт** — открытие gate привязано к `stage = done`. (Freeze BR-6 — отдельный, независимый от `stage` сигнал, т.к. к моменту деградации задача уже `done`.) | D-3, AC-5 |
|
||
| BR-8 | Поведение управляется kill-switch'ом и областью репо (как ORCH-35/43/58): выключение флага → строго прежнее поведение (нулевая регрессия для enduro). | NFR |
|
||
| BR-9 | Состояние gate наблюдаемо в `GET /queue` (активная задача репо, очередь ожидающих, статус freeze). | NFR |
|
||
|
||
---
|
||
|
||
## 5. Нефункциональные требования (NFR)
|
||
|
||
| ID | Требование |
|
||
|----|------------|
|
||
| NFR-1 | **never-raise:** любая ошибка логики gate не роняет claim/конвейер. Поведение при ошибке БД — **fail-open** для claim (транзиентный сбой не должен заклинить очередь ВСЕХ проектов), **fail-closed** для freeze (сомнение в безопасности прода → не стартовать). |
|
||
| NFR-2 | **Offline-устойчивость:** проверка gate в горячем цикле claim не должна ходить в сеть (Plane/Gitea) — иначе встанет очередь всех проектов. Источник истины — локальная БД. |
|
||
| NFR-3 | **Restart-safe:** никакого in-memory состояния; freeze и активная задача — в БД. |
|
||
| NFR-4 | **Нулевая регрессия:** при выключенном флаге запрос claim и путь старта идентичны текущим; enduro не затрагивается. |
|
||
| NFR-5 | **Инварианты неизменны:** `STAGE_TRANSITIONS`, реестр `QG_CHECKS`, `check_*`, exit-коды deploy-хука, merge-gate, схема post-deploy — не меняются (допустима только аддитивная, идемпотентная миграция БД). |
|
||
| NFR-6 | **Self-hosting безопасность:** механизм не рестартит/не роняет прод-контейнер; freeze — пассивная остановка стартов, не действие над прод. |
|
||
|
||
---
|
||
|
||
## 6. Допущения и ограничения
|
||
- `max_concurrency = 1` остаётся (Этап 1 без параллелизма); gate не зависит от значения, но не
|
||
ослабляет его.
|
||
- «Завершена» = `tasks.stage = 'done'`. Для self-hosting `done` достигается merge-verify + прод-деплой
|
||
(ORCH-071/036); пост-деплойное окно мониторинга идёт **после** `done` и gate его не ждёт (BR-7).
|
||
- Задача в статусе **Blocked / Needs Input** имеет `stage < done` и, следовательно, **держит gate
|
||
закрытым** — это сознательное поведение (Этап 1): пока задача не доведена до прод или не закрыта
|
||
оператором, пакет не движется. (Поведение зафиксировать в AC; альтернатива — вне скопа.)
|
||
- Снятие freeze (BR-6) — **ручное** (оператор), автоматического разбора деградации нет.
|
||
|
||
---
|
||
|
||
## 7. Критерии успеха (резюме; детали — 03-acceptance-criteria.md)
|
||
- AC-1 активная задача (`stage<done`) → новая не стартует анализ.
|
||
- AC-2 активная достигла `done` → следующая стартует автоматически.
|
||
- AC-3 gate переживает рестарт (состояние в БД).
|
||
- AC-4 разные репо идут параллельно.
|
||
- AC-5 auto-rollback → freeze + алерт, следующая не стартует до ручного снятия.
|
||
- AC-6 каждая ветка срезана от `main` со всеми предыдущими завершёнными задачами репо (нет stale-base).
|
||
|
||
---
|
||
|
||
## 8. Риски (детали — 10-tech-risks.md, заполняет архитектор)
|
||
- R-1: stale-base сохраняется, если ветка режется на входе (`_create_gitea_branch` в `start_pipeline`)
|
||
до завершения предшественника — gate обязан отсрочить **создание ветки**, а не только claim.
|
||
- R-2: gate, ошибочно fail-closed на транзиентной ошибке БД, заклинит очередь всех проектов.
|
||
- R-3: «вечный freeze» / залипшая активная задача в Blocked останавливает пакет — нужна наблюдаемость
|
||
и ручное снятие.
|