Files
orchestrator/docs/work-items/ORCH-090/02-trz.md

192 lines
18 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-090
stage: analysis
author_agent: analyst
status: ready-for-review
created_at: 2026-06-09
model_used: claude-opus-4-8
---
# 02 — ТЗ (TRZ): ORCH-090 — Механизм отмены задачи: статус STOP в Plane (остановка + полный сброс)
Work Item: **ORCH-090** · Repo: **orchestrator** · Стадия: analysis
> ТЗ описывает **что** и **где** должно измениться (модули/контракты/артефакты), выведенное из BRD
> и фактического кода. **Как** (хранилище статуса отмены, точка безопасного прерывания merge/deploy,
> удаление vs архив ветки, точные точки врезки) — решает архитектор в `06-adr/`. ТЗ фиксирует
> требования и границы, не предлагает архитектурное решение.
---
## 1. Сводка изменения
Ввести обработку нового Plane-статуса **STOP** как сигнала отмены задачи. При его получении
оркестратор: (1) останавливает активного агента (graceful SIGTERM через существующий каскад),
(2) отменяет все job'ы задачи и исчерпывает ретраи, (3) снимает таймеры/мониторы, (4) удаляет/
архивирует рабочую ветку+worktree и сбрасывает незавершённый прогресс в БД до состояния «отменена»
(durable), сохраняя docs-артефакты. Параллельно закрывается **дыра релонча**: ручной перевод в
промежуточный рабочий статус больше не запускает агента — единственный вход к запуску пайплайна
остаётся «To Analyse» (`start_pipeline`). Всё — аддитивно, под kill-switch, never-raise,
restart-safe. `STAGE_TRANSITIONS`, `QG_CHECKS`, `check_*` и семантика существующих статусов —
**не меняются**.
---
## 2. Задействованные модули / пути
| Путь | Действие |
|------|----------|
| `src/webhooks/plane.py` | изменить: добавить распознавание/маршрутизацию STOP (`handle_issue_updated`) → новый обработчик `handle_stop` (имя — на усмотрение архитектора); **загейтить/убрать релонч агента** в `handle_status_start` (промежуточные статусы не запускают агента; пайплайн — только из `To Analyse`/`start_pipeline`) |
| `src/agents/launcher.py` | изменить: предоставить/переиспользовать остановку активного процесса задачи (SIGTERM каскад `_watchdog`; `jobs.pid`), пометку «не релончить» (исчерпание `max_attempts`/запрет авто-requeue для отменённой задачи) |
| `src/queue_worker.py` / `src/db.py` | изменить: отмена job'ов задачи (queued/running → терминальный «cancelled»-исход); claim не выбирает отменённые; helper'ы выборки job'ов задачи; (возможно) новый терминальный статус job `cancelled` ИЛИ переиспользование `failed`+флаг — выбор архитектора; durable-пометка задачи «отменена» в `tasks` |
| `src/git_worktree.py` | изменить/переиспользовать: удаление/архив рабочей ветки и worktree отменённой задачи (`remove_worktree`; удаление/архив Gitea-ветки) — never-raise |
| `src/plane_sync.py` | изменить: маппинг Plane-статуса STOP (`_PLANE_NAME_TO_KEY` / `_DEFAULT_STATES`); переиспользовать группу `cancelled` для терминал-скипа; сеттер статуса (best-effort) |
| `src/stages.py` | при необходимости — терминальная трактовка отменённой задачи (НЕ менять exit-гейты рёбер; добавление `cancelled`-стадии — решение архитектора, см. §5) |
| `src/reconciler.py` | переиспользовать терминал-скип `done`/`cancelled` (`_is_terminal_state`) — отменённая задача не реконсилируется/не релончится |
| `src/job_reaper.py` | согласовать: reaper не «оживляет» отменённые job'ы (терминальный исход не requeue'ится) |
| `src/stage_engine.py` | согласовать: снятие таймеров/мониторов (post-deploy monitor, brd-review clock) и безопасное прерывание merge/deploy при STOP |
| `src/notifications.py` | переиспользовать `send_telegram`/`update_task_tracker` для алерта/карточки отмены (never-raise, кликабельный номер) |
| `src/config.py` | изменить: новый kill-switch `stop_status_enabled` (+ при необходимости область репо/доп-флаги) по образцу `serial_gate_enabled` |
| `src/main.py` | изменить: read-only блок наблюдаемости отмены в `GET /queue` (аддитивно) |
| `docs/architecture/README.md`, `CLAUDE.md`, `CHANGELOG.md` | обновить в том же PR (golden source) |
| `tests/` | добавить тесты (см. `04-test-plan.yaml`) |
> Чистую логику распознавания/решения по STOP желательно вынести в leaf-модуль (по образцу
> `src/serial_gate.py` / `src/labels.py`, never-raise) — окончательно решает архитектор.
---
## 3. Функциональные требования
### FR-1 — Распознавание и маршрутизация STOP (BR-1, BR-5)
- `handle_issue_updated` (`webhooks/plane.py`) распознаёт перевод задачи в логический статус STOP
(через `_PLANE_NAME_TO_KEY`/группа `cancelled`) и маршрутизирует в обработчик отмены.
- Обработчик идемпотентен: если задача уже отменена / `done` / отсутствует → no-op (BR-5).
- Контракт — never-raise: ошибка обработки STOP логируется, вебхук-поток не падает (NFR-5).
### FR-2 — Остановка активного агента (BR-1a)
- Для running-job'а задачи послать активному процессу SIGTERM (graceful) через существующий
каскад `launcher._watchdog` (SIGTERM → grace `agent_kill_grace_seconds` → SIGKILL); PID берётся
из `jobs.pid`.
- Если активного процесса нет (idle/queued) — шаг no-op.
### FR-3 — Отмена job'ов и исчерпание ретраев (BR-1b, BR-1c)
- Все job'ы задачи (`status IN (queued, running)`) переводятся в **терминальный отменённый исход**
так, что `claim_next_job` их больше не выбирает и `_finalize_*`/reaper не делает авто-requeue.
- Запрет авто-requeue: после STOP `attempts` считаются исчерпанными (либо отдельный терминальный
статус job `cancelled`, либо `failed`+маркер — выбор архитектора). Reaper (`job_reaper.py`) и
`_finalize_permanent` не должны возвращать отменённый job в `queued`.
### FR-4 — Снятие таймеров и мониторов (BR-1d)
- При STOP снимаются/обнуляются связанные с задачей таймеры и фоновые наблюдатели: post-deploy
monitor (ORCH-021), brd-review clock (ORCH-087), отложенные defer'ы merge-lease/serial-gate.
- Терминал-скип `done`/`cancelled` (`reconciler._is_terminal_state`, ORCH-068/086) применяется к
отменённой задаче, чтобы реконсилятор/мониторы её не трогали (NFR-4).
### FR-5 — Полный сброс прогресса (BR-2)
- Рабочая ветка и worktree задачи удаляются/архивируются (`git_worktree.remove_worktree` + удаление/
архив Gitea-ветки; never-raise). `main` не трогается, force-push в `main` запрещён.
- Незавершённый прогресс задачи в БД приводится к durable-состоянию «отменена» так, что повторный
запуск возможен ТОЛЬКО через `start_pipeline` с нуля (новая ветка от свежего `origin/main`, новый
analyst). Конкретика «очистить строку vs пометить cancelled» — архитектору; инвариант:
возобновления «с середины» не происходит.
- **Docs-артефакты задачи (`01..17`) сохраняются/бэкапятся** — не удаляются вместе с прогрессом.
### FR-6 — Закрытие дыры релонча (BR-3, BR-4)
- `handle_status_start` (или эквивалентная точка) **не должен релончить агента текущей стадии** при
ручном переводе в промежуточный рабочий статус (Architecture/Development/Review/Testing/
Deploying/Awaiting Deploy/Monitoring/…).
- Запуск пайплайна остаётся возможен **только** через статус «To Analyse» → `start_pipeline`
(создание ветки + docs + enqueue analyst). Любой намеренный сценарий «вернуть задачу в работу»
(например, после Needs Input) должен быть пересмотрен так, чтобы НЕ опираться на авто-релонч
агента сменой рабочего статуса (точный заменяющий механизм — архитектору).
### FR-7 — Безопасное прерывание критичных операций (BR-6, NFR-3)
- STOP во время merge/deploy не оставляет `main` в half-merged состоянии и не рестартит/не роняет
прод-контейнер. Если необратимый шаг (detached self-deploy / слияние PR) уже запущен — STOP не
«разрывает» его с порчей: допускается дать необратимому шагу завершиться/зафиксировать честный
исход, после чего применить отмену. Точка безопасного прерывания и обработка merge-lease — ADR.
### FR-8 — Наблюдаемость (BR-8)
- Каждое срабатывание STOP: `logger.info/warning` (что остановлено/сброшено), Telegram-алерт
(`send_telegram`, кликабельный номер `plane_issue_link`), Plane-коммент (best-effort), обновление
live-карточки (`update_task_tracker`, never-raise), read-only блок отмены в `GET /queue`.
---
## 4. Изменения API
- **Новых обязательных публичных endpoint'ов нет.** Триггер STOP — смена статуса Plane (webhook),
не REST. (По аналогии с ORCH-088 возможен опциональный админ-эндпоинт принудительной отмены —
на усмотрение архитектора; если вводится, описать в ADR и таблице API README.)
- `GET /queue`**аддитивно**: новый read-only блок (например `stop`/`cancel`) — флаг `enabled`,
счётчик отменённых задач/job'ов, последние отмены. Существующие ключи не меняются; never-raise.
- Внешний контракт вебхука `POST /webhook/plane` — не меняется (новая ветка обработки статуса
внутри `handle_issue_updated`).
---
## 5. Изменения схемы БД
> Только **аддитивные, идемпотентные** миграции (общая прод-БД; enduro не трогать).
> `CREATE TABLE IF NOT EXISTS` / `_ensure_column`.
- **Статус job «отменён» (FR-3):** требуется терминальный исход, который не requeue'ится. Варианты
(выбор — архитектор): новый статус `jobs.status='cancelled'` ИЛИ переиспользование `failed` +
аддитивный маркер. Требование к выбранному варианту: claim/finalize/reaper не возвращают его в
`queued`; restart-safe.
- **Состояние задачи «отменена» (FR-5, NFR-4):** durable-признак, что задача отменена и не
возобновляется с середины. Варианты: добавление терминальной стадии `cancelled` в `tasks.stage`
(учитывается терминал-скипом `done`/`cancelled`, уже поддержан reconciler'ом) ИЛИ аддитивная
колонка/таблица. `STAGE_TRANSITIONS` (exit-гейты рёбер) при этом **не меняются** — отмена это
терминальное состояние, не новое ребро конвейера.
- `QG_CHECKS`, `check_*`, `job_deps`, `agent_runs`-контракт, `repo_freeze`**без изменений**.
---
## 6. Требования к новым/изменённым QG checks
- **Новых QG-проверок не вводить.** STOP — это решение диспетчера статусов/планировщика (отмена),
а не Quality Gate стадии. Реестр `QG_CHECKS` и `check_*` не меняются (по образцу `task_deps`
ORCH-026 и `serial_gate` ORCH-088 — логика в обработчике/claim, не новый QG).
---
## 7. Совместимость / регресс
- **Kill-switch:** новый флаг `stop_status_enabled` (env `ORCH_STOP_STATUS_ENABLED`) по образцу
`serial_gate_enabled`; `False` → STOP-обработка и закрытие дыры релонча ведут себя нейтрально
(поведение строго как сейчас, нулевая регрессия). При необходимости — область репо
(`stop_status_repos`, CSV) с дефолтом «все репо» (отмена осмысленна и для enduro).
- **Аддитивность БД (NFR-2):** только идемпотентные миграции; enduro при выключенном/неприменимом
флаге не затрагивается.
- **Инварианты (не нарушать):** `STAGE_TRANSITIONS`, реестр `QG_CHECKS`, `check_*`, exit-коды
deploy-хука, merge-gate (ORCH-043), merge-verify (ORCH-071/073), image-freshness (ORCH-058),
post-deploy контракт (ORCH-021), serial-gate (ORCH-088), auto-label (ORCH-089), семантика
Rejected/Approved/Confirm Deploy — **без изменений**.
- **Self-hosting safety (NFR-3):** STOP не рестартит/не роняет прод-контейнер; не push/force-push в
`main`; merge/deploy прерываются fail-safe (без half-merge).
- **never-raise (NFR-5):** обработчик STOP и закрытие релонча не валят вебхук-поток; ошибка на
единице работы изолирована.
- **Артефакты pipeline (создать/обновить в том же PR):** `docs/work-items/ORCH-090/06-adr/ADR-001-…`
(решение архитектора), `docs/architecture/README.md` (раздел «STOP / отмена задачи (ORCH-090)»,
обновление описания `GET /queue`, раздела статусной модели и при новой таблице/колонке — раздела
«База данных»), `CLAUDE.md` (абзац о STOP в статусной модели), `CHANGELOG.md` (`feat:`); при новой
таблице/колонке — `docs/work-items/ORCH-090/08-data-requirements.md`; при админ-эндпоинте — таблица
API в README.
---
## 8. Открытые вопросы для архитектора (не блокируют анализ)
- OQ-1: Имя Plane-статуса — отдельный «STOP» (новый key) vs переиспользование существующего
«Cancelled» (key `cancelled` уже в `_PLANE_NAME_TO_KEY`). Влияет на маппинг и группу терминал-скипа.
- OQ-2: Статус отменённого job — новый `cancelled` vs `failed`+маркер.
- OQ-3: Состояние отменённой задачи — терминальная стадия `cancelled` vs аддитивная колонка/таблица.
- OQ-4: Сброс прогресса — удалить строку task (полный re-create через To Analyse) vs пометить
cancelled и при To Analyse создавать новую задачу.
- OQ-5: Удаление vs архив рабочей ветки (и Gitea-ветки) — что безопаснее для аудита.
- OQ-6: Точка безопасного прерывания merge/deploy (FR-7) и обработка удерживаемого merge-lease.
- OQ-7: Чем заменить легитимный «resume после Needs Input», который сейчас опирается на релонч в
`handle_status_start` (FR-6), чтобы не сломать намеренный сценарий возврата к работе.