# adr-0017: Per-repo serial gate (пакетный автономный режим, serial e2e) Статус: **proposed** · Дата: 2026-06-09 · Источник: **ORCH-088** (Этап 1) Детально: `docs/work-items/ORCH-088/06-adr/ADR-001-serial-gate.md`. ## Контекст Цель эпика ORCH-088 — масштаб автономности: накидать вечером 10–20 задач и получить к утру пакет, последовательно проведённый через весь конвейер (analysis → … → deploy → done). Корневая проблема — **stale-анализ**: ветка задачи N+1 срезается на входе в анализ (`start_pipeline._create_gitea_branch`) от `main`, ещё не содержащего код предшественника N. Физическое код-затирание уже закрыто (ORCH-026 auto_rebase + merge-lease); остаётся **логический** разрыв. Plane API v1 не имеет bulk/relations ⇒ очередь/зависимости хранятся у оркестратора (gate по локальной БД). ## Решение **Per-repo serial gate** — новая задача репо не входит в `analysis` (не режет ветку, не запускает analyst), пока в том же репо есть незавершённая задача (`stage != 'done'`) или репо заморожен. Три механизма, аддитивно, под kill-switch, с областью репо, never-raise, restart-safe: 1. **Gate-в-claim** (`db.claim_next_job`) — analyst-job (`jobs.agent='analyst'`) применимого репо не выбирается, если `EXISTS` другая незавершённая задача репо ИЛИ активна строка `repo_freeze`. По образцу `task_deps` `NOT EXISTS` (ORCH-026); только локальная БД (offline hot-path). Job'ы уже активной задачи проходят свободно; rework-analyst не блокирует себя (`t2.id != jobs.task_id`). 2. **Отложенный срез ветки** — для применимого репо `start_pipeline` создаёт task-row + enqueue analyst, но **не** создаёт Gitea-ветку/docs; срез релоцируется на момент claim analyst-job (launcher), когда `origin/main` уже содержит предшественника (`done` ⇔ SHA-в-main, ORCH-071/073). `ensure_worktree` режет от свежего `origin/main` ⇒ AC-6 структурно. Идемпотентно (409 = no-op). 3. **Durable per-repo freeze** (`repo_freeze`) — post-deploy `DEGRADED`/rollback (ORCH-021) → `set_repo_freeze` + Telegram-алерт; gate закрыт безусловно до **ручного** снятия (`POST /serial-gate/unfreeze`). Деградировавшая задача уже `done` (BR-7) ⇒ нужен отдельный сигнал. Чистая логика — leaf `src/serial_gate.py` (never-raise). Флаги `serial_gate_enabled` (kill-switch), `serial_gate_repos` (CSV; **пусто ⇒ все репо**, в отличие от self-hosting-only ORCH-35/43/58), `serial_gate_freeze_enabled`. Наблюдаемость — блок `serial_gate` в `GET /queue`. ## Альтернативы - **Гейт в `start_pipeline` + re-trigger при `done`** — больше состояния/путей, риск зависших задач; relocation на claim переиспользует restart-safe `jobs`-очередь. - **Freeze как колонка `tasks`** — неверная семантика (freeze per-repo, задача уже `done`). - **Self-hosting-only область** — лишает enduro анти-stale-base (FR-3). - **Отдельная таблица очереди ожидания** — избыточно; `jobs(queued)`+gate достаточно. - **Снятие freeze Plane-жестом** — перегрузка статусов (анти-паттерн ORCH-059). ## Последствия - **+** AC-6 закрыт структурно; AC-2/AC-3 «бесплатны» (ожидание = `queued` job без ветки); переиспользование проверенных паттернов; cross-repo параллелизм сохранён; `STAGE_TRANSITIONS` / `QG_CHECKS` / `check_*` / merge-gate / merge-verify / image-freshness / post-deploy / deploy-хук / `max_concurrency` — **без изменений**. - **NFR-1:** hot-claim тотальный сбой → **fail-open** (не заклинить очередь всех проектов); freeze в Python-слое → **fail-closed** (безопасность прода). - **−** Срез ветки/docs мигрируют из async в sync-путь launcher (обёртка); Blocked-задача держит пакет (Этап 1, осознанно); freeze снимается только вручную. - Откат: `serial_gate_enabled=False` ⇒ claim/старт 1:1 как до ORCH-088; таблица `repo_freeze` инертна. - **Вне скопа** (Этап 1): merge-очередь FIFO, pre-merge rebase как отдельная фича, фазы A/B/C, любой параллелизм задач внутри одного репо, зависимость от ORCH-83. ## Связи - Переиспользует: adr-0002 (очередь ORCH-1), adr-0015 (claim-gate/auto_rebase/merge-lease ORCH-026), adr-0010 (post-deploy monitor — источник DEGRADED), adr-0013/0014 (merge-verify ⇒ `done`⇔SHA-в-main). - Новая аддитивная таблица `repo_freeze` (`docs/work-items/ORCH-088/08-data-requirements.md`).