Addresses reviewer REQUEST_CHANGES (run 768) on ORCH-124 — docs-only, no src/tests touched, fix scope unchanged. P1: update docs/overview/ showcase for the new serial-gate "pause without blocking" axis (changed task-routing functionality, ORCH-011/ORCH-079): - tech-pipeline.md: FIFO exception "pause without blocking" next to freeze - tech-data-model.md: durable signal tasks.paused_at on the Task row - tech-observability.md: paused/reason in serial_gate GET /queue block + operator endpoints POST /serial-gate/pause|resume P2: strip leaked tool-call trailing tags (</content>/</invoke>) from 4 golden-source docs of this PR (06-adr/ADR-001, adr-0051, 08-data-requirements.md, 10-tech-risks.md). CHANGELOG "Доки" bullet extended accordingly. Full suite green (2178 passed); test_system_docs.py green (machine-checked showcase facts intact). Refs: ORCH-124 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
111 lines
8.6 KiB
Markdown
111 lines
8.6 KiB
Markdown
---
|
||
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)
|