Files
orchestrator/docs/work-items/ORCH-088/01-brd.md

146 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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. Цель эпика
Дать оркестратору **масштаб автономности**: накидать вечером 1020 задач и получить к утру
последовательно проведённый через весь конвейер (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 останавливает пакет — нужна наблюдаемость
и ручное снятие.