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

114 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.
</content>
</invoke>