Скан Jellyfin (POST /Library/Refresh) слался только при входе в done.
После Undo (reverted) и Delete (deleted) наши хардлинки сняты, а Jellyfin
держал битые записи до скана по расписанию.
Гейт скана в едином чекпоинте transitionErr переведён с state == done на
предикат triggersScan(state) по множеству {done, reverted, deleted}: гейт по
состоянию-цели естественно ловит пользовательские Undo/Delete и
reconcile-производный deleted, идемпотентно. target_missing/orphaned —
промежуточный рассинхрон (ждём relink/лечения) — исключены.
OpenSpec: заведена и влита дельта file-layout (требование
«Пересканирование Jellyfin после изменения библиотечных ссылок»); change
архивирован. Синк рукописных доков architecture.md/workflow.md. Тесты:
скан стреляет на reverted и deleted, молчит на входе вне множества.
Закрыта задача беклога jellyfin-skan-posle-udaleniya.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5.1 KiB
ADDED Requirements
Requirement: Пересканирование Jellyfin после изменения библиотечных ссылок
При сконфигурированном пересканировании Jellyfin (секция [jellyfin] включена) система SHALL при входе задачи в одно из состояний множества {done, reverted, deleted} неблокирующе просить Jellyfin пересканировать медиатеку (POST /Library/Refresh, скан всех библиотек). Эти три состояния — точки, где раскладка задачи улеглась так, что видимый Jellyfin каталог мог рассинхронизироваться с диском: done — наши хардлинки разложены (или восстановлены сверкой); reverted — Undo снял наши ссылки; deleted — ссылки сняты (Delete) либо констатировано их отсутствие (сверка), задача терминальна.
Условие срабатывания система SHALL проверять по состоянию-цели перехода в
едином чекпоинте записи состояния. Такой гейт SHALL естественно покрывать как
пользовательские команды (Undo → reverted, Delete → deleted), так и
reconcile-производный deleted — инициатор перехода роли не играет; повторный/
лишний скан безвреден (инкрементальный скан дёшев, операция идемпотентна).
Состояния вне этого множества система сканировать SHALL NOT. Сюда входят как
входы, не меняющие наши ссылки (review, linking, cancelled через Dismiss),
так и промежуточные состояния рассинхрона target_missing и orphaned: там
раскладка ещё не улеглась — задача ждёт relink/восстановления и может
«залечиться» обратно в done, поэтому скан на них система откладывает, а не шлёт
на каждое колебание сверки. target_missing система не сканирует сознательно,
хотя цель там пропала: это внешняя пропажа при живом источнике, не наше снятие.
Скан система SHALL выполнять вне блокировки воркера, в фоновом контексте и в
отдельной горутине, со scoped-логгером задачи для корреляции. Недоступность
Jellyfin на состояние задачи влиять SHALL NOT — ошибка вызова лишь логируется
(её пишет клиент Jellyfin как запись внешнего вызова). Если пересканирование не
сконфигурировано ([jellyfin] выключено), скан не дёргается ни в одном из этих
переходов.
Scenario: Скан после раскладки
- GIVEN пересканирование Jellyfin включено
- WHEN задача входит в
doneпосле успешной раскладки хардлинков - THEN система неблокирующе дёргает
POST /Library/Refresh
Scenario: Скан после отката (Undo)
- GIVEN пересканирование Jellyfin включено, задача в
doneс разложенными ссылками - WHEN пользователь выполняет Undo и задача входит в
reverted(наши ссылки сняты) - THEN система неблокирующе дёргает
POST /Library/Refresh
Scenario: Скан после удаления (Delete)
- GIVEN пересканирование Jellyfin включено, задача в
done - WHEN пользователь выполняет Delete и задача входит в
deleted(наши ссылки сняты) - THEN система неблокирующе дёргает
POST /Library/Refresh
Scenario: Без конфигурации Jellyfin скан не дёргается
- GIVEN пересканирование Jellyfin выключено (
[jellyfin]не сконфигурировано) - WHEN задача входит в
done,revertedилиdeleted - THEN система скан не дёргает
Scenario: Вход вне множества не сканирует
- GIVEN пересканирование Jellyfin включено
- WHEN задача входит в состояние вне
{done, reverted, deleted}(например,reviewили промежуточныйtarget_missing) - THEN система скан не дёргает