119 lines
9.7 KiB
Markdown
119 lines
9.7 KiB
Markdown
# 02 — ТЗ: Telegram live-tracker, режим bump + русификация
|
||
|
||
**Work Item:** ORCH-042 · См. `01-brd.md`, `03-acceptance-criteria.md`.
|
||
|
||
## 1. Задействованные модули `src/`
|
||
| Файл | Что меняется |
|
||
|------|--------------|
|
||
| `src/config.py` | Новое поле `Settings.tracker_mode` (env `ORCH_TRACKER_MODE`). |
|
||
| `src/notifications.py` | Новый helper `delete_telegram(message_id)`; ветвление `update_task_tracker` по режиму; текстовые правки в `_BRD_LABEL`, `_TRACKER_STAGES`, BRD-строке `render_task_tracker`, `_done_link`. |
|
||
|
||
БД — **без изменений** (используется существующая колонка `tasks.tracker_message_id` и хелперы `get_tracker_message_id` / `set_tracker_message_id` в `src/db.py`). API HTTP-эндпоинты оркестратора — **без изменений**. Новые QG checks — **не требуются**.
|
||
|
||
## 2. Изменения конфигурации (`src/config.py`)
|
||
|
||
Добавить в класс `Settings` (рядом с блоком «Telegram notifications»):
|
||
|
||
```python
|
||
# ORCH-042: режим live-трекера задачи.
|
||
# edit -> карточка редактируется на месте (editMessageText), ДЕФОЛТ (как было).
|
||
# bump -> при обновлении старое сообщение удаляется и карточка отправляется
|
||
# заново вниз чата (deleteMessage + sendMessage + repoint message_id),
|
||
# тихо (disable_notification). Одна карточка на задачу в обоих режимах.
|
||
# Неизвестное/пустое значение трактуется как edit (см. notifications).
|
||
tracker_mode: str = "edit"
|
||
```
|
||
|
||
- `env_prefix = "ORCH_"` уже задан → переменная окружения `ORCH_TRACKER_MODE`.
|
||
- Резолюция режима — в `notifications`: всё, что не равно (case-insensitive, trimmed) `"bump"`, считается `edit`. Не падать на любом значении.
|
||
|
||
## 3. Изменения нотификаций (`src/notifications.py`)
|
||
|
||
### 3.1. Новый low-level helper `delete_telegram`
|
||
Рядом с `send_telegram` / `edit_telegram`. Контракт «never raises».
|
||
|
||
```python
|
||
def delete_telegram(message_id: int) -> bool:
|
||
"""Delete a Telegram message. Never raises.
|
||
|
||
Returns True if the message is gone after the call (deleted now, OR Telegram
|
||
says it's already not there / can't be deleted -> treat as "no longer our
|
||
problem", caller proceeds to send a fresh card). Returns False only on a
|
||
transient failure (network / timeout / 5xx / unknown error) where the old
|
||
message may still be alive.
|
||
"""
|
||
```
|
||
|
||
Требования к реализации:
|
||
- Эндпоинт `https://api.telegram.org/bot{token}/deleteMessage`, тело `{chat_id, message_id}`, `timeout=5`.
|
||
- Нет токена/chat_id → вернуть `False` (как и прочие helpers при отсутствии кредов — ничего не отправлено, ничего не удалено).
|
||
- `ok:true` → `True`.
|
||
- `ok:false` с описанием «уже нет / нельзя удалить» (маркеры: `"message to delete not found"`, `"message can't be deleted"`, `"message_id_invalid"`) → `True` (сообщение и так недоступно; не транзиент).
|
||
- Прочие `ok:false` (неизвестный 400 / 5xx) и исключения (сеть/таймаут) → `False` + `logger.warning`.
|
||
- Вынести маркеры в модульную константу (по аналогии с `_GONE_MARKERS`), например `_DELETE_GONE_MARKERS`.
|
||
|
||
### 3.2. Ветвление `update_task_tracker` по режиму
|
||
Сохранить существующий путь `edit` без изменений поведения. Добавить путь `bump`.
|
||
|
||
Псевдокод целевой логики:
|
||
```python
|
||
def update_task_tracker(task_id: int):
|
||
try:
|
||
from .db import get_tracker_message_id, set_tracker_message_id
|
||
text = render_task_tracker(task_id)
|
||
mode = (_get_settings().tracker_mode or "edit").strip().lower()
|
||
mid = get_tracker_message_id(task_id)
|
||
|
||
if mode == "bump":
|
||
# bump: одна карточка, но всегда внизу.
|
||
if mid is not None:
|
||
delete_telegram(mid) # best-effort; fallback -> всё равно шлём новое
|
||
new_mid = send_telegram(text, disable_notification=True)
|
||
if new_mid is not None:
|
||
set_tracker_message_id(task_id, new_mid)
|
||
# send вернул None (нет кредов / транзиент) -> mid не трогаем,
|
||
# дубля в пределах вызова нет; перерисуется на следующем переходе.
|
||
return
|
||
|
||
# mode == "edit" (ДЕФОЛТ): существующая логика без изменений.
|
||
... # текущий код edit/EDIT_GONE-fallback as is
|
||
except Exception as e:
|
||
logger.warning(f"update_task_tracker({task_id}) failed: {e}")
|
||
```
|
||
|
||
Инварианты bump-ветки:
|
||
- В пределах ОДНОГО вызова отправляется максимум одно новое сообщение → дублей нет (BR-7).
|
||
- `set_tracker_message_id` вызывается ТОЛЬКО при успешном `send` (`new_mid is not None`). При сбое send id остаётся прежним; на следующем переходе старый будет удалён (или уже мёртв) и отправлен новый — без накопления карточек.
|
||
- `delete_telegram` — best-effort: его результат НЕ блокирует отправку новой карточки (BR-6: delete-fail → всё равно шлём новое).
|
||
- Bump всегда тихий: `disable_notification=True` (BR-4).
|
||
|
||
### 3.3. Текстовые правки (общие для обоих режимов)
|
||
|
||
| BR | Где | Было | Стало |
|
||
|----|-----|------|-------|
|
||
| BR-9 | `_BRD_LABEL` (модульная константа) | `"Ревью БРД"` | `"Подтверждение BRD"` |
|
||
| BR-10 | `render_task_tracker`, ветка BRD-строки при `review_seconds is not None` | префикс `⏸️` (`⏸️`) | `✅` (`✅`). Ветка ожидания (`review_seconds is None`, с ⏳) — НЕ менять. |
|
||
| BR-11 | `_TRACKER_STAGES` (метки) | `Analysis / Architecture / Development / Review / Testing / Deploy` | `Анализ / Архитектура / Разработка / Код ревью / Тестирование / Внедрение` |
|
||
| BR-12 | `_done_link` | `"\U0001f4e6 deployed"` | `"\U0001f4e6 Внедрено"` |
|
||
|
||
Примечания:
|
||
- В `_TRACKER_STAGES` меняется ТОЛЬКО display-label (2-й элемент кортежа). Ключи стадий (`analysis`,…) и имена агентов (`analyst`,…) НЕ трогать — они завязаны на `_STAGE_ACTIVE_AGENT`, `last_done`, БД.
|
||
- Выравнивание `{label:<13}` и `{_BRD_LABEL:<13}` оставить как есть (все новые русские метки ≤13 символов; «Подтверждение BRD» длиннее — формат просто не паддит, косметика, поведение не ломает).
|
||
- Метки используются и в «✅ …»-строках завершённых стадий, и в «🔄 … идёт»-строке активной стадии — обе автоматически станут русскими (правка в одном месте).
|
||
|
||
## 4. Совместимость и риски
|
||
- Дефолт `edit` гарантирует нулевую регрессию без явного включения bump (BR-8). Подробно — `10-tech-risks.md` (заводит архитектор/девелопер при необходимости).
|
||
- Самохостинг: изменения только в коде нотификаций, миграций БД нет, перезапуск self — по стандартной страховке `deploy-staging` (8501) перед prod (см. `CLAUDE.md`).
|
||
|
||
## 5. Артефакты pipeline, которые ДОЛЖНЫ быть обновлены в этом же PR
|
||
- `CHANGELOG.md` → запись в `[Unreleased] / Added` (режим bump) + `Changed` (русификация текста).
|
||
- `docs/architecture/internals.md` — секция про live-tracker: режимы `edit`/`bump`, `ORCH_TRACKER_MODE`, контракт `delete_telegram`.
|
||
- `.env.example` — `ORCH_TRACKER_MODE=edit` с комментарием.
|
||
- Тесты — см. `04-test-plan.yaml`. **Существующие тесты в `tests/test_telegram_tracker.py`, проверяющие англоязычные метки (`"✅ Analysis"`, `"🔄 Deploy"`, `"Review"`) и метку `"Ревью БРД"`, ОБЯЗАТЕЛЬНО обновить под новые русские строки** — иначе регрессия в CI. Это правка существующих ассертов, не изменение контракта.
|
||
|
||
## 6. Замечания по реализации (без расширения scope)
|
||
- Не вводить новых зависимостей; `httpx` уже используется.
|
||
- Не менять сигнатуры `send_telegram` / `edit_telegram` / `update_task_tracker` (внешние вызовы из `launcher`/`stage_engine` не трогаются).
|
||
- Не менять состав отдельных пингов (approve-gate / error / deploy-fail / agent-fail).
|
||
</content>
|