Files
orchestrator/docs/work-items/ORCH-100/01-brd.md

17 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

01 — BRD (бизнес-требования): ORCH-100 — FND/F1b: sidecar-watchdog (мозг мониторинга, отдельный контейнер)

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

1. Бизнес-контекст и проблема

Задача — фундаментный кирпич F1b домена 0 «Фундамент» эпика автономного саморазвития (docs/epics/self-evolution.md, §2, §«Архитектурные рамки наблюдаемости»). F1a (ORCH-099) уже реализовал лёгкий read-only GET /metrics в самом орке — он отдаёт только сырьё (стадии, очередь, agent-liveness, cost), без порогов/алертов/хранения. F1b — вторая половина пары: мозг мониторинга, который это сырьё читает, дополняет внешними сигналами (хост, контейнеры, внешние зависимости) и превращает в алерты.

Боль, которую закрывает F1b. Сегодня платформа слепа к собственному здоровью в реальном времени. Инциденты 0609.06 (диск хоста молча дорос до 100% и встал весь конвейер — ORCH-063; фантом-merge, deploy-петли, флапп-статусы, зомби-jobs) обнаруживались постфактум, человеком. Частичные стражи существуют, но они живут ВНУТРИ процесса орка (disk_watchdog ORCH-063, reaper ORCH-065, reconciler ORCH-053): если орк завис/съел память/упал — стражи лягут вместе с ним, и платформа слепа именно в критический момент.

Архитектурная рамка — установленный факт заказчика (Слава, 09.06), не предмет переизобретения:

  • C-1 / C-1б: наблюдатель ОТДЕЛЁН от наблюдаемого. Sidecar-контейнер на том же хосте; КОД sidecar — в репо орка (папка watchdog/), но рантайм — ОТДЕЛЬНЫЙ контейнер (свой Dockerfile + сервис orchestrator-watchdog в docker-compose.yml). Изоляция — на уровне контейнера, не репо.
  • C-2: без внешнего плеча (одна площадка; принятый риск — падёт весь хост → молчит и наблюдатель).
  • C-3: тонкий стек — НЕ Grafana/Prometheus. Хост впритык: RAM 171Mi free / 7.7Gi, диск 92%.
  • Разделение ответственности: орк отдаёт сырьё (/metrics), sidecar — мозг (пороги/алерты/свой Telegram-канал, независимый от кода орка). Орк лёг → /metrics недоступен = сам сигнал тревоги.

Критический инвариант наблюдаемости: падение/зависание орка должно делать sidecar громче, а не тише. Если орк не отвечает на /metrics — sidecar жив и обязан зарепортить это как тревогу «орк не отвечает».

2. Объём (scope)

В объёме

  • Новая папка watchdog/ в репо орка: тонкий код sidecar + собственный Dockerfile.
  • Сервис orchestrator-watchdog в docker-compose.yml (отдельный контейнер, свой рестарт/память).
  • Сбор сигналов (периодический тик): (a) GET /metrics орка по HTTP; (b) хост — диск %/inode, память, CPU; (c) контейнеры — через docker.sock read-only (статусы Up/healthy/restarting/ exited/unhealthy); (d) пинг внешних зависимостей — Plane / Gitea / Anthropic.
  • Алертинг по порогам: диск≥порог, память, agent-завис >N мин, job-failed, застрявшая стадия, контейнер-down/unhealthy, внешняя зависимость недоступна, орк-down (/metrics не отвечает).
  • Доставка: Telegram через СОБСТВЕННЫЙ канал sidecar (свой токен/chat в .env), НЕ через код/Telegram-функции орка.
  • Гигиена алертов: дедупликация + throttle (один алерт на пересечение порога, не флапп) + recovery-сообщение при возврате метрики в норму.
  • Управляемость: kill-switch, конфигурируемые пороги, конфигурируемые интервалы.
  • .env.example: токен/chat watchdog + пороги/интервалы (канон, без секретов).
  • Документация (07-infra-requirements.md — разовое инфра-действие) + CHANGELOG.md; pytest зелёный.

