feat(cancel): STOP-status task cancellation + relaunch-hole close (ORCH-090)
Introduce the dedicated Plane STOP status as a single declarative task-cancel
mechanism: stop the active agent (graceful SIGTERM cascade), cancel all jobs
(terminal `cancelled`, never requeued), remove the worktree + delete the remote
feature branch (never main, never force-push), drive the task to the new
system-terminal state `cancelled` and tombstone the natural keys so a later
"To Analyse" re-creates it from scratch (docs artefacts preserved). STOP during a
critical merge/deploy window is deferred until the irreversible step finishes
honestly. Also closes the relaunch hole: handle_status_start relaunch is gated to
the `analysis` stage; the only pipeline-start entry point remains "To Analyse".
Cross-cutting (adr-0026): the "task terminal" predicate is widened {done} ->
{done, cancelled} in serial_gate / task_deps / stages sink + reaper/worker
requeue guards. STAGE_TRANSITIONS exit-gates / QG_CHECKS / check_* are unchanged
(`cancelled` is a sink, not a new edge). Additive, never-raise, restart-safe,
under kill-switch ORCH_STOP_STATUS_ENABLED (off -> zero regression).
New: src/cancel.py (leaf), src/gitea.py (delete_remote_branch), tasks columns
cancelled_at/cancel_requested_at, jobs status `cancelled`, GET /queue `stop` block.
Tests: tests/test_stop_status.py (TC-01..TC-14 + D7); full suite green (1345).
Docs updated in-PR (architecture README, CLAUDE.md, README.md, .env.example,
CHANGELOG). ADR-001 D4 refinement: plane_issue_id is tombstoned too (the lookup
ORs on it) — original UUID recoverable from the parseable suffix.
Refs: ORCH-090
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
33
CLAUDE.md
33
CLAUDE.md
@@ -111,6 +111,39 @@ created → analysis → architecture → development → review → testing →
|
||||
Детали — `docs/work-items/ORCH-089/06-adr/ADR-001-auto-label-gates.md`,
|
||||
`docs/architecture/adr/adr-0018-auto-label-gates.md`.
|
||||
|
||||
## Отмена задачи: статус STOP (ORCH-090)
|
||||
Выделенный Plane-статус **STOP** — операторская кнопка «отменить + сбросить» задачу. Вводит
|
||||
**новое системное терминальное состояние `cancelled`** (стадия `tasks.stage='cancelled'` + job-исход
|
||||
`jobs.status='cancelled'`), равноправное `done`. Логический ключ `stop` — **fail-closed** (нет в
|
||||
`_DEFAULT_STATES`, по образцу `confirm_deploy`/ORCH-059): доска без статуса STOP → ветка не
|
||||
активируется. Маршрут `handle_issue_updated → handle_stop → stage_engine.cancel_task`:
|
||||
- **Полный сброс** (вне критичного окна): graceful SIGTERM активного агента (`launcher.stop_process`,
|
||||
переиспользует каскад `_watchdog`), все job'ы → терминальный `cancelled` (не реквью'ятся:
|
||||
`claim_next_job` берёт только `queued`, reaper/worker сверяют терминал задачи — TR-2), удаление
|
||||
worktree + **рабочей** Gitea-ветки (`gitea.delete_remote_branch`, **никогда** `main`, без
|
||||
force-push), durable `stage='cancelled'` + **тумбстон** натуральных ключей (`plane_id`/
|
||||
`work_item_id`/`plane_issue_id` → суффикс `#cancelled-<id>`; ADR-001 D4 уточнён: тумбстонится и
|
||||
`plane_issue_id`, т.к. `get_task_by_plane_id`/`create_task_atomic` матчат по нему — иначе re-create
|
||||
коллизирует; исходный UUID парсится из суффикса для аудита). Docs-артефакты (`01..17`) сохраняются.
|
||||
- **STOP в критичном окне merge/deploy** (ADR-001 D7): `cancel.in_critical_window` (INITIATED-sentinel
|
||||
self-deploy ORCH-036 / держание merge-lease ORCH-043) → **отложенная** отмена: `tasks.cancel_requested_at`,
|
||||
снимаются только `queued` job'ы (running-актор деплоя/мержа не трогается), алерт; детерминированный
|
||||
finalizer (`run_deploy_finalizer`) доводит необратимый шаг до честного исхода и применяет отмену
|
||||
(`force=True`). STOP **никогда** не трогает `main`/force-push/прод-контейнер/detached-процесс (NFR-3).
|
||||
- **Кросс-каттинг (adr-0026):** предикат «задача терминальна» расширен `{done}` → `{done, cancelled}`
|
||||
в `serial_gate`/`task_deps`/`stages.py`-сток (иначе отменённая задача заклинит очередь репо);
|
||||
reconciler-терминал-скип уже знал `cancelled` (ORCH-086). `STAGE_TRANSITIONS` exit-гейты рёбер /
|
||||
`QG_CHECKS` / `check_*` — **не тронуты** (`cancelled` — сток, не ребро).
|
||||
- **Дыра релонча закрыта (D6):** relaunch агента в `handle_status_start` ограничен стадией `analysis`
|
||||
(единственный владелец Needs Input, ORCH-066); ручной перевод существующей задачи в иной промежуточный
|
||||
статус больше не релончит середину пайплайна. Запуск пайплайна — только «To Analyse» → `start_pipeline`.
|
||||
- Флаги `stop_status_enabled` (kill-switch; `False` → всё инертно, нулевая регрессия) / `stop_status_repos`
|
||||
(CSV; пусто → все репо). Leaf `src/cancel.py` (never-raise). Read-only блок `stop` в `GET /queue`.
|
||||
Аддитивные колонки `tasks.cancelled_at`/`cancel_requested_at` (`_ensure_column`). **Инфра-предусловие:**
|
||||
создать статус **STOP** с группой `cancelled` на доске ORCH (его отсутствие = fail-safe no-op). Детали —
|
||||
`docs/work-items/ORCH-090/06-adr/ADR-001-stop-cancel-task.md`,
|
||||
`docs/architecture/adr/adr-0026-stop-cancel-task.md`.
|
||||
|
||||
## Конвенции
|
||||
- Conventional Commits (`feat:`, `fix:`, `docs:`, `refactor:`, `test:`)
|
||||
- Ветки: `feature/ORCH-NNN-slug`, `fix/ORCH-NNN-slug`
|
||||
|
||||
Reference in New Issue
Block a user