processCatched перед Add проверяет присутствие торрента в qBittorrent (один листинг на тик): если раздача уже есть — усыновляем (promote catched→downloading без повторного Add и без LLM-namer, имя из раздачи), иначе добавляем как раньше. Это убирает бесконечный цикл дубль-Add → 409 → ретрай и лишние вызовы LLM. Инвариант приёма «одна активная на infohash» делает различие «наш/чужой» ненужным. source_type перечитывается под замком (сужение гонки апгрейда F6); при недоступности qBittorrent тик пропускается без вызова LLM. Дедуп на приёме (дубль на уже активную задачу) теперь отражается явным ответом бота «дубль уже активной #id — добавление отменено». Спека download-tracking обновлена (OpenSpec change заархивирован); закрыта задача беклога review-f2-promote-without-add. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
58 lines
4.0 KiB
Markdown
58 lines
4.0 KiB
Markdown
## Why
|
||
|
||
Пойманная (`catched`) загрузка, чей торрент **уже присутствует в qBittorrent**
|
||
(добавлен раньше вручную/другим клиентом или прошлой попыткой jellybit), уходит в
|
||
бесконечный цикл: `processCatched` на каждом тике зовёт `qbt.Add`, qBittorrent
|
||
отбивает дубль (`409 Conflict`), сбой трактуется как транзиентный → задача
|
||
остаётся в `catched` → повтор, и на каждом безнадёжном тике впустую вызывается
|
||
LLM-namer. Диагноз: `docs/backlog/review-f2-promote-without-add.md`.
|
||
|
||
Решение: перед добавлением проверять присутствие торрента в qBittorrent. Раз
|
||
инвариант приёма гарантирует, что до воркера доходит лишь загрузка, для которой в
|
||
jellybit нет другой активной задачи (дубль на активную отсекается ещё на приёме),
|
||
присутствие торрента в qBittorrent означает «его надо **усыновить**» — довести до
|
||
`downloading` без повторного `add` и разложить, а не пытаться добавить дубль и
|
||
ловить 409.
|
||
|
||
Отдельно: повторное добавление торрента, который jellybit **уже ведёт активной
|
||
задачей**, транспорт должен явно отражать как дубль (сообщение «добавление
|
||
отменено»), а не молчаливым «уже в работе».
|
||
|
||
## What Changes
|
||
|
||
- В `processCatched` перед `qbt.Add` — **проверка присутствия торрента в
|
||
qBittorrent** (один листинг на тик). Присутствует → `catched → downloading`
|
||
**без `add`** (усыновление; `display_name` из имени раздачи, без LLM); нет →
|
||
прежний путь добавления. Проверка — **до namer**, чтобы не жечь LLM.
|
||
- При недоступности qBittorrent (листинг не удался) тик пропускается без вызова
|
||
LLM; предел ретрая — существующий предохранитель `catch_timeout`.
|
||
- Гонка апгрейда F6 сужается: `source_type` перечитывается под блокировкой
|
||
переходов перед добавлением.
|
||
- Транспорт Telegram на дедуп приёма (дубль на уже активную задачу) отвечает
|
||
явным «дубль уже активной #id — добавление отменено» (+ лог), без создания
|
||
новой записи.
|
||
|
||
## Capabilities
|
||
|
||
### New Capabilities
|
||
|
||
<!-- нет новых capability -->
|
||
|
||
### Modified Capabilities
|
||
|
||
- `download-tracking`: требование «Добавление пойманной загрузки в qBittorrent»
|
||
дополняется проверкой присутствия и усыновлением (promote без повторного
|
||
`add`) при наличии торрента, перечитыванием источника под замком и пропуском
|
||
тика при недоступности qBittorrent.
|
||
|
||
## Impact
|
||
|
||
- Код: `internal/worker/worker.go` (`processCatched`, presence-check,
|
||
усыновление вместо повторного `add`), `internal/tgbot/bot.go` (текст ответа на
|
||
дедуп).
|
||
- Внешние вызовы: убирает лишние `qbt.Add` (и 409) и `chat.completions`
|
||
(LLM-namer) на повторах; добавляет один `qbt.Torrents`-листинг на тик в
|
||
`processCatched`.
|
||
- Тесты: `internal/worker/catched_test.go`.
|
||
- **БД-миграции, новых состояний, конфигурации и внешнего API — нет.**
|