# 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 останавливает пакет — нужна наблюдаемость
  и ручное снятие.
