Files
jellybit/tasks/items/delete-checks-active-infohash-owner.md
T

67 lines
4.9 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.
# 🐞 Не сносить раздачу, которой владеет другая активная загрузка
- **Тип:** fix
- **Категория:** Ядро продукта
- **Зачем:** удаление старой закрытой задачи уничтожает файлы живой загрузки с тем же инфохэшем; воспроизведено падающим тестом на ревью bulk-delete-page
- **Теги:** goal:state-integrity
`Delete` зовёт `torrents/delete` с `deleteFiles=true` по хешам своей записи и не
спрашивает, не владеет ли этим инфохэшем другая **активная** загрузка. Человек
подтверждает удаление одной записи, а необратимое действие применяется к чужим
живым данным.
Двигает у цели `state-integrity` строку завершения «ни один известный сегодня
путь не оставляет состояние, которое не объясняется историей переходов»: живая
загрузка B уходит в `failed` по пропаже источника, и по её записи не видно, что
файлы снесла чужая операция.
## Воспроизведение
1. Довести задачу A по инфохэшу H до `done`.
2. Добавить тот же релиз заново: `CreateDownloadIfNoActive` ищет владельца
только среди активных, A терминальна, дедупа нет — заводится активная задача
B по тому же H. Файлы на диске у A и B общие, qBittorrent дедуплицирует
раздачу по хешу.
3. Удалить A — со страницы загрузки, из Telegram или со страницы группового
удаления.
Видно вместо ожидаемого: раздача снесена с файлами, B продолжает считать себя
качающейся и уходит в `failed` после дебаунса. Если B ещё не дошла до раскладки,
копии не остаётся нигде.
Прогнанные оракулы ревью (отчёт —
`openspec/changes/archive/2026-08-10-bulk-delete-page/review/report.md`, находка
2): `TestAdversaryDeletablePageOffersHashOwnedByActiveDownload`,
`TestAdversaryDeleteWipesSourceOfAnotherActiveDownload`.
## Затрагивает
- `Worker.Delete` (`internal/worker/review.go`) — вызов `qbt.Delete`;
- предикат `store.FindActiveByInfohash` — существующий, новый заводить не надо;
- спека `state-reconciliation`, требование «Полное удаление загрузки
пользователем» — сегодня оно про такую проверку молчит;
- строка выбора и подтверждения на странице группового удаления, если решено
показывать признак чужого владения;
- qBittorrent `torrents/delete` с `deleteFiles=true` — необратимая операция.
## Критерии приёмки
- Удаление задачи, чьим инфохэшем владеет активная загрузка, отклоняется
конфликтом и раздачу не сносит (оракул: тест `internal/worker` — задача A в
`done` и активная B по тому же хешу, `Delete(A)` возвращает `ErrConflict`, у
подставного клиента qBittorrent ноль вызовов `Delete`).
- Отказ виден человеку причиной, а не общим «внутренняя ошибка» (оракул: тест
`internal/httpapi` — строка отчёта называет, что инфохэшем владеет активная
задача).
- Удаление задачи, чей инфохэш никем больше не занят, работает как прежде
(оракул: существующие тесты `Delete` остаются зелёными).
- Поведение записано дельта-спекой `state-reconciliation` (оракул:
`openspec validate --strict` и `task gate`).
## Рамки
Необратимое: снос раздачи с файлами. Проверка ставится в ядре, а не в
транспорте — допуск обязан держаться без транспорта. Правило дедупа при приёме
(«владелец ищется среди активных») эта задача не меняет: связывание v1/v2 и
доверие к паре `xt` разбирает `infohash-identity-integrity`.