## ADDED Requirements

### Requirement: Единый список источников совпадения на ревью

Экран ревью (`/review/{id}`) SHALL показывать совпавшие источники **единым
списком**, в котором распознавание нейронкой (без базы) — такая же строка,
как кандидаты метабаз (TMDB/TVDB/TVMaze), а не отдельный режим сверху.
Ровно один источник в списке SHALL быть отмечен активным (эффективный
матч). Экран SHALL позволять как операции над этим списком: выбрать
кандидата базы, переключиться на другого кандидата и снять матч с базой
обратно на нейронку («без базы»). Смена активного источника SHALL
выполняться через раундтрип на сервер (форма/htmx), без клиентского
пересчёта доменного состояния. Список источников SHALL показываться только
при наличии плана распознавания.

#### Scenario: Нейронка — строка в общем списке

- **GIVEN** загрузка в `review` с распознаванием нейронкой и одним или
  несколькими кандидатами метабаз
- **WHEN** пользователь открывает `GET /review/{id}`
- **THEN** источники показаны единым списком, где строка «распознано
  нейронкой» стоит наравне с кандидатами баз
- **AND** активным отмечен ровно один источник (текущий эффективный матч)

#### Scenario: Переключение между кандидатами

- **GIVEN** на экране ревью выбран один кандидат метабазы
- **WHEN** пользователь выбирает другого кандидата из списка
- **THEN** активным становится выбранный кандидат, прочие — неактивны

#### Scenario: Снятие матча в пользу нейронки

- **GIVEN** на экране ревью активен кандидат метабазы с названием «Fargo»
- **WHEN** пользователь выбирает строку «распознано нейронкой»
- **THEN** матч с базой снимается (источник — нейронка, «без базы»), тег
  папки провайдера не проставляется
- **AND** поля источника — из распознавания нейронкой, без унаследованных
  от прежнего кандидата название/год

### Requirement: Ручное добавление источника по id или URL

Когда автопоиск по базам промахнулся, экран ревью SHALL позволять добавить
источник вручную — по идентификатору записи метабазы или, где применимо, по
её URL. Ввод SHALL разбираться и валидироваться в пару
`(provider, provider_id)` на входной границе (`internal/httpapi`); допустимые
провайдеры — `tmdb`, `tvdb`, `imdb`. Добавленный источник SHALL появляться в
списке как выбираемая строка; при совпадении `provider:id` с уже присутствующим
источником новая строка NOT создаётся, а выбирается существующая.
Некорректный ввод SHALL отклоняться с сообщением, не меняя текущий активный
источник.

#### Scenario: Добавление кандидата по URL TMDB

- **GIVEN** загрузка в `review`, где нужной записи нет среди автокандидатов
- **WHEN** пользователь вводит URL записи TMDB и подтверждает добавление
- **THEN** из URL извлекаются провайдер и id, источник добавляется в список
  выбираемой строкой

#### Scenario: Дубль id выбирает существующую строку

- **GIVEN** в списке уже есть кандидат с данным `provider:id`
- **WHEN** пользователь добавляет вручную тот же `provider:id`
- **THEN** новая строка не создаётся, активным становится существующий
  кандидат

#### Scenario: Некорректный ввод отклонён

- **WHEN** пользователь вводит нераспознаваемый id/URL
- **THEN** экран показывает сообщение об ошибке и не меняет текущий активный
  источник

### Requirement: Предпросмотр полей источника до фиксации выбора

Экран ревью SHALL показывать для рассматриваемого источника (нейронка,
кандидат базы или добавленный вручную) **поля** результата — тип, название,
год, с зарезервированным местом под режиссёра. Показ полей источника
MUST NOT менять сохранённый матч загрузки и MUST NOT создавать хардлинки:
сохранённый матч меняется только явным выбором источника, а раскладка —
только действием «Применить». Совпадение целевых путей предпросмотра с
результатом применения регулируется требованием «Превью раскладки через
единую логику именования».

#### Scenario: Предпросмотр полей без фиксации выбора

- **GIVEN** список источников на экране ревью
- **WHEN** пользователь рассматривает источник, ещё не выбрав его активным
- **THEN** показаны поля результата (тип, название, год) для этого источника
- **AND** сохранённый матч загрузки не меняется, хардлинки не создаются

#### Scenario: Зарезервированное место под режиссёра

- **GIVEN** режиссёр из метабазы пока не загружается
- **WHEN** отображается предпросмотр полей источника
- **THEN** в предпросмотре присутствует место под режиссёра, показанное
  пустым (или прочерком), не ломая вёрстку

## MODIFIED Requirements

### Requirement: Превью раскладки через единую логику именования

Превью целевых путей раскладки в веб-UI SHALL вычисляться той же логикой
именования, что и реальная раскладка (`internal/naming`/`internal/layout`), а
не дублировать правила в шаблоне. На экране ревью превью SHALL строиться **для
каждого источника в списке** (нейронка, кандидат базы, добавленный вручную) —
эфемерно на сервере, без записи сохранённого матча. Показанные для источника
пути MUST совпадать с теми, что создались бы при выборе этого источника и
применении.

#### Scenario: Превью совпадает с реальной раскладкой

- **WHEN** на экране ревью отображается превью целевых путей для источника
- **THEN** эти пути идентичны тем, что создаст применение при выборе этого
  источника (те же правила имён, спецвыпусков, мультифайла, запрещённых
  символов, тега провайдера и коллизий)

#### Scenario: Переключение источника не тянет чужие поля

- **GIVEN** активен кандидат с запиненными название/год, затем выбран
  источник без собственных названия/года (нейронка или ручной кандидат)
- **WHEN** строится превью и затем выполняется применение выбранного источника
- **THEN** и превью, и применение используют название/год этого источника
  (из плана распознавания), без унаследованных от прежнего кандидата

### Requirement: Матч с записью метабазы ссылкой

Веб-UI SHALL показывать подтверждённый матч с записью метабазы (TMDB/TVDB/IMDb)
как ссылку на эту запись — на странице просмотра `/download/{id}` (блок
распознавания) и в едином списке источников совпадения экрана ревью (у
активного источника-кандидата). Ссылка SHALL открываться в новой вкладке с
`rel="noopener"`. Рядом со ссылкой SHALL быть видны провайдер, идентификатор
записи и (при наличии) год.

#### Scenario: Матч виден ссылкой на странице просмотра

- **WHEN** у загрузки подтверждён матч с записью метабазы и известен URL записи
- **THEN** в блоке распознавания на `/download/{id}` матч показан ссылкой на
  запись с провайдером и id

#### Scenario: URL записи неизвестен

- **WHEN** матч подтверждён (например, id задан вручную), но канонический URL
  записи построить нельзя
- **THEN** матч показывается текстом (провайдер и id) без ссылки, строка списка
  не ломается
