14 KiB
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, логирует), но не падает.