Files
orchestrator/docs/work-items/ORCH-124/02-trz.md
claude-bot fef5ba15d5
All checks were successful
CI / test (push) Successful in 1m9s
analyst(ET): auto-commit from analyst run_id=763
2026-06-16 17:56:23 +03:00

134 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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).