# Ревью раскладки человеком

> **Источник истины переехал в OpenSpec** — `openspec/specs/review/`. Этот файл
> остаётся справочным нарративом (UI-макеты, разбор сценариев); при расхождении
> верна спека OpenSpec.

Что происходит, когда система не уверена в распознавании и не
раскладывает файлы автоматически. Когда именно наступает ревью — см.
[recognition.md](recognition.md); место состояния `review` в общем потоке —
[workflow.md](workflow.md); конвенции целевых имён —
[jellyfin-layout.md](jellyfin-layout.md).

Главный принцип: ревью — это **петля «догадка → подсказка человека →
перераспознавание»**, а не статичное «ок/нет». Человек остаётся
супервизором, а не оператором ручного ввода.

## Когда наступает

Загрузка уходит в `review`, если сработал любой триггер модели
уверенности: низкая самооценка LLM; нет матча в базе (или несколько
кандидатов); структурная валидация ругается (у фильма >1 основного
файла; число серий не бьётся с базой; дыры/дубли в нумерации S·E).
В интерфейсе всегда видна **конкретная причина**, а не просто «не уверен».

## Поверхность решения (едина для всех транспортов)

1. **Источник:** имя торрента, переданный контекст, дерево файлов с
   размерами, (если из бота) распарсенное сообщение.
2. **Догадка системы:** тип, название, год, сезон, матч базы и
   **превью целевой раскладки** — буквальные пути, которые создадутся.
3. **Причина сомнения.**

## Действия

- **Применить** — сделать хардлинки по плану.
- **Уточнить и перераспознать** — добавить подсказку текстом → LLM
  перезапускается с исходными сигналами и накопленными подсказками →
  новый план. Главный путь, когда «LLM не справился».
- **Поправить вручную** — объём зависит от версии (см. ниже).
- **Выбрать кандидата базы** / ввести id / «без базы».
- **Отклонить** / **Позже**.

**Подсказка vs override.** Подсказка мягкая — LLM её интерпретирует.
Ручная правка поля — жёсткий **override**: система берёт значение как
есть и «пиннит» его, перераспознавание не затирает уже поправленное.

## Веб-UI — точные правки

```
Fargo.S02.2015.WEB-DL.1080p.rus.eng                 🟡 review
Причины: нет в TMDB · уверенность 0.46

Контекст: «второй сезон, рус+англ дорожки»  [+ добавить → 🔁 перераспознать]

Тип: ( ) фильм  (•) сериал   Название: Фарго   Год: 2015   Сезон: 02

Источник совпадения (единый список — выбираем источник, а не режим):
 (•) распознано нейронкой (без базы)        [активен]
 ( ) tvdb  Fargo · 2014   id 269613 [запись↗] [предпросмотр▸] [выбрать]
 ( ) tmdb  Fargo          id 60622  [запись↗] [предпросмотр▸] [выбрать]
 + добавить вручную: [tmdb▾] [id или URL записи] [Добавить]
   предпросмотр▸ раскрывает поля (тип/название/год, место под режиссёра) и
   целевые пути ЭТОГО источника — до выбора, ничего не меняя

Файлы → серии:
 # | файл                 | размер | роль   | S  | E
 1 | Fargo.S02E01.rus.mkv | 3.1 GB | эпизод | 02 | 01
 …                                         [нумеровать подряд] [сброс]
 9 | sample.mkv           |  40 MB | игнор  |  – |  –

Превью:
 series/Фарго (2015)/Season 02/Фарго (2015) S02E01.mkv  ← #1
[ Применить ]   [ Отклонить ]   [ Позже ]
```

Ядро экрана для сериала — таблица «файл → серия» с живой валидацией
дыр/дублей и кнопкой «нумеровать подряд» (частый случай: файлы по
порядку, но подписаны криво). Для фильма проще: выбрать основной файл,
остальное — extra/sample/субтитры/игнор.

## Telegram — быстро, где пользователь и так есть

```
🟡 Нужно подтверждение
Источник: Fargo.S02.2015.WEB-DL.1080p
Похоже на: 📺 сериал «Фарго», сезон 2 (2015)
База: TMDB не найдено · уверенность низкая
План: 10 видео → series/Фарго (2015)/Season 02/…E01–E10

[✅ Применить]   [📺↔🎬 Тип]
[🔢 Выбрать в базе]   [🔁 Уточнить]
[🌐 Открыть в вебе]   [❌ Отклонить]
```

