Files
avandClaude Opus 4.8 0c9421f4c1 Имя: восстановление display_name после распознавания + гард пустого входа
Голый magnet без dn/контекста заставлял namer звать LLM на пустом входе,
и модель галлюцинировала мусорное имя («Unknown»), которое писалось и в
display_name, и в rename qBittorrent, а заодно ломало UI-фолбэк на
распознанное название. Верное каноническое имя, вычисляемое позже при
распознавании, никуда не переливалось.

- naming: гард пустого входа в DeriveName (нет контекста и подсказки → ""
  без вызова LLM) + детерминированный форматтер FormatTitleYear.
- qbt: операция RenameTorrent (переименование существующей раздачи).
- store: SetDisplayName — обновление имени постфактум без гарда состояния.
- worker: refreshDisplayNameLocked/RefreshDisplayName — перелив канонического
  имени (эффективный план) в display_name + best-effort rename раздачи по
  реальному t.Hash; авто-триггер при подтверждении матча (choose/manual add).
- web-ui: кнопка «Обновить имя» на странице загрузки (htmx-своп заголовка,
  деградация без JS), видимая при наличии распознавания (вкл. done/orphaned).

Спека: дельты ingest/review/web-ui влиты в openspec/specs; change
refresh-display-name заархивирован.

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

7.4 KiB
Raw Permalink Blame History

MODIFIED Requirements

Requirement: Вывод имени через LLM со структурированным выводом

Система SHALL строить отображаемое имя с помощью LLM (структурированный JSON-вывод), извлекая из контекста тип (movie/series), название, год, режиссёра и (для сериала) номер сезона. Год и режиссёр — опциональные поля.

Если и контекст, и подсказка из полей источника (dn magnet / имя .torrent) пусты, система SHALL считать имя не выведенным и SHALL NOT вызывать LLM (выводить имя не из чего — вызов на пустом входе способен лишь галлюцинировать). В этом случае загрузка добавляется без rename, а download.display_name остаётся пустым.

Название SHALL быть на русском языке для российского контента и на английском (оригинальном) — для остального.

Система SHALL предпринять ограниченное число попыток получить от LLM валидный результат (корректный JSON с непустым названием); бюджет попыток — [llm].max_retries (по умолчанию 3). Транспортные ретраи провайдера LLM (сетевые сбои, 429, 5xx) в этот счёт не входят.

Недоступность или ошибка LLM SHALL NOT прерывать приём загрузки: система переходит к алгоритмическому фолбеку.

Scenario: LLM возвращает структурированное имя

  • WHEN LLM по контексту возвращает валидный JSON с непустым названием
  • THEN система формирует отображаемое имя из его полей (название, год, для сериала — сезон)

Scenario: Российский контент — название на русском

  • WHEN контент распознан как российский
  • THEN в отображаемом имени используется русское название

Scenario: Исчерпан бюджет попыток LLM

  • WHEN LLM за отведённые попытки ([llm].max_retries) не вернул валидный результат либо недоступен
  • THEN система не прерывает приём и переходит к алгоритмическому фолбеку

Scenario: Пустой вход — LLM не вызывается

  • GIVEN пойманная загрузка без пользовательского контекста и без подсказки из полей источника (голый magnet без dn)
  • WHEN система выводит отображаемое имя на шаге добавления
  • THEN LLM не вызывается, имя считается не выведенным
  • AND загрузка добавляется без rename, а download.display_name пуст (заголовок в веб-UI берётся из фолбека)

ADDED Requirements

Requirement: Обновление отображаемого имени по распознаванию

Система SHALL уметь обновлять отображаемое имя загрузки после того, как распознавание дало каноническое название, — переливая уже вычисленное имя (без нового вызова LLM) в download.display_name и в имя раздачи qBittorrent.

Источником имени SHALL быть эффективное распознанное название: пины title/year (если матч подтверждён), иначе recognition.title/year — то же название, что использует раскладка. Формат SHALL быть коротким детерминированным ярлыком Title (Year) (год опционален), с той же очисткой от управляющих символов и обрезкой по длине, что и вывод имени на шаге добавления. Пустой источник (нет распознавания или пустое название) SHALL приводить к отсутствию изменений (no-op).

Переименование раздачи в qBittorrent SHALL адресоваться по infohash своей раздачи и SHALL быть best-effort: сбой (раздача удалена, qBittorrent недоступен) SHALL NOT проваливать обновление — download.display_name обновляется в любом случае, ошибка внешнего вызова логируется. Как и на шаге добавления, отображаемое имя SHALL влиять только на отображение и SHALL NOT влиять на пути файлов, распознавание или раскладку.

Обновление имени SHALL иметь две точки входа: авто — при подтверждённом матче (см. capability review); ручную — по явному действию пользователя. Ручное действие SHALL перезаписывать текущее имя всегда; авто SHALL перезаписывать, когда выведенное имя непусто.

Scenario: Перелив имени в загрузку и раздачу

  • GIVEN загрузка с распознанным непустым каноническим названием
  • WHEN запускается обновление отображаемого имени
  • THEN download.display_name устанавливается в Title (Year)
  • AND раздача в qBittorrent переименовывается в то же имя (по infohash своей раздачи)

Scenario: qBittorrent недоступен — имя у загрузки всё равно обновлено

  • GIVEN обновление отображаемого имени с выведенным непустым именем
  • WHEN переименование раздачи в qBittorrent завершается ошибкой (недоступен или раздача удалена)
  • THEN download.display_name всё равно обновлён
  • AND ошибка внешнего вызова qBittorrent логируется, операция не проваливается

Scenario: Нет распознавания — обновление ничего не делает

  • GIVEN загрузка без распознанного названия (пустой источник имени)
  • WHEN запускается обновление отображаемого имени
  • THEN ни download.display_name, ни имя раздачи не меняются (no-op)