## 1. Признак обрыва генерации из LLM - [x] 1.1 Довести `finish_reason` до вызывающего: добавить поле в `llm.Response`, заполнить его в `internal/llm/openai.go` (сейчас разбирается и выбрасывается) - [x] 1.2 Тест `internal/llm`: ответ с `finish_reason: "length"` доносит признак обрыва до вызывающего ## 2. Компактный формат ответа модели - [x] 2.1 Добавить `Index` в `recognize.PlanFile` (`i` в JSON), `Src` оставить для запасного формата - [x] 2.2 Переписать схему в `schemaText` (`prompt.go`) под адресацию номером: `i` вместо `src`, явно оговорить, что для ролей `sample`/`ignore`/`extra` номера сезона и серии не нужны, а `notes` — не более пары предложений - [x] 2.3 Поправить шапку списка файлов в `writeFileList`: номер строки — это то, что нужно вернуть в `i` - [x] 2.4 Резолв номера в `validate.go`: при `i` в диапазоне `1..len(in.Files)` заполнять `Src` из своего списка - [x] 2.5 Сохранить приём старого формата: пришёл `src` без `i` — проверять совпадение с реальным файлом торрента, как сегодня - [x] 2.6 Элемент с обоими полями резолвить по `i`; несовпадение присланного `src` с резолвом — причина ревью, а не тихое затирание - [x] 2.7 Отбраковка негодных элементов вместо отказа от плана: номер вне диапазона, повторная адресация файла (побеждает первый элемент), элемент без номера и пути — элемент отбрасывается, план живёт, каждая отбраковка названа причиной; план без единого годного файла считается неразобранным - [x] 2.8 Убедиться, что отбраковка элементов не порождает correction-ретрая (это не ошибка разбора) - [x] 2.9 Тесты `internal/recognize`: резолв номера в путь; номер вне диапазона; дубль адресации; элемент без `i` и `src`; запасной формат с `src`; `i` и `src` вместе — совпадающие и расходящиеся; план, где годных элементов не осталось. Отдельно: резолв не паникует ни на одном входе (`i` = 0, отрицательное, `len+1`, дробное или строковое в JSON, пустой `files[]`, `files[]` длиннее списка раздачи) ## 3. Порядок списка файлов — свойство узла - [x] 3.1 Упорядочивать список файлов по пути внутри `Recognize`, а не у вызывающего: сборки `Input` две — `internal/worker/review.go:126` и `cmd/jellybit/recognize.go:101` - [x] 3.2 Печать промпта и резолв номеров идут по одному и тому же упорядоченному срезу на всех попытках, включая correction-ретрай - [x] 3.3 Тест: `Recognize` с перемешанным `Input` даёт ту же нумерацию, что и с упорядоченным (тест зовёт узел, а не воркер) ## 4. Лимиты в конфиг - [x] 4.1 Добавить `[recognition].max_files` и `[recognition].max_tokens` в `internal/config` с валидацией и дефолтами 500 и 8000 - [x] 4.2 Пробросить их в `recognize.Config` на сборке зависимостей (воркер и CLI) - [x] 4.3 Описать обе настройки в `config.example.toml` (назначение, диапазон, единицы) и в `docs/database.md` (настройки с числовым значением) - [x] 4.4 Тест конфига: дефолты применяются, невалидные значения отвергаются ## 5. Цена попытки не растёт с размером раздачи - [x] 5.1 Убрать повторную печать списка файлов из `correctionMessage` (`prompt.go:180`): номера названы в первом сообщении диалога - [x] 5.2 Обрыв ответа по длине — review с причиной «ответ модели обрезан», без повторного запроса ни тем же промптом, ни его вариантом - [x] 5.3 Усечение списка пределом `max_files` — отдельная причина ухода в review; знаменатель покрытия считается по полному числу файлов раздачи, а не по усечённому списку - [x] 5.4 Отказ модели по размеру запроса называть отдельной причиной, а не текстом провайдера - [x] 5.5 Тесты: correction-ретрай не содержит списка файлов и сопоставим по размеру с первой попыткой; обрыв не порождает ни одного повторного запроса (тест считает вызовы фейкового клиента); снятие лимита не умножает число попыток сверх `[llm].max_retries` ## 6. Свёртка причин и покрытие плана - [x] 6.1 Переписать `seriesWarnings` (`validate.go:186`): одна причина на сезон с перечнем недостающих серий вместо строки на каждый разрыв - [x] 6.2 Завести перечень видеорасширений в одном месте рядом с ролями файлов (в проекте такого места сегодня нет) и покрыть его тестом - [x] 6.3 Покрытие плана: число «в плане N из M» показывается, когда покрыты не все файлы; блокирующей причиной становится только непокрытый видеофайл, файлы-спутники в решение auto/review не входят - [x] 6.4 Тесты: сезон с тремя дырами даёт одну причину; непокрытый видеофайл уводит в review; раздача, где не покрыты только `.nfo` и скриншоты, авто-раскладку не теряет ## 7. Список файлов раздачи рядом с планом - [x] 7.1 Миграция `0012`: колонка со списком файлов раздачи (JSON: путь и размер) в таблице `recognition` - [x] 7.2 Обновить ER-схему в `docs/database.md` — иначе краснеет шаг `canon` гейта - [x] 7.3 Заполнять колонку при создании записи распознавания из того же `Input`, из которого построен план (снимок и `src` плана обязаны происходить из одного среза); читать при рендере страниц загрузки и ревью - [x] 7.4 Записи, созданные до миграции: рендер строит строки по плану и явно называет, что список файлов недоступен и полнота не гарантирована — молчаливой деградации быть не должно ## 8. Раскладка показывает все файлы - [x] 8.1 Развернуть `buildFileRows` (`internal/httpapi/files.go`) на файлы раздачи: план подмешивается по `src`, файл без записи получает пометку «не в плане»; ровно одна строка на файл раздачи - [x] 8.2 Полный ключ сортировки: разложенные (сезон, серия, путь) → файлы плана без цели (`extra`/`sample`/`ignore`, по пути) → файлы вне плана (по пути); пустые `season`/`episode` не поднимают файл в начало списка - [x] 8.3 Отобразить пометку «не в плане» и строку о недоступном списке файлов в шаблонах раскладки в единой дизайн-системе - [x] 8.4 Колонка `#` в `web/templates/partials/layout_widget.html:8` печатает порядковый номер строки — привести её к номеру адресации из списка распознавания либо убрать: расхождение сделает разбор ошибок адресации ложным - [x] 8.5 Тесты `internal/httpapi`: файл вне плана присутствует в строках без целевого пути; порядок строк не зависит от порядка `plan.Files` (тест с перемешанным планом); запись без сохранённого списка файлов даёт строку о недоступности ## 9. Проверка на живом кейсе - [ ] 9.1 `task gate` зелёный - [ ] 9.2 Прогнать распознавание раздачи «Отчаянные домохозяйки» (180 файлов) на рабочем эндпоинте: план покрывает все файлы, причины ревью читаются с одного взгляда, раскладка упорядочена по сезонам - [ ] 9.3 Сверить фактический размер ответа модели с `max_tokens`: подтвердить, что запаса хватает, и при необходимости поправить дефолт - [ ] 9.4 Проверить, что сырой ответ модели и ключ LLM не попадают в лог (`docs/conventions/logging.md`), а отброшенные элементы и файлы вне плана видны числом в причинах