## 1. Ядро: общий билдер URL

- [x] 1.1 `internal/worker/review.go`: добавить экспортируемую
  `func ProviderURL(provider, id, mediaType string) string` (канонический URL;
  пустой id / неизвестный провайдер → пусто) и метод
  `func (rd *ReviewData) MatchURL() string` (приоритет URL выбранного кандидата
  при совпадении provider+id с эффективными, иначе `ProviderURL` по
  `rd.Provider`/`rd.ProviderID`/`rd.Plan.Type`).
- [x] 1.2 Перенести тесты `providerURL`/`matchURL` в `internal/worker`
  (из `internal/httpapi/providerurl_test.go`), поправив на новые имена/сигнатуры.

## 2. httpapi: делегирование

- [x] 2.1 `internal/httpapi/review.go`/`download.go`: удалить локальные
  `providerURL`/`matchURL`; `view.MatchURL = rd.MatchURL()`; `sourceMatchURL`
  зовёт `worker.ProviderURL(src.Provider, src.ProviderID, src.Type)`.
  `parseProviderURL`/`parseManualSource` остаются в httpapi.

## 3. tgbot: показ матча

- [x] 3.1 `internal/tgbot/render.go`: `escHref` — экранирование URL для значения
  атрибута `href` (поверх `esc` заменяет `"` → `&quot;`).
- [x] 3.2 `baseLine` принимает `*worker.ReviewData`, использует эффективные
  `rd.Provider`/`rd.ProviderID` и `rd.MatchURL()`: при непустом URL —
  `<a href="...">provider id ↗</a>` (provider/id через `esc`, URL через `escHref`),
  иначе текст `provider id`; при отсутствии матча — пусто.
- [x] 3.3 `reviewCard` зовёт `baseLine(rd)`, при пустом результате показывает
  «База: нет матча» (сохранение поведения); `renderDone` добавляет строку
  «База: …» только при непустом `baseLine(rd)` (без матча — опускает).

## 4. Тесты

- [x] 4.1 `internal/tgbot`: карточка ревью с матчем содержит `<a href=...↗`;
  провайдер/id экранированы; матч без URL — текстом; `renderDone` содержит строку
  матча при матче и опускает её без матча; карточка ревью без матча — «нет матча».
- [x] 4.2 `internal/tgbot`: id с `"` — кавычка в `href` экранируется (`&quot;`),
  атрибут остаётся целым.

## 5. Спека и проверки

- [x] 5.1 `openspec validate --strict telegram-metabase-match`.
- [x] 5.2 `task test` и `task lint` — зелёные.
