fix(cancel): narrow STOP critical-window so deploy-park cancel applies (ORCH-090)
All checks were successful
CI / test (push) Successful in 31s
CI / test (pull_request) Successful in 32s

Review P1: a STOP while a self-hosting task is PARKED on `deploy` awaiting the
manual `Confirm Deploy` was classified as a critical merge/deploy window solely
because the task still held the per-repo merge-lease (held from merge-gate through
deploy->done). That window is fully reversible — nothing is merged or deployed yet
(the irreversible merge_pr runs later in _handle_merge_verify, always under an
INITIATED marker). So the cancel was DEFERRED to run_deploy_finalizer, which only
runs after Phase B (Confirm Deploy) — the very step the operator pressed STOP to
avoid. Result: the deferred cancel was never applied, the task wedged non-terminal
holding the lease, blocking the repo's serial-gate (ORCH-088) and merges.

Fix: gate the merge-lease branch of cancel.in_critical_window on an actively
RUNNING actor (_task_has_running_actor). Lease held + running deploy/merge job ->
still deferred (genuine in-flight step). Lease held + no running actor (idle
deploy parking) -> NOT critical -> immediate full reset, which itself releases the
lease (step 3c) and drives the task terminal. INITIATED-marker deferral unchanged.

Also fixes review P2 (AC-6): set_task_cancel_requested now returns the first-stamp
fact (rowcount), and the deferred branch only notifies on the first transition —
a repeated STOP while still deferred no longer spams duplicate notifications.

Tests: test_d7_lease_held_idle_parking_is_not_critical,
test_d7_lease_held_with_running_actor_still_critical,
test_d7_stop_on_deploy_awaiting_confirm_full_resets,
test_d7_repeated_stop_in_critical_window_no_duplicate_notify. Full suite green (1349).

Refs: ORCH-090

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 21:19:28 +03:00
parent 61b8abe134
commit 0f945cfc1f
7 changed files with 162 additions and 32 deletions

View File

@@ -308,10 +308,16 @@ Phase A ждёт ручного `Confirm Deploy`, ORCH-059). ORCH-089 снима
> `plane_issue_id` оставил бы отменённую строку «находимой» и заблокировал бы re-create (BR-3/TR-4).
> Поэтому он тоже тумбстонится; исходный UUID (== исходный `plane_id` во всех путях создания) парсится
> из детерминированного суффикса для аудита.
- **Безопасное прерывание merge/deploy:** STOP в критическом окне (self-deploy `INITIATED`-sentinel
ORCH-036, держание merge-lease ORCH-043/071) → **отложенная отмена** (durable
- **Безопасное прерывание merge/deploy:** STOP в критическом окне **отложенная отмена** (durable
`cancel_requested_at`, отмена только `queued`-job'ов, алерт); необратимый шаг доводится до
честного исхода; `main`/прод-контейнер не трогаются (NFR-3).
честного исхода; `main`/прод-контейнер не трогаются (NFR-3). «Критическое окно» = реально начатый
необратимый шаг: self-deploy `INITIATED`-sentinel (ORCH-036; детач-деплой + поздний `merge_pr` в
`_handle_merge_verify` идут под тем же маркером) **либо** держание merge-lease (ORCH-043/071) **И**
активно бегущий актор (running-job). **P1-уточнение (ORCH-090 review):** удержание merge-lease в
Phase A на `deploy` в ожидании ручного `Confirm Deploy` без бегущего актора **обратимо**НЕ
критично → немедленный полный сброс (он сам отпускает lease). Иначе deferred-отмена ушла бы к
finalizer'у, который оператор (нажавший STOP, чтобы НЕ подтверждать) никогда не запустит — задача
застряла бы нетерминальной с удержанным lease, клиня serial-gate репо.
- **Закрытие дыры релонча:** relaunch в `handle_status_start` ограничен стадией `analysis`
(единственный владелец Needs-Input, ORCH-066) — тихий релонч середины пайплайна на старой ветке
устранён; единственный вход к запуску — «To Analyse» (`start_pipeline`).