107 lines
8.0 KiB
Markdown
107 lines
8.0 KiB
Markdown
---
|
||
work_item: ORCH-090
|
||
stage: architecture
|
||
author_agent: architect
|
||
status: proposed
|
||
created_at: 2026-06-09
|
||
model_used: claude-opus-4-8
|
||
---
|
||
|
||
# ADR-0026: Системное терминальное состояние `cancelled` — STOP-отмена задачи
|
||
|
||
Сквозной (cross-cutting) ADR. Детальное решение задачи —
|
||
`docs/work-items/ORCH-090/06-adr/ADR-001-stop-cancel-task.md`.
|
||
|
||
## Статус
|
||
Proposed
|
||
|
||
## Контекст
|
||
|
||
ORCH-090 вводит Plane-статус **STOP** — единый декларативный механизм отмены задачи (остановка
|
||
агента + полный сброс прогресса). Самое́ кросс-каттинговое следствие — появление **нового
|
||
системного терминального состояния `cancelled`** (стадия `tasks.stage='cancelled'` + терминальный
|
||
job-статус `jobs.status='cancelled'`). До ORCH-090 «терминальность задачи» в горячем планировщике
|
||
была захардкожена как **`stage == 'done'`** (единственный сток в `STAGE_TRANSITIONS`), и это
|
||
определение разъехалось между подсистемами:
|
||
|
||
- `src/reconciler.py` **уже** трактует `stage in ("done","cancelled")` как терминал-скип
|
||
(ORCH-086 D2 предвосхитил `cancelled`; стр. 196) и `_is_terminal_state` по группе Plane
|
||
`{completed, cancelled}` (ORCH-068, стр. 398–415).
|
||
- `src/serial_gate.py` (ORCH-088) и `src/task_deps.py` (ORCH-026) считают задачу «незавершённой»
|
||
по `stage != 'done'` — **без** `cancelled`. Если ввести `cancelled`-стадию, не тронув их,
|
||
отменённая задача навсегда будет «активной»/«незавершённой зависимостью» и **заклинит очередь
|
||
репо**.
|
||
|
||
Этот ADR фиксирует `cancelled` как первоклассное терминальное состояние, равноправное `done`, и
|
||
перечисляет ВСЕ точки, где системный предикат терминальности должен его признавать.
|
||
|
||
## Решение
|
||
|
||
### Инвариант
|
||
**«Задача терминальна» ⇔ `stage ∈ {done, cancelled}`.** Это единое определение для всех
|
||
подсистем планировщика/мониторинга. `cancelled` — терминальный **сток** (не новое ребро
|
||
конвейера): exit-гейты рёбер `STAGE_TRANSITIONS` и реестр `QG_CHECKS`/`check_*` **не меняются**.
|
||
|
||
### Точки, признающие `cancelled` терминальным (исчерпывающе)
|
||
1. `src/stages.py::STAGE_TRANSITIONS` — добавить сток
|
||
`"cancelled": {"next": None, "agent": None, "qg": None}` (параллельно `done`).
|
||
2. `src/serial_gate.py` — `repo_has_other_unfinished` и claim-фрагмент `t2.stage != 'done'`,
|
||
snapshot: `stage != 'done'` → `stage NOT IN ('done','cancelled')`. **(маркер ORCH-088)**
|
||
3. `src/task_deps.py` — dep-gate и `is_task_ready`: `stage != 'done'` →
|
||
`stage NOT IN ('done','cancelled')`. **(маркер ORCH-026)**
|
||
4. `src/reconciler.py` — уже покрыто скипом `stage in ("done","cancelled")` (стр. 196);
|
||
`get_active_tasks_for_reconcile` опционально сузить до `NOT IN ('done','cancelled')`.
|
||
5. `src/job_reaper.py` / `src/queue_worker.py` — перед авто-requeue dead/running-job'а сверять
|
||
терминал задачи: `stage in ("done","cancelled")` → job помечается `cancelled`, не реквью'ится.
|
||
6. `src/post_deploy.py` / `stage_engine.run_post_deploy_monitor` — монитор не тикает по
|
||
отменённой задаче (терминал-проверка/маркер `done`).
|
||
|
||
### Новые терминальные исходы
|
||
- **Job:** `jobs.status='cancelled'` — нигде не реквью'ится; `claim_next_job` выбирает только
|
||
`status='queued'` (изменений в claim нет). `mark_job` стампит `finished_at` для `cancelled`.
|
||
- **Задача:** `tasks.stage='cancelled'` + аддитивные колонки `cancelled_at`,
|
||
`cancel_requested_at` (отложенная отмена в критическом окне merge/deploy). Натуральные ключи
|
||
`plane_id`/`work_item_id` тумбстонятся (`#cancelled-<id>`) для переиспользования «To Analyse»
|
||
с нуля; `plane_issue_id` сохраняется (аудит). Детали — 08-data-requirements.md.
|
||
|
||
### Точки врезки STOP (компоненты)
|
||
- `plane.py` — маршрут `stop` (fail-closed, не в `_DEFAULT_STATES`) → `handle_stop`; гейт релонча
|
||
ограничен стадией `analysis`.
|
||
- `stage_engine.cancel_task` — оркестрация отмены (graceful SIGTERM, cancel-jobs, worktree+branch,
|
||
tombstone, notify); безопасное прерывание merge/deploy (D7 локального ADR).
|
||
- leaf `src/cancel.py` — чистая логика (`applies`/`in_critical_window`/`snapshot`), never-raise.
|
||
- `src/gitea.py` — `delete_remote_branch` (never-raise; только feature-ветка, `main` неприкосновенен).
|
||
- `GET /queue` — read-only блок `stop`.
|
||
|
||
### Флаги / совместимость
|
||
- Kill-switch `stop_status_enabled` + scope `stop_status_repos` (CSV, пусто → все репо).
|
||
- При `stop_status_enabled=False`: STOP-обработка и гейт релонча инертны; расширение
|
||
терминал-набора `cancelled` безвредно при отсутствии отменённых задач → **нулевая регрессия**.
|
||
- `STAGE_TRANSITIONS` (exit-гейты) / `QG_CHECKS` / `check_*` / семантика
|
||
Approved/Rejected/Confirm Deploy / merge-gate (ORCH-043) / merge-verify (ORCH-071/073) /
|
||
image-freshness (ORCH-058) / post-deploy (ORCH-021) / serial-gate FIFO (ORCH-088) / auto-label
|
||
(ORCH-089) — **без изменений**.
|
||
- Миграции БД — только аддитивные/идемпотентные (`_ensure_column`); enduro не затронут (NFR-2).
|
||
|
||
## Последствия
|
||
- **+** Единое, консистентное определение терминальности — устранён латентный рассинхрон
|
||
`done`-only между планировщиком и реконсилятором.
|
||
- **+** STOP безопасен для self-hosting: не трогает `main`/прод, отложенная отмена в критическом
|
||
окне.
|
||
- **−** Терминальность теперь читается из набора `{done, cancelled}`, а не из скаляра `'done'` —
|
||
будущие подсистемы обязаны использовать набор. Митигейшн: этот ADR + маркер `ORCH-090` в
|
||
изменённых местах + тесты.
|
||
- **Откат:** `stop_status_enabled=False`; полный revert — снять врезки и вернуть предикаты к
|
||
`stage != 'done'`.
|
||
|
||
## Эволюция маркеров `cancelled`-терминала
|
||
Места, признающие `cancelled` терминальным (см. список выше), несут маркер `ORCH-090`. Правка
|
||
любого из них — сверяться с этим ADR (анти-археология: 3+ маркеров → одна ссылка сюда,
|
||
TRACEABILITY.md).
|
||
|
||
## Ссылки
|
||
- Детальный ADR: `docs/work-items/ORCH-090/06-adr/ADR-001-stop-cancel-task.md`
|
||
- Data: `docs/work-items/ORCH-090/08-data-requirements.md`
|
||
- Связанные: adr-0017 (serial-gate), adr-0015 (task-deps), adr-0007 (self-deploy),
|
||
adr-0006 (merge-gate), adr-0018 (auto-label)
|