Когда для распознавания сохранены кандидаты метабазы, карточка подтверждения бота показывает кнопку «🗂 База (N)». По ней двухшагово (как delete/dismiss) разворачивается список кандидатов inline-кнопками; выбор пиннит источник через worker.ChooseCandidate (ручной матч, без авто-раскладки) и обновляет карточку. Веб остаётся точкой точных правок (ручной ввод id/URL, «без базы»). Безопасность границы: id кандидата из callback_data валидируется как ULID (ident.Parse) до доменного вызова, как в вебе. Текст inline-кнопок Telegram не парсится как HTML — название кандидата в подписи не экранируется. SDD: change telegram-vybor-nahodok — дельта notifications (ADDED «Выбор кандидата метабазы из карточки подтверждения бота») + review (MODIFIED «Разделение труда транспортов»: быстрый выбор кандидата — Telegram-действие). Влито в specs, change заархивирован. Миграций БД нет (кандидаты уже в БД). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.9 KiB
ADDED Requirements
Requirement: Выбор кандидата метабазы из карточки подтверждения бота
Карточка подтверждения бота SHALL предлагать выбрать источник из сохранённых кандидатов метабазы inline-кнопками, когда для распознавания загрузки есть хотя бы один такой кандидат (типичный триггер ревью — несколько подходящих записей без единичного сильного матча), а не молча оставлять загрузку с первым/лучшим или без базы. Когда сохранённых кандидатов нет, карточка кнопку выбора базы показывать SHALL NOT. Выбор SHALL быть двухшаговым: кнопка выбора базы разворачивает список кандидатов (по кнопке на кандидата, выбранный помечен), а нажатие кандидата пиннит его как источник и обновляет карточку. Отдельная кнопка SHALL возвращать карточку без изменений.
Выбор кандидата из бота — ручной матч (пиннинг источника): он SHALL NOT запускать авто-раскладку; раскладка выполняется отдельным подтверждением («Применить»). Бот SHALL оставаться быстрым выбором из готового короткого списка кандидатов, тогда как точные правки (ручной ввод id/URL, «без базы») — на веб- поверхности.
Id кандидата приходит в callback_data и является недоверенным входом: перед
доменным вызовом выбора система MUST валидировать его как ULID на границе
транспорта; невалидный id MUST отклоняться без изменения состояния загрузки.
Текст inline-кнопок Telegram не интерпретируется как разметка, поэтому название
кандидата в подписи кнопки инъекции разметки не создаёт.
Scenario: Карточка с кандидатами предлагает выбор базы
- GIVEN загрузка в
review, для распознавания которой сохранены кандидаты метабазы - WHEN бот рендерит карточку подтверждения
- THEN карточка содержит кнопку выбора базы, ведущую к списку кандидатов inline-кнопками
Scenario: Выбор кандидата пиннит источник без авто-раскладки
- GIVEN развёрнутый в карточке список кандидатов метабазы
- WHEN пользователь нажимает кнопку кандидата
- THEN этот кандидат закрепляется как источник (ручной матч), карточка обновляется на месте, а авто-раскладка не запускается — для раскладки нужно явное «Применить»
Scenario: Id кандидата из callback валидируется на границе
- GIVEN callback выбора кандидата, где значение id кандидата недоверенное/невалидное
- WHEN бот обрабатывает callback
- THEN id кандидата валидируется как ULID до доменного вызова, а невалидное значение отклоняется без изменения состояния загрузки
Scenario: Возврат из списка не меняет состояние
- GIVEN развёрнутый в карточке список кандидатов
- WHEN пользователь нажимает кнопку возврата
- THEN бот возвращает исходную карточку подтверждения, не меняя выбранный источник
Scenario: Без кандидатов кнопки выбора базы нет
- GIVEN загрузка в
review, для распознавания которой кандидаты метабазы не сохранены - WHEN бот рендерит карточку подтверждения
- THEN карточка не содержит кнопку выбора базы