## Context

Сейчас `metadata.Candidate` и `store.MetadataCandidate` хранят провайдера и
id, но не URL. Человек на ревью видит таблицу кандидатов с названием и id,
но чтобы проверить матч — должен вручную открыть сайт базы и вставить id в
поиск. Изменение добавляет поле `URL` на всех слоях: от клиентов метабаз до
шаблона ревью.

## Goals / Non-Goals

**Goals:**
- Каждый кандидат внешней базы несёт URL, по которому человек может
  перейти и проверить матч
- URL генерируется клиентом провайдера при поиске (source of truth)
- Сохраняется в БД и отображается в веб-интерфейсе ревью

**Non-Goals:**
- Не меняем Telegram-бот (в чате кандидатов нет, выбор — через веб)
- Не добавляем отдельную колонку для нативного провайдера (всегда можно
  вывести из URL или добавить позже)
- Не кешируем и не валидируем URL (не наша ответственность)

## Decisions

### 1. URL генерируется клиентом провайдера при Search, а не на лету при отображении

**Почему:** клиент знает и нативный провайдер, и нативный id. При
отображении в ревью мы имеем только теговые provider/provider_id (TVMaze →
tvdb/269613), и нативный tvMaze-id уже потерян. Генерация при поиске
сохраняет точную ссылку.

**Альтернатива (отклонена):** генерировать URL на лету в HTTP-обработчике
из provider/provider_id. Не работает для TVMaze (provider в БД уже
подменён на теговый). Хранить же оба id ради одной ссылки — дороже, чем
одно поле URL.

### 2. URL — простая строка, без структуры (provider + path + id)

**Почему:** URL у каждого провайдера формируется по-разному (у TMDB зависит
от mediaType, у TVDB — dereferrer, у IMDb — `/title/`). Хранить готовую
строку проще, чем набор параметров + функцию сборки.

### 3. БД: новая миграция `0004_candidate_url.sql`

**Почему:** изменение схемы — стандартно через goose-миграцию. Миграция
только добавляет nullable колонку (без DEFAULT, обратная совместимость).

## Risks / Trade-offs

- **URL может измениться** (провайдер меняет структуру сайта) → ссылка
  сломается. Вероятность низкая (TMDB/TVDB/IMDb не меняли схемы URL
  годами). Если сломается — фикс в одном месте (клиент провайдера),
  миграция не нужна.
- **Для старых кандидатов url будет пустым** → в шаблоне показываем ссылку
  только если url не пустой; старые записи останутся без ссылки, новые
  получат при следующем распознавании.
