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

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

01 — BRD (бизнес-требования): ORCH-109 — timeout budgets + launch-time model telemetry для developer/reviewer

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

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

Инцидент ORCH-104 (runs 658/659/660, прод-watchdog 1800s) вскрыл два независимых дефекта в подсистеме запуска агентов и телеметрии:

Дефект A — недостаточный wall-clock бюджет для тяжёлых ролей. Агенты developer и reviewer на сложных задачах честно упираются в общий тайм-аут agent_timeout_seconds = 1800 и убиваются watchdog'ом (launcher._watchdog → stop_process, exit 143 / -9). Этот тайм-аут — единый для ВСЕХ ролей, хотя developer (effort xhigh, кодирующая роль) и reviewer объективно требуют больше времени, чем механические роли (tester/deployer, effort medium). Существует механизм per-agent override (_resolve_timeout + agent_timeout_overrides_json), но в проде он пуст → все роли получают 1800s.

Дефект B — потеря модели в телеметрии при оборванном прогоне. Модель агента (agent_runs.model) пишется только постфактум — из финального usage-JSON прогона в launcher._monitor_agent → usage.record_usage (_extract_model). Убитый по тайм-ауту прогон не успевает эмитить финальный JSON_extract_model возвращает Nonerecord_usage пишет model=COALESCE(None, model) = остаётся NULL. В результате карточка Telegram-трекера (notifications._stage_line) и снимок GET /metrics/GET /queue (db.get_running_agents) показывают model=null именно тогда, когда что-то пошло не так — в момент, когда модель/эффорт критичны для разбора инцидента.

Существующий прецедент уже решает половину задачи: эффорт стампится в момент launch (launcher._spawn, ORCH-087, UPDATE agent_runs SET effort=?), потому что CLI его в result-JSON не возвращает. Модель резолвится в той же точке (resolve_agent_model, строка 559), но в БД на launch не пишется — стампится только эффорт. ORCH-109 распространяет ту же гарантию на модель.

Сопутствующие проверки (производные от A и B):

  • Поведение оборванного (timeout-killed) прогона в трекере и status-комментариях: модель и эффорт должны быть видны даже если финальный JSON не записан.
  • Нужен ли отдельный guard: не пускать timeout-killed developer/reviewer автоматически дальше по конвейеру (development → review, review → testing) без явного salvage-режима.

