10 KiB
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):
- В чате висит карточка с заголовком
📍 To Analyse, хотя конвейер прошёл весь путь и все стадии ✅ вплоть до «Внедрение». - Статусы деплоя не отражены (нет
⏸️ 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)
- Сколько РЕАЛЬНО карточек одной задачи висело в чате к моменту бага (собрать
message_idиз логов/Telegram) — сирот могло быть >1. - В какие МОМЕНТЫ
tracker_message_idрассинхронизируется с реальными сообщениями:- (a)
sendвернулNone(нет креды / transient) →midне перезаписан; - (b) рестарт орка между
deleteиsend; - (c) пересоздание карточки во время CLI-фикса / ручных операций;
- (d) гонка двух
update_task_trackerподряд (быстрые стадии); - (e)
deleteупал (rate-limit / >48ч), ноsendпрошёл.
- (a)
- Почему ИМЕННО заголовок застывает на
To Analyse: это старый рендер (до смены stage) или баг плана-лейбла? Воспроизвести на staging: прогнать задачу, на каждой стадии зафиксировать что РЕАЛЬНО в Telegram (заголовок+тело) vs что в БД (stage). bumpvsedit: какой режим реально надёжнее против сирот — замерить, а не предполагать.editправит ОДНО сообщение in-place (нет сирот, но не держит карточку внизу);bumpдержит внизу (фича-просьба ORCH-042), но плодит сирот при рассинхроне. Дать обоснованную рекомендацию с данными.
5. Не-цели
- Не плодить дубликаты — инвариант «одна карточка на задачу» сохранить.
- Не пинговать —
disable_notificationостаётся (карточка тихая). - Не ломать ссылки ORCH-067 (
plane_issue_link, кликабельный номер) иdisable_web_page_preview(ORCH-080). - Не вводить новую стадию конвейера / не менять
STAGE_TRANSITIONS/QG_CHECKS. - Не предрешать
bumpvseditв 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. Бизнес-ценность
Наблюдатель (Слава) видит ровно одну достоверную карточку: текущий статус, эффорт каждой стадии и честное время. Уходит класс багов «замёрзшая сирота вводит в заблуждение» и «магическое раздутое итоговое время».