Files
orchestrator/docs/work-items/ORCH-111/02-trz.md

11 KiB
Raw Blame History

work_item, stage, author_agent, status, created_at, model_used
work_item stage author_agent status created_at model_used
ORCH-111 analysis analyst ready-for-review 2026-06-15 claude-opus-4-8

02 — ТЗ (TRZ): ORCH-111 — alert на долго живущие pytest/дочерние процессы в watchdog

Work Item: ORCH-111 · Repo: orchestrator · Стадия: analysis

ТЗ описывает конкретные изменения к реализации, выведенные из BRD и фактического кода. Архитектурное обоснование/решения — задача архитектора (06-adr). В частности, выбор механизма видимости процессов хоста (см. §2 «развилка») — за ADR; ниже зафиксированы требования и ограничения, а не способ реализации.

1. Сводка изменения

Добавить в sidecar-watchdog (watchdog/) новый сигнал класса «долго живущий блокирующий тест/дочерний процесс» (рабочее имя ключа — proc_blocking; финальное имя утверждает разработчик/ADR). Сигнал активен, когда на хосте есть процесс тест-класса (pytest и его субпроцессы), чей возраст превысил настраиваемый порог и который не атрибутируется активному отслеживаемому джобу. Сигнал проходит через существующую машину decide()/AlertState (анти-спам/recovery) и публикуется в собственный Telegram-канал sidecar. Watchdog при этом не трогает процесс (BR-3/NFR-2). Это изменение внутри наблюдателя: машина стадий орка и Quality Gates не затрагиваются.

2. Задействованные модули / пути

Развилка механизма (решает архитектор, ADR). Часть путей условна и зависит от выбранного механизма видимости процессов. Ниже помечено явно.

Путь Действие
watchdog/signals.py изменить — чистый builder нового сигнала proc_blocking (по образцу host_signals/container_signals)
watchdog/config.py изменить — новые ключи WATCHDOG_PROC_* (enable/порог возраста/паттерны/cooldown); never-raise парсеры
watchdog/core.py изменить — врезка коллектора процессов + диспетч нового сигнала в tick() (per-source guard)
watchdog/collectors/proc.py создать — коллектор списка процессов-кандидатов (механизм — по ADR); never-raise → []
.env.watchdog.example изменить — задокументировать новые WATCHDOG_PROC_* ключи (канон)
.env.example (блок WATCHDOG_*) изменить — key-set-sync с .env.watchdog.example (тест test_lite_setup_doc/key-sync)
tests/watchdog/test_proc_blocking_signal.py создать — unit + регресс на новый сигнал
tests/watchdog/test_tick_proc_blocking_integration.py создать — интеграция tick→dispatch
docs/architecture/README.md, docs/deployment/LITE_SETUP.md изменить — описать сигнал/ключи (NFR-5)
docker-compose.yml (сервис orchestrator-watchdog) условно изменить — привилегия/mount (pid: host или /proc:ro) только если ADR выберет watchdog-side host-скан
src/metrics.py (_build_*, аддитивный раздел) условно изменить — только если ADR выберет orch-side обогащение /metrics; аддитивно, без бампа schema_version

3. Функциональные требования

FR-1 — Новый сигнал proc_blocking (чистый builder)

В watchdog/signals.py добавить чистую функцию-builder (без I/O), которая по списку записей о процессах-кандидатах и конфигу возвращает Signal-объекты:

  • Ключ — per-entity, со стабильной идентичностью процесса (напр. ("proc_blocking", pid) или хеш cmdline), чтобы AlertState/cooldown работали по каждому процессу отдельно (как ("container_down", name)).
  • active=True ⇔ возраст процесса > cfg.proc_age_s И командная строка матчит класс «тест/дочерний» (паттерн pytest и родственные, конфигурируемо) И процесс не принадлежит активному отслеживаемому джобу (анти-false-positive, BR-4).
  • title/detail — действенные: фрагмент cmdline, PID, возраст (сек), доля/время CPU при наличии (BR-2). Текст на русском, в стиле существующих сигналов.
  • Привязка: BR-1, BR-2, BR-4.

FR-2 — Коллектор процессов-кандидатов

