--- result: PASS work_item: ORCH-111 stage: testing author_agent: tester status: pass created_at: 2026-06-15 model_used: claude-opus-4-8 type: test-report work_item_id: ORCH-111 --- # Test Report — ORCH-111 BUG: watchdog must alert on long-lived pytest/child processes that block the pipeline. Новый opt-in сигнал sidecar-watchdog `proc_blocking` (дефолт-off, never-raise, read-only). ## Окружение - Python: 3.12.13 - pytest: 8.3.3 - Worktree: `/repos/_wt/orchestrator/feature_ORCH-111-bug-watchdog-must-alert-on-lon/` - Branch: `feature/ORCH-111-bug-watchdog-must-alert-on-lon` - Дата: 2026-06-15 ## Smoke API (read-only) - `GET /health` → `{"status":"ok","service":"orchestrator"}` — OK - `GET /status` → 200, активная задача `ORCH-111` (task 99) на стадии `testing` — OK - `GET /queue` → 200; блок `serial_gate` присутствует (orchestrator: active=ORCH-111), блок `auto_labels` присутствует — OK; counts: done=1873, failed=8, breaker=closed ## Результаты — покрытие тест-плана (04-test-plan.yaml) | TC ID | Описание | Тест(ы) | Результат | |-------|----------|---------|-----------| | TC-01 | РЕГРЕСС (red→green): builder активирует `proc_blocking` для долгоживущего pytest-процесса вне активного джоба (AC-1) | `test_proc_blocking_signal.py::test_tc01_builder_emits_active_proc_blocking_signal` | PASS | | TC-02 | Анти-false-positive: возраст ниже порога / атрибуция активному джобу → сигнал неактивен (AC-4) | `test_tc02_below_threshold_is_inactive`, `test_tc02_boundary_is_strict_greater_than` | PASS | | TC-03 | Конфиг/kill-switch: `WATCHDOG_PROC_*` парсятся с безопасными дефолтами; дефолт-off инертен; порог > merge_retest_timeout (AC-7) | `test_tc03_defaults_are_off_and_safe`, `test_tc03_env_overrides_and_malformed_degrade`, `test_tc03_killswitch_off_makes_collector_inert`, `test_config_killswitch.py::test_proc_blocking_*` | PASS | | TC-04 | never-raise/read-only коллектора: битый/пустой/недоступный источник → `[]`; нет os.kill/signal/subprocess (AC-3/AC-8) | `test_tc04_collector_degrades_to_empty_on_broken_source`, `test_tc04_collector_empty_when_btime_unreadable`, `test_tc04_collector_source_is_read_only`, `test_tc04_builder_skips_records_missing_fields` | PASS | | TC-05 | Анти-спам/recovery через decision.decide+AlertState: ALERT→NONE→REALERT→однократный RECOVERY (AC-6) | `test_tc05_alert_throttle_realert_then_recovery` | PASS | | TC-06 | Без дубля с `agent_hung`: процесс из /metrics agents[] / claude-агент не порождает `proc_blocking` (AC-5) | `test_tc06_claude_agent_cmdline_never_matches_pytest_pattern`, `test_tc06_collector_excludes_non_matching_processes` | PASS | | TC-07 | РЕГРЕСС tick→dispatch: `Watchdog.tick()` диспетчеризует `proc_blocking`-алерт; флаг-off → ничего; never-raise (AC-1/AC-7) | `test_tick_proc_blocking_integration.py::test_tc07_tick_dispatches_proc_blocking_alert`, `test_tc07_killswitch_off_dispatches_nothing`, `test_tc07_in_budget_process_does_not_alert`, `test_tc07_tick_never_raises_when_collector_explodes` | PASS | | TC-08 | Конфиг-канон/тираж: key-set `.env.watchdog.example` ↔ блок `WATCHDOG_*` в `.env.example` синхронизирован (AC-10) | `test_config_killswitch.py` + key-sync (`test_lite_setup_doc.py`) | PASS | | TC-09 | Полный регресс `pytest tests/` зелёный; конвейер/QG не затронуты; schema_version /metrics не бампнут (AC-9) | `pytest tests/` (1933 passed) | PASS | Дополнительно зелёный leaf-набор коллектора `tests/watchdog/test_proc_collector.py` (13 тестов: разбор `/proc/stat` btime, `pid/stat` с comm в скобках/пробелах, NUL-cmdline, паттерны, фильтрация, гонка «pid исчез mid-scan») и compose-инвариант `test_compose_service.py::test_watchdog_shares_host_pid_namespace` + read-only маунты. Все TC сопоставлены с критериями приёмки `03-acceptance-criteria.md` (AC-1…AC-10) и выполнены. ## Сверка инвариантов (AC-3 / AC-9) - `git diff origin/main...HEAD -- src/` — **пуст**: `src/**`/`STAGE_TRANSITIONS`/`QG_CHECKS`/`check_*`/ machine-verdict/схема БД байт-в-байт не тронуты. Изменения только в `watchdog/**`, `tests/**`, docs, `.env*.example` и одна строка `pid: host` сервиса `orchestrator-watchdog` в `docker-compose.yml`. - Read-only/без ремедиации: grep `os.kill|subprocess.(Popen|run|call)|send_signal|terminate|kill` по `watchdog/**` даёт единственное совпадение — докстринг `collectors/proc.py`, явно фиксирующий read-only-инвариант (`/proc/stat`, `/proc//stat`, `/proc//cmdline` только на чтение). ## Вывод pytest Независимый прогон tester (HEAD `521a72e`, worktree ветки задачи): ``` $ cd /repos/_wt/orchestrator/feature_ORCH-111-bug-watchdog-must-alert-on-lon && \ python -m pytest tests/ -q --tb=short ............................................................. [100%] ================ 1933 passed, 1 warning in 447.41s (0:07:27) ================== EXIT=0 ``` Целевые watchdog-модули детально: `37 passed` (proc_blocking_signal + tick_integration + config_killswitch + proc_collector). Единственный warning — предсуществующий `PydanticDeprecatedSince20` в `src/config.py:8` (не связан с ORCH-111, не блокирует). ## Итог PASS — полный регресс зелёный (1933 passed), smoke API (`/health`, `/status`, `/queue` с блоками `serial_gate`/`auto_labels`) — OK, каждый TC из `04-test-plan.yaml` выполнен и сопоставлен с критериями приёмки. Задача готова к переходу на `deploy-staging`.