Три мелких фикса из docs/backlog/review-lifecycle-minor.md (ревью Fable 2026-07-08). MINOR-9 (I/O под глобальным w.mu) осознанно waive для one-user home-сервера — не трогаем. MINOR-8: claim-токен распознавания. recognizeOne фиксирует updated_at на момент claim (перечитывая запись после перехода в recognizing), а finishRecognition коммитит результат, только если токен совпал. Иначе за время LLM-вызова задачу увели из recognizing и вернули обратно (cancel → relink revive) — это уже другой эпизод, устаревший результат отбрасываем, задача остаётся в recognizing для перезапуска поллингом. NIT-11: lookup-мапы (byHash/live/torrentByInfohash) больше не индексируют усечённый 40-hex t.Hash v2-only торрентов. Новый хелпер torrentIndexHashes зеркалит выбор torrentHashes: t.Hash берём только при отсутствии обоих infohash_v1/v2. Убирает теоретический ложный матч по коллизии длины. NIT-12: retry живого, но сломанного торрента (error/missingFiles) теперь отклоняется с подсказкой починить раздачу (recheck) в qBittorrent, вместо бессмысленной переотдачи источника (сверка тут же вернула бы задачу в failed). Повторный Add — только когда раздачи в qBittorrent нет. Меняет спеку state-reconciliation → дельта openspec/changes/2026-07-17-retry-reject-broken-torrent (не архивировал). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
43 lines
2.6 KiB
Markdown
43 lines
2.6 KiB
Markdown
## Why
|
|
|
|
Retry задачи в `failed`/`stuck`, чей торрент ЖИВ в qBittorrent, но в состоянии
|
|
ошибки (`error`/`missingFiles`), сейчас повторно отдаёт источник. На живом
|
|
сломанном торренте это бесполезно: qBittorrent отвергает дубль, задача уходит в
|
|
`downloading`, а на ближайшем тике сверки `classErrored` тут же возвращает её в
|
|
`failed` (+ дебаунс уведомления). Пользователю retry выглядит сломанным, а
|
|
реальное лекарство (перепроверка/`recheck`/восстановление файлов в qBittorrent)
|
|
не подсказано (находка ревью NIT-12).
|
|
|
|
## What Changes
|
|
|
|
- **Retry сломанного живого торрента отклоняется**, а не переотдаёт источник.
|
|
Отказ несёт понятное сообщение: починить раздачу (`recheck`/восстановить
|
|
файлы) в qBittorrent и повторить. Состояние задачи не меняется, повторного
|
|
`Add` не происходит.
|
|
- Повторный `Add` при retry остаётся только для случая, когда раздачи в
|
|
qBittorrent НЕТ (перецепка к здоровому живому торренту — без `Add`, как и
|
|
раньше).
|
|
|
|
## Capabilities
|
|
|
|
### New Capabilities
|
|
|
|
Нет.
|
|
|
|
### Modified Capabilities
|
|
|
|
- `state-reconciliation`: требование «Ручной повтор зависшей/упавшей загрузки из
|
|
транспортов» — ветка живого сломанного торрента меняет исход с «повторно отдать
|
|
источник» на «отклонить с подсказкой про `recheck`». Прочие ветки retry (нет
|
|
раздачи → `Add`; жив и здоров → перецепка без `Add`; сброс базиса `retried_at`)
|
|
без изменений.
|
|
|
|
## Impact
|
|
|
|
- **Спеки:** дельта `state-reconciliation` (одно MODIFIED-требование).
|
|
- **Код:** `internal/worker/worker.go` — `Retry` (ветка `alive && classErrored`
|
|
→ отказ `ErrConflict` с сообщением вместо `reAdd`).
|
|
- **Тесты:** `internal/worker/worker_test.go` — отклонение retry на живом
|
|
сломанном торренте (`error`/`missingFiles`), контроль перецепки здорового.
|
|
- **Миграции БД:** нет.
|