Files
jellybit/openspec/changes/archive/2026-07-18-telegram-metabase-match/specs/notifications/spec.md
T
avandClaude Opus 4.8 10a6348d39 tgbot: показывать запись матча метабазы (провайдер+id+ссылка)
В карточке подтверждения и уведомлении о готовности бот теперь показывает
запись матча метабазы — провайдер, id и кликабельную ссылку на страницу
записи, — как веб-страница /download/{id} и экран ревью. Так ошибочную
привязку видно и из Telegram.

Билдер URL записи (providerURL/matchURL) вынесен из internal/httpapi в ядро
internal/worker (worker.ProviderURL + метод (*ReviewData).MatchURL()), чтобы
оба транспорта строили ссылку одинаково; httpapi делегирует туда. baseLine
переведён на эффективные provider/id (с учётом ручных правок), URL в href
экранируется escHref (сверх esc закрывает кавычку — иначе изготовленный id
разорвал бы атрибут и Telegram отклонил бы сообщение). При отсутствии матча
карточка ревью показывает «нет матча», уведомление о готовности строку
опускает.

Capability notifications: ADDED «Показ записи матча метабазы» + MODIFIED
требования об экранировании (id матча и URL, контекст href).

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

7.5 KiB
Raw Blame History

ADDED Requirements

Requirement: Показ записи матча метабазы в уведомлениях

Уведомления и подтверждения бота по загрузке с матчем метабазы система SHALL сопровождать записью матча: provider и id, а при возможности построить URL записи — ссылкой на страницу записи (тот же канонический билдер URL, что и веб). Это SHALL применяться в карточке подтверждения (review) и в уведомлении о готовности. Provider и id SHALL отражать эффективный выбор (с учётом ручных правок), консистентно с веб-страницей загрузки и экраном ревью. Когда URL не строится, матч SHALL показываться текстом (provider и id без ссылки), чтобы ошибочную привязку было видно из бота.

Отсутствие матча (none/пусто) поверхности отражают по-разному: карточка подтверждения SHALL показывать явный индикатор «нет матча» (в ревью полезно видеть, что база не выбрана), а уведомление о готовности строку матча в этом случае SHALL опускать (в финальном пинге «нет матча» — шум).

Provider, id и URL — недоверенные (метабаза/LLM/ручной ввод), поэтому система MUST экранировать их перед вставкой в размеченное сообщение так, чтобы значение не могло разорвать разметку в своём контексте (для URL в атрибуте href — с учётом кавычки), иначе изготовленный id способен сломать сообщение и подавить доставку уведомления (инвариант «выход LLM недоверенный»).

Scenario: Матч со ссылкой в карточке review

  • GIVEN загрузка в review с подтверждённым матчем метабазы, для которого строится URL записи
  • WHEN бот рендерит карточку подтверждения
  • THEN матч выводится ссылкой на страницу записи с provider и id, а provider, id и URL экранированы (в т.ч. кавычка в значении href)

Scenario: Матч без строящегося URL показывается текстом

  • GIVEN загрузка с матчем, для провайдера которого URL записи не строится
  • WHEN бот рендерит карточку подтверждения
  • THEN матч выводится текстом (provider и id) без ссылки

Scenario: Показ матча в уведомлении о готовности

  • GIVEN загрузка с матчем метабазы, перешедшая в готовность
  • WHEN бот рендерит уведомление о готовности
  • THEN уведомление содержит запись матча (provider, id, при возможности — ссылку), чтобы ошибочную привязку было видно после раскладки

Scenario: Нет матча — индикатор в review, пропуск в готовности

  • GIVEN загрузка без матча метабазы (none/пусто)
  • WHEN бот рендерит карточку подтверждения, а затем уведомление о готовности
  • THEN карточка подтверждения показывает индикатор «нет матча», а уведомление о готовности строку матча не содержит

Scenario: Эффективный провайдер после ручной правки

  • GIVEN загрузка, где провайдер/id матча переопределены вручную
  • WHEN бот рендерит запись матча
  • THEN показываются эффективные provider и id (как на веб-странице загрузки), а не значения сырого распознавания

MODIFIED Requirements

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

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

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

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

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

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

Scenario: Кавычка в URL записи не разрывает атрибут href

  • GIVEN уведомление со ссылкой на запись метабазы, где id (а значит URL) содержит кавычку
  • WHEN бот рендерит ссылку матча
  • THEN кавычка в значении href экранируется, атрибут остаётся целым и сообщение доставляется