---
work_item: ORCH-124
stage: analysis
author_agent: analyst
status: ready-for-review
created_at: 2026-06-16
model_used: claude-opus-4-8
escalate: full-cycle
---

# 03 — Критерии приёмки (Acceptance Criteria): ORCH-124 — wait/terminal-семантика serial-gate

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

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

---

## AC-1 — Приостановленный предшественник не блокирует срочного успешника (регресс инцидента)

**Условие:** репо имеет более раннюю задачу A (`A.id < B.id`) в состоянии паузы (Blocked/Backlog/
Needs-Input по явному намерению оператора) и более позднюю срочную задачу B с queued analyst-job.
- **PASS:** `build_claim_clause()` НЕ блокирует analyst-job задачи B; `claim_next_job()` выбирает его;
  B входит в `analysis`. Тест воспроизводит инцидент ORCH-116/ORCH-123 (красный до фикса, зелёный
  после).
- **FAIL:** analyst-job B остаётся `queued`, потому что приостановленная A всё ещё считается активной.

---

## AC-2 — Нормально исполняемая задача по-прежнему держит гейт (анти-stale-base, без регресса ORCH-088)

**Условие:** репо имеет более раннюю задачу A, **реально исполняемую** (не на паузе, стадия вне
`{done,cancelled}`) и более позднюю задачу B с queued analyst-job.
- **PASS:** analyst-job B **остаётся заблокированным** (FIFO ORCH-088 цел); B стартует только когда A
  становится `done`/`cancelled` (или явно поставлена на паузу оператором).
- **FAIL:** B стартует поверх ещё не завершённой активной A → возврат stale-base дефекта, который
  закрывал ORCH-088.

---

## AC-3 — Пауза снимает гейт только по явному durable намерению

**Условие:** способ перевода задачи в «паузу» для serial-gate.
- **PASS:** освобождение гейта происходит **только** при явном операторском сигнале (эндпоинт
  pause / выбранный механизм ADR), сигнал **durable** (переживает рестарт процесса/контейнера) и
  **DB-резолвимый**. Никакого эвристического само-распаузивания.
- **FAIL:** гейт снимается без явного намерения (например, по умолчанию для любой не-`done` задачи) ИЛИ
  намерение теряется после рестарта.

---

## AC-4 — Анти-stale-base при возобновлении (R-1 разрешён архитектором)

**Условие:** приостановленная A возобновлена после того, как успешник B уже прошёл вперёд.
- **PASS:** ни A, ни B не остаются незаметно на устаревшей базе — выбранный механизм (демотирование A
  в FIFO + обязательный rebase / явный yield с rebase / иное по ADR) гарантирует, что возобновлённая
  задача строится/мержится на актуальном `origin/main`. Поведение зафиксировано тестом по контракту ADR.
- **FAIL:** возобновлённая A или прошедший B приводит к stale-ветке/затиранию кода без сигнала.

---

## AC-5 — Явные блоки (freeze + dependency) сохранены

**Условие:** активный `repo_freeze` для репо ИЛИ объявленная незавершённая зависимость в `job_deps`.
- **PASS:** claim успешника по-прежнему **заблокирован** freeze'ем (до ручного
  `POST /serial-gate/unfreeze`) и/или незавершённой зависимостью (`task_deps`) — пауза их не обходит.
- **FAIL:** «пауза» позволяет обойти freeze или объявленную зависимость.

---

## AC-6 — Корректная причина ожидания в `GET /queue`

**Условие:** блок `serial_gate` в снапшоте `GET /queue`.
- **PASS:** для ожидающего успешника видна корректная причина ожидания (`active-task` /
  `paused-predecessor` / `freeze` / `dependency`); приостановленный предшественник НЕ показывается как
  `active_task`. Существующие ключи снапшота не сломаны (BC).
- **FAIL:** снапшот показывает приостановленную задачу как `active_task` ИЛИ причина ожидания неверна/
  отсутствует.

---

## AC-7 — Hot-path остаётся offline (без сети)

**Условие:** путь `db.claim_next_job` → `serial_gate.build_claim_clause`.
- **PASS:** определение «активна/пауза» резолвится **только** локальной БД (SQL); ни одного сетевого
  вызова (Plane API и т.п.) на горячем пути claim. Проверяемо тестом (claim работает без сети / без
  Plane).
- **FAIL:** claim делает сетевой вызов для определения паузы.

---

## AC-8 — Машинные инварианты байт-в-байт; kill-switch off → прежнее поведение

**Условие:** реестры и выключатель.
- **PASS:** `STAGE_TRANSITIONS` / состав `QG_CHECKS` / имена и семантика `check_*` / machine-verdict
  ключи / схемы существующих таблиц — без изменений (структурный тест). При выключенном под-флаге паузы
  serial-gate ведёт себя байт-в-байт как ORCH-088/090.
- **FAIL:** изменён любой machine-verdict ключ / состав `QG_CHECKS` / `STAGE_TRANSITIONS`, либо
  выключенный флаг меняет поведение.

---

## AC-9 — never-raise и сохранённые fail-directions

**Условие:** ошибки внутри новой логики паузы.
- **PASS:** все публичные функции `serial_gate` never-raise; hot-claim build по-прежнему **fail-OPEN**
  (`""`-фрагмент при ошибке), freeze-решение по-прежнему **fail-CLOSED**; новая ветка паузы не
  инвертирует эти направления. Проверяемо тестом (инъекция ошибки → claim не падает, очередь не
  заклинивает).
- **FAIL:** ошибка в ветке паузы роняет claim/worker ИЛИ инвертирует fail-direction.

---

## AC-10 — Возобновление восстанавливает участие в гейте (BR-5)

**Условие:** распауза ранее приостановленной задачи.
- **PASS:** после resume задача снова участвует в serial-gate согласно выбранной семантике (держит
  гейт как активная либо ре-входит в FIFO); нет «вечного обхода».
- **FAIL:** возобновлённая задача навсегда остаётся вне гейта / её намерение паузы «залипает».

---

## Сводная матрица AC ↔ FR/BR
| AC | Покрывает |
|----|-----------|
| AC-1 | BR-1 / FR-1, FR-2 |
| AC-2 | BR-3 / FR-4 |
| AC-3 | BR-2 / FR-3 |
| AC-4 | BR-3, BR-5 / FR-4 |
| AC-5 | BR-6 / FR-6 |
| AC-6 | BR-4 / FR-5 |
| AC-7 | NFR-2 / FR-1 |
| AC-8 | NFR-3, NFR-4 / FR-7 |
| AC-9 | NFR-1 / FR-7 |
| AC-10 | BR-5 / FR-3 |