- **🔁 Уточнить** → бот просит подсказку ответом → перераспознаёт →
  редактирует то же сообщение новым планом. Петля коррекции прямо в чате.
- Точечное переназначение файлов и выбор кандидата базы в чат не
  помещаются → **🌐 В вебе** (deep-link на ту же страницу, строится из
  `telegram.web_base_url`).

> Реально в боте сейчас: ✅ Применить, 📺↔🎬 Тип, 🔁 Уточнить, 🕗 Позже,
> 🌐 В вебе, ❌ Отклонить. Кнопки «🔢 Выбрать в базе» в чате пока нет —
> выбор кандидата и ручной ввод id делаются в вебе.

## Разделение труда

Telegram = одобрить / подсказать / выбрать кандидата / эскалировать в
веб. Веб = точные правки. Состояние ревью одно (в SQLite); команды из
любого транспорта сериализует `worker` под per-download блокировкой —
гонки двух транспортов нет, применяется последняя валидная команда.

**Доступ.** Telegram — по `telegram.allowed_user_ids` (пусто = запрет
всем). Веб-UI в v1 без авторизации (доверенная LAN), поэтому deep-link из
бота ведёт на открытую страницу — приемлемо по решению; защиту навесим
позже.

## Крайние сценарии

- **База неоднозначна** → выбор кандидата (часто чинит всё разом: пиннит
  provider-id и каноническое имя).
- **База пустая (рус/аниме)** → «без базы» или ручной id/url. Аниме с
  абсолютной нумерацией → веб-хелпер «absolute → S·E»
  ([задача «Аниме с абсолютной нумерацией»](../backlog/anime-absolyutnaya-numeraciya.md)).
- **Не тот тип (movie↔series)** → переключатель пересобирает форму плана.
- **Мусор (sample/extra/дубли дорожек)** → роль «игнор».
- **Полный провал** (LLM ничего не вытащил) → веб-«ручной режим»: выбрать
  тип, ввести название/год, разложить файлы руками; в Telegram — сразу
  эскалация в веб.

## Вход в ревью и откат

- Переход в `review` **пингует** (сообщение в Telegram / бейдж в вебе) —
  пользователя зовут, а не он опрашивает. Таймера нет, источник
  продолжает сидировать.
- После «Применить» показываем, что создано. **Undo** — убрать созданные
  хардлинки одной кнопкой (источник цел); страховка от ошибочного
  подтверждения.
- **«Позже»** паркует загрузку в `deferred` (вернётся в review по
  действию), **«Отклонить»** → `cancelled` (раскладку не делаем), **undo**
  после применения → `reverted` (удаляет только ссылки своего батча, под
  `media`). Полная карта состояний — в [workflow.md](workflow.md).
- После отката или отклонения доступна **«Привязать заново»**: перезапускает
  распознавание для той же раздачи (`reverted`/`cancelled → recognizing`) и
  снова приводит в review — раскладка всегда требует ручного подтверждения,
  авто не делаем. Нужна, когда распознали неверно: откатил/отклонил,
  перепривязал, поправил и применил.
- В самом ревью, помимо **«Уточнить»** (подсказка + перераспознавание), есть
  **«Распознать заново»** — повторный прогон распознавания без новой подсказки
  (контекст и прежние подсказки уже учтены). Полезно, когда модель один раз
  споткнулась на разовой ошибке.

## Объём по версиям

- **Ф3 (готово):** в вебе — подсказка + перераспознавание, «Распознать
  заново», переключатель типа, **единый список источников совпадения**
  (нейронка наравне с кандидатами баз; выбор/переключение/снятие в пользу
  нейронки), **ручное добавление источника по id или URL** (TMDB/IMDb — по
  URL, TVDB — по числовому id), **предпросмотр полей и целевых путей каждого
  источника до применения** (место под режиссёра зарезервировано), пометка
  файла «игнор», «Применить»/«Отклонить»/«Позже», Undo и «Привязать заново».
  В Telegram — подтверждение с reply-подсказкой
  («Уточнить»), переключатель типа, «Позже»/«Отклонить» и эскалация в веб;
  пинги о входе в review и готовности.
- **Ф5 (на будущее):** полный редактор маппинга «файл → серия»
  (правка S·E, «нумеровать подряд»), ручной режим при полном провале LLM,
  выбор кандидата базы и ввод id прямо в Telegram.
