Files
jellybit/openspec/changes/large-release-recognition/tasks.md
T
av 01ac0430a9 спека: заведён change large-release-recognition для больших раздач
- контракт с моделью переводится на адресацию файла номером строки списка,
  усечение промпта снимается, лимиты уходят в конфиг
- раскладка показывает все файлы раздачи, покрытие плана блокирует авто
  только при непокрытом видеофайле
2026-09-02 08:54:32 +03:00

67 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 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`), а отброшенные элементы и файлы вне плана видны числом в причинах