Когда для распознавания сохранены кандидаты метабазы, карточка подтверждения бота показывает кнопку «🗂 База (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>
60 lines
5.1 KiB
Markdown
60 lines
5.1 KiB
Markdown
## Why
|
||
|
||
Распознавание уже копит кандидатов метабазы (`recognize.Result.Candidates` →
|
||
`metadata_candidate`), и веб даёт полный «единый список источников совпадения»
|
||
для точного выбора. В Telegram же карточка подтверждения показывает лишь
|
||
эффективный матч (строка «База: …» / «нет матча»), но **не даёт выбрать** между
|
||
несколькими найденными кандидатами: когда единичного сильного матча нет (частый
|
||
триггер ревью — «несколько кандидатов»), из бота нельзя быстро закрепить нужный,
|
||
приходится открывать веб. Ядро для этого уже готово: команда
|
||
`worker.ChooseCandidate` пиннит кандидата как источник (ручной матч), не
|
||
запуская авто-раскладку.
|
||
|
||
## What Changes
|
||
|
||
- **Карточка подтверждения (review)** в боте, когда для распознавания есть
|
||
сохранённые кандидаты метабазы, показывает кнопку выбора базы. По ней карточка
|
||
двухшагово (как подтверждение удаления) разворачивает **список кандидатов
|
||
inline-кнопками** — быстрый выбор из готового короткого списка.
|
||
- **Выбор кандидата** кнопкой вызывает `ChooseCandidate` (пиннинг источника,
|
||
ручной матч) и обновляет карточку на месте. Активный (уже выбранный) кандидат
|
||
помечается в списке. Кнопка «← Назад» возвращает карточку без изменений.
|
||
- **Веб остаётся точкой точных правок** (ввод id/URL вручную, «без базы»,
|
||
предпросмотр путей): бот — только быстрый выбор из уже найденных кандидатов.
|
||
- **Безопасность границы:** id кандидата приходит в `callback_data` — недоверенный
|
||
вход; перед доменным вызовом он валидируется как ULID (`ident.Parse`), как это
|
||
уже делает веб-транспорт. Выбор кандидата — ручной матч, авто-раскладку не
|
||
запускает (нужно явное «Применить»), инвариант авто-раскладки не двигается.
|
||
|
||
## Capabilities
|
||
|
||
### New Capabilities
|
||
|
||
Нет.
|
||
|
||
### Modified Capabilities
|
||
|
||
- `notifications`: добавляется требование к **поверхности** бота — предлагать
|
||
выбор среди сохранённых кандидатов метабазы в карточке подтверждения
|
||
(inline-кнопки, двухшаговое разворачивание), с валидацией id кандидата из
|
||
`callback_data` на границе и без авто-раскладки. Доменная механика выбора
|
||
(`ChooseCandidate`, пиннинг источника) уже покрыта capability `review` — здесь
|
||
не дублируется.
|
||
- `review`: MODIFIED требование «Разделение труда транспортов в ревью» — быстрый
|
||
выбор источника из готового списка кандидатов теперь и Telegram-действие; точный
|
||
ручной ввод id/URL и «без базы» остаются за вебом. Снимает межспековое
|
||
расхождение (прежде выбор источника целиком относился к веб-точным-правкам).
|
||
|
||
## Impact
|
||
|
||
- **Спеки:** дельта `notifications` — ADDED «Выбор кандидата метабазы из карточки
|
||
подтверждения бота»; дельта `review` — MODIFIED «Разделение труда транспортов в
|
||
ревью» (быстрый выбор кандидата — Telegram-действие).
|
||
- **Код:** `internal/tgbot/bot.go` (интерфейс `Reviewer` + `ChooseCandidate`;
|
||
callback-и списка/выбора/назад), `internal/tgbot/render.go` (кнопка выбора базы
|
||
в `reviewKeyboard`, клавиатура списка кандидатов).
|
||
- **Тесты:** `internal/tgbot` — карточка с кандидатами содержит кнопку выбора;
|
||
разворачивание даёт список; выбор зовёт `ChooseCandidate` с валидным id;
|
||
невалидный id из callback отклоняется.
|
||
- **Миграции БД:** нет (кандидаты уже в БД — таблица `metadata_candidate`).
|