work_item: ORCH-100 stage: analysis author_agent: analyst status: ready-for-review created_at: 2026-06-10 model_used: claude-opus-4-8 title: "FND/F1b sidecar-watchdog — пороговый алертинг, орк-down, изоляция, self-hosting safety" framework: pytest scope: > Покрывает чистую логику sidecar (решающая функция порога, парсинг конверта /metrics, детект orchestrator-down, never-raise) и структурно-инфраструктурные инварианты (отдельный сервис в compose, read-only docker.sock, независимый Telegram-транспорт, kill-switch, анти-дубль диск-алерта). ВНЕ покрытия: реальный Telegram-API, живой docker.sock, живой хост-хост-стек (мокаются); сетевые коллекторы тестируются на моках, не на боевых Plane/Gitea/ Anthropic. Стек sidecar (Python/Go) и точное размещение тестов выбирает архитектор — при Python тесты идут в общий pytest; если архитектор выберет Go, набор тест-кейсов переносится на go test 1:1 по смыслу (решение/парсинг/детект/never-raise остаются обязательными). notes: > Образец чистой решающей функции и её тестов — src/disk_watchdog.py::decide и его тесты в tests/. Все коллекторы/транспорт мокаются (никаких боевых сетевых/docker-вызовов в CI). Полный регресс tests/ орка должен оставаться зелёным (src/** не меняется). Тесты sidecar изолированы и не требуют поднятого контейнера/таймера. Пути модулей watchdog/* — ориентировочные; финальные имена задаёт архитектор/developer, id и смысл тест-кейсов сохраняются. tests: - id: TC-01 type: unit description: "Решающая функция: not-alerting & value>=threshold -> ALERT (один на пересечение порога)" module: watchdog/tests/test_decision.py expected: PASS - id: TC-02 type: unit description: "Решающая функция: alerting & still>=threshold & cooldown НЕ истёк -> NONE (анти-спам throttle)" module: watchdog/tests/test_decision.py expected: PASS - id: TC-03 type: unit description: "Решающая функция: alerting & still>=threshold & cooldown истёк -> REALERT (повторный алерт)" module: watchdog/tests/test_decision.py expected: PASS - id: TC-04 type: unit description: "Решающая функция: alerting & value вернулось ниже порога -> RECOVERY (recovery-сообщение)" module: watchdog/tests/test_decision.py expected: PASS - id: TC-05 type: unit description: "Детект orchestrator-down: /metrics таймаут/connection-refused/5xx -> сигнал orchestrator_down -> ALERT «орк не отвечает»" module: watchdog/tests/test_orch_down.py expected: PASS - id: TC-06 type: unit description: "never-raise: исключение в одном коллекторе (хост/контейнеры/деп) деградирует один сигнал, тик доходит до конца и собирает остальные" module: watchdog/tests/test_never_raise.py expected: PASS - id: TC-07 type: unit description: "Kill-switch: при выключенном флаге sidecar инертен/не стартует тик; пороги/интервалы/таймауты читаются из env (не хардкод)" module: watchdog/tests/test_config_killswitch.py expected: PASS - id: TC-08 type: integration description: "Полный тик при недоступном орке (мок /metrics down): тик не падает, собирает хост/контейнеры/деп, формирует ровно один алерт orchestrator_down, recovery при восстановлении" module: watchdog/tests/test_tick_orch_down_integration.py expected: PASS - id: TC-09 type: unit description: "Self-hosting safety: docker-клиент используется только для чтения (list/inspect); нет вызовов start/stop/restart/exec/записи (статическая/мок-проверка)" module: watchdog/tests/test_docker_readonly.py expected: PASS - id: TC-10 type: unit description: "Независимый транспорт: алерт-отправка использует СВОИ токен/chat sidecar из env и НЕ импортирует src/notifications.py / код орка" module: watchdog/tests/test_notify_isolation.py expected: PASS - id: TC-11 type: unit description: "Толерантность к контракту /metrics: неизвестное поле игнорируется, отсутствие опционального не падает, рост schema_version логируется (warning) без крэша" module: watchdog/tests/test_metrics_parse.py expected: PASS - id: TC-12 type: integration description: "Compose-инвариант: orchestrator-watchdog объявлен отдельным сервисом (свой build watchdog/, restart, mem_limit) с docker.sock в режиме :ro" module: watchdog/tests/test_compose_service.py expected: PASS - id: TC-13 type: unit description: "Анти-дубль диск-алерта: согласно решению ADR владельца — sidecar не порождает второй диск-алерт на то же событие переполнения (по образцу взаимодействия с ORCH-063)" module: watchdog/tests/test_disk_alert_dedup.py expected: PASS - id: TC-14 type: unit description: "Регресс орка: полный pytest tests/ зелёный — src/** не изменён, /metrics-контракт (ORCH-099) не сломан" module: tests/ expected: PASS