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

14 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-100 analysis analyst ready-for-review 2026-06-10 claude-opus-4-8

02 — ТЗ (TRZ): ORCH-100 — FND/F1b: sidecar-watchdog (мозг мониторинга, отдельный контейнер)

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

ТЗ описывает конкретные изменения к реализации, выведенные из BRD (01-brd.md) и фактического кода. Архитектурное обоснование/решения (выбор стека Python/Go, формат хранения порогов, владелец диск-алерта, точная топология сети sidecar, бюджет памяти/mem_limit) — зона архитектора (06-adr/). ТЗ фиксирует ТРЕБОВАНИЯ и ограничения, не способ реализации.

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

Добавить отдельный sidecar-контейнер orchestrator-watchdog, код которого лежит в новой папке watchdog/ репозитория орка, а рантайм — изолированный контейнер (свой watchdog/Dockerfile + сервис в docker-compose.yml). Sidecar периодически (тик): (1) тянет GET /metrics орка; (2) меряет хост (диск/inode/память/CPU); (3) читает статусы контейнеров через read-only docker.sock; (4) пингует Plane/Gitea/Anthropic. По набору конфигурируемых порогов через чистую решающую функцию (образец disk_watchdog.decide) принимает решение alert | realert | recovery | none с дедупом/ throttle, и шлёт алерт в собственный Telegram-канал (свой токен/chat, независимо от кода орка). Особый сигнал: /metrics не отвечает → алерт «орк не отвечает». Всё — never-raise, под kill-switch, строго read-only к наблюдаемому (self-hosting-безопасно).

