---
work_item: ORCH-090
stage: analysis
author_agent: analyst
status: ready-for-review
created_at: 2026-06-09
model_used: claude-opus-4-8
---

# 01 — BRD (бизнес-требования): ORCH-090 — Механизм отмены задачи: статус STOP в Plane (остановка + полный сброс)

Work Item: **ORCH-090** · Repo: **orchestrator** · Стадия: analysis

## 1. Бизнес-контекст и проблема

Сегодня в оркестраторе **нет штатного способа отменить/остановить задачу**. Оператор вынужден
выполнять разрозненные ручные действия: убить процесс агента, дождаться исчерпания ретраев job,
удалить ветку/worktree/строку task из БД и вручную сбросить статус в Plane. Это медленно,
ошибкоопасно и не воспроизводимо (инцидент 09.06 с ORCH-087 — оператор делал всё это руками).

Вторая, связанная проблема — **дыра ручного релонча**: `src/webhooks/plane.py::handle_status_start`
при ручном переводе задачи в рабочий статус (через «To Analyse» / In Progress) **повторно ставит в
очередь агента текущей стадии на той же ветке** (`has_active_job_for_task` → иначе
`enqueue_job(stage_agent, …)`). Это означает, что попытка оператора «подтолкнуть» задачу сменой
статуса может незаметно релончить агента — именно этот механизм усугубил сегодняшний инцидент.

Требуется единый, декларативный механизм: **перевод задачи в новый Plane-статус STOP →
оркестратор немедленно останавливает всю работу по задаче и полностью сбрасывает её прогресс**.
Повторный запуск возможен ТОЛЬКО через «To Analyse» (с нуля). Никакой другой статус пайплайн не
запускает.

**Установленные факты (по текущему коду, не изобретать):**
- Машина стадий — `src/stages.py::STAGE_TRANSITIONS`; терминальная стадия только `done`
  (`cancelled`-стадии нет).
- Plane-маппинг — `src/plane_sync.py`: `_PLANE_NAME_TO_KEY` уже содержит `"Cancelled" → "cancelled"`,
  `_DEFAULT_STATES` содержит UUID `cancelled`; имени «STOP» в маппинге сейчас нет.
- Остановка процесса агента уже реализована как graceful-каскад в
  `src/agents/launcher.py::_watchdog` (SIGTERM → grace `agent_kill_grace_seconds` → SIGKILL); PID
  задачи хранится в `jobs.pid`.
- Статусы job в `jobs` — `queued | running | done | failed`; статуса `cancelled` нет.
- Терминал-скип для реконсилятора/мониторов уже учитывает `done` и `cancelled`
  (`src/reconciler.py::_is_terminal_state`, ORCH-068/086).
- Запуск пайплайна с нуля — `handle_status_start → start_pipeline` (создаёт ветку + docs + analyst).

## 2. Объём (scope)

### В объёме
- Новый Plane-статус **STOP** как сигнал отмены задачи (распознавание в диспетчере статусов).
- **Остановка задачи (G1):** graceful-стоп активного агента (SIGTERM), отмена всех job'ов задачи
  (queued/running → терминальный «cancelled»-исход), исчерпание ретраев (запрет авто-requeue),
  снятие таймеров/мониторов (post-deploy monitor, brd-review clock и т.п.).
- **Полный сброс прогресса (G2):** удаление/архив рабочей ветки и worktree, очистка незавершённого
  прогресса задачи в БД так, чтобы повторный старт шёл строго через `start_pipeline` (с нуля).
  Docs-артефакты задачи — сохранить/забэкапить (не теряем аналитику).
- **Закрытие дыры релонча (G3/G4):** перевод в любой промежуточный рабочий статус
  (Development/Architecture/Review/Deploying/…) вручную **не** запускает агента; единственный вход
  к запуску пайплайна — «To Analyse» (старт с нуля).
- **Идемпотентность и fail-safe (G5):** STOP на уже остановленной/завершённой задаче — no-op; STOP
  во время критичной операции (merge/deploy) — корректное прерывание без порчи `main`/прода.
