Files
orchestrator/docs/work-items/ORCH-063/03-acceptance-criteria.md

133 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
work_item: ORCH-063
stage: analysis
author_agent: analyst
status: ready-for-review
created_at: 2026-06-09
model_used: claude-opus-4-8
---
# 03 — Критерии приёмки (Acceptance Criteria): ORCH-063 — INFRA: мониторинг диска mva154 + алерт при >85%
Work Item: **ORCH-063** · Repo: **orchestrator** · Стадия: analysis
Формат: каждый критерий имеет **PASS** (что должно быть истинно для приёмки) и **FAIL** (что
считается провалом). Reviewer/tester проверяет их буквально по файлам репозитория и тестам.
---
## AC-1 — Heartbeat-демон запускается с приложением
**Условие:** фоновый disk-watchdog периодически измеряет заполнение диска и стартует вместе с
приложением без ручного запуска.
- **PASS:** есть daemon-поток (паттерн `reconciler`/`job_reaper`: `threading.Thread(daemon=True)` +
`threading.Event`), стартующий в `main.lifespan` (после `reaper.start()`) и останавливающийся на
shutdown; период замера = `disk_monitor_interval_s`; есть метод `status()`.
- **FAIL:** watchdog не стартует автоматически; блокирующий `time.sleep` без чистого stop; замер
выполняется в обработчике вебхука/в горячем пути конвейера, а не в отдельном демоне.
---
## AC-2 — Алерт при заполнении ≥ порога
**Условие:** при заполнении отслеживаемого пути ≥ `disk_monitor_threshold_pct` (дефолт 85%) оператор
получает Telegram-алерт с действенными деталями.
- **PASS:** при `used_pct ≥ threshold` вызывается `send_telegram` (notifying, не silent) с
сообщением, содержащим путь/точку монтирования, занято %, свободно (ГБ или %) и порог.
- **FAIL:** алерт не отправляется при превышении; отправляется silent (`disable_notification=True`)
и не пингует; сообщение без действенных деталей (нет %/пути/свободно).
---
## AC-3 — Анти-спам: не на каждом тике
**Условие:** при длительном превышении порога алерт не дублируется на каждом тике.
- **PASS:** алерт отправляется при пересечении порога (переход «ниже→на/выше»); пока заполнение
остаётся выше порога, повторный алерт шлётся не чаще `disk_monitor_realert_s`. Решение об отправке
выражено чистой функцией от `(current_pct, threshold, previous_state, now)` и покрыто юнит-тестом.
- **FAIL:** алерт шлётся на каждом тике при стабильном превышении; нет cooldown/состояния; логика
отправки не тестируема (зашита в поток с реальным таймером).
---
## AC-4 — Recovery при возврате ниже порога
**Условие:** при возврате заполнения ниже порога состояние сбрасывается и приходит однократное
сообщение восстановления.
- **PASS:** переход «выше→ниже порога» сбрасывает состояние алерта и отправляет ровно одно
recovery-сообщение «диск ниже порога»; последующее новое превышение снова алертит (цикл повторяем).
- **FAIL:** после спада ниже порога состояние не сбрасывается (новое превышение молчит из-за
«залипшего» cooldown); recovery шлётся повторно на каждом тике ниже порога.
---
## AC-5 — Конфигурируемость и kill-switch
**Условие:** порог, период, период повтора, пути и включение конфигурируемы; выключение даёт нулевую
регрессию.
- **PASS:** в `config.py` есть `disk_monitor_enabled` / `disk_monitor_interval_s` /
`disk_monitor_threshold_pct` / `disk_monitor_realert_s` / `disk_monitor_paths` (с env-маппингом);
при `disk_monitor_enabled=False` демон не стартует, `/queue`-блок отдаёт `{"enabled": false}`,
поведение приложения идентично текущему. Новые env задокументированы в `.env.example`.
- **FAIL:** значения захардкожены; нет kill-switch; при выключении меняется поведение конвейера;
env не задокументированы.
---
## AC-6 — never-raise (изоляция от конвейера)
**Условие:** любой сбой watchdog не роняет и не блокирует конвейер.
- **PASS:** замер по несуществующему/недоступному пути, ошибка `send_telegram`, исключение в тике —
логируются и **не** пробрасываются; демон продолжает работу; конвейер и enduro-trails не
затронуты. Покрыто тестом (замер по битому пути / исключение в отправке → тик не падает).
- **FAIL:** исключение в тике останавливает поток или всплывает в приложение; недоступный путь
роняет замер всех путей.
---
## AC-7 — Наблюдаемость в `GET /queue`
**Условие:** состояние watchdog видно в `GET /queue`.
- **PASS:** ответ `GET /queue` содержит аддитивный блок `disk_monitor` с `enabled`, `threshold_pct`,
`interval_s`, `paths` (последний замер: `used_pct`, свободно), `alerting`, `last_alert_at`;
существующие ключи ответа не изменены; блок never-raise (при ошибке — минимальный словарь).
- **FAIL:** блока нет; изменены/сломаны существующие ключи `/queue`; блок может выбросить исключение.
---
## AC-8 — Корректный источник замера (хост-ФС)
**Условие:** замер отражает заполнение хост-раздела, а не overlay-ФС контейнера.
- **PASS:** дефолтный набор путей — смонтированные хост-пути (`/repos`, `/app/data`); замер по ним
репрезентативен для заполняющегося хост-раздела. Источником истины **не** является `shutil.disk_usage("/")`
(overlay контейнера).
- **FAIL:** мониторится только `/` контейнера → ложно-низкое заполнение при реально полном хосте
(риск повтора инцидента 07.06).
---
## AC-9 — Документация обновлена (golden source)
**Условие:** документация обновлена в том же PR (CLAUDE.md §2; reviewer-ось).
- **PASS:** обновлены `docs/architecture/README.md` (компонент + блок `/queue`),
`docs/operations/INFRA.md` (что мониторится, порог, как отключить, реакция на алерт),
`.env.example` (новые `ORCH_DISK_*`), `CHANGELOG.md` (`feat:`); создан
`docs/work-items/ORCH-063/06-adr/ADR-001-*.md`.
- **FAIL:** функционал добавлен, но обзорные/операционные доки или ADR не обновлены.
---
## Сводная матрица AC ↔ FR/BR
| AC | Покрывает |
|----|-----------|
| AC-1 | BR-1 / BR-8 / FR-1 |
| AC-2 | BR-2 / FR-2 / FR-3 |
| AC-3 | BR-3 / FR-4 |
| AC-4 | BR-4 / FR-4 |
| AC-5 | BR-5 / FR-5 / NFR-4 |
| AC-6 | BR-6 / NFR-1 |
| AC-7 | BR-7 / FR-6 |
| AC-8 | NFR-3 / FR-2 |
| AC-9 | CLAUDE.md §2 (документация = golden source) |