From 66100855f63d5b00aa90c6e63a2f95fb06b3400f Mon Sep 17 00:00:00 2001 From: claude-bot Date: Sat, 6 Jun 2026 09:49:58 +0000 Subject: [PATCH] analyst(ET): auto-commit from analyst run_id=167 --- docs/work-items/ORCH-042/01-brd.md | 65 +++++++ docs/work-items/ORCH-042/02-trz.md | 118 +++++++++++++ .../ORCH-042/03-acceptance-criteria.md | 55 ++++++ docs/work-items/ORCH-042/04-test-plan.yaml | 160 ++++++++++++++++++ 4 files changed, 398 insertions(+) create mode 100644 docs/work-items/ORCH-042/01-brd.md create mode 100644 docs/work-items/ORCH-042/02-trz.md create mode 100644 docs/work-items/ORCH-042/03-acceptance-criteria.md create mode 100644 docs/work-items/ORCH-042/04-test-plan.yaml diff --git a/docs/work-items/ORCH-042/01-brd.md b/docs/work-items/ORCH-042/01-brd.md new file mode 100644 index 0000000..28857ec --- /dev/null +++ b/docs/work-items/ORCH-042/01-brd.md @@ -0,0 +1,65 @@ +# 01 — BRD: Telegram live-tracker, режим bump + русификация карточки + +**Work Item:** ORCH-042 +**Тип:** UX-улучшение (notifications) +**Приоритет:** средний +**Запрос:** Слава, 05.06. Связь: `feat/telegram-live-tracker` (Variant B+). +**Self-hosting:** да — правка самого оркестратора, проходит через его же конвейер (общая БД/очередь с enduro-trails). См. `docs/operations/INFRA.md`. + +## 1. Контекст и проблема + +Live-tracker задачи (`src/notifications.py`) — это ОДНА карточка на задачу в Telegram, которая обновляется на каждом переходе стадии через `editMessageText` (Variant B+). Так сделано СПЕЦИАЛЬНО, чтобы убить старую проблему «~15 отдельных карточек/дублей на задачу». + +Побочный эффект текущего решения: карточка редактируется **на месте в истории чата**. При активной переписке в чате карточка «тонет» вверху и её неудобно искать — приходится скроллить вверх к старому сообщению, чтобы увидеть актуальный статус задачи. + +Дополнительно накопились косметические претензии к тексту карточки: смесь англоязычных меток стадий с русским текстом, неудачная формулировка «Ревью БРД», и финальный технический хвост `deployed` вместо человекочитаемого «Внедрено». + +## 2. Цель + +1. Дать Славе альтернативный режим отображения трекера — **bump**: при каждом обновлении карточка «падает вниз» свежим сообщением (всегда последняя в чате), но БЕЗ возврата к проблеме дублей (по-прежнему ОДНА карточка на задачу) и БЕЗ спама звуками/пингами. +2. Привести текст карточки к единому русскому виду и поправить формулировки. + +## 3. Заинтересованные лица + +- **Слава (Owner)** — единственный получатель Telegram-уведомлений; принимает UX. +- **Агенты конвейера** — косвенно: трекер обновляется из `notify_*`-хелперов на каждой стадии. + +## 4. Требования (бизнес-уровень) + +### 4.1. Режим работы трекера (флаг) +- **BR-1.** Новый конфиг-флаг `ORCH_TRACKER_MODE` с двумя значениями: + - `edit` — текущее поведение (редактирование на месте). **Это ДЕФОЛТ** (обратная совместимость, никакой регрессии без явного включения). + - `bump` — новый режим «карточка падает вниз». +- **BR-2.** Неизвестное/пустое значение флага трактуется как `edit` (безопасный фолбэк, оркестратор не падает). + +### 4.2. Поведение режима bump +- **BR-3.** При обновлении карточки в режиме `bump`: старое сообщение удаляется (`deleteMessage`), отправляется новое (`sendMessage`), указатель `tracker_message_id` перенаправляется на новое сообщение. Итог: в чате всегда ровно ОДНА карточка задачи, и она всегда внизу. +- **BR-4.** Bump тихий: новое сообщение отправляется с `disable_notification=true` — карточка всплывает внизу, но БЕЗ звука/пинга на каждой стадии (как и сейчас в edit-режиме). +- **BR-5.** Первое обновление (карточки ещё нет) в режиме `bump` — просто тихо отправить новое и запомнить id (удалять нечего). + +### 4.3. Устойчивость (критично — не сломать защиту от дублей) +- **BR-6.** Fallback: если `deleteMessage` не удался (сообщение старше 48 ч / уже удалено / недоступно) — карточка всё равно отправляется заново, оркестратор НЕ падает. +- **BR-7.** Любой сбой нотификации (сеть/таймаут/5xx/Telegram-ошибка) НЕ роняет оркестратор (контракт «never raises» сохраняется) и НЕ плодит дубли карточек в пределах одного обновления. +- **BR-8.** Режим `edit` после изменений работает строго как раньше — без регрессий (защита от ~15 дублей сохранена). + +### 4.4. Текстовые правки карточки (применяются в ОБОИХ режимах) +- **BR-9.** Метку «Ревью БРД» заменить на «Подтверждение BRD». +- **BR-10.** После того как задача переведена в Approved (человеческий gate пройден, время ревью зафиксировано), эмодзи в строке подтверждения BRD заменить на галочку (✅) вместо текущей паузы (⏸️). Пока ждём человека — оставить прежний индикатор ожидания. +- **BR-11.** Русифицировать метки стадий карточки: `Analysis → Анализ`, `Architecture → Архитектура`, `Development → Разработка`, `Review → Код ревью`, `Testing → Тестирование`, `Deploy → Внедрение`. +- **BR-12.** В итоговой (последней) строке готовой задачи заменить технический `deployed` на «Внедрено». + +## 5. Вне scope +- Изменение состава событий, которые шлются ОТДЕЛЬНЫМИ пингами (approve-gate / deploy-fail / agent-fail / error) — остаётся как есть. +- Изменение формата метрик (токены/стоимость/длительность), макета строк, логики «попытка N». +- Любые изменения в Plane-комментариях агентов (`usage.build_status_comment`). +- Хранение истории карточек / несколько карточек на задачу. + +## 6. Влияние на документацию (golden source) +- `CHANGELOG.md` — запись в `[Unreleased]`. +- `docs/architecture/internals.md` (или соответствующая секция про live-tracker) — описать режимы `edit`/`bump` и `ORCH_TRACKER_MODE`. +- `.env.example` — добавить `ORCH_TRACKER_MODE` с пояснением. + +## 7. Критерии успеха (резюме) +Слава может выставить `ORCH_TRACKER_MODE=bump` и видеть актуальную карточку всегда внизу чата, одну на задачу, без звона; при откате на `edit` (дефолт) поведение неотличимо от текущего; текст карточки полностью русифицирован по BR-9..BR-12. Полные условия PASS/FAIL — `03-acceptance-criteria.md`. + + diff --git a/docs/work-items/ORCH-042/02-trz.md b/docs/work-items/ORCH-042/02-trz.md new file mode 100644 index 0000000..23241b8 --- /dev/null +++ b/docs/work-items/ORCH-042/02-trz.md @@ -0,0 +1,118 @@ +# 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). + diff --git a/docs/work-items/ORCH-042/03-acceptance-criteria.md b/docs/work-items/ORCH-042/03-acceptance-criteria.md new file mode 100644 index 0000000..a019b34 --- /dev/null +++ b/docs/work-items/ORCH-042/03-acceptance-criteria.md @@ -0,0 +1,55 @@ +# 03 — Критерии приёмки: ORCH-042 + +Каждый критерий — однозначное условие PASS/FAIL. Покрытие тестами — `04-test-plan.yaml`. + +## Конфигурация +- **AC-1.** `Settings.tracker_mode` существует, дефолт `"edit"`, читается из env `ORCH_TRACKER_MODE`. + - PASS: `Settings().tracker_mode == "edit"` без env; `ORCH_TRACKER_MODE=bump` → `"bump"`. + - FAIL: поле отсутствует / другой дефолт / не читает env. +- **AC-2.** Неизвестное/пустое значение режима трактуется как `edit` (оркестратор не падает). + - PASS: `ORCH_TRACKER_MODE=garbage` (или пусто) → `update_task_tracker` идёт по edit-ветке, исключений нет. + - FAIL: исключение / выбор bump-ветки на мусоре. + +## Режим edit (регрессия — поведение как было) +- **AC-3.** Первый вызов (нет `tracker_message_id`): `sendMessage` тихо (`disable_notification=True`), id сохраняется; `editMessageText` НЕ вызывается. +- **AC-4.** Повторный вызов при живом сообщении: `editMessageText` на сохранённый id; новое сообщение НЕ шлётся. +- **AC-5.** `edit` вернул `EDIT_GONE` → шлётся НОВОЕ сообщение, id обновляется (fallback как раньше). +- **AC-6.** `edit` вернул `EDIT_NOT_MODIFIED` или `EDIT_FAILED` → новое сообщение НЕ шлётся, id не меняется (защита от дублей сохранена). + - Все AC-3..AC-6 проверяются при `tracker_mode="edit"` (дефолт). FAIL — любое расхождение с текущим поведением. + +## Режим bump +- **AC-7.** Первый вызов в `bump` (нет id): `deleteMessage` НЕ вызывается; `sendMessage` тихо (`disable_notification=True`); возвращённый id сохраняется. + - PASS: ровно один `send_telegram(..., disable_notification=True)`, `delete_telegram` не вызван, `get_tracker_message_id == new_id`. + - FAIL: вызван delete / громкое сообщение / id не сохранён. +- **AC-8.** Повторный вызов в `bump` при существующем id: вызывается `delete_telegram(старый_id)`, затем `send_telegram(..., disable_notification=True)`, затем `tracker_message_id` перенаправляется на новый id. + - PASS: порядок delete→send соблюдён, id == новый. + - FAIL: нет delete / нет send / id остался старым. +- **AC-9.** Bump тихий: новое сообщение всегда с `disable_notification=True`. + - FAIL: `disable_notification` False/отсутствует. +- **AC-10.** Одна карточка на задачу: за один вызов `update_task_tracker` в bump шлётся НЕ более одного нового сообщения. + - FAIL: более одного `send_telegram` за вызов. + +## Устойчивость +- **AC-11.** Fallback при delete-fail: если `delete_telegram` вернул False (старое >48ч / транзиент) — новое сообщение всё равно отправляется, id обновляется, исключений нет. + - PASS: `delete_telegram→False` → ровно один send → id == новый. + - FAIL: send пропущен / исключение всплыло. +- **AC-12.** `delete_telegram` классификация (httpx замокан, never raises): + - `ok:true` → `True`; + - `ok:false` с `"message to delete not found"` / `"message can't be deleted"` / `"message_id_invalid"` → `True`; + - неизвестный `ok:false` / 5xx → `False`; + - исключение (таймаут/сеть) → `False`; + - нет токена/chat_id → `False`, HTTP-вызов не выполняется. +- **AC-13.** Транзиентный сбой send в bump (send вернул None): `tracker_message_id` НЕ затирается на None; исключений нет; дублей нет (≤1 попытка send за вызов). +- **AC-14.** `update_task_tracker` никогда не выбрасывает исключение ни в одном режиме (контракт «never raises») при любых сбоях БД/сети/Telegram. + +## Текстовые правки (оба режима) +- **AC-15.** Метка «Подтверждение BRD» присутствует в карточке там, где раньше была «Ревью БРД»; строки «Ревью БРД» в выводе нет. +- **AC-16.** После прохождения approve-gate (зафиксированы `brd_review_started_at` и `brd_review_ended_at`) строка подтверждения BRD начинается с ✅ (не ⏸️). Пока ждём человека (`brd_review_ended_at` пуст) — индикатор ожидания/⏳ сохраняется (не ✅). +- **AC-17.** Метки стадий в карточке русские: `Анализ`, `Архитектура`, `Разработка`, `Код ревью`, `Тестирование`, `Внедрение`. Английских меток (`Analysis`/`Architecture`/`Development`/`Review`/`Testing`/`Deploy`) в выводе нет — ни в «✅ …»-строках, ни в «🔄 … идёт». +- **AC-18.** Итоговая строка готовой задачи содержит «📦 Внедрено» (не «deployed»). + +## Регрессия и качество +- **AC-19.** Состав отдельных пингов не изменён: `notify_approve_requested` шлёт ровно один НЕтихий пинг и стартует BRD-часы; `notify_error` — один НЕтихий пинг; `notify_stage_change` / `notify_agent_started` / `notify_qg_failure` — НЕ шлют отдельных сообщений (только refresh трекера). +- **AC-20.** Вся существующая и новая pytest-сюита зелёная (`pytest tests/ -q`). Существующие ассерты в `tests/test_telegram_tracker.py` обновлены под русские метки и «Подтверждение BRD». +- **AC-21.** Документация обновлена в ТОМ ЖЕ PR: `CHANGELOG.md`, `docs/architecture/internals.md` (режимы + `ORCH_TRACKER_MODE` + `delete_telegram`), `.env.example` (`ORCH_TRACKER_MODE`). Отсутствие — REQUEST_CHANGES на ревью. + diff --git a/docs/work-items/ORCH-042/04-test-plan.yaml b/docs/work-items/ORCH-042/04-test-plan.yaml new file mode 100644 index 0000000..c9640be --- /dev/null +++ b/docs/work-items/ORCH-042/04-test-plan.yaml @@ -0,0 +1,160 @@ +work_item: ORCH-042 +description: > + Режим bump live-трекера (delete+send+repoint, тихо, fallback, never-raises), + сохранение режима edit без регрессий, и текстовые правки карточки + (Подтверждение BRD, ✅ после approve, русские метки стадий, «Внедрено»). + Сеть не трогаем: httpx / низкоуровневые helpers мокаются; изолированная temp-БД. + +tests: + # --- config --- + - id: TC-01 + type: unit + description: "Settings.tracker_mode по умолчанию 'edit' и читается из ORCH_TRACKER_MODE (AC-1)" + module: tests/test_config.py + expected: PASS + + - id: TC-02 + type: unit + description: "Неизвестное/пустое значение режима -> update_task_tracker идёт по edit-ветке, без исключений (AC-2)" + module: tests/test_telegram_tracker.py + expected: PASS + + # --- edit mode regression --- + - id: TC-03 + type: unit + description: "edit: первый вызов -> sendMessage тихо, id сохранён, editMessageText не вызван (AC-3)" + module: tests/test_telegram_tracker.py + expected: PASS + + - id: TC-04 + type: unit + description: "edit: повторный вызов -> editMessageText на сохранённый id, нового send нет (AC-4)" + module: tests/test_telegram_tracker.py + expected: PASS + + - id: TC-05 + type: unit + description: "edit: EDIT_GONE -> отправка нового, id обновлён (AC-5)" + module: tests/test_telegram_tracker.py + expected: PASS + + - id: TC-06 + type: unit + description: "edit: EDIT_NOT_MODIFIED и EDIT_FAILED -> нового сообщения нет, id не меняется (AC-6)" + module: tests/test_telegram_tracker.py + expected: PASS + + # --- bump mode --- + - id: TC-07 + type: unit + description: "bump: первый вызов (нет id) -> delete не вызван, send тихий, id сохранён (AC-7, AC-9)" + module: tests/test_tracker_bump.py + expected: PASS + + - id: TC-08 + type: unit + description: "bump: повторный вызов -> delete(старый) затем send(тихо), id перенаправлен на новый, порядок delete->send (AC-8, AC-9, AC-10)" + module: tests/test_tracker_bump.py + expected: PASS + + - id: TC-09 + type: unit + description: "bump fallback: delete_telegram->False -> новое всё равно отправлено, id обновлён, без исключений (AC-11)" + module: tests/test_tracker_bump.py + expected: PASS + + - id: TC-10 + type: unit + description: "bump: send вернул None (транзиент) -> id не затёрт на None, ровно одна попытка send, без исключений (AC-13)" + module: tests/test_tracker_bump.py + expected: PASS + + - id: TC-11 + type: unit + description: "bump: одна карточка за вызов -> send_telegram вызван <=1 раза (AC-10)" + module: tests/test_tracker_bump.py + expected: PASS + + # --- delete_telegram classification --- + - id: TC-12 + type: unit + description: "delete_telegram: ok:true -> True (httpx замокан)" + module: tests/test_tracker_bump.py + expected: PASS + + - id: TC-13 + type: unit + description: "delete_telegram: ok:false 'message to delete not found' / 'message can't be deleted' / 'message_id_invalid' -> True (AC-12)" + module: tests/test_tracker_bump.py + expected: PASS + + - id: TC-14 + type: unit + description: "delete_telegram: неизвестный ok:false / 5xx -> False (AC-12)" + module: tests/test_tracker_bump.py + expected: PASS + + - id: TC-15 + type: unit + description: "delete_telegram: исключение (таймаут/сеть) -> False, never raises (AC-12, AC-14)" + module: tests/test_tracker_bump.py + expected: PASS + + - id: TC-16 + type: unit + description: "delete_telegram: нет токена/chat_id -> False, HTTP не вызывается (AC-12)" + module: tests/test_tracker_bump.py + expected: PASS + + # --- never raises --- + - id: TC-17 + type: unit + description: "update_task_tracker никогда не бросает (DB/сеть сбой) в обоих режимах (AC-14)" + module: tests/test_tracker_bump.py + expected: PASS + + # --- text changes --- + - id: TC-18 + type: unit + description: "render: метка 'Подтверждение BRD' присутствует, 'Ревью БРД' отсутствует (AC-15)" + module: tests/test_telegram_tracker.py + expected: PASS + + - id: TC-19 + type: unit + description: "render: approve-gate пройден (brd_review_ended_at задан) -> строка BRD с ✅, не ⏸️ (AC-16)" + module: tests/test_telegram_tracker.py + expected: PASS + + - id: TC-20 + type: unit + description: "render: ожидание человека (brd_review_ended_at пуст) -> индикатор ожидания/⏳, не ✅ (AC-16)" + module: tests/test_telegram_tracker.py + expected: PASS + + - id: TC-21 + type: unit + description: "render: русские метки стадий (Анализ/Архитектура/Разработка/Код ревью/Тестирование/Внедрение), английских нет — в ✅- и 🔄-строках (AC-17)" + module: tests/test_telegram_tracker.py + expected: PASS + + - id: TC-22 + type: unit + description: "render done: итоговая строка содержит '📦 Внедрено', не 'deployed' (AC-18)" + module: tests/test_telegram_tracker.py + expected: PASS + + # --- separate alerts regression --- + - id: TC-23 + type: unit + description: "Состав отдельных пингов не изменён: approve-gate/error шлют 1 нетихий пинг; stage_change/agent_started/qg_failure не шлют (AC-19)" + module: tests/test_telegram_tracker.py + expected: PASS + + # --- full suite --- + - id: TC-24 + type: integration + description: "Вся pytest-сюита зелёная; обновлённые ассерты под русские метки проходят (AC-20)" + module: tests/ + expected: PASS +