Создать watchdog/collectors/proc.py — собирает «сырьё» (список записей {pid, cmdline, age_s, cpu?}) тем механизмом, который утвердит ADR. Контракт коллектора фиксирован независимо от механизма: stdlib-only (NFR-6), read-only (NFR-2), never-raise → при любой ошибке/ недоступности источника возвращает [] (один сигнал пропущен, тик жив). Привязка: NFR-1, NFR-2, NFR-6.

FR-3 — Конфиг + kill-switch

В watchdog/config.py добавить ключи (имена финализирует разработчик/ADR; предложение): WATCHDOG_PROC_ENABLED (kill-switch), WATCHDOG_PROC_AGE_MIN (порог возраста в минутах; дефолт должен превышать макс. легитимный бюджет тест-прогона — см. §7), WATCHDOG_PROC_PATTERNS (CSV паттернов cmdline, дефолт включает pytest), при необходимости отдельный WATCHDOG_PROC_COOLDOWN_S. Все парсеры never-raise с безопасными дефолтами (как существующие _int/_bool/_csv). Выключенный флаг → коллектор/сигнал инертны (нулевой эффект). Привязка: BR-6, NFR-1.

FR-4 — Инвариант «только наблюдение»

На всём новом пути запрещены os.kill, отправка сигналов, subprocess.Popen/run, любые мутации процессов/ФС/БД. Watchdog только читает и уведомляет. Привязка: BR-3, NFR-2.

FR-5 — Диспетч через существующую машину решения

Новый сигнал диспетчеризуется в core.tick() через тот же путь decision.decide(...) + self._states[key] + self._send(...): ALERT на пересечении порога, REALERT по cooldown, RECOVERY при исчезновении процесса. Никакой отдельной логики анти-спама не вводить. Привязка: BR-5.

FR-6 — Без дубля с agent_hung

Новый сигнал покрывает только процессы, не представленные в /metrics agents[] (нетреканые/ осиротевшие). Атрибуция «процесс принадлежит активному джобу» исключает такие процессы из proc_blocking (предотвращает двойной алерт и ложные срабатывания на живом агенте). Привязка: NFR-4, BR-4.

4. Изменения API

  • Орк HTTP API: новых эндпоинтов не требуется. Условно (если ADR выберет orch-side путь): аддитивный раздел в ответе GET /metrics (src/metrics.py) о бесхозных тест-субпроцессах — строго read-only, без бампа schema_version (ORCH-099 NFR-6), sidecar толерирует отсутствие.
  • Watchdog: внутренний сигнал, внешнего API не имеет.

5. Изменения схемы БД

Нет.

6. Требования к новым/изменённым QG checks

Нет. Watchdog — наблюдатель вне конвейера и вне Quality Gates: QG_CHECKS / check_* / machine-verdict ключи / STAGE_TRANSITIONSбайт-в-байт не трогаются (как disk_watchdog/ reaper/reconciler).

7. Совместимость / регресс

  • Kill-switch + дефолты: при выключенном WATCHDOG_PROC_ENABLED (или при дефолте, выбранном безопасно) — нулевая регрессия: ни одного нового алерта, тик 1:1 как до ORCH-111. Дефолтный порог возраста обязан превышать максимальный легитимный бюджет тест-прогона (merge_retest_timeout_s ≈ 600s, coverage_run_timeout_s) — иначе нормальный прогон даст ложный alert (анти-false-positive, BR-4).
  • never-raise / read-only: новый код не может уронить тик и не выполняет управляющих действий (NFR-1/NFR-2).
  • Контракт /metrics: при orch-side варианте — только аддитивно, без бампа версии; при watchdog-side варианте — /metrics не трогается вовсе.
  • Self-hosting (NFR-3): выкат — пересборка/рестарт только orchestrator-watchdog; прод orchestrator не перезапускается. Если механизм требует привилегии/mount в compose — это правка только сервиса watchdog.
  • Канон тиража (NFR-5): новые ключи синхронизировать в .env.watchdog.example ↔ блок WATCHDOG_* в .env.example и описать в LITE_SETUP.md в том же PR (key-set-sync тест должен остаться зелёным).
  • Область: сигнал config-gated; enduro-trails не затронут.