Вне объёма

  • Любая авто-ремедиация (рестарт контейнеров, очистка диска, requeue jobs). F1b — только наблюдение + алерт (L0 reactive, эпик §9). Авто-фиксы — домен D1 (отдельные задачи).
  • Grafana / Prometheus / TSDB / дашборд-UI / исторические графики (C-3 — тонкий стек).
  • Изменение /metrics орка (контракт F1a/ORCH-099 — данность; sidecar — потребитель). Если обнаружится нехватка поля — это отдельная задача-расширение F1a, не часть F1b.
  • Изменение STAGE_TRANSITIONS / QG_CHECKS / check_* / схемы БД орка — sidecar их не касается (он вне процесса орка).
  • Журнал уроков (F2) — отдельная задача; F1b не пишет в БД орка.
  • Второе внешнее плечо мониторинга (L2) — сознательно отложено (C-2).

3. Заинтересованные стороны

  • Заказчик / приёмка: Слава (зафиксировал архитектурные рамки 09.06).
  • Постановщик / ведение: Стрим.
  • Затрагивает: операторов платформы (получатели алертов), все проекты в общем прод-инстансе (enduro-trails и пр.) — sidecar повышает наблюдаемость их общей инфраструктуры, не вмешиваясь.
  • Исполнители конвейера: architect (стек, формат хранения порогов, владелец диск-алерта), developer, reviewer, tester, deployer.

4. Бизнес-требования (BR)

  • BR-1 (отдельный контейнер). Sidecar собирается в отдельный образ (watchdog/Dockerfile) и работает как сервис orchestrator-watchdog в docker-compose.yml — отдельный процесс/память/ рестарт, НЕ внутри процесса орка.
  • BR-2 (сбор сырья орка). На каждом тике sidecar делает GET /metrics орка по HTTP и разбирает версионированный конверт (schema_version/stages/queue/agents/cost), толерантно к неизвестным/отсутствующим полям (контракт F1a — additive, версия не растёт на добавление поля).
  • BR-3 (сбор хоста). Sidecar измеряет хост: заполнение диска (% и, где доступно, inode), память, CPU — по смонтированным хост-путям/интерфейсам, доступным контейнеру.
  • BR-4 (сбор контейнеров). Sidecar читает состояние контейнеров через docker.sock (read-only mount): различает Up / healthy / restarting / exited / unhealthy. Минимум — статус ключевых контейнеров платформы (включая сам orchestrator).
  • BR-5 (пинг зависимостей). Sidecar периодически проверяет доступность внешних зависимостей — Plane, Gitea, Anthropic (лёгкий health/ping, короткий таймаут) — и алертит при недоступности.
  • BR-6 (пороговый алертинг). При пересечении порога сигналом (диск≥порог, память, agent-завис >N мин, job-failed, застрявшая стадия, контейнер-down/unhealthy, зависимость недоступна) sidecar шлёт ровно один Telegram-алерт.
  • BR-7 (орк-down = тревога). Если GET /metrics орка не отвечает (таймаут/connection refused/5xx) — sidecar шлёт алерт «орк не отвечает». Это главный сценарий ценности: наблюдатель жив, наблюдаемый лёг.
  • BR-8 (свой Telegram-канал). Алерты идут через независимый транспорт sidecar — собственные bot-токен и chat-id из .env, БЕЗ обращения к коду/функциям/токену орка (иначе падение орка утянуло бы и алерт-канал — нарушение C-1).
  • BR-9 (дедуп / throttle / recovery). Повторное нахождение метрики за порогом не флаппит: один алерт на пересечение + анти-спам cooldown между повторами + recovery-сообщение при возврате метрики в норму. Поведение — по образцу disk_watchdog (ORCH-063): чистая решающая функция (value, threshold, prev_state, now, cooldown) → alert | realert | recovery | none.
  • BR-10 (нет дубля диск-алерта). Диск уже алертит disk_watchdog ORCH-063 (порог 85%, через Telegram орка). F1b НЕ должен порождать второй диск-алерт на то же событие. Владельца диск-алерта (sidecar vs внутренний disk_watchdog) выбирает архитектор — BRD лишь фиксирует требование «один диск-алерт на событие, без дублирования».

