--- work_item: ORCH-090 stage: architecture author_agent: architect status: proposed created_at: 2026-06-09 model_used: claude-opus-4-8 --- # 08 — Требования к данным: ORCH-090 — Механизм отмены задачи (STOP) Work Item: **ORCH-090** · Repo: **orchestrator** · Стадия: architecture > Общая прод-БД (orchestrator + enduro). Все изменения — **только аддитивные и идемпотентные** > (`_ensure_column`); существующие таблицы-контракты не переопределяются (NFR-2, AC-9). ## Изменения схемы БД ### Таблица `tasks` — аддитивные колонки (через `_ensure_column`) | Колонка | Тип | Назначение | |---------|-----|------------| | `cancelled_at` | `TEXT` | durable-метка времени отмены (аудит/наблюдаемость). NULL для неотменённых. | | `cancel_requested_at` | `TEXT` | durable-метка «отмена запрошена, но отложена» (STOP в критическом окне merge/deploy, ADR-001 D7). Снимается при доведении отмены до конца. | Никаких `ALTER` существующих колонок. `init_db` идемпотентен (повторный вызов — no-op). ### Без DDL-изменений (расширение допустимых значений TEXT) - **`jobs.status`** — добавляется значение `cancelled` к набору `queued|running|done|failed`. Колонка уже `TEXT`; DDL не меняется. `claim_next_job` выбирает только `status='queued'` → `cancelled` исключён нативно. - **`tasks.stage`** — добавляется терминальное значение `cancelled` (сток, параллельно `done`). Колонка уже `TEXT DEFAULT 'created'`; DDL не меняется. `STAGE_TRANSITIONS` exit-гейты рёбер **не меняются** — `cancelled` это терминальное состояние, не новое ребро. ### Без изменений `job_deps`, `agent_runs`, `repo_freeze`, `tracker_messages`, индексы — контракты нетронуты. `QG_CHECKS` / `check_*` — без изменений. ## Новые/изменённые сущности ### Тумбстон натуральных ключей отменённой задачи (ADR-001 D4) На cancel выполняется UPDATE отменённой строки `tasks`: - `plane_id := plane_id || '#cancelled-' || id` - `work_item_id := work_item_id || '#cancelled-' || id` - `stage := 'cancelled'`, `cancelled_at := datetime('now')` - `plane_issue_id` — **сохраняется нетронутым** (аудит-связь с issue Plane). Цель: освободить натуральные ключи, чтобы повторный «To Analyse» создал свежую задачу (`get_task_by_plane_id(plane_id)` → `None`; anti-dup `create_task_atomic` / `ensure_unique_work_item_id` не коллизируют), сохранив строку для аудита. Формат суффикса `#cancelled-` детерминирован и парсится. ### Отмена job'ов (ADR-001 D3) `cancel_jobs_for_task(task_id)` — guarded UPDATE `SET status='cancelled', finished_at=datetime('now') WHERE task_id=? AND status IN ('queued','running')`. Терминальный исход, нигде не реквью'ящийся. ## Совместимость данных / миграции - **Аддитивность/идемпотентность:** только `_ensure_column` (no-op если колонка есть) и расширение наборов TEXT-значений; деструктивных/несовместимых миграций нет (AC-9). Повторная `init_db` после рестарта не падает. - **Restart-safe (NFR-4):** durable терминал = `tasks.stage='cancelled'` (уже понимается терминал-скипом реконсилятора, стр. 196). После рестарта `requeue_running_jobs` флипает только `running` → отменённые job'ы (`cancelled`) не оживают; отменённая задача не реконсилируется. - **Влияние на общую прод-БД:** изменения строго per-task; enduro не затрагивается, при `stop_status_enabled=False` или отсутствии отменённых задач — поведение БД 1:1 как сейчас. - **Кросс-каттинг (adr-0026):** предикат «задача незавершена» в `serial_gate`/`task_deps` расширяется `stage != 'done'` → `stage NOT IN ('done','cancelled')`, иначе отменённая задача заклинит очередь репо. Чтение БД (offline hot-path) не приобретает новых сетевых вызовов (NFR-6).