--- verdict: APPROVED work_item: ORCH-063 stage: review author_agent: reviewer status: approved created_at: 2026-06-09 model_used: claude-opus-4-8 type: review work_item_id: ORCH-063 version: 1 --- # Review ORCH-063 — INFRA: disk-watchdog мониторинг диска mva154 + алерт при ≥85% > Машинный вердикт читается ТОЛЬКО из `verdict:` во frontmatter. `APPROVED` → дальше по конвейеру. ## Summary PR реализует disk-watchdog — фоновый daemon-поток `src/disk_watchdog.py` по канону `reconciler`/`job_reaper`, точно по ТЗ `02-trz.md` и ADR-001/`adr-0024`. Все 9 критериев приёмки (`03-acceptance-criteria.md` AC-1..AC-9) выполнены и покрыты содержательными тестами (`tests/test_disk_watchdog.py`, TC-01..TC-12, 18 кейсов). Полный регресс зелёный: **1296 passed**. Инварианты соблюдены: `STAGE_TRANSITIONS`/`QG_CHECKS`/`check_*`/схема БД — **не тронуты** (проверено `git diff` — `src/stages.py`/`src/stage_engine.py`/`src/qg/` без изменений), миграций нет. Документация обновлена как golden source в том же work-item. **Блокеров (P0/P1) нет → APPROVED.** ## Оси проверки ### 1. Соответствие ТЗ / Acceptance Criteria - **AC-1 (heartbeat-демон):** `DiskWatchdog(threading.Thread(daemon=True) + threading.Event)`, `_stop.wait(interval)` (чистый stop, без блокирующего `time.sleep`), контракт `start()`/`stop(timeout)`/`status()`. Старт в `main.lifespan` **после** `reaper.start()`, стоп **первым** в `finally` (reverse) — `src/main.py`. ✓ - **AC-2 (алерт ≥ порога):** `format_alert_message` несёт host/путь/`used_pct`/свободно (ГБ+%)/порог; отправка `send_telegram(..., disable_notification=False)` — notifying. Подтверждено TC-08. ✓ - **AC-3 (анти-спам):** чистая `decide_action(used_pct, threshold, prev, now, realert_s)`, cooldown `disk_monitor_realert_s`, время инъецируется `now_provider`. TC-02/TC-03 + e2e. ✓ - **AC-4 (recovery):** переход «выше→ниже» → ровно одно recovery-сообщение + сброс `alerting`; ниже порога молчит. TC-04 + e2e (`test_tick_antispam_then_realert_then_recovery`). ✓ - **AC-5 (config + kill-switch):** 5 флагов `disk_monitor_*` (env `ORCH_DISK_MONITOR_*`, `env_prefix=ORCH_`) + defensive-валидаторы (порог 1..100, интервалы > 0 → дефолт + warning). `enabled=False` → `start()` no-op (TC-09), `.env.example` обновлён. ✓ - **AC-6 (never-raise):** три уровня — per-path (`_measure_one`), per-tick (`_run` outer try/except), per-send (`_send`). TC-07 (битый путь / падение `send_telegram`). ✓ - **AC-7 (наблюдаемость):** аддитивный блок `disk_monitor` в `GET /queue`; `status()` never-raise (минимум `{"enabled": …}` при ошибке). TC-11 проверяет сохранность всех существующих ключей. ✓ - **AC-8 (источник = хост-ФС):** дефолт `/repos,/app/data` через `shutil.disk_usage`, не overlay `/`, не субпроцесс `df`; дедуп по `st_dev`. TC-06. ✓ - **AC-9 (документация):** см. секцию «Документация». ✓ ### 2. Соответствие ADR / инвариантам - Реализация 1:1 с ADR-001 D1–D8: stdlib-замер (D1), дедуп `st_dev` fail-open (D2), pure `decide_action` + in-memory state (D3), прямой `send_telegram` без helper (D4), один порог 85% (D5), lifecycle/врезки (D6), config (D7), инварианты (D8). Сквозной `adr-0024` зарегистрирован в ряду `reconciler`/`job_reaper`. - **Трассировка:** врезки в `main.lifespan` и `@app.get("/queue")` — строго **аддитивные** (новый импорт + один вызов `disk_watchdog.start()/stop()` + ключ `"disk_monitor"`); зафиксированные инварианты соседних маркеров не сломаны. `STAGE_TRANSITIONS`/`QG_CHECKS` не затронуты — подтверждено. ### 3. Качество кода - Docstrings на всех публичных функциях/классе; чистая leaf-логика отделена от потока (тестируемо). - Defensive-граничные случаи покрыты: `total == 0` → `0.0`, пустой CSV → дефолт, `os.stat` fail → fail-open. - Тесты содержательные (не тривиальные): юнит-решения, измерение/дедуп, e2e цикл alert→silent→realert→ recovery, интеграция `/queue`. Полный suite зелёный (1296). ### 4. Документация (golden source) - `docs/architecture/README.md` — компонент «Disk-watchdog» + описание блока `/queue`. ✓ - `docs/operations/INFRA.md` — что мониторится / порог / как отключить / реакция на алерт. ✓ - `.env.example` — 5 дескрипторов `ORCH_DISK_MONITOR_*`. ✓ - `CHANGELOG.md` — запись `feat:`. ✓ - `docs/work-items/ORCH-063/06-adr/ADR-001-disk-watchdog.md` + сквозной `adr-0024`. ✓ - `src/` изменён → документация обновлена в том же work-item. Ось пройдена. ## Findings ### P0 — Blocker - (нет) ### P1 — Must fix - (нет) ### P2 — Should fix - (нет) ### P3 — Nice-to-have - [ ] Косметика: хвостовые артефакты тул-обёртки `` / ``, протёкшие в текст golden-source доков, авторизованных на стадии architecture (НЕ в developer-коммите): `06-adr/ADR-001-disk-watchdog.md` (строки 195–196), `docs/architecture/adr/adr-0024-disk-watchdog.md` (стр. 59), `07-infra-requirements.md` (стр. 63), `10-tech-risks.md` (стр. 39). На парсинг frontmatter/QG не влияют (находятся в конце файла), функциональность не затрагивают — поэтому P3. Рекомендуется зачистить при следующем касании этих доков (правка чужой стадии — по согласованию, CLAUDE.md §3). Не блокирует вердикт. ## Документация Обновлена полностью в том же work-item: `architecture/README.md` (компонент + блок `/queue`), `operations/INFRA.md` (мониторинг/порог/отключение/реакция), `.env.example` (новые `ORCH_DISK_*`), `CHANGELOG.md` (`feat:`), задачный ADR-001 + сквозной `adr-0024`. Обзорная витрина (README «Известные ограничения») этим PR не затрагивается. Ось документации пройдена — оснований для `REQUEST_CHANGES` нет.