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

# 03 — Критерии приёмки (Acceptance Criteria): ORCH-123 — staging-runner execution strategy fix

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

Формат: каждый критерий имеет **PASS** (что должно быть истинно для приёмки) и **FAIL** (что
считается провалом). Любой машинный/ручной reviewer проверяет их буквально по файлам репозитория.
Критерии **механизм-агностичны** (конкретную стратегию выбирает архитектор), проверяют **требуемый
инвариант**, а не способ реализации.

---

## AC-1 — staging-сюита реально исполняется в проде (без docker CLI в контейнере)

**Условие:** Для self-hosting `orchestrator` на `deploy-staging` staging-runner запускает
`staging_check.py` так, что сюита **исполняется** в боевом окружении, не завися от бинаря `docker`
внутри прод-контейнера.
- **PASS:** Стратегия исполнения staging-сюиты не требует наличия `docker` CLI **внутри**
  прод-контейнера `orchestrator` (исполняется host-side через существующий доверенный канал, либо
  через смонтированный сокет/SDK с security-обоснованием, либо CLI добавлен в образ — на выбор ADR);
  тест воспроизводит окружение «нет `docker` в контейнере» и подтверждает, что сюита **исполняется**
  (а не падает spawn-error'ом).
- **FAIL:** staging-runner по-прежнему вызывает `docker exec` напрямую из прод-контейнера без
  доступного исполняемого/канала → `proc_group` spawn-error → tool-error.

---

## AC-2 — ORCH-116-подобная задача проходит `deploy-staging`

**Условие:** Задача с зелёными reviewer/tester и корректным кодом доходит до `deploy`, а не
откатывается на `development` из-за раннера.
- **PASS:** При успешной staging-сюите (exit 0) → `staging_status: SUCCESS` → штатное продвижение по
  под-гейтам ребра `deploy-staging → deploy` (контракт не тронут); ORCH-116-подобный сценарий в
  тесте/симуляции достигает `deploy`.
- **FAIL:** Корректная задача откатывается на `development` по причине раннера/окружения.

---

## AC-3 — environment/tool-error НЕ даёт вводящего в заблуждение код-фейл-отката

**Условие:** Невозможность исполнить сюиту по причине окружения (нет исполняемого/канал недоступен) не
трактуется как код-фейл.
- **PASS:** environment/tool-error → **отличимый** инфра/environment-исход (задача **не** откатывается
  на `development` как код-фейл; developer-retry **не** инкрементируется; оператору идёт алерт «инфра/
  окружение, НЕ дефект кода»). Подтверждено тестом: при недоступном исполняемом задача **не** уходит в
  `development` и счётчик developer-retry не растёт.
- **FAIL:** environment-дефект завершается `write_staging_log("FAILED") + advance` (откат на
  `development`) и/или жжёт developer-retry.

---

## AC-4 — настоящий код-фейл по-прежнему откатывает (анти-over-tolerance)

**Условие:** Если сюита **реально исполнилась** и упала (exit≠0), поведение не ослаблено.
- **PASS:** exit≠0 при исполнившейся сюите → `staging_status: FAILED` → **прежний** откат
  `deploy-staging → development` + developer-retry (байт-в-байт контракт ORCH-115 для «сюита
  исполнилась»). Тест подтверждает, что реальный фейл сюиты НЕ маскируется как «инфра».
- **FAIL:** Настоящий код-фейл сюиты трактуется как environment/инфра и не откатывает на `development`.

---

## AC-5 — prod-like preflight ловит «исполняемое/стратегия неработоспособна» до раската

**Условие:** Условие инцидента детектируется заранее, а не ложным откатом реальной задачи.
- **PASS:** Существует preflight (на старте сервиса и/или в smoke/`staging_check.py`/`applies`/
  `should_intercept`), который при неработоспособной стратегии исполнения сигнализирует (лог/алерт/
  отметка) **до** того, как задача дойдёт до ложного отката; есть тест, симулирующий «нет `docker` в
  контейнере», и он проверяет именно это.
- **FAIL:** Нет механизма раннего детекта; «исполняемое отсутствует» обнаруживается только постфактум
  откатом реальной задачи.

---

## AC-6 — обязательный регресс-тест (red → green)

**Условие:** Инцидент ORCH-116 воспроизводится тестом.
- **PASS:** Есть тест, моделирующий исполнение staging-сюиты в окружении **без `docker` CLI в
  контейнере**; он **красный на коде до фикса** (воспроизводит spawn-error → tool-error → ложный
  откат/false-FAILED) и **зелёный после фикса** (сюита исполняется / environment-дефект не даёт
  код-фейл-отката).
- **FAIL:** Регресс-теста нет, либо он зелёный и до фикса (значит, не воспроизводит дефект).

---

## AC-7 — гейт/контракт/стадии — без регресса (NFR-1)

**Условие:** Меняется продюсер/стратегия исполнения, не гейт.
- **PASS:** `STAGE_TRANSITIONS`, реестр `QG_CHECKS`, имена и семантика `check_staging_status`/
  `_parse_staging_status`, machine-verdict ключи (`staging_status:`/`deploy_status:`/…), схема БД —
  **байт-в-байт прежние** (структурный анти-регресс-тест зелёный); `15-staging-log.md` сохраняет
  `staging_status:`-frontmatter + 52c-схему (`author_agent: staging-runner`/`model_used: n/a`).
- **FAIL:** Любое из перечисленного изменено (имя/регистр/семантика/значение) либо введена миграция БД.

---

## AC-8 — self-hosting safety (NFR-2)

**Условие:** Путь исполнения staging-runner безопасен для общего прод-инстанса.
- **PASS:** Путь **никогда** не рестартит прод `orchestrator` (8500), не делает `docker compose up
  orchestrator`/`--build` прода, не force-push, не пишет в `main`, не правит `.env`; оперирует только
  запуском staging-сюиты (8501) и записью лога (статический/поведенческий ассерт зелёный).
- **FAIL:** Путь способен затронуть прод-контейнер/`main`/`.env`/сделать force-push.

---

## AC-9 — security-review для socket/CLI-вариантов (NFR-3)

**Условие:** Если выбран вариант с прямым `docker.sock`/CLI из контейнера.
- **PASS:** ADR (`06-adr/`) содержит явный security-разбор (поверхность атаки `docker.sock` =
  root-эквивалент, ограничение прав, `:ro` где возможно) **либо** выбран host-side ssh-вариант,
  переиспользующий доверенный ORCH-036 механизм без расширения привилегий — и это зафиксировано.
- **FAIL:** Введён доступ к `docker.sock`/CLI из контейнера без security-обоснования в ADR.

---

## AC-10 — kill-switch / область (NFR-4, NFR-6)

**Условие:** Обратимость и скоуп сохранены.
- **PASS:** `staging_runner_enabled=False` → `deploy-staging` снова через LLM-`deployer` (`_spawn`)
  байт-в-байт; `applies(repo)`: self-hosting `orchestrator` — real, прочие репо — no-op; любой новый
  флаг стратегии имеет дефолт = боевое (откат флагом → поведение до ORCH-123). Подтверждено тестом.
- **FAIL:** Выключение kill-switch не возвращает прежний путь, либо затронуты прочие репо.

---

## AC-11 — never-raise / очередь не клинится (NFR-5)

**Условие:** Сбой стратегии исполнения не роняет worker и не клинит очередь.
- **PASS:** Любая ошибка исполнения/классификации изолирована (never-raise); задача либо штатно
  продвигается, либо остаётся на `deploy-staging` для reconciler/reaper без вечного залипания; полный
  `pytest tests/ -q` зелёный.
- **FAIL:** Ошибка исполнения роняет worker / задача залипает на `deploy-staging` навсегда / тесты
  красные.

---

## AC-12 — документация границы исполнения (BR-5)

**Условие:** Граница исполнения staging-сюиты задокументирована.
- **PASS:** `docs/operations/INFRA.md` и `docs/architecture/README.md` содержат явное описание: где/как
  исполняется staging-сюита относительно прод-контейнера, какие исполняемые/сокеты доступны, почему
  docker-операции идут host-side (структурная сверка зелёная); `CHANGELOG.md`/`CLAUDE.md` обновлены.
- **FAIL:** Граница исполнения нигде не описана, или docs противоречат фактическому коду.

---

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