Files
avandClaude Opus 4.8 3a00fde058 Жизненный цикл: уборка торрента при отмене во время добавления (F3/NIT-13)
Отмена задачи (catched→cancelled) в окно, пока worker вне блокировки выводит
имя (LLM) и делает qbt.Add, оставляла добавленный торрент в qBittorrent без
задачи-владельца: PromoteCatched корректно пропускал переход, но источник уже
качался/сидировал вечно, а усыновить его назад нельзя (хеши принадлежат
отменённой задаче). Спека покрывала переход состояния, но не побочный эффект.

Комбинированная защита в processCatched:
- re-read состояния под w.mu прямо перед qbt.Add — при отмене источник не
  добавляется вовсе (сужает окно гонки);
- свежий листинг перед add подтверждает отсутствие infohash — признак «своего»
  торрента; при сбое листинга/присутствии add не делаем (усыновит следующий тик);
- при отмене в окне после add (промах PromoteCatched, подтверждённый re-read'ом
  state != catched) — уборка добавленного нами торрента qbt.Delete(_, true);
- WARN/ERROR-логи по этому пути с корреляцией по download_id, без секретов.

Гарантия «удаляем только своё»: удаление-с-данными достижимо ТОЛЬКО после
подтверждённого отсутствия infohash перед add, поэтому пред-существующий/чужой
торрент с тем же хешем никогда не сносится (негативный инвариант). Обоснование
по инварианту «источник неприкосновенен» — в design.md изменения.

Дельта — download-tracking (требование «Добавление пойманной загрузки в
qBittorrent»): re-read перед add, подтверждение отсутствия, уборка при отмене,
негативный сценарий. Тесты покрывают все ветки (skip-before-add, cleanup после
add, пред-существующий не удаляется, сбой БД не удаляет, сбой листинга не
добавляет).

Change archived: 2026-07-17-cancel-during-add-cleanup.

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

57 lines
4.4 KiB
Markdown

## Why
На шаге добавления пойманной загрузки (`catched`) worker выводит имя (LLM,
секунды) и вызывает `qbt.Add` **вне** блокировки переходов. Если в это окно
пользователь отменяет задачу (`catched → cancelled`), запись перехода
`PromoteCatched` корректно пропускается (гард `state='catched'`), НО источник
уже добавлен в qBittorrent под нашей категорией. Такой торрент качается/сидирует
вечно, ест диск, а видимой задачи-владельца нет: усыновить назад его нельзя —
`adopt` проверяет `ExistsByInfohash` (любое состояние), а хеши уже принадлежат
отменённой задаче. Спека покрывает переход состояния, но не этот побочный эффект
(находка ревью F3/NIT-13).
## What Changes
- **Re-read состояния прямо перед `qbt.Add`** (под блокировкой переходов, после
медленного вывода имени): если задача уже не в `catched` (отменена) — источник
в qBittorrent НЕ добавляется вовсе. Сужает окно гонки до промежутка между
re-read и записью перехода.
- **Scoped cleanup**: если отмена случилась в оставшемся окне (уже ПОСЛЕ
успешного `add`, но до записи перехода), worker удаляет только что добавленный
торрент из qBittorrent **вместе с данными** — уборка собственного мусора.
- **Гарантия «удаляем только своё»**: удаление-с-данными допустимо ТОЛЬКО для
торрента, который worker создал именно этим `add`. Признак — подтверждённое
**отсутствие** infohash в qBittorrent непосредственно перед `add`. Если
infohash уже присутствовал до нашего `add` (внешний клиент раздаёт тот же
торрент), worker источник не добавляет и чужие данные не трогает.
- **WARN-логи** на этом пути (торрент оставлен после отмены → удаляем; факт
удаления), с корреляцией по `download_id`, без секретов.
## Capabilities
### New Capabilities
Нет.
### Modified Capabilities
- `download-tracking`: требование «Добавление пойманной загрузки в qBittorrent» —
добавляется re-read состояния перед `add`, подтверждение отсутствия infohash
перед `add` как признак «своего» торрента и уборка добавленного торрента при
отмене в окне после `add`. Сценарий «Отмена во время добавления» уточняется и
дополняется сценариями уборки и негативного инварианта.
## Impact
- **Спеки:** дельта `download-tracking` (одно MODIFIED-требование + сценарии).
- **Код:** `internal/worker/worker.go``processCatched` (re-read state и
проверка присутствия перед `Add`; уборка добавленного при промахе
`PromoteCatched`). Новых методов `qbt`/`store` не требуется (переиспользуем
`Torrents`, `Delete`).
- **Тесты:** `internal/worker/catched_test.go` — обновление сценария отмены во
время namer (Add не вызывается) + новые: уборка после Add, негативный инвариант
(пред-существующий торрент не удаляется с данными).
- **Миграции БД:** нет.
- **Инвариант «источник неприкосновенен»:** обоснование удаления-с-данными и
негативная гарантия — в `design.md`.