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

10 KiB
Raw Permalink Blame History

BRD — ORCH-087

Тип: Багфикс (UX live-трекера) + малая фича (эффорт в карточке) + корректность метрики времени Приоритет: MEDIUM Зона: src/notifications.py (update_task_tracker bump-режим, render_task_tracker), src/db.py (учёт message_id / колонка effort), src/agents/launcher.py + src/usage.py (стамп эффорта) Связь: ORCH-067 (формат карточки/ссылки/статусы), ORCH-042 (режим bump), ORCH-52h/ORCH-081 (эффорт реально работает), ORCH-086 (свежий reconciler — см. G6)


1. Контекст и проблема

Каждая задача имеет ОДНУ live-карточку в Telegram (update_task_tracker, инвариант «одна карточка на задачу»). Дефолтный режим — bump (ORCH-067): на каждом обновлении старая карточка удаляется и новая шлётся вниз чата, указатель tasks.tracker_message_id перепонтуется на свежий message_id.

Скриншот Славы (08.06, задача ORCH-082):

  1. В чате висит карточка с заголовком 📍 To Analyse, хотя конвейер прошёл весь путь и все стадии вплоть до «Внедрение».
  2. Статусы деплоя не отражены (нет ⏸️ Awaiting Deploy / Confirm Deploy), хотя задача реально на стадии deploy.

Диагноз код-аудита (08.06): сам рендер render_task_tracker исправен (на стадии deploy корректно даёт заголовок и весь deploy-цикл). Карточка со скриншота — ОСИРОТЕВШАЯ старая (msg 18204), застрявшая на первом рендере (To Analyse = _DEFAULT_STATUS_LABEL). bump её не удалил: delete_telegram(mid) — best-effort и НЕ блокирует send (BR-6); указатель tracker_message_id хранит ТОЛЬКО последний mid, поэтому удаляется только он. При рассинхроне указателя часть карточек осиротевает и висит «замёрзшей» на старом статусе. Проверено: бот МОЖЕТ удалять (deleteMessage → ok:true и для 18204, и для 18227) — дело не в правах, а в потере ссылки на старые message_id.

Расширение (09.06) — G5: итоговое время в карточке (⏱️ Всего … · агенты … · твоё …) считается неверно — раздувается на простое/застое (пример ORCH-087: «Подтверждение BRD 392м» при реальном отсутствии обдумывания).

Расширение — эффорт в карточке: строка стадии показывает модель (opus-4-8), но НЕ эффорт. После ORCH-52h эффорт реально работает (developer=xhigh, прочие high/medium) — его надо показать.

2. Цель

Обеспечить, чтобы в чате жила РОВНО ОДНА актуальная карточка задачи с корректным текущим статусом (включая весь deploy-цикл), без осиротевших «замёрзших» карточек; показать эффорт каждой стадии рядом с моделью; считать итоговое время честно и сходимо. Перед разработкой G0-исследование фиксирует ТОЧНУЮ механику рассинхрона и даёт обоснованную (data-backed) рекомендацию bump vs edit → ADR.

3. Бизнес-требования

ID Требование
BR-G0 Сначала расследование, не фикс вслепую. Установить точную механику bump-режима, не принимая на веру workaround-диагноз. Ответить на вопросы расследования (см. §4). Воспроизвести на staging. Вывод → ADR (06-adr/), и только ПОТОМ фикс.
BR-G1 Не оставлять осиротевших карточек: при bump гарантировать удаление ВСЕХ ранее созданных карточек задачи (хранить полный учёт message_id, а не только последний), либо иной механизм, доказательно исключающий сирот.
BR-G2 Заголовок живой карточки отражает ТЕКУЩУЮ стадию на каждой карточке — не застывает на To Analyse.
BR-G3 Статусы деплоя (Awaiting Deploy / Deploying / Confirm Deploy / Monitoring / Done) видимы на карточке на соответствующих стадиях (offline-label + live-overlay покрывают весь deploy-цикл).
BR-EFF Строка каждой стадии карточки показывает уровень эффорта рядом с моделью (формат … · opus-4-8 · xhigh или opus-4-8/xhigh). developer-строка → xhigh; механические (tester/deployer) → medium.
BR-G5 Итоговое время разделить честно: (1) чистое рабочее время агентов (Σ agent_runs) — главная метрика; (2) человеческое время BRD-approve — ТОЛЬКО фактическое, без аномального застоя/рассинхрона; (3) wall-clock — если показываем, помечать как «общее (с ожиданием)», не выдавать за рабочее. Итог должен СХОДИТЬСЯ.
BR-G6 Ветка ORCH-087 должна разрабатываться/мержиться поверх свежего origin/main (где уже ORCH-86). Использовать свежий notifications/reconciler из 86. Явно проверить на merge-gate (пересечение reconciler.py — не append-only, .gitattributes union не спасёт).

4. Вопросы G0-расследования (обязательны к ответу в ADR)

  1. Сколько РЕАЛЬНО карточек одной задачи висело в чате к моменту бага (собрать message_id из логов/Telegram) — сирот могло быть >1.
  2. В какие МОМЕНТЫ tracker_message_id рассинхронизируется с реальными сообщениями:
    • (a) send вернул None (нет креды / transient) → mid не перезаписан;
    • (b) рестарт орка между delete и send;
    • (c) пересоздание карточки во время CLI-фикса / ручных операций;
    • (d) гонка двух update_task_tracker подряд (быстрые стадии);
    • (e) delete упал (rate-limit / >48ч), но send прошёл.
  3. Почему ИМЕННО заголовок застывает на To Analyse: это старый рендер (до смены stage) или баг плана-лейбла? Воспроизвести на staging: прогнать задачу, на каждой стадии зафиксировать что РЕАЛЬНО в Telegram (заголовок+тело) vs что в БД (stage).
  4. bump vs edit: какой режим реально надёжнее против сирот — замерить, а не предполагать. edit правит ОДНО сообщение in-place (нет сирот, но не держит карточку внизу); bump держит внизу (фича-просьба ORCH-042), но плодит сирот при рассинхроне. Дать обоснованную рекомендацию с данными.

5. Не-цели

  • Не плодить дубликаты — инвариант «одна карточка на задачу» сохранить.
  • Не пинговать — disable_notification остаётся (карточка тихая).
  • Не ломать ссылки ORCH-067 (plane_issue_link, кликабельный номер) и disable_web_page_preview (ORCH-080).
  • Не вводить новую стадию конвейера / не менять STAGE_TRANSITIONS / QG_CHECKS.
  • Не предрешать bump vs edit в BRD — это вывод G0/ADR.

6. Ограничения и грабли

  • Telegram не даёт удалять сообщения старше 48ч — для совсем старых сирот зачистка может не сработать. Документировать как ограничение (delete_telegram уже классифицирует это как «gone»/не-transient).
  • Эффорт не возвращается Claude CLI в result-JSON (в отличие от модели, которая берётся из modelUsage). Поэтому надёжный источник эффорта — стамп резолва (resolve_agent_effort) В МОМЕНТ запуска, а не пересчёт постфактум.
  • Контракт всего компонента нотификаций — never raises; карточка всегда silent.
  • Self-hosting: задача правит инструмент, работающий в проде и обслуживающий enduro-trails. НЕ ронять прод-контейнер; обязательная страховка — deploy-staging (8501).

7. Бизнес-ценность

Наблюдатель (Слава) видит ровно одну достоверную карточку: текущий статус, эффорт каждой стадии и честное время. Уходит класс багов «замёрзшая сирота вводит в заблуждение» и «магическое раздутое итоговое время».