Установленные факты (по коду, не изобретать):

  • agent_runs.model — колонка TEXT (NULLABLE), уже существует (db._ensure_column); миграция не нужна.
  • record_usage уже использует model=COALESCE(?, model) — то есть постфактум-парс уже сохраняет ранее проставленное значение и не затирает его NULL'ом. Не хватает только записи на launch.
  • _resolve_timeout(agent) уже умеет per-agent override через agent_timeout_overrides_json; малформный JSON → откат на глобальный дефолт + лог (never-break).
  • Кросс-инвариант reaper: reaper_max_running_s = 3600 с зафиксированным в config.py правилом «MUST be > max agent_timeout + grace» (Tier-3 backstop job-reaper'а, ORCH-065).

2. Объём (scope)

В объёме

  • Launch-time стамп модели: записывать резолвенную resolve_agent_model(...) в agent_runs.model в момент launch (launcher._spawn), рядом со стампом эффорта (ORCH-087).
  • Конфигурируемый поднятый wall-clock бюджет для developer и reviewer через config-override, без изменения бюджета остальных ролей (analyst/architect/tester/deployer).
  • Сохранение постфактум-enrich: usage.record_usage остаётся источником обогащения модели/токенов/стоимости из usage-JSON, но перестаёт быть единственным источником истины о модели (launch-стамп — первичный, JSON — уточняющий).
  • Видимость при timeout/kill: строка стадии трекера и status-комментарии показывают реальные модель + эффорт для оборванного прогона (model не null).
  • Guard анти-salvage: гарантия (и регресс-тест), что timeout-killed прогон (exit_code != 0, в т.ч. -9/-15/143) не продвигает стадию автоматически в следующую без явного решения.
  • Обновление документации/комментариев по конфигу тайм-аутов (config.py, .env.example).
  • Тесты, покрывающие все перечисленные FR.

Вне объёма

  • Изменение model-routing: все 6 агентов остаются на claude-opus-4-8 (ORCH-41 G3 не включается).
  • Любые изменения STAGE_TRANSITIONS / QG_CHECKS / check_* / machine-verdict ключей / схемы БД (колонка agent_runs.model уже есть — миграции нет).
  • Изменение тайм-аута для ролей кроме developer/reviewer.
  • Salvage / возобновление недоделанной работы убитого прогона (поднять «как было», дописать, переиспользовать частичный результат) — в объёме ТОЛЬКО гарантия не-продвижения, не salvage.
  • Изменения транспорта Telegram/Plane (send_telegram/комментарии) — только использование уже доступных полей.
  • Перезапуск/деплой прод-контейнера в рамках задачи (self-hosting безопасность).

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

  • Заказчик/Owner (Слава) — инициатор; нуждается в надёжной телеметрии для разбора инцидентов и в адекватных бюджетах тяжёлых ролей при пакетном автономном прогоне (эпик ORCH-088).
  • Оператор self-hosting — потребитель карточки трекера и GET /metrics/GET /queue; без модели в карточке теряет ключевой контекст инцидента.
  • Сам конвейер (self-hosting) — затрагивается поведение запуска агентов; общий прод-инстанс обслуживает и enduro-trails (тайм-аут — глобальная per-agent настройка, не repo-scoped).

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

  • BR-1 — Резолвенная модель агента сохраняется в agent_runs.model в момент launch, рядом с эффортом, а не только постфактум из usage-JSON. Значение присутствует на строке прогона с момента запуска и переживает любой исход прогона.
  • BR-2 — Постфактум-парс usage/model (usage.record_usage) сохраняется как обогащение, но не как единственный источник истины: при отсутствии/обрыве финального JSON launch-стамп модели не теряется.
  • BR-3 — Wall-clock тайм-аут для developer и reviewer поднимается и настраивается через config-override, без изменения тайм-аута остальных ролей; механизм покрыт тестом/проверкой.
  • BR-4 — При timeout/kill (оборванный прогон без финального JSON) строка стадии в трекере и status-комментарии показывают реальную модель (не null) и эффорт.
  • BR-5 — Timeout-killed прогон developer/reviewer не продвигается автоматически на следующую стадию без явного salvage-режима; поведение зафиксировано регресс-тестом. (Анализ определяет, нужен ли отдельный guard поверх существующей гарантии «advance только при чистом exit + зелёный QG».)
  • BR-6 — Документация и комментарии по конфигу тайм-аутов обновлены (паспорт изменения внутри config.py + .env.example).

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

  • NFR-1 — Обратная совместимость / нулевая регрессия. Стамп модели аддитивен (колонка уже существует, миграции нет). Дефолтный тайм-аут ролей, кроме developer/reviewer, не меняется; при пустом override-конфиге поведение байт-в-байт прежнее.
  • NFR-2 — never-raise / never-break. Сбой стампа модели (ошибка БД) не блокирует launch (та же try/except-изоляция, что у стампа эффорта). Малформный/невалидный timeout-конфиг → откат на глобальный дефолт + WARNING, прогон не падает.
  • NFR-3 — Неприкосновенность контрактов. STAGE_TRANSITIONS, QG_CHECKS, check_*, machine-verdict ключи (verdict:/result:/deploy_status:/staging_status:/security_status:/ coverage_status:), схема БД — не трогаются.
  • NFR-4 — Сохранение reaper-инварианта. Любой поднятый бюджет developer/reviewer обязан сохранять reaper_max_running_s > max(резолвенный тайм-аут любого агента) + agent_kill_grace_seconds (Tier-3 backstop ORCH-065); иначе job-reaper может реапнуть здоровый долгоиграющий прогон до срабатывания его собственного watchdog'а. Если новый бюджет нарушает неравенство — reaper_max_running_s поднимается синхронно (решение архитектора).
  • NFR-5 — Self-hosting безопасность. Изменение не рестартит/не роняет прод-контейнер, не трогает deploy-путь, безопасно для общего инстанса (enduro-trails не затронут негативно).
  • NFR-6 — Наблюдаемость in-flight. Модель становится видна в GET /metrics/GET /queue (db.get_running_agents) во время прогона, а не только после завершения (побочное улучшение launch-стампа).

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

  • Тайм-аут — глобальная per-agent настройка (не repo-scoped): поднятие бюджета developer/reviewer действует на все репо. Для enduro это благоприятно/нейтрально.
  • Колонка agent_runs.model уже существует и NULLABLE — повторная запись/COALESCE безопасны.
  • CLI не возвращает effort в result-JSON (причина launch-стампа эффорта ORCH-087); модель в JSON возвращается, но только при успешном финале — отсюда необходимость launch-стампа модели.
  • Точные числовые значения новых бюджетов (developer/reviewer) и способ их конфигурации (выделенные ключи vs agent_timeout_overrides_json) — решение архитектора/Owner в рамках FR-3; BRD фиксирует только способность + инвариант NFR-4 + тест.
  • Salvage недоделанной работы — отдельная возможность, вне этой задачи.

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

Модель агента видна (не null) в трекере, status-комментариях и /metrics для ЛЮБОГО исхода прогона, включая timeout-kill; бюджеты developer/reviewer подняты и конфигурируемы без влияния на прочие роли и без нарушения reaper-инварианта; timeout-killed прогон не «протекает» в следующую стадию; всё покрыто тестами; конфиг задокументирован. Детальные PASS/FAIL — 03-acceptance-criteria.md.

8. Риски

  • R-1 — Поднятие бюджета выше reaper_max_running_s grace → ложный reap здорового прогона (NFR-4). Митигируется sanity-тестом конфига и/или синхронным поднятием reaper_max_running_s.
  • R-2 — Постфактум-enrich затирает корректный launch-стамп при странном JSON. Митигируется семантикой COALESCE (NULL не затирает) + тестом enrich-кейсов.
  • R-3 — Гонка двух писателей exit_code (_record_kill = -9 и _monitor_agent = proc.wait()) не должна влиять на телеметрию модели (модель — отдельная колонка). Подтверждается тестом FR-4.
  • R-4 — Глобальность тайм-аута: поднятие для enduro-developer могло бы маскировать зависший прогон. Митигируется тем, что Tier-3 backstop reaper'а сохраняется (NFR-4).

Детали рисков и архитектурные трейд-оффы — 10-tech-risks.md (заполняет архитектор).