Files
jellybit/openspec/specs/review/spec.md
T
avandClaude Opus 4.8 1f4267a046 Ревью: единый блок выбора источника, клик = выбор (review-unified-source-block)
Три секции экрана ревью (Догадка/Источник/Раскладка) слиты в один блок:
список вариантов (радио) → инфо о выбранном → предпросмотр раскладки. Клик
по варианту сразу выбирает и сохраняет источник и обновляет инфо+раскладку
частичным htmx-свопом блока, без полной перезагрузки и без кнопки «выбрать».

- httpapi: reviewBlockAction (htmx-aware, детект HX-Request) для
  candidate/nobase/source; вынос buildReviewView; поля SeasonSummary и
  BlockError; сводка сезонов (seasonSummary/seasonRanges)
- тип movie↔series убран из UI (read-only); удалён веб-роут /type и
  handleSetType, метод SetType из интерфейса httpapi (worker/Telegram не тронуты)
- шаблон: партиал review_source_block, ссылка «запись ↗» вне кликабельного
  label, фокус радио с клавиатуры; чистка мёртвого sourceView.Files/IsSeries
- тесты: htmx-своп выбора, htmx-путь ошибки, юнит-тесты сводки сезонов
- openspec: спеки review/web-ui синхронизированы, change заархивирован
- беклог: сложные сериальные раздачи; oob-обновление панели действий

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 08:12:28 +03:00

16 KiB
Raw Blame History

review Specification

Purpose

Ревью раскладки человеком после распознавания и матча: петля «догадка → подсказка → перераспознавание», команды (Применить/Уточнить/Распознать заново/ Игнор/Позже/Отклонить/Undo/Привязать заново; тип — read-only, корректируется через «Уточнить»), мягкие подсказки vs жёсткие override, единый список источников совпадения с ручным добавлением и предпросмотром (превью = применение), разделение труда транспортов (веб — точные правки, Telegram — быстрые действия и эскалация в веб).

Requirements

Requirement: Вход в review с явной причиной

Когда модель уверенности не разрешает авто-раскладку, система SHALL переводить загрузку в review и SHALL показывать конкретную причину (низкая самооценка LLM; нет матча в базе или несколько кандидатов; предупреждение структурной валидации; неразобранный ответ LLM), а не обобщённое «не уверен». Поверхность решения SHALL быть единой для всех транспортов и содержать источник (имя, контекст, дерево файлов), догадку системы (тип, название, год, матч) и превью целевой раскладки.

Scenario: Причина видна в интерфейсе

  • GIVEN загрузка ушла в review из-за отсутствия матча в базе
  • WHEN пользователь открывает ревью
  • THEN показана конкретная причина (напр. «нет в TMDB · уверенность 0.46»)

Requirement: Команды ревью и их эффекты

Экран ревью SHALL предоставлять команды: Применить (создать хардлинки по эффективному плану), Уточнить (добавить подсказку → перераспознать), Распознать заново (повторный прогон без новой подсказки), Игнор файла, Позже (deferred), Отклонить (cancelled), Undo (снять созданные ссылки → reverted) и Привязать заново (из reverted/cancelled/target_missing → перераспознавание с ручным подтверждением). Экран ревью MUST NOT содержать команду переключения типа movie↔series: тип показывается read-only, а его корректировка выполняется мягкой подсказкой через Уточнить. Команды из любого транспорта SHALL сериализоваться worker'ом под единой блокировкой; применяется последняя валидная команда. Команды, которым нужен источник, SHALL проверять его наличие синхронно перед действием.

Scenario: Применение создаёт раскладку

  • GIVEN загрузка в review с эффективным планом
  • WHEN пользователь выбирает «Применить»
  • THEN создаются хардлинки по плану, задача переходит к раскладке

Scenario: Отклонить и привязать заново

  • GIVEN загрузка в review
  • WHEN пользователь «Отклонить», затем «Привязать заново»
  • THEN задача уходит в cancelled, а затем снова на распознавание с ручным подтверждением (авто-раскладка не делается)

Scenario: Тип не переключается кнопкой

  • GIVEN загрузка в review с распознанным типом
  • WHEN пользователь открывает экран ревью
  • THEN отдельной команды/кнопки переключения movie↔series на экране нет
  • AND тип показан read-only в инфо-части выбранного источника

Requirement: Подсказка мягкая, override жёсткий

Подсказка (hint) SHALL быть мягким сигналом — её интерпретирует LLM при перераспознавании. Ручная правка поля SHALL быть жёстким override: система берёт значение как есть и «пиннит» его; перераспознавание НЕ SHALL затирать уже поправленное поле. Накопленные подсказки и правки SHALL переживать перераспознавание и накладываться на новый план.

Scenario: Override переживает перераспознавание

  • GIVEN пользователь зафиксировал тип series как override
  • WHEN запускается перераспознавание по новой подсказке
  • THEN в новом эффективном плане тип остаётся series

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

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

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

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

Scenario: Выбор кандидата одним кликом

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

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 показывать для выбранного (активного) источника две части: инфо — тип (read-only, movie/series), название, оригинальное название, год, для сериала — сводку сезонов (один сезон, диапазон/список для многосезонного пака или «Спецвыпуски»), с зарезервированным местом под режиссёра; и предпросмотр раскладки — целевые пути хардлинков этого источника. Обе части SHALL относиться именно к активному источнику и SHALL обновляться при смене выбора. Отрисовка блока (показ инфо и предпросмотра) MUST NOT создавать хардлинки: раскладка создаётся только явным действием «Применить». Совпадение целевых путей предпросмотра с результатом применения регулируется требованием «Превью раскладки через единую логику именования» (web-ui).

Scenario: Инфо и предпросмотр относятся к активному источнику

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

Scenario: Просмотр блока не создаёт раскладку

  • GIVEN экран ревью с показанным блоком выбора источника
  • WHEN пользователь только просматривает инфо и предпросмотр, не нажимая «Применить»
  • THEN хардлинки не создаются, файлы под paths.movies/series не меняются

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

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

Requirement: Разделение труда транспортов в ревью

Состояние ревью SHALL быть единым (в SQLite) для всех транспортов. Веб-UI SHALL быть поверхностью точных правок (маппинг файлов, выбор/ввод источника, предпросмотр). Telegram SHALL давать быстрые действия (одобрить, подсказать, переключить тип, «Позже»/«Отклонить») и эскалацию в веб deep-link'ом на ту же страницу; точечные правки, не помещающиеся в чат, SHALL делаться в вебе.

Scenario: Эскалация из Telegram в веб

  • GIVEN загрузка в review, требующая точечного маппинга файлов
  • WHEN пользователь в Telegram выбирает «В вебе»
  • THEN бот даёт deep-link на страницу ревью той же загрузки