Files
jellybit/openspec/changes/archive/2026-07-18-telegram-vybor-nahodok/proposal.md
T
avandClaude Opus 4.8 f75d6b1f91 tgbot: выбор кандидата метабазы inline-кнопками в карточке ревью
Когда для распознавания сохранены кандидаты метабазы, карточка подтверждения
бота показывает кнопку «🗂 База (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>
2026-07-18 16:02:47 +03:00

60 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 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`).