## 1. qBittorrent: проброс имени - [x] 1.1 Добавить поле `Rename string` в `qbt.AddRequest` - [x] 1.2 В `Client.Add` писать form-field `rename` при непустом `Rename` - [x] 1.3 Тест: при заданном `Rename` form-data содержит `rename`, при пустом — поля нет ## 2. Вывод имени: структура и рендер - [x] 2.1 Описать структуру извлечённого имени (type, title, original_title, year, director, season, is_russian) - [x] 2.2 Реализовать чистую функцию рендера структуры в короткий ярлык: `Title (Director, Year)` с опциональными режиссёром и годом (скобка опускается, если обоих нет), для сериала — суффикс `. Сезон N`; очистка управляющих символов/переводов строк и обрезка по длине - [x] 2.3 Тесты рендера: фильм с режиссёром+годом / только год / только режиссёр / без обоих, сериал с сезоном/без, обрезка длины, очистка ## 3. Вывод имени: LLM - [x] 3.1 Узкий промпт извлечения имени (RU-название для российского контента, иначе EN/оригинал) + описание JSON-схемы ответа - [x] 3.2 Парсинг и валидация ответа (валидный JSON, непустой `title`); бюджет попыток — `[llm].max_retries` (переиспользуем существующий) - [x] 3.3 Тесты на фикстурах (без сети): успешный разбор, выбор языка названия, исчерпание попыток ## 4. Вывод имени: алгоритмический фолбек - [x] 4.1 Реализовать фолбек без сети: первая содержательная строка контекста (без ссылок/команд/UI-мусора), отсечение тех. характеристик, очистка и обрезка - [x] 4.2 Фолбек самодостаточен (лёгкая фильтрация шума для сырого HTTP/CLI-ввода). `tgbot.cleanContext` НЕ рефакторил: он работает на слое транспорта (чистит UI-мусор бота), фолбек — на слое ядра (берёт заголовок из уже-контекста); преждевременная общая зависимость связала бы транспорт с util ядра. Дублирование минимально (две эвристики) - [x] 4.3 Тесты фолбека: заголовок релиза, пустой/бесполезный контекст → "" ## 5. Интеграция в ingest - [x] 5.1 Ввести узкий интерфейс `ingest.Namer` (`DeriveName`), реализован пакетом `internal/naming` поверх LLM + фолбек; зависимость опциональна (nil-провайдер → только фолбек) - [x] 5.2 В `Ingest()` синхронно выводить имя из `req.Context` (подсказка — `magnet.DisplayName`) перед `qbt.Add`, класть в `AddRequest.Rename` - [x] 5.3 Graceful-деградация: ошибка/таймаут вывода имени логируется и не прерывает приём (пустое имя → без `rename`). Бюджет попыток и таймаут — общие `[llm].max_retries` / `[llm].timeout`, новых параметров не вводим - [x] 5.4 Прокинуть зависимость (LLM-провайдер) в сборке сервиса (`cmd/jellybit`): провайдер поднимается один раз, переиспользуется `naming` и `recognize` ## 6. Проверка - [x] 6.1 `task test` и `task lint` зелёные - [ ] 6.2 Ручная проверка на реальном qBittorrent: имя видно в списке; подтвердить поведение `rename` для многофайловой раздачи - [x] 6.3 При архивации: дельта-спека синкнута в `openspec/specs/ingest` (новая capability), пункт «Название из контекста» убран из `docs/todo.md` (реализованное переехало в OpenSpec-спеки)