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

4.4 KiB
Raw Permalink Blame History

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.goprocessCatched (re-read state и проверка присутствия перед Add; уборка добавленного при промахе PromoteCatched). Новых методов qbt/store не требуется (переиспользуем Torrents, Delete).
  • Тесты: internal/worker/catched_test.go — обновление сценария отмены во время namer (Add не вызывается) + новые: уборка после Add, негативный инвариант (пред-существующий торрент не удаляется с данными).
  • Миграции БД: нет.
  • Инвариант «источник неприкосновенен»: обоснование удаления-с-данными и негативная гарантия — в design.md.