work_item: ORCH-114 stage: analysis author_agent: analyst status: ready-for-review created_at: 2026-06-15 model_used: claude-opus-4-8 escalate: full-cycle title: "Ownership-lease для side-effectful переходов стадий + умное восстановление при старте" framework: pytest scope: > Покрывается: единое владение side-effectful переходом (FR-1), CAS/epoch на запись стадии (FR-2), осведомлённость reaper о живом/мёртвом владении на всех путях (FR-3), умное восстановление при рестарте (FR-4), skip/defer в reconciler F-1 и webhook (FR-5), наблюдаемость (FR-6), kill-switch и never-raise (FR-7). Вне покрытия: переход на uvicorn --workers>1, частные симптомы ORCH-110/112 (уже закрыты и переиспользуются), изменение STAGE_TRANSITIONS/QG_CHECKS/check_*. notes: > TC-01 — ОБЯЗАТЕЛЬНЫЙ регресс класса инцидента ORCH-111: красный до фикса, зелёный после. Все side-effectful вызовы (merge_pr / coverage-ratchet / image-rebuild / deploy-init) проверяются через тест-двойники со счётчиком вызовов — без сети/реального git/прода/ssh. Restart-recovery моделируется сбросом in-memory состояния + повторным прогоном стартового восстановления над durable состоянием БД. Полный регресс tests/ должен оставаться зелёным; при выключенном kill-switch поведение байт-в-байт прежнее. tests: - id: TC-01 type: integration description: "ОБЯЗАТЕЛЬНЫЙ РЕГРЕСС. Два конкурентных входа в advance_stage(deploy-staging) одной задачи (живой финализатор + reaper/reconciler/webhook): каждый side-effectful шаг (merge_pr/ratchet/rebuild/deploy-init/enqueue) вызывается ровно один раз; персистится один согласованный исход; второй актор — lost-race/defer без побочных эффектов. Красный до фикса, зелёный после." module: tests/test_orch114_transition_ownership.py expected: PASS - id: TC-02 type: unit description: "CAS-запись стадии: первый writer применяет (rowcount=1), второй с устаревшим предусловием получает lost-race (rowcount=0) и не мутирует стадию (FR-2/AC-2)." module: tests/test_orch114_transition_ownership.py expected: PASS - id: TC-03 type: unit description: "Жизненный цикл владения: acquire на границе финализации; release в try/finally при нормальном завершении, при исключении и при откате (lease не течёт); durable-запись видна другому актору (FR-1/AC-3)." module: tests/test_orch114_transition_ownership.py expected: PASS - id: TC-04 type: integration description: "Reaper defer при живом владении на путях за пределами Tier-2/deploy-staging ORCH-113: повторный advance не выполняется, атомарный reap_running_job rowcount-guard сохранён, ведётся счётчик defer (FR-3/AC-4)." module: tests/test_orch114_transition_ownership.py expected: PASS - id: TC-05 type: unit description: "Reaper добивает мёртвое/устаревшее владение в пределах Tier-3 backstop reaper_max_running_s; маркер владения backstop не обходит; задача не клинится бессрочно (FR-3/AC-5/NFR-6)." module: tests/test_orch114_transition_ownership.py expected: PASS - id: TC-06 type: integration description: "Умное восстановление при рестарте: процесс убит в середине финализации, in-memory сброшен; стартовое восстановление над durable-состоянием + авторитетными фактами (SHA-in-main, INITIATED) сходится к единственному исходу без повторного необратимого эффекта (FR-4/AC-6)." module: tests/test_orch114_transition_ownership.py expected: PASS - id: TC-07 type: integration description: "Reconciler F-1 (advance_if_gate_passed) делает defer/skip при активном lease перехода; fail-safe: неопределённость lease → консервативный skip (FR-5/AC-7)." module: tests/test_orch114_transition_ownership.py expected: PASS - id: TC-08 type: integration description: "Webhook-путь (plane._try_advance_stage, Approved/Confirm Deploy) делает defer при активном lease; поздний легитимный сигнал не теряется (повтор после release / идемпотентный no-op) (FR-5/AC-8)." module: tests/test_orch114_transition_ownership.py expected: PASS - id: TC-09 type: integration description: "Kill-switch off: lease не пишется/не читается, CAS вырождается в прежний безусловный update_task_stage, reaper/reconciler/webhook/startup — байт-в-байт до ORCH-114; существующий pytest tests/ зелёный (FR-7/AC-9/NFR-8)." module: tests/test_orch114_transition_ownership.py expected: PASS - id: TC-10 type: unit description: "never-raise + fail-open/fail-closed: ошибка/повреждённая запись lease/исключение БД не роняют конвейер; hot-path claim/guard fail-open; prod-safety решение fail-closed; WARNING + безопасный дефолт (FR-7/AC-10/NFR-1)." module: tests/test_orch114_transition_ownership.py expected: PASS - id: TC-11 type: unit description: "Структурный аудит инвариантов: STAGE_TRANSITIONS / QG_CHECKS / имена-семантика check_* / вердикт-ключи байт-в-байт; новое хранилище аддитивно/идемпотентно (CREATE TABLE IF NOT EXISTS / _ensure_column), схемы существующих таблиц неизменны (NFR-3/AC-11)." module: tests/test_orch114_transition_ownership.py expected: PASS - id: TC-12 type: integration description: "Наблюдаемость: GET /queue несёт аддитивный read-only блок владения (держатели/возраст/defer/реклеймы), существующие ключи не сломаны; форсированный/устаревший реклейм даёт Telegram-алерт с кликабельным номером (FR-6/AC-12)." module: tests/test_orch114_transition_ownership.py expected: PASS - id: TC-13 type: unit description: "Self-hosting безопасность: механизм владения не инициирует рестарт прод-контейнера, не пушит/force-push в main, не трогает detached deploy-процесс (NFR-5/AC-13)." module: tests/test_orch114_transition_ownership.py expected: PASS - id: TC-14 type: integration description: "Полный регресс конвейера: pytest tests/ остаётся зелёным; deploy-staging-ребро и deploy-finalizer (Phase C) проходят при включённом механизме без двойных эффектов в одно-акторном happy-path (BR-8)." module: tests/ expected: PASS