Отмена задачи (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>
3.6 KiB
3.6 KiB
1. Код
- 1.1 В
processCatched(internal/worker/worker.go), на обычном пути добавления, ПОСЛЕ вывода имени и НЕПОСРЕДСТВЕННО передqbt.Add: подw.muперечитать запись и, еслиstate != catched,addне делать и загрузку пропустить (D1) - 1.2 Там же (вне
w.mu) добавить свежий листинг присутствия любого из infohash загрузки в qBittorrent передadd(переиспользоватьqbt.Torrents); при присутствии —addне делать, пропустить (усыновит следующий тик); при СБОЕ листинга —addне делать,WARN, повтор на следующем тике (D2, Б1) - 1.3 При неуспехе
PromoteCatchedпосле успешногоadd— подw.muперечитать состояние: только еслиstate != catched(подтверждённая отмена) — удалить добавленный торрент с даннымиqbt.Delete(ctx, HashList, true);WARNоб оставленном/удалённом торренте;ERRORпри сбое удаления. Еслиstate == catched(транзиентный сбой БД) или re-read упал — НЕ удалять,WARN«promote failed, will retry» (D3, Б2). Решение по состоянию, не по тексту ошибки (см. errors.md) - 1.4 Убедиться, что путь уборки достижим только после подтверждённого
отсутствия перед
add(негативный инвариант «удаляем только своё»)
2. Тесты
- 2.1 Обновить
TestProcessCatchedCancelledDuringAddSkipsPromote: отмена во время namer →qbt.AddНЕ вызывается (re-read перед add), состояние остаётсяcancelled(было: Add вызывался) - 2.2 Новый тест: отмена в окне ПОСЛЕ
add(hook наAdd, ставящийcancelled) →qbt.Deleteвызван сdeleteFiles=trueпо infohash загрузки, состояниеcancelled - 2.3 Новый тест (негативный инвариант): infohash появился в qBittorrent во
время namer (hook на namer) → перед
addлистинг видит присутствие →qbt.AddНЕ вызывается,qbt.DeleteНЕ вызывается (чужие данные не трогаем) - 2.4 Новый тест (Б2): сбой
PromoteCatchedприstate == catched(транзиентная ошибка БД, без отмены) → торрент НЕ удаляется (qbt.Deleteне вызван) - 2.6 Новый тест (Б1, safety-critical): свежий листинг перед
addупал (второй вызовTorrents) →Add/DeleteНЕ вызваны, остаётсяcatched - 2.5 Регрессия: штатный успех (нет отмены) по-прежнему добавляет и
промоутит (
TestProcessCatchedAddsToQbit); усыновление присутствующего и сбой листинга тика (TestProcessCatchedListErrorKeepsCatched) — без изменений
3. Спека
- 3.1 MODIFIED-требование «Добавление пойманной загрузки в qBittorrent» в
download-tracking;openspec validate cancel-during-add-cleanup --strict