5.8 KiB
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:
- Gate-в-claim (
db.claim_next_job) — analyst-job (jobs.agent='analyst') применимого репо не выбирается, еслиEXISTSдругая незавершённая задача репо ИЛИ активна строкаrepo_freeze. По образцуtask_depsNOT EXISTS(ORCH-026); только локальная БД (offline hot-path). Job'ы уже активной задачи проходят свободно; rework-analyst не блокирует себя (t2.id != jobs.task_id). - Отложенный срез ветки — для применимого репо
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). - Durable per-repo freeze (
repo_freeze) — post-deployDEGRADED/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-safejobs-очередь. - 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 «бесплатны» (ожидание =
queuedjob без ветки); переиспользование проверенных паттернов; 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).