Files
jellybit/openspec/specs/notifications/spec.md
T
avandClaude Opus 4.8 4a58b0bda0 tgbot: download id моноширинным (code) для tap-to-copy
Включён HTML parse mode у всех исходящих сообщений бота (send + edit-путь
refreshCard), download id выводится моноширинным <code> — в клиентах Telegram
по нему работает tap-to-copy (скопировать id для /download/{id} или диагностики).
Префикс # / download_id= остаётся вне <code>, чтобы копировался чистый id.

Parse mode делает разметку значимой для всех текстов, поэтому добавлен
escape-хелпер и экранированы все внешние/недоверенные фрагменты: display name,
распознанное название, источник/контекст, целевой путь, причины, provider,
error_code/error_msg (инвариант «выход LLM недоверенный»). esc применяется
последним шагом, после усечения, чтобы обрез не разрубил HTML-сущность.

Capability notifications: два ADDED-требования (формат id + экранирование).
Беклог: задача закрыта, зонтичный telegram-revyu-uvedomleniy обновлён.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 15:17:26 +03:00

7.0 KiB
Raw Blame History

notifications Specification

Purpose

Уведомление автора загрузки о значимых событиях: падения (failed/stuck, включая приёмный qbit_add мимо поллинга) с дебаунсом повторов, приглашение в review и готовность, рассинхрон (orphaned/target_missing). Единое место доставки пингов, над которым транспорты (Telegram и др.) — тонкие адаптеры.

Requirements

Requirement: Уведомление о падении загрузки

Любой переход загрузки в failed/stuck система SHALL сопровождать уведомлением автора загрузки через настроенный механизм (notifier), чтобы падение не оставалось незамеченным. Это SHALL включать приёмное падение qbit_add (не удалось добавить раздачу в qBittorrent), которое идёт мимо поллинг-цикла worker.

Scenario: Уведомление при падении приёма

  • GIVEN приём загрузки, где добавление в qBittorrent не удалось
  • WHEN загрузка помечается failed с error_code qbit_add
  • THEN автор загрузки получает уведомление о падении

Requirement: Дебаунс повторных падений

Повторные падения одной задачи в пределах окна дебаунса система SHALL уведомлять лишь один раз, чтобы мерцающий stalled-торрент (stuckdownloading) не спамил автора.

Scenario: Мерцающий stalled не спамит

  • GIVEN задача, многократно переходящая stuckdownloading в пределах окна дебаунса
  • WHEN происходят повторные падения
  • THEN уведомление отправляется один раз за окно

Requirement: Пинг о входе в review и готовности

При переходе загрузки в review система SHALL пинговать автора (сообщение в Telegram / бейдж в вебе) — пользователя зовут, а не он опрашивает. После успешного применения (готовность) система SHALL показывать, что создано.

Scenario: Пинг при входе в review

  • GIVEN загрузка переходит в review
  • WHEN происходит переход
  • THEN автор получает пинг с приглашением подтвердить раскладку

Requirement: Уведомление о рассинхроне

При переходе задачи в orphaned или target_missing система SHALL уведомлять автора загрузки через настроенный механизм уведомлений (notifier), чтобы рассинхрон не оставался незамеченным.

Scenario: Уведомление при потере источника

  • WHEN задача переходит в orphaned
  • THEN автор загрузки получает уведомление о рассинхроне

Requirement: Download id в уведомлениях моноширинным для tap-to-copy

Уведомления, содержащие download id, система SHALL отображать id моноширинным блоком (в Telegram — <code>), чтобы в клиенте работало tap-to-copy: id можно скопировать одним касанием для перехода на /download/{id} или диагностики по логам. Визуальный префикс (# / download_id=) SHALL оставаться вне моноширинного блока, чтобы копировался чистый id без лишних символов.

Scenario: Id карточки review копируется одним касанием

  • GIVEN уведомление о входе загрузки в review с download id
  • WHEN бот рендерит сообщение
  • THEN download id выводится моноширинным блоком (tap-to-copy), а префикс # остаётся обычным текстом вне блока

Scenario: Id в сообщении об ошибке копируется

  • GIVEN сообщение об отказе операции с download_id
  • WHEN бот рендерит сообщение
  • THEN значение download id выводится моноширинным блоком для копирования

Requirement: Экранирование внешнего текста при форматированных уведомлениях

При включённом форматировании исходящих сообщений (parse mode) система MUST экранировать все внешние/недоверенные фрагменты перед вставкой в размеченное сообщение: display name, распознанное название, источник/контекст, целевой путь, причины распознавания, provider, код и текст ошибки. Это защищает от того, что спецсимволы разметки сломают сообщение или что разметка будет инъектирована из недоверенного источника (инвариант «выход LLM недоверенный»). Секреты (токены/ключи/пароли) MUST NOT попадать в текст уведомлений и логи.

Scenario: Спецсимволы в названии не ломают разметку

  • GIVEN уведомление, где display name или распознанное название содержит символы разметки (<, >, &)
  • WHEN бот рендерит форматированное сообщение
  • THEN эти символы экранируются, сообщение доставляется корректно, а разметка из недоверенного текста не интерпретируется

Scenario: Внешний путь и причины экранируются

  • GIVEN уведомление с целевым путём плана и причинами распознавания
  • WHEN бот рендерит форматированное сообщение
  • THEN символы разметки в пути и причинах экранируются перед вставкой