---
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
---

# 03 — Критерии приёмки (Acceptance Criteria): ORCH-114 — Ownership-lease для side-effectful переходов + умное восстановление при старте

Work Item: **ORCH-114** · Repo: **orchestrator** · Стадия: analysis

Формат: каждый критерий имеет **PASS** (что должно быть истинно для приёмки) и **FAIL** (что
считается провалом). Reviewer/CI проверяет их буквально по файлам и тестам репозитория.

---

## AC-1 — Обязательный регресс: нет двойного эффекта при конкурентном входе в переход

**Условие:** два актора одновременно входят в `advance_stage(deploy-staging)` для одной задачи
(живой монитор-финализатор + второй путь: reaper / reconciler F-1 / webhook). Тест-двойники для
`merge_pr` / coverage-ratchet / image-rebuild / deploy-init считают число вызовов.
- **PASS:** side-effectful шаги (merge_pr, ratchet baseline, image-rebuild, инициация прод-деплоя,
  enqueue следующего агента) выполняются **ровно один раз**; персистится **ровно один** согласованный
  исход стадии; второй актор получает «lost-race»/defer и **не** выполняет побочных эффектов. Тест
  **красный до фикса, зелёный после** (воспроизводит класс инцидента ORCH-111).
- **FAIL:** любой side-effectful шаг вызван дважды; либо два противоречивых исхода (один откатил на
  `development`, другой довёл до `done`); либо тест не воспроизводит проблему до фикса.

---

## AC-2 — Compare-and-swap на запись стадии

**Условие:** CAS-вариант записи стадии вызывается двумя писателями с одинаковым ожидаемым предусловием;
первый применяется, второй приходит со «устаревшим» ожиданием.
- **PASS:** первый writer применяет переход (rowcount=1); второй получает «lost-race» (rowcount=0),
  стадия **не** мутируется повторно; при выключенном kill-switch CAS вырождается в прежний безусловный
  `update_task_stage` (байт-в-байт).
- **FAIL:** второй writer перезатирает стадию; либо CAS меняет семантику записи при выключенном флаге.

---

## AC-3 — Жизненный цикл владения: acquire / release / реклейм

**Условие:** актор начинает side-effectful финализацию.
- **PASS:** владение захватывается на границе финализации и освобождается в `try/finally` — в т.ч.
  при исключении и при откате (rollback); durable-запись владения видна другому актору; после release
  владение реклеймится/свободно.
- **FAIL:** владение не освобождается при исключении/откате (lease «течёт» и клинит задачу); либо не
  захватывается на границе.

---

## AC-4 — Reaper откладывает реклейм при живом владении (все пути, не только Tier-2/deploy-staging)

**Условие:** durable-владение активно (живой финализатор), reaper делает тик.
- **PASS:** reaper **defer** (лог + счётчик, без повторного `advance_stage`) пока владение живо и в
  пределах бюджета; область defer обобщена за пределы Tier-2/`deploy-staging` ORCH-113 на релевантные
  пути; атомарный `reap_running_job` rowcount-guard сохранён.
- **FAIL:** reaper повторно исполняет финализацию при живом владельце; либо defer ограничен только
  прежней узкой областью, оставляя кросс-путь открытым.

---

## AC-5 — Reaper добивает мёртвое/устаревшее владение в ограниченное время

**Условие:** владелец провально мёртв/завис (lease устарел), финализация не прогрессирует.
- **PASS:** reaper реклеймит задачу в пределах Tier-3 backstop `reaper_max_running_s` (маркер владения
  backstop не обходит); задача не остаётся навсегда заклиненной; сквозной инвариант
  `reaper_max_running_s > Σ(deploy-staging gate-work) + grace` сохранён.
- **FAIL:** мёртвое владение блокирует задачу бессрочно; либо нарушен бюджетный инвариант ORCH-065/
  109/110/113.

---

## AC-6 — Умное восстановление при рестарте процесса

**Условие:** процесс убит **в середине** финализации `deploy-staging`/`deploy` (in-memory состояние
потеряно); процесс перезапущен (`requeue_running_jobs` + цикл).
- **PASS:** восстановление через durable-владение/эпоху + авторитетные факты (SHA-in-main ORCH-071/073,
  маркер `INITIATED`) детерминированно сходится к **единственному** согласованному исходу: незавершённое
  дорешается, **уже применённый** необратимый шаг (мерж/ratchet/прод-деплой) **не** применяется повторно.
- **FAIL:** после рестарта переход исполняется заново с двойным необратимым эффектом; либо возникает
  противоречие rollback↔done; либо задача застревает нетерминальной с удержанным lease.

---

## AC-7 — Reconciler F-1 пропускает переход при активном lease

