14 KiB
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 |
02 — ТЗ (TRZ): ORCH-109 — timeout budgets + launch-time model telemetry для developer/reviewer
Work Item: ORCH-109 · Repo: orchestrator · Стадия: analysis
ТЗ описывает конкретные изменения к реализации, выведенные из BRD и фактического кода. Архитектурное обоснование/решения (выбор «выделенные config-ключи vs
agent_timeout_overrides_json», точные числовые бюджеты, синхронная правкаreaper_max_running_s) — задача архитектора (06-adr).
1. Сводка изменения
Две независимые, но связанные правки в подсистеме запуска агентов:
-
Launch-time стамп модели. В
launcher._spawnрезолвеннаяresolve_agent_model(...)(уже вычисляется на launch, строка ~559) записывается вagent_runs.modelв той же DB-сессии, что и стамп эффорта (ORCH-087, строки ~566–571). Постфактум-парс (usage.record_usage,model=COALESCE(?, model)) сохраняется как обогащение и уже не затирает launch-значениеNULL'ом. Следствие: модель присутствует на строке прогона с момента запуска, переживает timeout-kill и видна in-flight вGET /metrics/GET /queue. -
Конфигурируемый поднятый wall-clock бюджет для
developer/reviewer._resolve_timeout(agent)должен возвращать поднятый бюджет дляdeveloperиreviewer, конфигурируемый и не затрагивающий прочие роли; механизм покрыт тестом. Сохраняется never-break (малформный конфиг → глобальный дефолт) и кросс-инвариант reaper (reaper_max_running_s > max(timeout)+grace).
Плюс верификационные требования: телеметрия timeout-killed прогона (модель+эффорт не null) и
guard анти-salvage (timeout-killed прогон не продвигает стадию).
2. Задействованные модули / пути
| Путь | Действие |
|---|---|
src/agents/launcher.py |
изменить — стамп model в _spawn рядом с effort (≈ стр. 559–573); проверка _resolve_timeout обслуживает override developer/reviewer (≈ стр. 661–679) |
src/config.py |
изменить — config для поднятого тайм-аута developer/reviewer (выделенные ключи и/или дефолт agent_timeout_overrides_json); обновить комментарии-паспорт (≈ стр. 115–126); проверить/при необходимости поднять reaper_max_running_s (≈ стр. 494–499) |
src/usage.py |
проверить/зафиксировать тестом — record_usage (model=COALESCE(?, model)) НЕ затирает launch-стамп при model=None (≈ стр. 207–230); _extract_model (≈ стр. 95–118) |
src/notifications.py |
проверить (правка, вероятно, не нужна) — _stage_line рендерит · {model} · {effort} из agent_runs для строки с exit_code=-9 (≈ стр. 360–373, 498–542) |
src/db.py |
НЕ менять схему — agent_runs.model TEXT уже есть; проверить, что get_running_agents (≈ стр. 1370–1405) отдаёт launch-стампнутую модель для running-job |
src/stage_engine.py |
проверить — путь продвижения стадии не advance'ит прогон с exit_code != 0 (guard FR-5); правка только если найден разрыв |
.env.example |
обновить — задокументировать ключи тайм-аута developer/reviewer (BR-6) |
tests/test_orch109_timeout_model.py (новый) |
создать — покрытие FR-1…FR-5 |
CHANGELOG.md, CLAUDE.md (паспорт), docs/architecture/README.md (модель/эффорт-секция) |
обновить в том же PR (правило агентов №2) |
3. Функциональные требования
FR-1 — Launch-time стамп модели (BR-1)
В launcher._spawn, после model = resolve_agent_model(agent, project_id), резолвенное значение
записывается в agent_runs.model для текущего run_id в момент launch, по образцу стампа
эффорта (ORCH-087):
- Запись в той же открытой
conn, что и стамп эффорта (допустимо объединить в одинUPDATE agent_runs SET model=?, effort=? WHERE id=?— решение реализации). - Пустой резолв (
model == "", CLI-дефолт без--model) → пишетсяNULL(как эффорт:effort or None), чтобы суффикс модели в трекере корректно опускался. - Инвариант: значение
agent_runs.modelприсутствует с момента launch и не зависит от исхода прогона. - never-raise (NFR-2): сбой записи изолирован
try/except+ WARNING; launch продолжается.
FR-2 — Постфактум-enrich сохраняет launch-стамп (BR-2)
usage.record_usage остаётся источником обогащения (токены/стоимость/модель из usage-JSON), но:
- При
usage is Noneилиusage.get("model") is None(оборванный/малформный JSON) launch-стамп модели не затирается (текущая семантикаmodel=COALESCE(?, model)это уже обеспечивает — требование зафиксировать тестом, не регрессировать). - При наличии непустой модели в JSON enrich уточняет значение (например, полный provider-prefixed id или фактический fallback-model) — допустимая перезапись непустым на непустое.
- Семантика парсинга
_extract_model(приоритетmodelUsage→ top-levelmodel) — без изменений.
FR-3 — Конфигурируемый поднятый тайм-аут developer/reviewer (BR-3)
_resolve_timeout(agent)возвращает поднятый бюджет дляagent in {"developer","reviewer"}, конфигурируемый, детерминированный, и не затрагивающий прочие роли (они продолжают получать глобальныйagent_timeout_seconds, если для них нет override).- Механизм: либо документированный дефолт
agent_timeout_overrides_json, либо выделенные ключи (напримерagent_timeout_developer_s/agent_timeout_reviewer_s) — выбор архитектора; контракт FR-3 — резолв per-agent поднятого бюджета. - never-break (NFR-2): малформный/невалидный конфиг → откат на глобальный дефолт + WARNING
(поведение
_resolve_timeoutсохраняется). - Кросс-инвариант (NFR-4): итоговый
max(резолвенный тайм-аут)+agent_kill_grace_secondsобязан оставаться< reaper_max_running_s; при нарушении — синхронно поднятьreaper_max_running_s.
FR-4 — Телеметрия timeout-killed прогона (BR-4)
Для прогона с exit_code != 0 без финального usage-JSON (timeout-kill, _record_kill стампит -9):
- Строка стадии трекера (
notifications._stage_line) рендерит· {short_model} · {effort}с реальными значениями (модель неnull), т.к. оба стампнуты на launch (FR-1 + ORCH-087). db.get_running_agents(источникGET /metrics/GET /queue) отдаёт launch-стампнутую модель и для running-job (in-flight видимость, NFR-6).- Изменения
notifications.py, вероятно, не требуются (рендер уже читаетmodel); требование — верифицировать тестом, что при стампе на launch значение долетает.
FR-5 — Guard анти-salvage timeout-killed прогона (BR-5)
- Timeout-killed прогон (
exit_code != 0, в т.ч. -9/-15/143)developer/reviewerне продвигает стадию (development → review,review → testing) автоматически. - Существующий контракт (advance только при чистом exit-коде + зелёный exit-гейт; иначе
attempts<max → queued, иначеfailed+ Telegram —launcher._monitor_agent/queue_worker/job_reaper) реализует это структурно. - Требование: анализ подтверждает достаточность существующей гарантии; поведение фиксируется регресс-тестом. Отдельный guard в коде добавляется только если тест выявит разрыв.
- salvage-режим НЕ вводится (вне объёма) — задача гарантирует не-продвижение, не возобновление.
FR-6 — Документация конфига (BR-6)
- Комментарий-паспорт в
config.py(блок ORCH-7, строки ~115–126) расширяется описанием поднятых бюджетовdeveloper/reviewerи ссылкой на reaper-инвариант (NFR-4). .env.exampleнесёт соответствующие ключи с дефолтами = боевым значениям (канон ORCH-101).- Сквозная документация (
CLAUDE.md,docs/architecture/README.md— таблица «модель/эффорт по ролям») обновляется в том же PR.
4. Изменения API
Нет. Ни одного нового/изменённого endpoint'а. GET /metrics и GET /queue отдают тот же контракт
(schema_version: 1) — поле agents[].model лишь начинает заполняться для running-job
(аддитивное улучшение данных, не контракта; sidecar обязан толерировать, ORCH-099 NFR-6).
5. Изменения схемы БД
Нет. Колонка agent_runs.model (TEXT, NULLABLE) уже существует (db._ensure_column, инициализация
init_db). Никаких CREATE/ALTER/новых таблиц. Меняется только момент и частота записи в
существующую колонку (launch + опциональный постфактум-enrich).
6. Требования к новым/изменённым QG checks
Нет. QG_CHECKS / check_* / _parse_* / machine-verdict ключи — не трогаются. Задача целиком вне
слоя Quality Gate (подсистема launch/телеметрия/конфиг). FR-5 опирается на существующий
exit-code-контракт продвижения, не на новый гейт.
7. Совместимость / регресс
- Обратная совместимость: стамп модели аддитивен; при пустом timeout-override поведение байт-в-байт прежнее (NFR-1). Никаких kill-switch не требуется — изменение не вводит новых ветвей риска (стамп модели всегда безопасен; тайм-аут конфигурируем и fail-safe на глобальный дефолт).
- Область раската: стамп модели — все репо/роли (безопасно). Поднятый тайм-аут — только
developer/reviewer(все репо, т.к. тайм-аут глобален per-agent); прочие роли неизменны. - Обратимость: вернуть тайм-аут — снять override-конфиг (откат на 1800s). Launch-стамп модели отката не требует (чистое улучшение телеметрии).
- Кросс-каттинг (NFR-4): при поднятии бюджета выше
reaper_max_running_s − graceсинхронно поднятьreaper_max_running_s(ORCH-065). Sanity-тест конфига стережёт инвариант. - never-raise (NFR-2): обе правки изолированы; сбой не роняет launch и не падает на старте при плохом env.
- Self-hosting (NFR-5): ни рестарта прода, ни изменения deploy-пути; общий инстанс безопасен.
- Полный регресс
tests/остаётся зелёным; coverage-гейт (ORCH-027) удовлетворён новым тест-файлом (изменения вsrc/минимальны и покрыты).