---
work_item: ORCH-117
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-117 — sandbox-only fail-closed изоляция записи в Plane

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

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

---

## AC-1 — Регресс ORCH-114: живой прод-токен + pytest не мутируют боевой проект (ОБЯЗАТЕЛЬНЫЙ)

**Условие:** В тест-процессе с **живым боевым** `ORCH_PLANE_API_TOKEN` в окружении вызывается
`notify_stage_change("ORCH-114", "deploy", "done")` (и/или прямые `update_issue_state`/`add_comment`/
`_set_issue_state_direct`) с целевым боевым проектом `7a79f0a9-5278-49cd-9007-9a338f238f9c`.
- **PASS:** **ноль** реальных `httpx.patch`/`httpx.post` уходит в боевой проект (мок `httpx` не
  вызван для prod-URL **или** гард блокирует до сетевого вызова). Существует тест, который **красный
  до фикса** (воспроизводит запись инцидента) и **зелёный после**.
- **FAIL:** хоть один PATCH/POST достигает боевого проекта из тест-процесса; либо регресс-тест
  отсутствует; либо он зелёный и до фикса (значит ничего не проверяет).

---

## AC-2 — Sandbox-e2e сохранён: запись в SANDBOX под opt-in проходит

**Условие:** В тест-процессе включён явный интеграционный opt-in **и** целевой проект — SANDBOX
`8c5a3025-4f9d-4190-b79f-fa06276bb27e`.
- **PASS:** примитивы записи выполняют реальный `httpx`-вызов в SANDBOX (в тесте — через мок,
  подтверждающий, что вызов разрешён и адресован sandbox-URL); `scripts/staging_check.py` Block C
  (E2E в SANDBOX) остаётся работоспособным.
- **FAIL:** запись в SANDBOX заблокирована при корректном opt-in; либо sandbox-e2e сломан.

---

## AC-3 — Sandbox-only даже с opt-in: боевой проект запрещён всегда

**Условие:** В тест-процессе включён opt-in, но целевой проект — боевой (`7a79f0a9-…` ORCH или
боевой enduro-проект).
- **PASS:** запись блокируется (allowlist sandbox-only) независимо от opt-in; аудит-лог фиксирует
  причину `prod-project-in-test`.
- **FAIL:** включённый opt-in разрешает запись в любой проект, включая боевой.

---

## AC-4 — Default-deny: без opt-in запись из тестов заблокирована (fail-closed)

**Условие:** Обычный `pytest tests/` без явного opt-in; целевой проект — любой (sandbox или боевой).
- **PASS:** все 3 примитива (`update_issue_state`, `add_comment`, `_set_issue_state_direct`) не
  делают реальной записи; autouse-фикстура `conftest.py` обеспечивает это по умолчанию во всех
  тестах. Неопределённый/неразрешимый целевой проект → блок (NFR-1).
- **FAIL:** без opt-in возможна реальная запись в Plane; либо autouse-страховка отсутствует.

---

## AC-5 — Нулевая регрессия боевого рантайма

**Условие:** Процесс — **не** pytest (боевой рантайм оркестратора).
- **PASS:** гард — no-op; запись в Plane выполняется как до ORCH-117 (тот же URL/headers/payload).
  `STAGE_TRANSITIONS`/`QG_CHECKS`/`check_*`/machine-verdict ключи/схема БД — **байт-в-байт не
  изменены** (проверяемо diff'ом / структурными тестами).
- **FAIL:** боевая запись подавлена/изменена; либо изменены гейты/схема БД.

---

## AC-6 — Staging-рантайм пишет в SANDBOX

**Условие:** Процесс — staging-рантайм (8501), **не** pytest; сконфигурирован на sandbox-проект.
- **PASS:** детект тест-процесса **не** срабатывает для staging → запись в SANDBOX проходит как
  прежде; staging не приравнивается к pytest.
- **FAIL:** staging-запись в SANDBOX заблокирована (ложноположительный детект тест-процесса).

---

## AC-7 — Устойчивость к захвату токена на импорте

**Условие:** `PLANE_HEADERS`/`PROJECT_ID` захвачены на импорте `plane_sync` (стр. 17/57); тест
выполняет запись, не подменяя токен/env постфактум.
- **PASS:** гард срабатывает **на момент вызова** примитива и блокирует прод-запись независимо от
  того, какой токен в `PLANE_HEADERS`; защита **не** опирается на `os.environ.setdefault(...)`/
  подмену `settings.plane_api_token`.
- **FAIL:** защита зависит от подмены токена/env и потому не срабатывает в проде (как было до фикса).

---

## AC-8 — Аудит/наблюдаемость блокировок и разрешений

**Условие:** Происходит блокировка записи (или разрешённая sandbox-запись).
- **PASS:** на блокировку эмитится структурный WARNING/ERROR с `project_id`, `work_item`, операцией и
  причиной; на разрешённую sandbox-запись — audit-INFO. Сообщения делают инцидент видимым.
- **FAIL:** блокировка/разрешение происходят молча (нет логов с требуемыми полями).

---

## AC-9 — Kill-switch без чёрного хода

**Условие:** Если введён kill-switch гарда — он выключен.
- **PASS:** выключение деградирует к прежнему (до-ORCH-117) поведению, но **не** разрешает молча
  запись в **боевой** проект из pytest сверх того, что было; реальная sandbox-запись из тестов
  управляется только opt-in + allowlist (не общим kill-switch).
- **FAIL:** общий kill-switch служит чёрным ходом, переоткрывающим прод-запись из тест-процесса.

---

## AC-10 — Документация и конфиг обновлены (golden source)

**Условие:** PR закрывает ORCH-117.
- **PASS:** обновлены `.env.example` (новые `ORCH_*` ключи с безопасными дефолтами), `CLAUDE.md`,
  `docs/architecture/README.md`, `docs/operations/INFRA.md` — описан инвариант изоляции тест/staging
  записи в Plane. ADR выпущен (`06-adr/ADR-001-*.md`).
- **FAIL:** код есть, документация/конфиг не обновлены (по правилу reviewer'а ORCH — finding ≥P1).

---

## AC-11 — Полный регресс зелёный

**Условие:** `pytest tests/ -q` после фикса.
- **PASS:** весь сьют зелёный (автоматическая autouse-страховка не ломает существующие тесты).
- **FAIL:** появились падения/флапы из-за внедрённого гарда/фикстуры.

---

## Сводная матрица AC ↔ FR/BR
| AC | Покрывает |
|----|-----------|
| AC-1 | BR-1 / FR-1 / FR-6 (обязательный регресс ORCH-114) |
| AC-2 | BR-5 / FR-2 |
| AC-3 | BR-2 / FR-2 |
| AC-4 | BR-3 / BR-4 / FR-2 / FR-3 / NFR-1 |
| AC-5 | NFR-2 / FR-4 |
| AC-6 | NFR-3 / FR-4 |
| AC-7 | NFR-4 / FR-1 |
| AC-8 | BR-6 / FR-5 |
| AC-9 | NFR-6 / FR-7 |
| AC-10 | BR-7 |
| AC-11 | NFR-2 (регресс-нейтральность) |
