Files
orchestrator/docs/work-items/ORCH-087/01-brd.md
claude-bot 1d6c7663a4
All checks were successful
CI / test (push) Successful in 27s
analyst(ET): auto-commit from analyst run_id=420
2026-06-09 01:54:24 +03:00

10 KiB
Raw Blame History

01-BRD — ORCH-087

Заголовок: Трекер-карточка застревает на старом статусе («To Analyse») + осиротевшие карточки при bump; + эффорт в строке стадии.

Тип: Багфикс (UX live-трекера) + малое расширение карточки. Приоритет: MEDIUM. Зона: src/notifications.py (update_task_tracker bump-режим, render_task_tracker), src/db.py (хранение message_id'ов задачи + колонка agent_runs.effort), src/agents/launcher.py (стамп фактического эффорта). Связь: ORCH-042 (введён bump), ORCH-067 (формат карточки, статус-строка, ссылки), ORCH-052h/ORCH-081 (эффорт реально работает), ORCH-080 (link-preview).


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

Live-трекер ведёт одну карточку на задачу (update_task_tracker). Дефолтный режим — bump (ORCH-067): на каждом обновлении delete_telegram(old_mid) (best-effort, НЕ блокирует send) → send_telegram(new) (тихо, вниз чата) → set_tracker_message_id(new_mid) — но указатель tasks.tracker_message_id хранит только последний message_id.

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

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

Код-аудит 08.06 (предварительная гипотеза, подлежит ПОДТВЕРЖДЕНИЮ — см. G0): render_task_tracker СЕЙЧАС рендерит корректно (заголовок текущей стадии, deploy виден). Значит карточка со скриншота — осиротевшая старая (msg 18204), застрявшая на первом рендере (_DEFAULT_STATUS_LABEL = "To Analyse"). bump не удалил её: при рассинхроне tracker_message_id (сбой send → new_mid=None → указатель не перезаписан; рестарт орка между delete и send; пересоздание во время ручного фикса/CLI) часть карточек осиротевает и навсегда замирает на старом статусе. Проверено: бот МОЖЕТ удалять (deleteMessage → ok:true для 18204 и 18227) — дело не в правах, а в потере ссылки на старые message_id.

Это диагноз-кандидат, его нельзя принимать на веру (см. требование G0 ниже — расследование должно установить точную механику, потому что предыдущие диагнозы уже путали причины).

2. Цели (бизнес-уровень)

  • G0 (расследование ПЕРВЫМ, до фикса): установить ТОЧНУЮ механику бага по данным, не вслепую.
  • G1: Не оставлять осиротевших карточек: при bump старая карточка удаляется ВСЕГДА, либо хранится список ВСЕХ созданных message_id задачи и незакрытые подчищаются.
  • G2: Заголовок живой карточки отражает ТЕКУЩУЮ стадию (не застывает на «To Analyse»).
  • G3: Статусы деплой-цикла (Awaiting Deploy / Confirm Deploy / Deploying / Monitoring / Done) видны на карточке на соответствующих стадиях.
  • G4 (расширение): В строку каждой стадии карточки добавить уровень эффорта рядом с моделью.

3. G0 — обязательное расследование (вход в ADR)

Вывод G0 оформляется архитектором в 06-adr/ ДО фикса. Расследование обязано ответить на:

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

4. G4 — эффорт в строке стадии (расширение)

Сейчас строка стадии показывает модель (opus-4-8), но не эффорт. После ORCH-052h/ORCH-081 эффорт реально применяется (developer=xhigh, tester/deployer=medium, прочие=high). Нужно показывать фактически применённый уровень рядом с моделью.

  • Формат (компактно, на усмотрение архитектора): … · opus-4-8 · xhigh ИЛИ … · opus-4-8/xhigh. Пример полной строки: ✅ Разработка 7м · 4.9M↓/32k↑ · $3.97 · opus-4-8 · xhigh.
  • Источник эффорта (предпочтительно): сохранять ФАКТИЧЕСКИ применённый эффорт в agent_runs (то, что реально ушло в CLI), а не пересчитывать resolve_agent_effort(agent) на рендере — новая колонка agent_runs.effort (как уже есть agent_runs.model). Это надёжнее: не зависит от изменения конфига между запуском и рендером.
  • developer-строка показывает xhigh; механические (tester/deployer) — medium.
  • Архитектор вправе вынести G4 отдельной мелкой задачей; по умолчанию — в этом же PR (одна зона: notifications.py + agent_runs).

5. Не-цели

  • Не плодить дубликаты — инвариант «одна карточка на задачу» сохранить.
  • Не пинговать — disable_notification остаётся (карточка тихая).
  • Не ломать ссылки ORCH-067 (plane_issue_link) и подавление link-preview ORCH-080.
  • Не менять STAGE_TRANSITIONS, QG_CHECKS, статусную модель ORCH-066.
  • Не трогать поведение для не-self проектов сверх необходимого (общая БД/очередь — enduro-trails).

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

  • Telegram 48ч: сообщения старше 48ч удалить нельзя (deleteMessage вернёт «message can't be deleted»). Для совсем старых сирот зачистка может не сработать — документировать как ограничение (поведение delete_telegram: такой исход трактуется как «уже не наша проблема», _DELETE_GONE_MARKERS).
  • Rate-limit / 429 на массовом удалении при накопившихся сиротах.
  • Общая БД/очередь с enduro-trails — любое изменение схемы строго аддитивно и идемпотентно (_ensure_column), нулевая регрессия для других проектов.
  • never-raise: компонент нотификаций никогда не валит конвейер (CLAUDE.md, ORCH-067).

7. Заинтересованные лица

  • Owner / наблюдатель (Слава) — видит карточку, инициатор бага.
  • Self-hosting прод — карточки всех проектов из одного инстанса.

8. Критерии успеха (бизнес)

См. 03-acceptance-criteria.md. Кратко: завершённая задача не оставляет карточек с устаревшим заголовком; единственная актуальная карточка показывает текущий статус включая deploy-цикл; сироты не возникают/подчищаются при сбое send/рестарте; эффорт виден в каждой строке стадии; pytest зелёный, never-raise.