# Критерии приёмки — ORCH-068

Формат: каждый AC имеет чёткое условие PASS/FAIL. Задача принимается только при ВСЕХ PASS.

## Основное (P0) — livelock / спам

### AC-1. Синхронизированная done-задача → тишина
**Дано:** задача в Plane `state=Done`, БД `stage=done`, активных job нет.
**Когда:** выполняется один тик F-2 (`reconcile_plane_once` / `_reconcile_plane_project`).
- PASS: `_note_unblock` НЕ вызван; `send_telegram` НЕ вызван; `unblocked_total` не изменился; создано 0 jobs.
- FAIL: любое уведомление/лог «разблокирована»/инкремент счётчика для этой задачи.

### AC-2. Терминалы исключены из actionable-выборки
**Дано:** проект, где `Done` (и/или `Cancelled`) по UUID совпадает/«схлопнут» с `approved`/`rejected`.
**Когда:** `_reconcile_plane_project` формирует набор и обходит issues.
- PASS: issue в терминальном статусе (completed-группа / `Cancelled`) НЕ попадает ни в одну из веток `in_progress/approved/rejected`; для неё F-2 — no-op silence.
- FAIL: терминальная issue заходит в ветку approved/rejected/in_progress.

### AC-3. `_note_unblock` только при реальном state change
**Дано:** dispatch обработчика (`handle_verdict`/`handle_status_start`) фактически НЕ изменил стадию задачи (no-op, задача уже в целевом состоянии).
- PASS: `_note_unblock` НЕ вызван.
- FAIL: `_note_unblock` вызван после no-op dispatch.

### AC-4. Дедуп по неизменному состоянию
**Дано:** две последовательные итерации тика по одной и той же синхронизированной задаче, состояние между тиками не менялось.
- PASS: суммарно 0 повторных уведомлений по этой задаче.
- FAIL: повторное уведомление на втором тике без изменения состояния.

### AC-5. Нет регресса легитимной разблокировки F-2
**Дано:** задача, у которой Plane=`Approved`, а локальная стадия НЕ продвинулась (реально потерянный verdict-webhook), grace выдержан, активных job нет.
**Когда:** тик F-2.
- PASS: `handle_verdict(approved)` доигран; задача продвинута; `_note_unblock` вызван РОВНО один раз (реальный state change).
- FAIL: задача не продвинута ИЛИ нотификация не отправлена ИЛИ отправлена многократно.

### AC-6. Аналогично для in_progress-старта и rejected-отката
- PASS: потерянный `In Progress` (task is None) → старт пайплайна + 1 unblock; потерянный `Rejected` → откат + 1 unblock — оба только при реальном изменении.
- FAIL: ложный/повторный unblock или отсутствие легитимного.

## Инварианты (P0)

### AC-7. Деньги/ресурсы не тратятся на синхронизированные задачи
- PASS: 0 jobs, 0 agent_runs, 0 токенов для done/cancelled задач (как до бага).
- FAIL: любой созданный job/agent_run.

### AC-8. never-raise сохранён
**Дано:** `list_issues_by_state` / `get_project_states` / `_dispatch` / `send_telegram` бросают исключение.
- PASS: тик не падает; ошибка изолирована (per-issue / per-project), логируется; остальные задачи обрабатываются.
- FAIL: непойманное исключение роняет тик/поток.

### AC-9. Kill-switch'и работают
- PASS: `ORCH_RECONCILE_ENABLED=false` → F-1 и F-2 не выполняются; `ORCH_RECONCILE_PLANE_ENABLED=false` → F-2 не выполняется, F-1 работает.
- FAIL: любой свитч не гасит соответствующую ветку.

### AC-10. F-1 / F-3 / реестры / схема БД не изменены
- PASS: `STAGE_TRANSITIONS`, `QG_CHECKS`, схема БД, контракты `handle_*`, поведение F-1/F-3 — без изменений (diff не затрагивает).
- FAIL: любое изменение перечисленного.

### AC-11. Self-hosting безопасность
- PASS: ни один путь reconciler не рестартит/не роняет прод-контейнер `orchestrator`.
- FAIL: обратное.

## Secondary (P1) — кэш статусов

### AC-12. Устаревший `_STATES_CACHE` обновляется без рестарта
**Дано:** после старта процесса в Plane появился новый статус (его UUID отсутствует в кэше).
**Когда:** срабатывает выбранный механизм (TTL истёк / flush-on-unknown / ручной flush).
- PASS: следующий `get_project_states` возвращает свежий набор, включающий новый статус; webhook на новый статус даёт корректное pipeline-действие БЕЗ рестарта.
- FAIL: процесс продолжает отдавать устаревший набор → «no pipeline action».

### AC-13. Совместимость кэша по умолчанию
- PASS: при дефолтных настройках (TTL не задан / flush не сработал) поведение `get_project_states` не регрессирует; enduro по-прежнему получает свои UUID, fallback на `_DEFAULT_STATES` при недоступности API сохранён.
- FAIL: регресс резолва статусов или потеря fallback.

## Документация (P0 по правилам проекта)

### AC-14. Документация обновлена в том же PR
- PASS: обновлены применимые из {`docs/architecture/README.md` (Reconciler/Plane Sync), ADR, `CHANGELOG.md`, `CLAUDE.md`, `.env.example`}; reviewer подтверждает.
- FAIL: поведение изменено, доки/ADR/CHANGELOG не обновлены.

## Тесты (P0)

### AC-15. `pytest tests/ -q` зелёный
- PASS: весь набор тестов проходит; добавлены регресс-тесты из `04-test-plan.yaml`, включая done→silence на enduro и orchestrator.
- FAIL: любой красный тест или отсутствие регресс-теста на основной баг.
