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

# 03 — Критерии приёмки (Acceptance Criteria): ORCH-100 — FND/F1b: sidecar-watchdog

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

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

---

## AC-1 — Sidecar стартует отдельным контейнером и собирает все источники

**Условие:** есть папка `watchdog/` с кодом + `watchdog/Dockerfile`; в `docker-compose.yml` есть
сервис `orchestrator-watchdog`, собираемый из `watchdog/`; запущенный sidecar на тике собирает
сырьё орка (`GET /metrics`) + хост (диск/память/CPU) + контейнеры (`docker.sock`) + пинг зависимостей.
- **PASS:** `watchdog/Dockerfile` существует; сервис `orchestrator-watchdog` объявлен отдельным
  сервисом в `docker-compose.yml` (свой build/restart/`mem_limit`, read-only `docker.sock`); код
  sidecar реализует все 4 коллектора (метрики орка, хост, контейнеры, зависимости); тик опрашивает
  все 4 (подтверждается тестами/логами).
- **FAIL:** мониторинг встроен в процесс орка (`src/**`) / нет отдельного сервиса в compose / отсутствует
  любой из 4 коллекторов / `docker.sock` смонтирован НЕ read-only.

---

## AC-2 — Пороговый алерт: один на пересечение + throttle + recovery + орк-down

**Условие:** при пересечении порога — ровно один Telegram-алерт со **своего** канала sidecar; повтор
в cooldown молчит; возврат в норму шлёт recovery; недоступность `/metrics` орка → алерт «орк не
отвечает».
- **PASS:** чистая решающая функция возвращает `alert | realert | recovery | none` по семантике FR-7
  (тесты TC-01…TC-04 зелёные); алерт идёт через независимый транспорт sidecar (свой токен/chat, БЕЗ
  импорта `src/notifications.py`); сценарий `orchestrator_down` (таймаут/refused/5xx) даёт алерт
  «орк не отвечает» (TC-05) и recovery при восстановлении.
- **FAIL:** флапп (>1 алерта на одно пересечение без cooldown) / нет recovery / алерт шлётся через
  код/токен орка / `orchestrator_down` не детектируется или не алертит.

---

## AC-3 — Изоляция: падение орка не роняет sidecar

**Условие:** орк недоступен/упал → sidecar продолжает работать и репортит проблему.
- **PASS:** при недоступном `/metrics` (мок таймаута/refused) тик sidecar не падает, проходит до конца,
  формирует алерт `orchestrator_down` (TC-05, TC-08); демон never-raise на трёх уровнях (per-source/
  per-tick/per-send) — ошибка одного источника не валит тик, ошибка тика не валит демон (TC-06).
- **FAIL:** исключение в коллекторе/отправке роняет тик или демон / недоступность орка приводит к
  падению/остановке sidecar.

---

## AC-4 — Тонкость, kill-switch, конфигурируемые пороги

**Условие:** контейнер лёгкий; есть kill-switch; пороги/интервалы конфигурируемы через env.
- **PASS:** `docker-compose.yml` задаёт ограничение памяти sidecar (`mem_limit`/эквивалент) в разумных
  пределах (НЕ Grafana/Prometheus-стек); kill-switch (env) при выключении → sidecar не стартует/инертен,
  нулевой эффект на орк (TC-07); пороги (диск/память/agent-завис N мин/очередь и т.п.), интервал,
  таймауты, cooldown читаются из env; `.env.example` содержит токен/chat watchdog + все пороги/интервалы
  (канон, без реальных секретов).
- **FAIL:** нет `mem_limit` / тянется Grafana/Prometheus / нет kill-switch или он не отключает sidecar /
  пороги захардкожены / `.env.example` не обновлён или содержит реальный секрет.

---

## AC-5 — Анти-дубль диск-алерта (согласовано с ORCH-063)

**Условие:** на одно событие переполнения диска — не более одного алерта; владелец зафиксирован в ADR.
- **PASS:** в `06-adr/` зафиксировано решение о владельце диск-алерта (sidecar vs внутренний
  `disk_watchdog` ORCH-063); реализация не порождает два алерта на то же событие переполнения; выбор
  обоснован.
- **FAIL:** диск алертится дважды (и sidecar, и `disk_watchdog`) на одно событие / решение о владельце
  не задокументировано.

---

## AC-6 — Безопасность self-hosting (только чтение/алерт)

**Условие:** sidecar ничего не мутирует в наблюдаемой системе.
- **PASS:** код sidecar не содержит вызовов записи/управления — нет start/stop/restart/exec контейнеров,
  нет записи в `docker.sock` (mount read-only), нет записи в БД орка, нет операций с диском хоста
  (кроме чтения заполнения), нет push в `main`. Подтверждается ревью кода + статической проверкой
  (TC-09: docker-клиент используется только для list/inspect).
- **FAIL:** sidecar выполняет любое мутирующее действие над контейнерами/диском/БД/прод-инстансом.

---

## AC-7 — Разовое инфра-действие задокументировано; pytest зелёный; доки+CHANGELOG

**Условие:** инфра-предусловие описано; тесты проходят; документация обновлена.
- **PASS:** `07-infra-requirements.md` описывает разовое действие (добавить сервис в compose, создать
  bot/chat watchdog, первый запуск на хосте); `pytest` (полный `tests/` + тесты sidecar) зелёный;
  `CHANGELOG.md` содержит запись F1b; релевантные доки (CLAUDE.md/README при необходимости) обновлены.
- **FAIL:** нет `07-infra-requirements.md` / падают тесты / нет записи в CHANGELOG / функционал добавлен
  без обновления документации.

---

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

| AC | Покрывает |
|----|-----------|
| AC-1 | BR-1/2/3/4/5 · FR-1/2/4/5/6 · NFR-4 |
| AC-2 | BR-6/7/8/9 · FR-3/7/8 |
| AC-3 | NFR-1/3 · FR-3/11 |
| AC-4 | NFR-2/5 · FR-10 |
| AC-5 | BR-10 · FR-9 |
| AC-6 | NFR-4 · FR-5/8 |
| AC-7 | BR (доки) · NFR-7 · процессные правила агентов |
