11 KiB
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 не затронут.