Files
wiki/tasks/orchestrator/reports/dev-2026-06-04-telegram-tracker.md
2026-06-04 11:50:01 +03:00

114 lines
8.1 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.
# Dev Report: orchestrator — живой Telegram-трекер задачи (Вариант B+)
Дата: 2026-06-04
Статус: DONE
## Задача
Заменить ~15 отдельных ТГ-сообщений на задачу одним живым сообщением-трекером
(editMessageText по стадиям). Отдельными сообщениями с пингом — только approve-gate,
deploy-fail, agent-fail, error.
## Результат (кратко)
- **PR:** #21 — https://git.mva154.duckdns.org/admin/orchestrator/pulls/21
- **Ветка:** `feat/telegram-live-tracker` от `main` (2801983)
- **Commit:** `9a0298d`
- **pytest:** `259 passed, 9 failed` (те же off-limits HMAC/401 в test_webhooks.py).
Baseline на этом хосте — `244 passed, 9 failed`; +15 новых проходящих тестов,
0 новых падений. (В ТЗ значился baseline 243+10 — на этом хосте фактически 244+9,
набор падений идентичный и не тронут.)
- НЕ смержен — на ревью Стрим.
## Изменённые файлы (6, +893/-35)
- `src/db.py` — idempotent ALTER-ы: `tasks.tracker_message_id`, `tasks.title`,
`tasks.brd_review_started_at`, `tasks.brd_review_ended_at`, `agent_runs.model`.
Хелперы: `get/set_tracker_message_id`, `mark_brd_review_started/ended`.
- `src/usage.py``short_model_name()` (отрезает провайдер-префикс и `claude-`);
`_extract_model()` парсит модель из result-JSON (`modelUsage`); `record_usage`
сохраняет модель (`COALESCE`, не затирает существующую).
- `src/notifications.py` — ядро:
- `render_task_tracker(task_id)` — stateless рендер из `agent_runs` + task.
- `update_task_tracker(task_id)` — sendMessage→store id→editMessageText,
fallback на новое сообщение при фейле edit, всегда `disable_notification=true`.
- `edit_telegram()`, `send_telegram()` теперь возвращает message_id.
- `notify_*` переведены в режим «только трекер / только лог», кроме 4 алертов.
- `src/stage_engine.py` — штамп `brd_review_ended` на переходе analysis→architecture.
- `src/webhooks/plane.py` — сохранение `title` задачи при создании.
- `tests/test_telegram_tracker.py` — новые тесты (см. ниже).
## Формат трекера
### В процессе (deploy идёт)
```
🛠️ ET-012 · Треки с зума z5
━━━━━━━━━━━━━━━━━━━━━━
✅ Analysis 10м · 1.1M↓/39.6k↑ · $2.38 · opus-4-8
⏸️ Ревью БРД 8м · твоё время
✅ Architecture 9м · 1.5M↓/34.4k↑ · $2.24 · opus-4-8
✅ Development 11м · 8.4M↓/45.8k↑ · $7.29 · opus-4-8
✅ Review 3м · 1.2M↓/12.9k↑ · $1.53 · sonnet-4.6
✅ Testing 5м · 1.2M↓/19.5k↑ · $1.51 · sonnet-4.6
🔄 Deploy … · идёт
━━━━━━━━━━━━━━━━━━━━━━
💰 13.4M↓ / 152.2k↑ · $14.95
```
(пока Deploy ещё не финишировал — токены deployer-а не учтены в Итого; на финише
учтутся.) Пока идёт ожидание ревью БРД, строка показывает `· твоё время ⏳`.
### На финише
```
🎉 ET-012 · Треки с зума z5 — ГОТОВО
━━━━━━━━━━━━━━━━━━━━━━
✅ Analysis 10м · 1.1M↓/39.6k↑ · $2.38 · opus-4-8
⏸️ Ревью БРД 8м · твоё время
✅ Architecture 9м · 1.5M↓/34.4k↑ · $2.24 · opus-4-8
✅ Development 11м · 8.4M↓/45.8k↑ · $7.29 · opus-4-8
✅ Review 3м · 1.2M↓/12.9k↑ · $1.53 · sonnet-4.6
✅ Testing 5м · 1.2M↓/19.5k↑ · $1.51 · sonnet-4.6
✅ Deploy 6м · 1.6M↓/22.4k↑ · $1.73 · opus-4-8
━━━━━━━━━━━━━━━━━━━━━━
💰 15.0M↓ / 174.6k↑ · $16.68
⏱️ Всего 56м · агенты 44м · твоё 8м
🔗 PR #24 · 📦 deployed
```
(Точные значения совпадают с макетом из ТЗ; токены in = input+cache_read+cache_creation,
out — отдельно; модель — короткая; время в минутах, <1м → `<1м`.)
## Что теперь НЕ шлётся отдельными сообщениями (только трекер / лог)
- Старт агента (`notify_agent_started`) → refresh трекера.
- Завершение агента (`notify_agent_finished`) → refresh трекера.
- Переход стадии (`notify_stage_change`) → refresh трекера.
- QG-pending / QG-failed (`notify_qg_failure`, `notify_qg_result`) → только лог
(CI state: pending — не ошибка).
- QG-passed → только лог.
- Тех-мусор (run_id, exit_code, пути логов) — убран из ТГ-текста.
## Что ОСТАЁТСЯ отдельным сообщением (с пингом, disable_notification=false)
- 📋 Approve-gate (`notify_approve_requested`) — текст про перевод в Approved
(устаревший `:approved:` заменён). Заодно стартует «твоё время» (BRD review clock).
- 🚨 Deploy упал / откат (`send_telegram` в launcher.py и stage_engine.py) — без изменений.
- ❌ Агент упал, exit_code != 0 (`send_telegram` в launcher) — путь к логам только в лог-файл (в коде launcher уже так; текст ТГ не содержит run_id-мусора по новым правилам трекера).
- 🔴 Ошибка задачи (`notify_error`).
## Тесты (tests/test_telegram_tracker.py)
- `short_model_name`: tokenator/claude-opus-4-8→opus-4-8, vibecode/claude-sonnet-4.6→sonnet-4.6, None/'' → ''.
- парсинг модели из `modelUsage`.
- `render_task_tracker`: строки этапов (in↓/out↑·cost·model), строка Ревью БРД (твоё время / ⏳ при ожидании), активная стадия 🔄 идёт, блок Итого 💰, финиш ⏱️ (три времени) + 🔗/📦, экранирование <>& в названии, опускание модели если неизвестна.
- send/edit/fallback: первое сообщение → send + store id (silent); переход → editMessageText существующего; edit-фейл → новое сообщение + обновлённый id.
- алерты: approve-gate и error шлются ОТДЕЛЬНО (disable_notification=false); stage-change / agent-start / QG-pending НЕ шлют отдельных сообщений.
## Проблемы и решения
- **f-string + backslash на Python 3.10 (прод-хост):** литерал с `\uXXXX` внутри
`{...:<13}` ломал парсинг. Решено — вынес метку «Ревью БРД» в константу `_BRD_LABEL`.
- **локальный `import httpx` в `_done_link`** мешал моку → перешёл на module-level `httpx`.
- **Источник модели:** конфиг launcher задаёт `--model` не для всех агентов;
надёжный источник — `modelUsage` из result-JSON → сохраняю в `agent_runs.model`.
- **title задачи** в `tasks` не хранился → добавил колонку + populate при создании.
## НЕ тронуто (по ТЗ)
- usage_comment / Plane-комменты (формат fix #20), PLANE_STATES, launcher.py
deployer-guard, HMAC/queue/cost-расчёт, conftest, nginx/.env.
## Деплой
Код в проде НЕ задеплоен (образ собирается из src в image, не из репо) — это
задача мержа/CI после ревью. Миграции БД idempotent (`_ensure_column`), безопасны
на живой проде при следующем `init_db()`.