- Kill-switch фичи; наблюдаемость (лог + Telegram + блок в `GET /queue`).
- Обновление документации (CLAUDE.md, architecture/README.md, CHANGELOG.md) и инфра-предусловие
  (создать статус STOP на доске Plane).

### Вне объёма
- Автоматическая отмена задач по таймауту/эвристике — STOP только по явному человеческому сигналу.
- Возобновление задачи «с середины» после STOP — сознательно НЕ поддерживается (только перезапуск
  с нуля через To Analyse).
- Изменение семантики Rejected (откат на стадию назад) — STOP это отдельный путь, не Rejected.
- Изменение состава/семантики `STAGE_TRANSITIONS` exit-гейтов и `QG_CHECKS` / `check_*`.
- Откат уже задеплоенного в прод кода (rollback) — STOP не выполняет rollback; он лишь прерывает
  незавершённую работу безопасно.
- Кросс-проектная отмена пакета задач (отменяется одна задача за сигнал).

## 3. Заинтересованные стороны

- **Заказчик / владелец продукта:** Слава (идея STOP-статуса).
- **Оператор оркестратора** (Стрим и др.) — главный потребитель: получает кнопку «отменить» вместо
  ручной хирургии по БД/процессам.
- **Затрагиваемые проекты:** orchestrator (self-hosting) и enduro-trails (общая прод-БД/очередь) —
  изменения должны быть аддитивны и не задевать enduro при выключенном/неприменимом флаге.
- **Принимает результат:** reviewer/tester по критериям приёмки (`03`/`04`).

## 4. Бизнес-требования (BR)

- **BR-1 (STOP останавливает работу)** — перевод задачи в Plane-статус STOP → оркестратор
  останавливает всю работу по задаче: (a) активному агенту посылается SIGTERM (graceful, с
  последующим жёстким kill по существующему grace-каскаду); (b) все job'ы задачи (queued и running)
  переводятся в терминальный «отменённый» исход и не выбираются claim'ом; (c) ретраи исчерпываются
  (никакого авто-requeue после STOP); (d) таймеры/мониторы задачи (post-deploy monitor, brd-review
  clock, merge-lease defer и т.п.) снимаются. Контракт фичи — **never-raise**.
- **BR-2 (STOP = полный сброс)** — после STOP задача НЕ продолжается с середины. Рабочая
  ветка+worktree удаляются/архивируются; незавершённый прогресс задачи в БД очищается или
  помечается так, что повторный запуск идёт через `start_pipeline` с нуля (свежая ветка от
  актуального `origin/main`, новый аналитик). Docs-артефакты (`01..17`) — сохранить/забэкапить.
- **BR-3 (единственный вход — To Analyse)** — единственный Plane-статус, запускающий пайплайн —
  «To Analyse» (старт с нуля). После STOP повторный «To Analyse» создаёт задачу заново.
- **BR-4 (закрыть дыру релонча)** — ручной перевод задачи в любой промежуточный рабочий статус
  (Architecture/Development/Review/Testing/Deploying/Awaiting Deploy/…) **не** запускает агента
  соответствующей стадии. Текущее поведение `handle_status_start`, релончащее агента текущей стадии
  на той же ветке, должно быть устранено/загейчено так, чтобы пайплайн стартовал только из
  «To Analyse».
- **BR-5 (идемпотентность)** — STOP на задаче, которая уже остановлена (cancelled), уже `done` или
  не существует, — **no-op** (без ошибок, без побочных эффектов, без повторного kill).
- **BR-6 (безопасное прерывание критичных операций)** — STOP во время merge/deploy не оставляет
  `main` в half-merged состоянии и не роняет/не рестартит прод-контейнер. Если критичный шаг уже
  необратимо запущен (детач-деплой/слияние в процессе), STOP не должен его «разорвать» с порчей —
  допустимо дождаться/пропустить необратимый шаг и зафиксировать честный итог (детали безопасной
  точки прерывания — архитектору).
- **BR-7 (STOP ≠ Rejected)** — STOP это полная остановка+сброс задачи, а не откат на предыдущую
  стадию. Существующий путь Rejected (`handle_verdict(approved=False)` → `_rollback_stage`) не
  меняется и не смешивается с STOP.
