Files
jellybit/tasks/items/delete-leaves-stale-done.md
T

4.7 KiB
Raw Blame History

🐞 Не оставлять задачу в done, когда ссылки сняты, а раздача не снесена

  • Тип: fix
  • Категория: Ядро продукта
  • Зачем: при недоступном qBittorrent задача весь простой соседа показывает done, хотя тайтла в Jellyfin уже нет: сверка падает на первом шаге и до коррекции не доходит
  • Теги: goal:state-integrity

Delete снимает библиотечные ссылки раньше, чем зовёт qbt.Delete. Если сосед недоступен, локальный шаг проходит, внешний падает, и задача остаётся в done. Спека рассчитывает, что расхождение живёт «до тика сверки», но Poll возвращается на первой же ошибке qbt.Torrents и до reconcileDesync не доходит вовсе — значит, расхождение живёт весь простой qBittorrent.

Двигает у цели state-integrity ту же строку завершения — «ни один известный сегодня путь не оставляет состояние, которое не объясняется историей переходов»: done держится сколь угодно долго и историей переходов не объясняется.

Воспроизведение

  1. Довести задачу до done с существующими библиотечными ссылками.
  2. Погасить qBittorrent (в тесте — клиент на несуществующий адрес).
  3. Выполнить удаление — поштучно или пачкой.

Видно вместо ожидаемого: ссылки сняты, строки file_link удалены, раздача цела, место не освобождено, а карточка, список и Telegram показывают done. Пока qBittorrent лежит, ни один тик сверки этого не исправит. Замер ревью: отказ приходит за 524.545µs, то есть пачка успевает пройти по всем выбранным раньше, чем человек поймёт, что сосед недоступен.

Отчёт ревью — openspec/changes/archive/2026-08-10-bulk-delete-page/review/report.md, находка 4.

Затрагивает

  • Worker.Delete (internal/worker/review.go) — порядок шагов и переход после отказа внешнего шага;
  • Worker.Poll (internal/worker/worker.go) — ранний возврат по отказу qbt.Torrents перед reconcileDesync;
  • спека state-reconciliation, требование «Полное удаление загрузки пользователем» — формулировка «кратковременное рассогласование до тика сверки ожидаемо» сегодня неверна;
  • граф переходов: done → target_missing уже легален, нового состояния не надо.

Критерии приёмки

  • После отказа внешнего шага при снятых ссылках задача не остаётся в done (оракул: тест internal/worker — клиент qBittorrent отказывает, задача оказывается в target_missing, а не в done).
  • В deleted при этом задача не переходит: место не освобождено (оракул: тот же тест).
  • Повторное удаление после подъёма соседа идемпотентно дожимает раздачу (оракул: тест — второй вызов при живом клиенте уводит задачу в deleted).
  • Формулировка спеки приведена к тому, что код делает (оракул: openspec validate --strict и task gate).

Рамки

Порядок шагов менять можно, но снос раздачи раньше снятия ссылок — отдельное решение с ценой: оно убирает необратимый локальный шаг при мёртвом соседе, но меняет поведение при отказе на втором шаге. Ранний возврат Poll по отказу qbt.Torrents трогать осторожно: на нём стоит вся сверка.