# 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