**Условие:** lease перехода активен; reconciler F-1 сканирует задачу.
- **PASS:** F-1 **defer/skip** (silent, наблюдаемо), не вызывает `advance_stage`, по образцу skip-guard'ов
  escalated/Blocked/task-deps; fail-safe: неопределённость lease → консервативный skip.
- **FAIL:** F-1 продвигает стадию параллельно живому владельцу.

---

## AC-8 — Webhook-путь пропускает переход при активном lease

**Условие:** lease перехода активен; приходит Plane-webhook (Approved / Confirm Deploy) на ту же задачу.
- **PASS:** webhook-путь продвижения **defer**, не дублирует переход/финализацию при живом владельце;
  поздний легитимный сигнал не теряется (повторно отработает после release или станет идемпотентным no-op).
- **FAIL:** webhook повторно входит в переход параллельно владельцу и даёт двойной эффект.

---

## AC-9 — Kill-switch off → поведение байт-в-байт прежнее

**Условие:** новый kill-switch выключен.
- **PASS:** lease не пишется/не читается; CAS вырождается в прежний безусловный `update_task_stage`;
  reaper/reconciler/webhook/startup ведут себя как до ORCH-114; существующий `pytest tests/` зелёный
  без правок ожиданий; enduro не затронут.
- **FAIL:** при выключенном флаге наблюдается любое отличие от до-ORCH-114 поведения.

---

## AC-10 — never-raise + fail-open (hot-path) / fail-closed (prod-safety)

**Условие:** механизм владения сталкивается с ошибкой (БД-сбой/повреждённая запись lease/исключение).
- **PASS:** ни одна публичная функция владения не роняет конвейер; горячий путь claim/guard —
  **fail-open** (общая очередь всех проектов не клинится); решения, критичные для необратимости/прода —
  **fail-closed**; на ошибке — WARNING + безопасный дефолт.
- **FAIL:** ошибка механизма роняет claim/конвейер; либо hot-path заклинивает очередь; либо
  prod-критичное решение фейлит «открыто».

---

## AC-11 — Инварианты конвейера не тронуты

**Условие:** аудит диффа против `src/stages.py`, `src/qg/checks.py`, схемы БД.
- **PASS:** `STAGE_TRANSITIONS` / реестр `QG_CHECKS` / семантика и имена `check_*` / машинные
  вердикт-ключи (`verdict:`/`result:`/`deploy_status:`/`staging_status:`/`security_status:`/
  `coverage_status:`) — **байт-в-байт**; любое новое хранилище аддитивно/идемпотентно
  (`CREATE TABLE IF NOT EXISTS` / `_ensure_column`), схемы существующих таблиц неизменны.
- **FAIL:** изменены состав/порядок стадий, реестр/семантика гейтов, вердикт-ключи или существующие
  столбцы/индексы.

---

## AC-12 — Наблюдаемость

**Условие:** `GET /queue` при включённом флаге.
- **PASS:** присутствует аддитивный read-only блок владения (держатели/возраст/defer-счётчики/реклеймы);
  существующие ключи `/queue` не сломаны; форсированный/устаревший реклейм даёт Telegram-алерт с
  кликабельным номером.
- **FAIL:** блок отсутствует/ломает существующий вывод; реклейм происходит молча без наблюдаемости.

---

## AC-13 — Self-hosting безопасность

**Условие:** аудит механизма владения на побочные действия.
- **PASS:** механизм **никогда** не рестартит прод-контейнер `orchestrator`, не пушит/force-push в
  `main`, не трогает detached deploy-процесс; деплой орка по-прежнему только через staging-гейт (8501)
  и `Confirm Deploy`.
- **FAIL:** механизм владения инициирует рестарт прода/мутацию `main`/вмешательство в detached-деплой.

---

## Сводная матрица AC ↔ FR/BR/NFR

| AC | Покрывает | Тип проверки |
|----|-----------|--------------|
| AC-1 | BR-1, BR-6, BR-8 / FR-1, FR-2 | integration (regression, red→green) |
| AC-2 | BR-2 / FR-2 | unit |
| AC-3 | BR-1 / FR-1 | unit |
| AC-4 | BR-3 / FR-3 | integration |
| AC-5 | BR-3, NFR-6 / FR-3 | unit/integration |
| AC-6 | BR-4, BR-6, NFR-4, NFR-7 / FR-1…FR-4 | integration (restart-recovery) |
| AC-7 | BR-5 / FR-5 | unit/integration |
| AC-8 | BR-5 / FR-5 | unit/integration |
| AC-9 | BR-9, NFR-8 / FR-7 | regression (kill-switch off) |
| AC-10 | NFR-1 / FR-7 | unit |
| AC-11 | NFR-3 / FR-6 (negative) | structural audit |
| AC-12 | BR-7 / FR-6 | unit/integration |
| AC-13 | NFR-5 / FR-1 | structural audit |
