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

64 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🐞 Не оставлять задачу в 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` трогать осторожно: на нём стоит вся сверка.