5. Нефункциональные требования (NFR)

  • NFR-1 (изоляция / резилентность). Падение/зависание/рестарт орка НЕ роняет sidecar (доказывается: орк down → sidecar продолжает тикать и шлёт алерт). Обратное тоже: sidecar — чисто наблюдатель, его падение не влияет на конвейер.
  • NFR-2 (тонкость). Контейнер лёгкий: предсказуемо малое потребление памяти (хост впритык — 171Mi free). Конкретный бюджет памяти и mem_limit — решение архитектора; BRD требует «в разумных пределах, измеримо». НЕ Grafana/Prometheus.
  • NFR-3 (never-raise). Любая ошибка сбора/парсинга/сети/отправки — best-effort: один битый источник деградирует один сигнал, не роняет тик; ошибка тика не роняет демон. По образцу disk_watchdog / metrics (три уровня never-raise: per-source, per-tick, per-send).
  • NFR-4 (безопасность self-hosting). Sidecar только читает и шлёт Telegram — НИКОГДА не трогает диск/контейнеры/прод, не рестартит, не пишет в docker.sock (mount read-only), не пишет в БД орка, не пушит в main. Безопасен для общего инстанса (enduro-trails не затронут).
  • NFR-5 (управляемость / обратимость). Kill-switch (выключить → sidecar инертен/не стартует, нулевой эффект на орк). Пороги и интервалы конфигурируемы через .env (не хардкод).
  • NFR-6 (изоляция контракта). Sidecar толерантен к версии /metrics: неизвестное поле игнорируется, отсутствие опционального — не падение; рост schema_version логируется (предупреждение), не крэшит.
  • NFR-7 (наблюдаемость самого sidecar). Стартап/тик/решения логируются достаточно, чтобы по логам контейнера понять, что sidecar жив и почему (не)сработал алерт.

6. Допущения и ограничения

  • Зависимость: F1b зависит от F1a (ORCH-099) — читает GET /metrics. Контракт /metrics (envelope schema_version/generated_at/clk_tck/stages/queue/agents/cost/enabled) — установленный факт, sidecar его потребитель.
  • Сеть: орк работает network_mode: host (порт 8500) → из host-network sidecar /metrics достижим как http://127.0.0.1:8500/metrics. Точный сетевой режим sidecar — решение архитектора.
  • docker.sock доступен на хосте /var/run/docker.sock; монтируется в sidecar read-only.
  • Разовое инфра-действие (добавить сервис в compose + первый запуск + создать bot/chat watchdog) выполняется человеком (Слава/Стрим) на хосте — фиксируется в 07-infra-requirements.md. Дальше код watchdog катится через конвейер (self-hosting).
  • Стек (Python/Go), формат хранения порогов, владелец диск-алертазона архитектора в рамках C-1…C-3; BRD их не предрешает.
  • Известный принятый риск (C-2): падёт весь хост/Docker → молчит и sidecar (нет внешнего плеча).
  • Telegram 48ч и прочие лимиты транспорта — как у орка (best-effort доставка).

7. Критерии успеха

Sidecar стартует отдельным контейнером, на каждом тике собирает сырьё орка + хост + контейнеры + зависимости, при пересечении порога шлёт ровно один Telegram-алерт со своего канала (throttle + recovery), при недоступности орка шлёт «орк не отвечает», и переживает падение орка не падая сам. Тонкий, с kill-switch и конфигурируемыми порогами. Разовое инфра-действие задокументировано, pytest зелёный, доки + CHANGELOG обновлены. Детальные PASS/FAIL — 03-acceptance-criteria.md.

8. Риски

  • Дубль диск-алерта с disk_watchdog ORCH-063 (BR-10) — нужно явное решение владельца (архитектор).
  • Шум алертов (флапп на границе порога) при недостаточном throttle/recovery — закрывается BR-9.
  • Зависимость от /metrics: ложный «орк-down» при сетевой икоте — нужен разумный таймаут/ретрай в пороге, чтобы единичный transient не флаппил (детали — архитектор/developer).
  • Ресурсы хоста впритык — sidecar обязан быть лёгким (NFR-2), иначе сам станет частью проблемы.
  • docker.sock доступ — строго read-only; риск привилегий минимизируется mount-режимом (NFR-4).
  • Детальный реестр и митигации — 10-tech-risks.md (заполняет архитектор).