По итогам аудита соответствия спек и кода (11 сабагентов, по одному на capability): Беклог: - новый пункт: гейт авто-раскладки по confidence (спека говорит «вспомогательный сигнал», код делает жёсткий AutoThreshold=0.85) — определиться, что правда - новый пункт: привязка внешних субтитров к серии (спека требует, для сериала связь субтитр→эпизод и пары .idx/.sub в коде не выражены) - новый пункт: раздачи-копии диска DVD/BluRay (VIDEO_TS/BDMV — каталог целиком, не пофайловый разбор) - дополнен существующий баг TVDB /series/: спека metadata-match теперь тоже кодифицирует баг — фикс должен править и требование Спеки (правка на точность, поведение не меняется): - download-tracking, review: «per-download блокировка» → «единая блокировка воркера» (по факту глобальный w.mu, а не per-download) openspec validate --strict — проходит. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
13 KiB
review Specification
Purpose
Ревью раскладки человеком после распознавания и матча: петля «догадка →
подсказка → перераспознавание», команды (Применить/Уточнить/Распознать заново/
Тип/Игнор/Позже/Отклонить/Undo/Привязать заново), мягкие подсказки vs жёсткие
override, единый список источников совпадения с ручным добавлением и
предпросмотром (превью = применение), разделение труда транспортов (веб —
точные правки, Telegram — быстрые действия и эскалация в веб).
Requirements
Requirement: Вход в review с явной причиной
Когда модель уверенности не разрешает авто-раскладку, система SHALL переводить
загрузку в review и SHALL показывать конкретную причину (низкая самооценка
LLM; нет матча в базе или несколько кандидатов; предупреждение структурной
валидации; неразобранный ответ LLM), а не обобщённое «не уверен». Поверхность
решения SHALL быть единой для всех транспортов и содержать источник (имя, контекст,
дерево файлов), догадку системы (тип, название, год, матч) и превью целевой
раскладки.
Scenario: Причина видна в интерфейсе
- GIVEN загрузка ушла в
reviewиз-за отсутствия матча в базе - WHEN пользователь открывает ревью
- THEN показана конкретная причина (напр. «нет в TMDB · уверенность 0.46»)
Requirement: Команды ревью и их эффекты
Экран ревью SHALL предоставлять команды: Применить (создать хардлинки по
эффективному плану), Уточнить (добавить подсказку → перераспознать),
Распознать заново (повторный прогон без новой подсказки), Тип (переключить
movie↔series), Игнор файла, Позже (deferred), Отклонить
(cancelled), Undo (снять созданные ссылки → reverted) и Привязать
заново (из reverted/cancelled/target_missing → перераспознавание с ручным
подтверждением). Команды из любого транспорта SHALL сериализоваться worker'ом под
единой блокировкой; применяется последняя валидная команда. Команды,
которым нужен источник, SHALL проверять его наличие синхронно перед действием.
Scenario: Применение создаёт раскладку
- GIVEN загрузка в
reviewс эффективным планом - WHEN пользователь выбирает «Применить»
- THEN создаются хардлинки по плану, задача переходит к раскладке
Scenario: Отклонить и привязать заново
- GIVEN загрузка в
review - WHEN пользователь «Отклонить», затем «Привязать заново»
- THEN задача уходит в
cancelled, а затем снова на распознавание с ручным подтверждением (авто-раскладка не делается)
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
выполняться через раундтрип на сервер (форма/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 создавать хардлинки:
сохранённый матч меняется только явным выбором источника, а раскладка —
только действием «Применить». Совпадение целевых путей предпросмотра с
результатом применения регулируется требованием «Превью раскладки через
единую логику именования» (web-ui).
Scenario: Предпросмотр полей без фиксации выбора
- GIVEN список источников на экране ревью
- WHEN пользователь рассматривает источник, ещё не выбрав его активным
- THEN показаны поля результата (тип, название, год) для этого источника
- AND сохранённый матч загрузки не меняется, хардлинки не создаются
Scenario: Зарезервированное место под режиссёра
- GIVEN режиссёр из метабазы пока не загружается
- WHEN отображается предпросмотр полей источника
- THEN в предпросмотре присутствует место под режиссёра, показанное пустым (или прочерком), не ломая вёрстку
Requirement: Разделение труда транспортов в ревью
Состояние ревью SHALL быть единым (в SQLite) для всех транспортов. Веб-UI SHALL быть поверхностью точных правок (маппинг файлов, выбор/ввод источника, предпросмотр). Telegram SHALL давать быстрые действия (одобрить, подсказать, переключить тип, «Позже»/«Отклонить») и эскалацию в веб deep-link'ом на ту же страницу; точечные правки, не помещающиеся в чат, SHALL делаться в вебе.
Scenario: Эскалация из Telegram в веб
- GIVEN загрузка в
review, требующая точечного маппинга файлов - WHEN пользователь в Telegram выбирает «В вебе»
- THEN бот даёт deep-link на страницу ревью той же загрузки