Files
orchestrator/docs/architecture/adr/adr-0026-stop-cancel-task.md

8.0 KiB
Raw Permalink Blame History

work_item, stage, author_agent, status, created_at, model_used
work_item stage author_agent status created_at model_used
ORCH-090 architecture architect proposed 2026-06-09 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, стр. 398415).
  • 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.pyrepo_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.pydelete_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)