134 lines
13 KiB
Markdown
134 lines
13 KiB
Markdown
---
|
||
work_item: ORCH-124
|
||
stage: analysis
|
||
author_agent: analyst
|
||
status: ready-for-review
|
||
created_at: 2026-06-16
|
||
model_used: claude-opus-4-8
|
||
escalate: full-cycle
|
||
---
|
||
|
||
# 02 — ТЗ (TRZ): ORCH-124 — wait/terminal-семантика serial-gate (пауза без блокировки)
|
||
|
||
Work Item: **ORCH-124** · Repo: **orchestrator** · Стадия: analysis
|
||
|
||
> ТЗ описывает **требования к изменениям**, выведенные из BRD и фактического кода. **Выбор
|
||
> механизма** «паузы без блокировки» (release-on-status / per-task hold-флаг / task_deps) и его
|
||
> архитектурное обоснование — задача **архитектора** (`06-adr`, эскалация в full-cycle). Ниже —
|
||
> что должно стать истинным и какие модули это затронет, без предписания «как именно».
|
||
|
||
## 1. Сводка изменения
|
||
Сейчас serial-gate (`src/serial_gate.py`) считает «активной» любую задачу репо со стадией вне
|
||
`{done,cancelled}` — в трёх точках (`build_claim_clause` / `repo_has_active_task` /
|
||
`_per_repo_snapshot`). Поскольку Plane-статусы Backlog/Blocked/Needs-Input (слой B индикации) **не
|
||
меняют `tasks.stage`** (слой A), приостановленная задача неотличима от активной и держит FIFO-гейт
|
||
закрытым для более поздних analyst-job. Требуется ввести **явный, durable, DB-резолвимый** признак
|
||
«пауза/park» и научить определение «активной задачи» его учитывать, **сохранив** анти-stale-base
|
||
ORCH-088 при возобновлении (R-1). Машинные гейты не трогаются — это правка **планировщика очереди**.
|
||
|
||
## 2. Задействованные модули / пути
|
||
| Путь | Действие |
|
||
|------|----------|
|
||
| `src/serial_gate.py` | изменить — определение «активной задачи» во всех 3 точках (`build_claim_clause`, `repo_has_active_task`, `_per_repo_snapshot`); причина ожидания в снапшоте |
|
||
| `src/db.py` | изменить (вероятно) — `claim_next_job` (учёт нового предиката в горячем SQL) + (если выбран DB-сигнал) аддитивная колонка/таблица `_ensure_column`/`CREATE TABLE IF NOT EXISTS` + read-only/мутатор-хелперы |
|
||
| `src/config.py` | изменить — под-флаг(и) семантики паузы (kill-switch), область репо (по образцу `serial_gate_*`) |
|
||
| `src/main.py` | изменить (вероятно) — операторский эндпоинт pause/resume **или** расширение блока `serial_gate` в `GET /queue` причиной ожидания |
|
||
| `src/webhooks/plane.py` и/или `src/plane_sync.py` | изменить (если механизм = release-on-status) — захват намерения паузы из смены Plane-статуса в durable DB-сигнал (не на hot-path) |
|
||
| `tests/test_serial_gate*.py` (новый `tests/test_orch124_serial_gate_pause.py`) | создать/дополнить — кейсы паузы + регресс инцидента |
|
||
| `docs/architecture/README.md`, `CHANGELOG.md`, `docs/work-items/ORCH-124/06-adr/` | обновить — раздел serial-gate + ADR (архитектор) |
|
||
|
||
> Точный набор модулей зависит от выбранного механизма (ADR). Минимально-необходимый набор —
|
||
> `serial_gate.py` (3 точки) + `db.py` (hot-path) + `config.py` (флаг) + тесты; остальное — по решению
|
||
> архитектора.
|
||
|
||
## 3. Функциональные требования
|
||
|
||
### FR-1 — Признак паузы исключает задачу из «активных» в горячем SQL (BR-1, NFR-2)
|
||
`build_claim_clause()`: подзапрос `EXISTS (... t2.stage NOT IN ('done','cancelled'))` должен
|
||
**дополнительно** исключать приостановленные задачи-предшественники. Предикат — **чисто SQL по
|
||
локальной БД** (никакой сети). Форма исключения — функция выбранного DB-сигнала (доп. колонка
|
||
`tasks.<paused>` / отдельная таблица hold / `task_deps`): архитектор фиксирует точную SQL-форму в ADR.
|
||
Инвариант: job'ы уже активной задачи (`agent != 'analyst'`) проходят как раньше; самоблокировки
|
||
собственной строки (R-7 ORCH-088) нет.
|
||
|
||
### FR-2 — Зеркало и снапшот согласованы с FR-1 (BR-1/BR-4)
|
||
`repo_has_active_task()` и `_per_repo_snapshot()` используют **тот же** предикат «активна», что и
|
||
`build_claim_clause` — приостановленный предшественник не попадает в `active_task`. Все три точки
|
||
правятся согласованно (анти-дрейф: они должны давать один ответ на один вход).
|
||
|
||
### FR-3 — Явное durable намерение паузы (BR-2, BR-5, NFR-2)
|
||
Должен существовать **явный** оператор-инициируемый сигнал «park/pause» задачи, **durable**
|
||
(переживает рестарт) и **DB-резолвимый**. Распауза (resume) — обратная операция, восстанавливающая
|
||
участие задачи в serial-gate (BR-5). Сигнал **отличен** от `cancel`/`done` (не терминальный) и от
|
||
глобального kill-switch. Конкретная форма (новый эндпоинт `POST /serial-gate/pause|resume`, или
|
||
маппинг Plane-статуса в DB-сигнал через webhook, или декларация `task_deps`) — **решение архитектора**.
|
||
|
||
### FR-4 — Анти-stale-base при возобновлении (BR-3, R-1 — критично)
|
||
Решение обязано **не регрессировать** ORCH-088: нормально исполняемая задача держит гейт; а
|
||
возобновлённая ранее приостановленная задача не должна приводить к stale-базе у успешника, который
|
||
прошёл вперёд. Механизм разрешения (демотирование в FIFO + обязательный rebase при resume / явный
|
||
yield с принятием rebase / иное) фиксируется архитектором в ADR. ТЗ требует лишь: после распаузы
|
||
ни одна из задач не остаётся на устаревшей базе незаметно.
|
||
|
||
### FR-5 — Корректная причина ожидания (BR-4)
|
||
Блок `serial_gate` в `GET /queue` для ожидающего успешника различает причины: `active-task`
|
||
(идёт работа) / `paused-predecessor` (предшественник на паузе — не должно случаться после фикса, но
|
||
наблюдаемо) / `freeze` / `dependency`. Аддитивно к существующим ключам снапшота (`active_task` /
|
||
`waiting` / `frozen` / `frozen_reason` / `frozen_at`).
|
||
|
||
### FR-6 — Явные блоки сохранены (BR-6)
|
||
`repo_freeze` (durable per-repo freeze) и `task_deps` (`job_deps`) продолжают блокировать claim ровно
|
||
как сейчас. «Пауза» НЕ должна обходить freeze/dependency — это разные оси (freeze = весь репо;
|
||
dependency = конкретная пара; пауза = «пропустите меня»).
|
||
|
||
### FR-7 — Условность и нейтральность (NFR-3)
|
||
Поведение под выключателем (существующий `serial_gate_enabled` либо новый под-флаг паузы) → байт-в-байт
|
||
до ORCH-124. Область репо — по образцу `serial_gate_repos` (CSV; пусто → текущая область serial-gate,
|
||
т.е. все репо). enduro не затронут при нейтральном поведении.
|
||
|
||
## 4. Изменения API
|
||
Вероятно (на усмотрение архитектора, ADR):
|
||
- **Новые операторские эндпоинты** `POST /serial-gate/pause?work_item=<id>` и
|
||
`POST /serial-gate/resume?work_item=<id>` (по образцу существующего `POST /serial-gate/unfreeze`),
|
||
если механизм = явный per-task hold. Возвращают новое состояние (paused/active) задачи.
|
||
- **Расширение** `GET /queue` → блок `serial_gate` дополняется причиной ожидания (FR-5) и (если есть)
|
||
списком приостановленных задач репо. Существующие ключи не меняются (BC).
|
||
|
||
Если механизм = release-on-status, новых ручных эндпоинтов может не быть (намерение — смена Plane-статуса,
|
||
захватываемая webhook'ом в durable DB-сигнал); тогда раздел сводится к расширению `GET /queue`.
|
||
|
||
## 5. Изменения схемы БД
|
||
Вероятно **аддитивно** (на усмотрение архитектора):
|
||
- Колонка `tasks.paused_at TEXT` (NULL = не на паузе), идемпотентно через `_ensure_column` — паттерн
|
||
существующих `tasks.cancelled_at` / `tasks.cancel_requested_at` / `tasks.track`; **или**
|
||
- Отдельная таблица hold (по образцу `repo_freeze`: `work_item_id`/`task_id`, `paused_at`,
|
||
`cleared_at IS NULL` = активна), `CREATE TABLE IF NOT EXISTS`.
|
||
Схемы **существующих** таблиц (`tasks`/`jobs`/`job_deps`/`repo_freeze`) — без деструктивных изменений.
|
||
Если механизм = task_deps, новой схемы может не понадобиться вовсе. Финальное решение — ADR +
|
||
`08-data-requirements.md` (архитектор).
|
||
|
||
## 6. Требования к новым/изменённым QG checks
|
||
**Нет.** `STAGE_TRANSITIONS` / состав `QG_CHECKS` / семантика и имена `check_*` / machine-verdict ключи
|
||
(`verdict:`/`result:`/`deploy_status:`/`staging_status:`/`security_status:`) — **байт-в-байт не
|
||
трогаются**. ORCH-124 — правка планировщика очереди (claim) и наблюдаемости, **не** Quality Gate и
|
||
**не** новая стадия конвейера.
|
||
|
||
## 7. Совместимость / регресс
|
||
- **Обратная совместимость:** аддитивно; выключатель → поведение до ORCH-124 байт-в-байт (NFR-3,
|
||
FR-7). Существующие тесты serial-gate (`tests/test_serial_gate*.py`) остаются зелёными.
|
||
- **Kill-switch / область:** переиспользовать `serial_gate_enabled` (kill-switch) + при необходимости
|
||
ввести под-флаг семантики паузы (независимый тумблер, по образцу `serial_gate_freeze_enabled`) и
|
||
область `*_repos` (CSV).
|
||
- **Обратимость:** выставить под-флаг паузы в `False` → serial-gate работает как ORCH-088/090
|
||
(приостановленные снова считаются активными — т.е. возврат к текущему багу, но это осознанный
|
||
rollback-режим, не дефолт).
|
||
- **never-raise / fail-direction (NFR-1):** сохранить fail-OPEN на hot-claim build и fail-CLOSED на
|
||
freeze; новая ветка паузы не инвертирует эти направления.
|
||
- **Гармонизация предиката (NFR-4):** не сломать `task_deps`/`stages.py` терминальное множество
|
||
`{done,cancelled}` (ORCH-090/adr-0026); расхождение serial-gate (паузой) — только осознанно и
|
||
задокументированно в ADR.
|
||
- **Артефакты pipeline:** обновляются `docs/architecture/README.md` (раздел serial-gate ORCH-088 +
|
||
семантика паузы), `CHANGELOG.md`, новый `docs/work-items/ORCH-124/06-adr/ADR-001-*.md` (архитектор),
|
||
при необходимости `08-data-requirements.md` (архитектор). Reviewer проверяет обновление доки (правило
|
||
агентов §6).
|