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
