133 lines
8.6 KiB
Markdown
133 lines
8.6 KiB
Markdown
---
|
||
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) |
|