- **BR-8 (наблюдаемость)** — каждое срабатывание STOP прозрачно: лог, Telegram-уведомление (с
  кликабельным номером задачи), Plane-коммент (best-effort), отражение в live-карточке и read-only
  блок в `GET /queue`.

## 5. Нефункциональные требования (NFR)

- **NFR-1 (нулевая регрессия + kill-switch)** — фича под флагом включения (по образцу
  `serial_gate_enabled`/`merge_gate_enabled`); при выключенном флаге поведение оркестратора строго
  как сейчас. `STAGE_TRANSITIONS` / `QG_CHECKS` / `check_*` / семантика существующих статусов — без
  изменений.
- **NFR-2 (общая прод-БД, аддитивность)** — любые изменения схемы БД — только аддитивные и
  идемпотентные (`CREATE TABLE IF NOT EXISTS` / `_ensure_column`); enduro-trails не затрагивается.
- **NFR-3 (self-hosting safety)** — STOP не должен убить сам оркестратор / прод и не портить `main`.
  Прерывание merge/deploy — fail-safe (не оставлять half-merge; не рестартить прод).
- **NFR-4 (restart-safe)** — состояние «задача отменена» durable (БД); после рестарта контейнера
  отменённая задача не «оживает» и не релончится reconciler'ом/reaper'ом (переиспользовать
  терминал-скип `done`/`cancelled`).
- **NFR-5 (never-raise)** — обработчик STOP и закрытие дыры релонча не должны валить вебхук-поток;
  ошибка на единице работы логируется и не прерывает обработку других задач/проектов.
- **NFR-6 (offline-устойчивость горячего пути)** — закрытие дыры релонча и терминал-скип не должны
  добавлять обязательных сетевых вызовов в горячий claim-цикл.

## 6. Допущения и ограничения

- На доске Plane проекта ORCH будет создан статус **STOP** (инфра-предусловие); до его создания
  фича в режиме fail-safe (нет статуса → нет STOP-действия, ничего не ломается).
- Логический ключ `cancelled` и его UUID/группа уже присутствуют в `plane_sync` — STOP может
  переиспользовать «cancelled»-семантику терминал-скипа (точное соответствие имя→ключ и
  группа-`cancelled` — решение архитектора).
- Существующий graceful kill-каскад агента (`_watchdog`: SIGTERM→grace→SIGKILL) переиспользуется
  для остановки активного агента; новый механизм kill не изобретается.
- Терминал-скип `done`/`cancelled` в `reconciler`/мониторах уже есть и должен покрыть
  STOP-отменённые задачи (NFR-4) — переиспользовать, не дублировать.
- Архитектурные решения (хранилище статуса отмены, точка безопасного прерывания merge/deploy,
  удаление vs архив ветки, точные точки врезки в `plane.py`) — зона архитектора (`06-adr/`).

## 7. Критерии успеха

STOP-статус, выставленный на задаче, приводит к: остановленному агенту, отменённым job'ам без
авто-requeue, снятым таймерам/мониторам, удалённой/заархивированной ветке+worktree, durable-статусу
«отменена» (переживает рестарт), сохранённым docs-артефактам. Ручной перевод в промежуточный
рабочий статус более не релончит агента; пайплайн стартует только из «To Analyse». STOP
идемпотентен и безопасен при merge/deploy. Детальные PASS/FAIL — в `03-acceptance-criteria.md`.

## 8. Риски

- Гонка «STOP во время merge/deploy» → риск half-merge/порчи `main` (mitigation: безопасная точка
  прерывания, fail-safe — детали архитектору).
- Закрытие дыры релонча может задеть легитимный сценарий resume после «Needs Input» → нужно
  сохранить намеренные сценарии возврата к работе, не ломая их (уточнить с архитектором, какой путь
  заменяет релонч).
- Очистка прогресса в БД при общей прод-БД → риск задеть enduro/другие задачи (mitigation:
  строго per-task, аддитивно).
- Детали — `10-tech-risks.md` (заполняет архитектор).
