--- work_item: ORCH-124 stage: architecture author_agent: architect status: proposed created_at: 2026-06-16 model_used: claude-opus-4-8 --- # ADR-0051: Ось «пауза» serial-gate — park-сигнал без блокировки FIFO Сквозной (cross-cutting) ADR. Детальное решение задачи — `docs/work-items/ORCH-124/06-adr/ADR-001-serial-gate-pause-without-blocking.md`. Статус: **Proposed** · Дата: 2026-06-16 · Источник: **ORCH-124** (bug → escalate full-cycle) ## Контекст ORCH-088 (serial-gate, adr-0017) определяет «активную задачу репо» **исключительно по машинной стадии** `tasks.stage NOT IN ('done','cancelled')` (после ORCH-090/adr-0026 — с учётом терминала `cancelled`). Plane-статусы Backlog/Blocked/Needs-Input — **слой B (индикация), ORCH-066** — не меняют `tasks.stage` (слой A); у таблицы `tasks` нет колонки статуса. ⇒ приостановленная оператором задача неотличима от активно исполняемой и держит FIFO-гейт (`t2.id < jobs.task_id`) закрытым для более поздних analyst-job того же репо. **Инцидент ORCH-116/ORCH-123:** ORCH-116 поставили на паузу, чтобы пропустить срочный фикс ORCH-123, но serial-gate держал analyst-job ORCH-123 в `queued`. Единственные обходы (терминальный `cancel`, довод до `done`, глобальное `serial_gate_enabled=false`) — грубые. Горячий путь `serial_gate.build_claim_clause` врезан в `claim_next_job` — **offline SQL** — и сетевого чтения Plane-статуса (как делает reconciler ORCH-060) позволить не может. Нужен **DB-резолвимый** сигнал паузы. ## Решение ### Инвариант: «пауза» — ОТДЕЛЬНАЯ ОСЬ планировщика, ортогональная «терминальности» Вводится **per-task park-сигнал** — аддитивная нуллабельная колонка **`tasks.paused_at TEXT`** (NULL = не на паузе) — и **новая ось планировщика «пауза»**, независимая от оси «терминальность». | Ось | Предикат | Кто использует | Меняется ORCH-124? | |-----|----------|----------------|--------------------| | **Терминальность** (adr-0026) | `stage IN ('done','cancelled')` | `serial_gate` + `task_deps` + `stages.py` | **НЕТ — байт-в-байт** | | **Пауза** (новая, ORCH-124) | `paused_at IS NOT NULL` | **только** FIFO «active» предикат `serial_gate` | да (аддитивно) | **serial-gate «активная задача» ⇔ `stage NOT IN ('done','cancelled') AND paused_at IS NULL`.** Это **осознанная, задокументированная** дивергенция serial-gate от чисто-терминального предиката (требование гармонизации adr-0026): пауза выводит предшественника из FIFO-учёта serial-gate, **не делая его терминальным**. ### Что НЕ меняется (анти-регресс adr-0026) - **`task_deps`** (adr-0015) и **`stages.py::STAGE_TRANSITIONS`** колонку `paused_at` **не читают** — остаются чисто терминальными. Явно объявленная зависимость (`job_deps`) на **приостановленную** задачу **по-прежнему блокирует** зависимый job. Пауза («пропустите меня в FIFO») и dependency («B нужен результат A») — разные оси; пауза НЕ обходит dependency и НЕ обходит per-repo `repo_freeze`. - `STAGE_TRANSITIONS` / `QG_CHECKS` / `check_*` / machine-verdict / схемы существующих таблиц — без изменений. Пауза — не стадия и не Quality Gate, а признак планировщика очереди. ### Точки, признающие ось «пауза» (исчерпывающе) 1. `src/serial_gate.py::build_claim_clause` — терм `AND t2.paused_at IS NULL` внутри `active_clause` (под под-флагом). **(маркер ORCH-124, рядом с ORCH-088/ORCH-090)** 2. `src/serial_gate.py::repo_has_active_task` / `_per_repo_snapshot` — тот же предикат + наблюдаемость (ключ `paused`, `reason` ожидания). 3. `src/db.py` — колонка `tasks.paused_at` (`_ensure_column`) + хелперы `set_task_paused`/ `clear_task_paused`/`is_task_paused`. 4. `src/main.py` — операторские эндпоинты `POST /serial-gate/pause|resume` (по образцу `POST /serial-gate/unfreeze`). ### Анти-stale-base при возобновлении (ORCH-088 не регрессирует) Пауза «демотирует» задачу в FIFO; свежесть базы при resume обеспечивают **существующие** механизмы — новой rebase-машинерии нет: отложенный срез ветки (ORCH-088, для паузнутой-в-`analysis`) + безусловный pre-merge `auto_rebase_onto_main` под merge-lease (ORCH-026/093) + merge-gate re-test (ORCH-110) для уже материализованной ветки. Нормальная задача (`paused_at IS NULL`) по-прежнему держит гейт. ### Флаги / совместимость - Независимый под-флаг `serial_gate_pause_enabled` (env `ORCH_SERIAL_GATE_PAUSE_ENABLED`, дефолт `True`) — зеркало `serial_gate_freeze_enabled`. `False` ⇒ pause-терм опущен из SQL, эндпоинты no-op ⇒ serial-gate байт-в-байт как ORCH-088/090. Область — переиспользует `serial_gate_repos` (новый `*_repos` не вводится). - Дефолт `True` безопасен: пока ни одна задача не на паузе, `paused_at` везде `NULL` ⇒ истинный no-op (enduro не затронут). - never-raise: pause-терм в `build_claim_clause` сохраняет **fail-OPEN**; freeze — **fail-CLOSED**. - Миграция — только аддитивная/идемпотентная (`_ensure_column`); общая прод-БД безопасна (NFR-3). ## Последствия - **+** Чистая операторская «пауза без блокировки», отличная от cancel (терминал) и от kill-switch; durable, offline, webhook-независимая; закрывает инцидент ORCH-116/ORCH-123. - **+** Единый, явно описанный двухосевой предикат планировщика (терминальность ⊥ пауза) — устранён риск будущего рассинхрона. - **−** Появилась вторая ось «активности» serial-gate — будущие подсистемы планировщика обязаны помнить: serial-gate «активна» = `не терминальна И не на паузе`, но **терминал** (`task_deps`/`stages.py`) ось «пауза» НЕ включает. Митигейшн: этот ADR + маркер `ORCH-124` в изменённых местах + тесты. - **Откат:** `ORCH_SERIAL_GATE_PAUSE_ENABLED=false` (serial-gate 1:1 как ORCH-088/090; колонка `paused_at` инертна). ## Эволюция маркеров Горячий SQL serial-gate несёт теперь 3 маркера (`ORCH-088` FIFO-гейт, `ORCH-090` терминал `cancelled`, `ORCH-124` ось паузы) — правка любого из них сверяется с этим сводным ADR (анти-археология: 3+ маркеров → одна ссылка сюда, `docs/_standards/TRACEABILITY.md`). ## Ссылки - Детальный ADR: `docs/work-items/ORCH-124/06-adr/ADR-001-serial-gate-pause-without-blocking.md` - Данные: `docs/work-items/ORCH-124/08-data-requirements.md` - Связанные: adr-0017 (serial-gate ORCH-088), adr-0026 (терминал `{done,cancelled}` ORCH-090), adr-0015 (task-deps), adr-0027 (merge-актор rebase/retry ORCH-093), adr-0042 (merge-gate re-test ORCH-110)