Орк-сторона (src/**) не меняется: F1b — потребитель уже существующего GET /metrics (F1a, ORCH-099). STAGE_TRANSITIONS / QG_CHECKS / check_* / схема БД орка — не тронуты.

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

Путь Действие
watchdog/ создать — корень кода sidecar (новая папка в репо орка)
watchdog/Dockerfile создать — отдельный тонкий образ sidecar (стек — выбор архитектора)
watchdog/<entrypoint> создать — демон/цикл сбора+решения+отправки (имя/структура — архитектор)
watchdog/<collectors> создатьсбор: /metrics орка (HTTP), хост (диск/inode/память/CPU), контейнеры (docker.sock ro), пинг Plane/Gitea/Anthropic
watchdog/<decision> создатьчистая решающая функция порога (value, threshold, prev_state, now, cooldown) → alert|realert|recovery|none (образец src/disk_watchdog.py::decide)
watchdog/<notify> создать — независимый Telegram-транспорт sidecar (свой токен/chat; НЕ импорт src/notifications.py)
watchdog/<config> создать — чтение порогов/интервалов/токенов/kill-switch из env
watchdog/tests/ (или tests/watchdog/) создать — pytest на чистые функции (решение/парсинг/детект орк-down); размещение — архитектор
docker-compose.yml изменить — добавить сервис orchestrator-watchdog (build watchdog/, restart-policy, read-only docker.sock, mem_limit, env, kill-switch)
.env.example изменить — канон: токен/chat watchdog + пороги + интервалы + kill-switch (без секретов)
CHANGELOG.md изменить — запись о F1b
docs/work-items/ORCH-100/07-infra-requirements.md создать (architect) — разовое инфра-действие: добавить сервис в compose, создать bot/chat watchdog, первый запуск на хосте

src/** НЕ редактируется. Если в ходе разработки выяснится нехватка поля в /metrics — это отдельная задача-расширение F1a (ORCH-099), а не правка в рамках F1b (см. BRD §«Вне объёма»).

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

FR-1 — Отдельный контейнер sidecar (BR-1, NFR-1)

Sidecar собирается из watchdog/Dockerfile в отдельный образ и поднимается сервисом orchestrator-watchdog в docker-compose.yml: отдельный процесс/память/рестарт-политика, НЕ внутри процесса орка. restart: unless-stopped (или эквивалент) — sidecar самовосстанавливается.

FR-2 — Сбор сырья орка (BR-2, NFR-6)

На каждом тике GET <orch-metrics-url> (дефолт-достижимость http://127.0.0.1:8500/metrics при host-network; URL конфигурируем). Тело — версионированный конверт F1a: {schema_version, generated_at, clk_tck, stages[], queue, agents[], cost, enabled}. Парсинг толерантен: неизвестные поля игнорируются, отсутствие опционального — не ошибка, рост schema_version логируется (warning), не крэшит. Из конверта извлекаются сигналы для порогов: agent-liveness (cpu_ticks/runtime → «завис»), застрявшая стадия, job-failed, длина очереди.

FR-3 — Детект «орк не отвечает» (BR-7) — главный сигнал

Если GET /metrics завершается таймаутом / connection refused / 5xx / нечитаемым телом — это отдельный сигнал тревоги orchestrator_down. Проходит через ту же машину порога/дедупа/recovery (BR-9): один алерт «орк не отвечает», recovery при восстановлении. Единичный transient не должен немедленно флаппить — порог/таймаут/ретрай подбираются так, чтобы алерт был осмысленным (детали — архитектор/developer; требование: «не флаппить на одиночной сетевой икоте»).

FR-4 — Сбор хоста (BR-3)

Измерять заполнение диска (% и, где доступно, inode), память, CPU по доступным контейнеру хост-путям/интерфейсам (стдлиб-средствами выбранного стека; без тяжёлых агентов). Пути/пороги — конфигурируемы. Диск: см. FR-9 (анти-дубль с ORCH-063).

FR-5 — Сбор контейнеров (BR-4, NFR-4)

Через docker.sock, смонтированный read-only, читать состояния контейнеров платформы: различать Up / healthy / restarting / exited / unhealthy. Минимум — статус orchestrator (и других ключевых сервисов). Только чтение Docker API (list/inspect) — никаких start/stop/restart/exec.

FR-6 — Пинг внешних зависимостей (BR-5)

Периодически проверять доступность Plane / Gitea / Anthropic лёгким запросом (health/ping, короткий таймаут, never-raise). Недоступность → сигнал для порога. Эндпоинты/таймауты — конфигурируемы.

FR-7 — Пороговый алертинг (BR-6, BR-9)

Каждый сигнал проходит через чистую решающую функцию (образец disk_watchdog.decide): вход (value/state, threshold, prev_state, now, cooldown), выход alert | realert | recovery | none. Семантика:

  • не-alerting & за порогом → ALERT (один на пересечение);
  • alerting & за порогом & cooldown истёк → REALERT;
  • alerting & за порогом & в cooldown → NONE (анти-спам);
  • alerting & вернулось в норму → RECOVERY;
  • не-alerting & в норме → NONE. Состояние порога (alerting/last_alert_at) — per-signal, in-memory (best-effort; рестарт sidecar сбрасывает → корректно повторно алертит ещё стоящую проблему, как disk_watchdog). Хранилище состояния/порогов (in-memory vs файл/иное) — решение архитектора.

FR-8 — Независимый Telegram-транспорт (BR-8, NFR-4)

Отправка через собственный код sidecar (свой <notify>), читающий свои bot_token/chat_id из env. Запрещено импортировать/вызывать src/notifications.py или использовать токен/функции орка (иначе падение орка утянет алерт-канал). disable_web_page_preview/parse_mode — по усмотрению; сообщение содержит суть алерта (сигнал, значение, порог, хост/контейнер).

FR-9 — Анти-дубль диск-алерта (BR-10)

Диск уже алертит disk_watchdog (ORCH-063, порог 85%, Telegram орка). F1b не должен слать второй диск-алерт на то же событие. Владельца диск-алерта выбирает архитектор (варианты: sidecar становится единственным владельцем и внутренний disk_watchdog остаётся как fallback на случай down-канала орка; ИЛИ sidecar не дублирует диск, оставляя его за ORCH-063). ТЗ фиксирует инвариант: на одно событие переполнения диска — не более одного алерта, решение и его обоснование — в 06-adr/.

FR-10 — Управляемость (NFR-5)

Kill-switch (env): выключен → sidecar не стартует / инертен, нулевой эффект на орк и конвейер. Пороги (диск, память, agent-завис N мин, длина очереди, и т.п.), интервал тика, таймауты, cooldown — из env (.env.example — канон).

FR-11 — never-raise (NFR-3)

Три уровня: per-source (битый источник деградирует один сигнал, прочие собираются), per-tick (внешний try/except цикла), per-send (обёрнутая отправка). Демон не падает от ошибки сбора/сети/парсинга.

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

Нет изменений API орка. Sidecar — клиент существующего GET /metrics (F1a, ORCH-099). Орк новых эндпоинтов не получает. Sidecar собственного входящего HTTP-API не обязан иметь (опциональный liveness-эндпоинт самого sidecar — на усмотрение архитектора, вне обязательного объёма).

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

Нет. Sidecar не пишет в БД орка (NFR-4) и не имеет своей БД (тонкий стек, C-3). Состояние порогов — in-memory best-effort (FR-7). Журнал уроков (F2, БД орка) — отдельная задача, не F1b.

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

Нет. F1b живёт вне процесса орка и вне конвейера Quality Gate. QG_CHECKS / check_* / STAGE_TRANSITIONSне тронуты (по образцу operational-демонов disk_watchdog/reaper/ reconciler, которые тоже не являются Quality Gate). Sidecar — операционный наблюдатель, не гейт.

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

  • Обратная совместимость: изменения аддитивны — новая папка watchdog/, новый сервис в compose, новые ключи в .env.example. Существующий орк-контейнер и его поведение — без изменений.
  • Kill-switch: выключенный sidecar = нулевой эффект (не стартует), полная обратимость (NFR-5).
  • Область раската: только инфраструктура наблюдения; конвейер всех проектов не затронут (self-hosting-безопасно, NFR-4).
  • Регресс: существующий pytest tests/ остаётся зелёным; новые тесты sidecar добавляются изолированно (FR — чистые функции тестируемы без контейнера/таймера, образец tests/ для disk_watchdog.decide).
  • Разовое инфра-предусловие (не код): добавить сервис в compose + создать bot/chat watchdog + первый запуск на хосте (Слава/Стрим). Зафиксировать в 07-infra-requirements.md. Отсутствие bot/chat watchdog = sidecar не шлёт (fail-safe, логирует), но не падает.