From 66d39297c5de99b42059c77d45b5e85593d57baf Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Thu, 6 Aug 2026 13:32:06 +0300 Subject: [PATCH] =?UTF-8?q?docs:=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=B2=D0=B5=D0=B4=D1=91=D0=BD=20=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=BA=D0=B0=D0=BD=D0=BE=D0=BD=20=D0=B4=D0=BE=D0=BA?= =?UTF-8?q?=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=BE=D0=B2=20=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D1=81=D0=B8=D0=B8=204?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - каталог задач: PLAN.md → ROADMAP.md с каноническими секциями, все 44 записи получили тип, заголовки приведены к форме своего типа - расхождения, найденные судьями канона: исключения инварианта «источник неприкосновенен», инвариант про один активный infohash, UTC в logging.md, поведение из architecture.md заменено ссылками на спеки - триггеры профиля ревью переписаны под умолчание standard --- CLAUDE.md | 36 ++++++-- README.md | 2 +- docs/.pm.json | 2 +- ...-2026-06-13-auto-link-requires-db-match.md | 3 + docs/adr/ADR-2026-06-13-docker-deploy.md | 3 + docs/adr/ADR-2026-06-13-go-single-binary.md | 3 + docs/adr/ADR-2026-06-13-hardlinks.md | 3 + ...R-2026-07-23-review-pipeline-generative.md | 2 + docs/adr/ADR-2026-07-24-local-image-build.md | 2 + docs/architecture.md | 29 ++++--- docs/conventions/config.md | 10 ++- docs/conventions/database.md | 2 +- docs/conventions/errors.md | 2 +- docs/conventions/logging.md | 7 +- docs/passport.md | 12 ++- docs/research/README.md | 6 +- docs/research/torrent-bot-message.md | 4 +- docs/review.md | 38 +++++---- docs/tasks/BACKLOG.md | 82 ++++++++++--------- docs/tasks/PLAN.md | 22 ----- docs/tasks/ROADMAP.md | 34 ++++++++ docs/tasks/items/anime-absolute-numbering.md | 5 +- docs/tasks/items/auto-link-confidence-gate.md | 12 ++- docs/tasks/items/background-error-noise.md | 5 +- docs/tasks/items/candidate-match-strength.md | 5 +- .../items/catched-source-type-refresh.md | 5 +- docs/tasks/items/completion-webhook.md | 5 +- docs/tasks/items/complex-releases.md | 5 +- docs/tasks/items/complex-series-releases.md | 5 +- docs/tasks/items/convention-candidates.md | 5 +- docs/tasks/items/db-retention-cleanup.md | 5 +- .../items/deep-healthcheck-dependencies.md | 5 +- docs/tasks/items/dev-process-quality.md | 5 +- docs/tasks/items/disc-image-releases.md | 5 +- docs/tasks/items/dismiss-marker-lost.md | 5 +- .../items/download-transition-history.md | 5 +- docs/tasks/items/episode-numbering-canon.md | 5 +- docs/tasks/items/external-subtitles.md | 5 +- .../items/free-space-check-copy-fallback.md | 5 +- docs/tasks/items/guessit-sidecar.md | 5 +- .../items/infohash-identity-integrity.md | 5 +- .../items/ingest-and-review-interfaces.md | 5 +- docs/tasks/items/ingest-nits.md | 5 +- .../items/learn-from-user-corrections.md | 5 +- .../items/merge-incremental-redownload.md | 5 +- docs/tasks/items/metadata-cache.md | 5 +- docs/tasks/items/multi-pass-verification.md | 5 +- docs/tasks/items/naming-edge-cases.md | 5 +- .../items/notification-source-binding.md | 5 +- docs/tasks/items/operational-resilience.md | 5 +- docs/tasks/items/quality-review-agents.md | 5 +- docs/tasks/items/recognition-accuracy.md | 5 +- docs/tasks/items/recognition-eval-harness.md | 5 +- docs/tasks/items/review-mapping-editor.md | 5 +- docs/tasks/items/scale-100-downloads.md | 5 +- docs/tasks/items/sqlite-backup.md | 5 +- docs/tasks/items/sse-live-updates.md | 5 +- docs/tasks/items/state-integrity.md | 5 +- docs/tasks/items/telegram-messages-audit.md | 5 +- docs/tasks/items/title-versions-repacks.md | 5 +- docs/tasks/items/torrent-url-fetch.md | 5 +- docs/tasks/items/tvdb-title-locale.md | 5 +- .../items/ubiquitous-language-glossary.md | 5 +- docs/tasks/items/web-ui-auth.md | 5 +- docs/tasks/items/web-ui-pwa.md | 5 +- openspec/specs/live-status/spec.md | 7 +- 66 files changed, 334 insertions(+), 204 deletions(-) delete mode 100644 docs/tasks/PLAN.md create mode 100644 docs/tasks/ROADMAP.md diff --git a/CLAUDE.md b/CLAUDE.md index ef1ade6..23c2dea 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -22,7 +22,9 @@ Driven Development** через OpenSpec — см. раздел ниже. Go 1.26, один статический бинарь (`CGO_ENABLED=0`). Module path — `git.vakhrushev.me/av/jellybit`. SQLite через `modernc.org/sqlite` + `sqlx`, миграции `goose`, HTTP — `chi` + `html/template` + htmx, конфиг — -`pelletier/go-toml/v2`, логи — `log/slog` (структурированный JSON). +`pelletier/go-toml/v2`, разбор `.torrent` и инфохэшей — `anacrolix/torrent`, +пред-парс имени раздачи — `middelink/go-parse-torrent-name`, логи — `log/slog` +(структурированный JSON). ## Инварианты @@ -32,6 +34,15 @@ Go 1.26, один статический бинарь (`CGO_ENABLED=0`). Module - **Источник неприкосновенен** — под `paths.downloads` допустимы только чтение и `link(2)`; никаких `unlink`, `rename`, записи. Нарушение уничтожает невосстановимые данные пользователя. **Необратимо. `critical`.** + Исключения два, и оба — не наши операции с файловой системой, а вызов + `torrents/delete` qBittorrent с `deleteFiles=true`: (1) `Delete` из + `done`/`orphaned`/`target_missing` по явному подтверждению человека — гард + последней копии там выключен сознательно + ([state-reconciliation](openspec/specs/state-reconciliation/spec.md)); + (2) уборка воркером **собственного** торрента, добавленного этим же `add` + секундами ранее, когда задачу отменили в окне после `add` + ([download-tracking](openspec/specs/download-tracking/spec.md)). Всё + остальное под `paths.downloads` — по-прежнему `critical`. - **Последняя копия не снимается** — `Undo` отклоняется целиком, если у цели не осталось других жёстких ссылок (`nlink <= 1`) или исходного файла уже нет. Частичный откат тоже стёр бы часть данных. **Необратимо. `critical`.** @@ -47,14 +58,24 @@ Go 1.26, один статический бинарь (`CGO_ENABLED=0`). Module qBittorrent, ключи LLM и метабаз, токен Telegram, API-ключ Jellyfin. Утёкший в лог секрет отзывается вручную. **`major`.** - **Авто-раскладка только при подтверждённом матче в метабазе** — самооценка - LLM гейтом не является - ([ADR](docs/adr/ADR-2026-06-13-auto-link-requires-db-match.md)). Обратимо + LLM **единственным** гейтом не является и матч не заменяет: порог + `[recognition].auto_confidence_threshold` стоит поверх матча дополнительным + условием + ([ADR](docs/adr/ADR-2026-06-13-auto-link-requires-db-match.md), + [recognition](openspec/specs/recognition/spec.md)). Обратимо через `Undo`. **`major`.** +- **Не более одной активной загрузки на infohash** — проверка отсутствия другой + активной загрузки и вставка идут одной write-транзакцией (`_txlock=immediate`, + guarded-методы `store`); обход даёт две задачи, претендующие на одну раздачу и + один целевой путь. Обратимо (лишняя закрывается), но состояние расходится. + **`major`.** Поведение — [ingest](openspec/specs/ingest/spec.md). - **Переходы состояний — только через `worker` под per-download блокировкой**, и только легальные по декларативному графу. Обход даёт гонку двух транспортов. **`major`.** - **Время — только `store.Now()` (UTC), идентификаторы — только `ident`**; - `ident.Parse` на каждой входной границе. Механизировано линтером. **`minor`.** + `ident.Parse` на каждой входной границе. Время механизировано линтером + (`forbidigo` на `time.Now`); правило про `ident` линтером не проверяется — + держится на ревью. **`minor`.** ## Команды @@ -117,9 +138,10 @@ Go 1.26, один статический бинарь (`CGO_ENABLED=0`). Module (`git merge-base HEAD master`), в неё вливает батч, от неё ветвятся задачи. - **Необратимое** (спрашивается у человека всегда): всё, что пишет в `paths.downloads` или удаляет оттуда; удаление раздачи из qBittorrent вместе - с файлами (`Delete`); снятие последней копии данных; правка уже применённой - миграции; `git push --force`; удаление или перезапись файла в библиотеке - Jellyfin, которого мы не создавали. + с файлами (`Delete`) — кроме уборки собственного, только что добавленного + торрента при отмене (см. исключения инварианта выше); снятие последней копии + данных; правка уже применённой миграции; `git push --force`; удаление или + перезапись файла в библиотеке Jellyfin, которого мы не создавали. - **Общий станок** — покрасневший `task gate` на `master` врывается в замороженный спринт: пока он красный, ни одна задача не считается сделанной. - **Ориентир по размеру спринта:** 5–8 задач. Ориентир, а не закон. diff --git a/README.md b/README.md index 771373b..7d0a004 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ TMDB/TVDB/TVMaze) → раскладка в библиотеку хардлин REST API, веб-UI, Telegram-бот и CLI (`jellybit add`). Из источников поддержаны magnet и `.torrent`-файл; фетч `.torrent` по обычной -ссылке — в планах. Что дальше — [docs/tasks/PLAN.md](docs/tasks/PLAN.md). +ссылке — в планах. Что дальше — [docs/tasks/ROADMAP.md](docs/tasks/ROADMAP.md). ## Документация diff --git a/docs/.pm.json b/docs/.pm.json index 29317f1..d8bb38b 100644 --- a/docs/.pm.json +++ b/docs/.pm.json @@ -1,4 +1,4 @@ { - "canon": 2, + "canon": 4, "migrations": "internal/store/migrations" } diff --git a/docs/adr/ADR-2026-06-13-auto-link-requires-db-match.md b/docs/adr/ADR-2026-06-13-auto-link-requires-db-match.md index a8eca36..b152db6 100644 --- a/docs/adr/ADR-2026-06-13-auto-link-requires-db-match.md +++ b/docs/adr/ADR-2026-06-13-auto-link-requires-db-match.md @@ -1,6 +1,9 @@ # Авто-раскладка только при подтверждённом матче в метабазе - **Дата:** 2026-06-13 +- **Источник:** архивного `design.md` нет — решение принято до перехода + проекта на OpenSpec (первый архивный change — `2026-06-28`); первичный + материал — история git ## Контекст diff --git a/docs/adr/ADR-2026-06-13-docker-deploy.md b/docs/adr/ADR-2026-06-13-docker-deploy.md index 3431c1d..7809937 100644 --- a/docs/adr/ADR-2026-06-13-docker-deploy.md +++ b/docs/adr/ADR-2026-06-13-docker-deploy.md @@ -1,6 +1,9 @@ # Docker как единица деплоя, образ собирается на сервере - **Дата:** 2026-06-13 +- **Источник:** архивного `design.md` нет — решение принято до перехода + проекта на OpenSpec (первый архивный change — `2026-06-28`); первичный + материал — история git - **Статус:** заменено на ADR-2026-07-24-local-image-build ## Контекст diff --git a/docs/adr/ADR-2026-06-13-go-single-binary.md b/docs/adr/ADR-2026-06-13-go-single-binary.md index c2a7558..110cb11 100644 --- a/docs/adr/ADR-2026-06-13-go-single-binary.md +++ b/docs/adr/ADR-2026-06-13-go-single-binary.md @@ -1,6 +1,9 @@ # Go и доставка одним бинарём - **Дата:** 2026-06-13 +- **Источник:** архивного `design.md` нет — решение принято до перехода + проекта на OpenSpec (первый архивный change — `2026-06-28`); первичный + материал — история git ## Контекст diff --git a/docs/adr/ADR-2026-06-13-hardlinks.md b/docs/adr/ADR-2026-06-13-hardlinks.md index 4195697..e9ece61 100644 --- a/docs/adr/ADR-2026-06-13-hardlinks.md +++ b/docs/adr/ADR-2026-06-13-hardlinks.md @@ -1,6 +1,9 @@ # Хардлинки вместо копирования и симлинков - **Дата:** 2026-06-13 +- **Источник:** архивного `design.md` нет — решение принято до перехода + проекта на OpenSpec (первый архивный change — `2026-06-28`); первичный + материал — история git ## Контекст diff --git a/docs/adr/ADR-2026-07-23-review-pipeline-generative.md b/docs/adr/ADR-2026-07-23-review-pipeline-generative.md index 317d910..9dc6581 100644 --- a/docs/adr/ADR-2026-07-23-review-pipeline-generative.md +++ b/docs/adr/ADR-2026-07-23-review-pipeline-generative.md @@ -1,6 +1,8 @@ # Конвейер ревью: гейт, generative-проходы и обязательный триаж - **Дата:** 2026-07-23 +- **Источник:** архивного `design.md` нет — решение процессное, change'ем jellybit + не велось ## Контекст diff --git a/docs/adr/ADR-2026-07-24-local-image-build.md b/docs/adr/ADR-2026-07-24-local-image-build.md index 5865031..4aa4078 100644 --- a/docs/adr/ADR-2026-07-24-local-image-build.md +++ b/docs/adr/ADR-2026-07-24-local-image-build.md @@ -1,6 +1,8 @@ # Образ собирается локально и едет на сервер через docker save/load - **Дата:** 2026-07-24 +- **Источник:** архивного `design.md` нет — решение деплойное, change'ем jellybit + не велось ## Контекст diff --git a/docs/architecture.md b/docs/architecture.md index 46c9fd1..de42cc2 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -53,16 +53,20 @@ retry / delete / dismiss) идут командами к `worker`. ## Внешние границы и форматы -- **qBittorrent WebUI API** — единственный способ качать: источник (magnet, URL, - `.torrent`) **отдаём ему**, сами по пользовательскому URL не ходим (SSRF - исключён). Пути берём из API (`save_path` + относительные имена из - `/torrents/files`), не из константы. -- **LLM** — OpenAI-совместимый Chat Completions (`[llm].type = "openai-compat"`), - структурированный вывод через `response_format: json_object`; валидация ответа - своя, в Go. +- **qBittorrent WebUI API** — единственный способ качать: источник **отдаём + ему**, сами по пользовательскому URL не ходим (SSRF исключён). Какие виды + источника принимаются и как разбираются — + [ingest](../openspec/specs/ingest/spec.md); способ добавления по + `source_type` — [download-tracking](../openspec/specs/download-tracking/spec.md); + откуда берутся пути файлов — + [file-layout](../openspec/specs/file-layout/spec.md). +- **LLM** — OpenAI-совместимый Chat Completions за интерфейсом (`[llm].type`); + контракт вызова, формат вывода и разбор ответа — + [recognition](../openspec/specs/recognition/spec.md). - **Метабазы** — TMDB, TVDB, TVMaze (последняя без ключа, только сериалы). -- **Jellyfin** — один вызов `POST /Library/Refresh`, авторизация заголовком - `X-Emby-Token`. +- **Jellyfin** — HTTP-триггер пересканирования медиатеки, опционален; когда он + дёргается и по каким переходам — + [file-layout](../openspec/specs/file-layout/spec.md). - **Telegram Bot API** — приём сообщений и исходящие карточки/пинги. - **Сообщение торрент-бота** — чужой текстовый формат, разбирается парсером `tgbot`; наблюдения по формату — в @@ -90,8 +94,9 @@ retry / delete / dismiss) идут командами к `worker`. задаче, застрявшей в промежуточном состоянии; логи в stdout контейнера. Автоматического алертинга нет, метрик нет — только уведомления в Telegram о падении загрузки и о рассинхроне. -- **Характер потока:** непрерывный фон (тик поллинга qBittorrent, по умолчанию - 5 с, и периодическая сверка) плюс редкие события по запросу человека. Объём — +- **Характер потока:** непрерывный фон (тик поллинга qBittorrent — период в + [database.md](database.md) → «Настройки с числовым значением» — и + периодическая сверка) плюс редкие события по запросу человека. Объём — единицы загрузок в день, десятки одновременно; ориентир масштаба и его аудит — задача в беклоге. @@ -101,7 +106,7 @@ retry / delete / dismiss) идут командами к `worker`. | Что | Где | | --- | --- | -| Время | `store.Now()` — единственный источник, всегда UTC; формат хранения — RFC 3339 | +| Время | `store.Now()` — единственный источник меток времени в данных, всегда UTC; формат хранения — RFC 3339. Вторая санкционированная точка wall-clock — timestamp-часть ULID в `ident.NewID` (исключение в `.golangci.yml`) | | Идентификаторы | `internal/ident` — генерация и нормализация ULID; `ident.Parse` на каждой входной границе | | Целевые имена и превью раскладки | `internal/naming` — одна логика для превью в UI и для реального применения | | Разбор источника | `internal/magnet` и `internal/torrent`; инфохэш извлекается только здесь | diff --git a/docs/conventions/config.md b/docs/conventions/config.md index 01f2a1c..3e8fceb 100644 --- a/docs/conventions/config.md +++ b/docs/conventions/config.md @@ -6,9 +6,6 @@ Краткая выжимка и инварианты — в [CLAUDE.md](../../CLAUDE.md), раздел «Конвенции кода». -> Каркас. Загрузчик `internal/config/config.go` уже грузит TOML; валидация -> на старте — в работе (`TODO`), обкатывается на следующем шаге. - ## Принципы - **Конфигурация — только TOML.** Env-переменные для конфига **не @@ -41,7 +38,7 @@ ```toml [worker] poll_interval = "5s" # как часто опрашивать qBittorrent; Go-duration (s/m/h) -magnet_timeout = "30m" # ждать метаданные magnet не дольше; Go-duration +magnet_timeout = "24h" # ждать метаданные magnet не дольше; Go-duration source_missing_threshold = 3 # тиков сверки без раздачи, чтобы счесть источник удалённым [recognition] @@ -51,6 +48,11 @@ auto_confidence_threshold = 0.85 # порог авто-раскладки бе max_retries = 3 # попыток получить валидный ответ LLM; целое ≥ 0 ``` +Значения в примере — иллюстрация формы комментария. Действующие умолчания и их +смысл живут одним домом — таблица «Настройки с числовым значением» в +[../database.md](../database.md); `config.example.toml` — источник истины по +составу полей. + Секретные поля оставляем пустыми — значение приходит из деплоя (см. «Секреты»). diff --git a/docs/conventions/database.md b/docs/conventions/database.md index f5e4922..6479cc7 100644 --- a/docs/conventions/database.md +++ b/docs/conventions/database.md @@ -2,7 +2,7 @@ Как мы устраиваем таблицы и ключи в SQLite. Актуальная схема — [../database.md](../database.md); обоснование выбора ULID — -`openspec/changes/ulid-identity/design.md` (после архивации — в истории git). +[архивный design.md change'а `ulid-identity`](../../openspec/changes/archive/2026-07-02-ulid-identity/design.md). **Механизировано:** `AUTOINCREMENT` и `DEFAULT (datetime('now'))` в новых миграциях (`internal/archrules`), время мимо `store.Now()` (`forbidigo`). diff --git a/docs/conventions/errors.md b/docs/conventions/errors.md index 6886863..4a4cb84 100644 --- a/docs/conventions/errors.md +++ b/docs/conventions/errors.md @@ -97,7 +97,7 @@ jellybit — **приложение, а не библиотека**: внешн в review/failed: коллизия, рассинхрон, сбой ФС) и `reasons` распознавания, сохранённые в БД и показываемые на экране ревью и в Telegram-карточке. Это **операторская поверхность владельца**: сервис однопользовательский в - доверенной LAN (см. [architecture.md](../architecture.md)), эти поля — + доверенном контуре (см. [security.md](../security.md) → «Периметр»), эти поля — диагностический контекст для того, кто разбирает задачу. Здесь сырой текст ошибки (пути, фрагмент ответа LLM/qBittorrent) **допустим и полезен** — но: - **секреты запрещены** абсолютно (токены/ключи/пароли/`Authorization`) — так diff --git a/docs/conventions/logging.md b/docs/conventions/logging.md index f14e8bb..f147413 100644 --- a/docs/conventions/logging.md +++ b/docs/conventions/logging.md @@ -74,9 +74,10 @@ OpenSpec-спеках (`### Requirement` с `SHALL`). - Поле — `time` (ключ по умолчанию `slog`). - UTC, RFC 3339 с долями секунды, суффикс `Z`: `2026-06-28T11:23:45.123456Z`. -- Логи — **в UTC** (это явный TZ, не нарушает инвариант проекта): даёт - однозначный порядок событий и лексикографическую сортировку. Бизнес-логика - по-прежнему работает в `Europe/Moscow` — UTC только в логах. +- Логи — **в UTC**, как и хранение в БД: это даёт однозначный порядок событий и + лексикографическую сортировку. Часовой пояс есть только у **отображения** в + веб-UI (`[general].timezone`, дефолт `UTC`) — см. + [database.md](../database.md); бизнес-логика в локальной зоне не работает. ## Поля: словарь имён diff --git a/docs/passport.md b/docs/passport.md index b828744..92813d1 100644 --- a/docs/passport.md +++ b/docs/passport.md @@ -1,7 +1,7 @@ # Паспорт проекта Зачем это и для кого. [architecture.md](architecture.md) отвечает «как -устроено», [tasks/PLAN.md](tasks/PLAN.md) — «в каком порядке», паспорт — +устроено», [tasks/ROADMAP.md](tasks/ROADMAP.md) — «что уже умеет», паспорт — «зачем и для кого». ## Цель @@ -44,8 +44,14 @@ Jellyfin, без ручного переименования и без ката - **Не медиасервер.** Обложки, метаданные, учёт просмотренного и сам просмотр — забота Jellyfin. Мы отвечаем только за то, чтобы файл лежал там, где Jellyfin его правильно опознает. -- **Не хранилище медиа.** Данные живут в раздаче; мы создаём только хардлинки и - не владеем ни одним байтом контента. +- **Не хранилище медиа.** Данные живут в раздаче, мы создаём хардлинки и не + дублируем контент намеренно. Два исключения хранилищем нас не делают, но байты + у нас появляются: copy-fallback, когда хардлинк невозможен + ([file-layout](../openspec/specs/file-layout/spec.md)), и состояние + `orphaned`, где библиотечная ссылка осталась последней копией + ([state-reconciliation](../openspec/specs/state-reconciliation/spec.md)). + Сохранность мы и в этих случаях на себя не берём — резервных копий медиа у нас + нет. - **Не мультипользовательский сервис.** Контур один, оператор один; разграничение доступа сводится к allowlist Telegram (см. [security.md](security.md)). diff --git a/docs/research/README.md b/docs/research/README.md index 985dc0d..1608006 100644 --- a/docs/research/README.md +++ b/docs/research/README.md @@ -17,9 +17,9 @@ LLM-эндпоинта на живых раздачах. Автоматичес это отдельная задача (eval-харнес распознавания), до неё числа здесь единичные и приведены как условия, а не как статистика. -Зафиксированные образцы чужих форматов лежат прямо в тестах пакета-разборщика -(`internal/tgbot/parse_test.go`, `internal/magnet`, `internal/torrent`) — там -они заодно и проверяются; каталог `testdata/` под них не заводился. +Зафиксированные образцы чужих форматов лежат прямо в тестах пакета-разборщика — +там они заодно и проверяются; где именно и почему без каталога `testdata/`, см. +[CLAUDE.md](../../CLAUDE.md) → «Запреты». ## Записи diff --git a/docs/research/torrent-bot-message.md b/docs/research/torrent-bot-message.md index 5b13ff1..edce433 100644 --- a/docs/research/torrent-bot-message.md +++ b/docs/research/torrent-bot-message.md @@ -72,4 +72,6 @@ cправка: /help, index (https://exfreedomist.com/stats/) - Как выглядит сообщение для сериала-сезонника и для аниме — образцов не снимали, а именно на них строится самый сложный случай раскладки. - Что бот присылает при неудачном поиске и при слишком длинном описании - (обрезка Telegram — 4096 символов на сообщение). + (лимит Telegram на текст сообщения — 4096 символов UTF-16, по документации + Bot API `sendMessage`; сами обрезанные сообщения мы не наблюдали, поведение + бота на обрезке неизвестно). diff --git a/docs/review.md b/docs/review.md index a118e65..29d63a3 100644 --- a/docs/review.md +++ b/docs/review.md @@ -70,10 +70,12 @@ Go-сервиса и что здесь уже проскакивало. Устр снаружи LAN. - **«Ошибка на htmx-пути возвращает 200».** Так и задумано — [conventions/web-ui.md](conventions/web-ui.md). -- **«Решение auto/review должно опираться на `confidence` модели».** Наоборот: - авто только при подтверждённом матче в базе — +- **«Решение auto/review должно опираться на `confidence` модели».** Неверно в + форме «вместо матча»: авто только при подтверждённом матче в базе — [ADR-2026-06-13-auto-link-requires-db-match](adr/ADR-2026-06-13-auto-link-requires-db-match.md). - Самооценка LLM плохо откалибрована и поддаётся инъекции. + Самооценка LLM плохо откалибрована и поддаётся инъекции. Порог `confidence` + при этом существует **дополнительным** блокирующим условием поверх матча — + его наличие дефектом не является. - **«Копировать надёжнее, чем хардлинк» / «взять симлинк».** Хардлинк — осознанный выбор ради неприкосновенности источника и недублирования диска, [ADR-2026-06-13-hardlinks](adr/ADR-2026-06-13-hardlinks.md); copy — только @@ -129,19 +131,25 @@ Go-сервиса и что здесь уже проскакивало. Устр ### Триггеры профиля -Уточняет умолчания конвейера, не отменяет их. +Уточняет умолчания конвейера, не отменяет их. Рабочее умолчание — `standard`; +миграция схемы и публичный контракт ступень **не** поднимают: их проверяют +проходы, которые в `standard` и так есть. + +**Новым понятием или структурной единицей здесь считается** (→ `wide`): новый +пакет `internal/*`; новая capability в `openspec/specs/`; новый транспорт приёма +или уведомлений рядом с REST, веб-UI, ботом и CLI; новый провайдер метабазы за +существующим интерфейсом; новое состояние в графе переходов загрузки; перенос +ответственности между `worker`, `recognition`, `layout` и `store`. + +**Правила идентичности, слияния и разбора живут здесь** (→ `deep`): владение +раздачей по инфохэшу и сверка с qBittorrent (`ident`, `internal/store`, +`state-reconciliation`); построение целевых путей и санитизация имён +(`internal/layout`, `naming`); разбор недоверенного входа — bencode, magnet, +текст контекста, ответ LLM (`internal/torrent`, `internal/magnet`, +`internal/tgbot/parse.go`, разбор ответа модели); выбор кандидата метабазы и +слияние его полей с догадкой LLM (`internal/metadata`, `metadata-match`); +merge-раскладка при повторном добавлении раздачи. -- **`deep`** — есть миграция в `internal/store/migrations/`; появляется новый - пакет `internal/*`; меняется сигнатура публичной команды воркера; трогается - раскладка файлов, построение целевых путей или удаление ссылок; трогается - разбор недоверенного входа. -- **`standard`** — меняется поведение, видимое снаружи: REST-эндпоинт, - htmx-путь, набор или семантика состояний загрузки, формат сообщения бота, - поле конфига. -- **`quick`** — всё остальное: локальный багфикс, документация, тесты. -- **Независимая реализация (`reimpl`)** запускается, когда узел одновременно - новый и имеет внешний оракул в виде спеки: новый парсер, новый провайдер за - существующим интерфейсом, новая стадия конвейера распознавания. - «Поведение, видимое снаружи» здесь включает **тексты и карточки Telegram** — для единственного пользователя это и есть интерфейс. diff --git a/docs/tasks/BACKLOG.md b/docs/tasks/BACKLOG.md index 2d4f499..5ac3a12 100644 --- a/docs/tasks/BACKLOG.md +++ b/docs/tasks/BACKLOG.md @@ -1,8 +1,8 @@ # Беклог Что **можно взять**. Одна задача = один файл `items/.md` -+ строка здесь. Целей тут нет — они в [PLAN.md](PLAN.md): беклог — то, что берут, -план — то, подо что берут. Порядка внутри секции нет: «что делать ++ строка здесь. Целей тут нет — они в [ROADMAP.md](ROADMAP.md): беклог — то, что +берут, роадмап — то, подо что берут. Порядка внутри секции нет: «что делать дальше» отвечает набор спринта. Ведётся скиллом `tasks`. Секции «блокеры» здесь нет и не заводится: блокер — это состояние @@ -10,43 +10,45 @@ человека, а его следы — вопросами в файлах задач. ## Ядро продукта -- [Аниме с абсолютной нумерацией](items/anime-absolute-numbering.md) — аниме со сквозной нумерацией (#137) не раскладывается в SxxEyy, который ждёт Jellyfin — нужен пересчёт абсолютной нумерации -- [`addReq` не пересобирается из свежего `source_type` перед `Add` (окно namer'а)](items/catched-source-type-refresh.md) — При апгрейде magnet→.torrent в окне namer'а добавится magnet из устаревшего снимка; самоисцеляется через magnet_timeout→failed→Retry _(аудит 2026-07-17)_ -- [Раздачи-копии диска (DVD/BluRay: VIDEO_TS/BDMV)](items/disc-image-releases.md) — раздача-образ диска (VIDEO_TS/BDMV) сейчас разбирается пофайлово вместо раскладки каталога целиком — редкий, но реальный случай -- [Веб-UI зовёт Cancel вместо Dismiss на не-терминальных → теряется `user_dismiss`](items/dismiss-marker-lost.md) — Функционально ок (Cancel даёт cancelled), но маркер user_dismiss в error_code теряется; расхождение с буквой спеки _(аудит 2026-07-17)_ -- [Фетч .torrent по URL — остаток «единого окна»](items/torrent-url-fetch.md) — magnet и .torrent-файл приняты; остался фетч .torrent по URL (нужен SSRF-гард) -- [Confidence-гейт авто-раскладки: узаконить в спеке + сделать выключаемым (дефолт 0.7)](items/auto-link-confidence-gate.md) — Решено (B): гейт оставляем как доп. проверку на ревью — выключаемый порог, дефолт 0.85→0.7, записать в спеку -- [[idea] guessit как сервис-спутник](items/guessit-sidecar.md) — go-ptn слабее питоновского guessit — если точности пред-парса не хватит, завернуть guessit в сервис-спутник рядом с бинарём -- [Согласование канона нумерации серий с провайдером тега](items/episode-numbering-canon.md) — Косметика/редкость: порядок просмотра ок, но у тайтлов со спорным порядком (Бибоп) Jellyfin подтягивает не те подписи серий, если канон файлов ≠ дефолтный порядок провайдера тега -- [Раздачи с докачиванием (merge при повторном добавлении)](items/merge-incremental-redownload.md) — повторная заливка сериала целиком должна доложить недостающие эпизоды merge-раскладкой, не трогая существующие ссылки — блокирует типовой сценарий свежих сериалов -- [[idea] Многоступенчатая верификация привязки](items/multi-pass-verification.md) — несколько проходов распознавания с консенсусом подняли бы точность ценой стоимости/латентности — проработать, когда включать и как мерджить расхождения -- [Обучение на правках человека (few-shot из прошлых ревью)](items/learn-from-user-corrections.md) — правки человека (матч/тип/нумерация) не переиспользуются — few-shot из прошлых ревью поднял бы точность на «своих» трекерах без смены модели -- [Идентичность инфохэшей: split v1/v2 одного торрента + крафт-магнет отравляет владение (F4, F5)](items/infohash-identity-integrity.md) — split v1/v2 идентичность и крафт-магнет отравляют владение инфохэшами _(ревью 2026-07-08)_ -- [Нити приёма: NoName в контексте, устаревшие комментарии, лог без причины, bencode-аллокации (N1, N3, N4, N5)](items/ingest-nits.md) — косметика приёма: NoName в контексте, устаревшие комментарии, лог, bencode-аллокации _(ревью 2026-07-08)_ -- [[idea] Сила совпадения кандидата и пересмотр распознавания/матчинга](items/candidate-match-strength.md) — у кандидата метабазы нет метрики силы совпадения — список кандидатов на ревью нечем отсортировать по уверенности (сперва проработать процесс матчинга) -- [[idea] Сложные сериальные раздачи: все сезоны разом, паки, спецраскладки](items/complex-series-releases.md) — сложные раздачи (все сезоны разом, паки, смешанная нумерация) целостно не проработаны — распознавание/ревью/раскладка заточены под один сезон -- [Мгновенные обновления через SSE](items/sse-live-updates.md) — живые обновления на htmx-поллинге дают задержку и холостые запросы — SSE убрал бы то и другое (поллинг работает, поэтому улучшение, не блокер) -- [Проверка свободного места перед copy-fallback](items/free-space-check-copy-fallback.md) — copy-fallback при невозможности хардлинка может упереться в переполненный диск посреди раскладки — нет проверки места до копирования -- [Ревью уведомлений в Telegram (аудит текстов и формата)](items/telegram-messages-audit.md) — зонтичный проход по всем текстам бота: полнота карточек, единый язык, оформление; порождает под-задачи -- [Привязка уведомлений к источнику в ботах (мульти-бот)](items/notification-source-binding.md) — пинги и ревью должен получать автор загрузки в своём транспорте — нет привязки загрузки к источнику/отправителю (нужно для мульти-бота) -- [Версии/качество одного тайтла (репаки, апгрейд 1080p → 2160p)](items/title-versions-repacks.md) — По калибровке болей (2026-07-02) — не боль, из приоритета выпало -- [Внешние субтитры: пары VobSub и языковой суффикс](items/external-subtitles.md) — Привязка субтитр→серия уже работает; остались пары VobSub .idx+.sub и потеря Lang/Flags -- [Современный Web-UI как PWA](items/web-ui-pwa.md) — текущий server-rendered UI функционален — PWA (устанавливаемое, удобное с телефона) это улучшение большого объёма, не блокер -- [Полный редактор маппинга «файл → серия» и ручной режим ревью](items/review-mapping-editor.md) — правка S·E, «нумеровать подряд» и ручной режим при полном провале LLM были запланированы объёмом Ф5 и не заведены задачей — в ревью сегодня можно только подсказать текстом -- [Крайние случаи именования: многофайловый фильм, редакции, двойная серия](items/naming-edge-cases.md) — стэкинг частей (part1/cd1), редакции [edition-…] и двойная серия SxxEyy-Eyy описаны нарративом, но в file-layout не заказаны — раскладка таких раздач не определена -- [Локаль TVDB: перевод названия и оригинальное название](items/tvdb-title-locale.md) — [general].language правит только TMDB и промпт LLM — TVDB отдаёт primary name, и при language=ru в карточку ревью и имя папки попадает 哪吒之魔童降世 вместо «Нэчжа» + +- [✨ Пересчитывать абсолютную нумерацию аниме в SxxEyy](items/anime-absolute-numbering.md) — аниме со сквозной нумерацией (#137) не раскладывается в SxxEyy, который ждёт Jellyfin — нужен пересчёт абсолютной нумерации +- [🐞 Пересобирать `addReq` из свежего `source_type` перед `Add` (окно namer'а)](items/catched-source-type-refresh.md) — При апгрейде magnet→.torrent в окне namer'а добавится magnet из устаревшего снимка; самоисцеляется через magnet_timeout→failed→Retry _(аудит 2026-07-17)_ +- [✨ Раскладывать раздачу-копию диска (VIDEO_TS/BDMV) каталогом целиком](items/disc-image-releases.md) — раздача-образ диска (VIDEO_TS/BDMV) сейчас разбирается пофайлово вместо раскладки каталога целиком — редкий, но реальный случай +- [🐞 Не терять маркер `user_dismiss` при закрытии не-терминальной загрузки из веб-UI](items/dismiss-marker-lost.md) — Функционально ок (Cancel даёт cancelled), но маркер user_dismiss в error_code теряется; расхождение с буквой спеки _(аудит 2026-07-17)_ +- [✨ Принимать ссылку на .torrent и скачивать файл самим (нужен SSRF-гард)](items/torrent-url-fetch.md) — magnet и .torrent-файл приняты; остался фетч .torrent по URL (нужен SSRF-гард) +- [✨ Узаконить confidence-гейт авто-раскладки в спеке и сделать его выключаемым (дефолт 0.7)](items/auto-link-confidence-gate.md) — Решено (B): гейт оставляем как доп. проверку на ревью — выключаемый порог, дефолт 0.85→0.7, записать в спеку +- [✨ Докладывать недостающие эпизоды merge-раскладкой при повторной заливке](items/merge-incremental-redownload.md) — повторная заливка сериала целиком должна доложить недостающие эпизоды merge-раскладкой, не трогая существующие ссылки — блокирует типовой сценарий свежих сериалов +- [✨ Подмешивать прошлые правки человека в промпт распознавания (few-shot)](items/learn-from-user-corrections.md) — правки человека (матч/тип/нумерация) не переиспользуются — few-shot из прошлых ревью поднял бы точность на «своих» трекерах без смены модели +- [🐞 Связать v1/v2-хеши одного торрента и не доверять паре xt из магнета (F4, F5)](items/infohash-identity-integrity.md) — split v1/v2 идентичность и крафт-магнет отравляют владение инфохэшами _(ревью 2026-07-08)_ +- [🧹 Закрыть мелочи приёма: NoName в контексте, устаревшие комментарии, лог без причины, bencode-аллокации (N1, N3, N4, N5)](items/ingest-nits.md) — косметика приёма: NoName в контексте, устаревшие комментарии, лог, bencode-аллокации _(ревью 2026-07-08)_ +- [✨ Слать живые обновления через SSE вместо htmx-поллинга](items/sse-live-updates.md) — живые обновления на htmx-поллинге дают задержку и холостые запросы — SSE убрал бы то и другое (поллинг работает, поэтому улучшение, не блокер) +- [✨ Проверять свободное место перед copy-fallback](items/free-space-check-copy-fallback.md) — copy-fallback при невозможности хардлинка может упереться в переполненный диск посреди раскладки — нет проверки места до копирования +- [✨ Слать уведомления автору загрузки в его транспорт (мульти-бот)](items/notification-source-binding.md) — пинги и ревью должен получать автор загрузки в своём транспорте — нет привязки загрузки к источнику/отправителю (нужно для мульти-бота) +- [✨ Заменять разложенную версию тайтла новой (репаки, апгрейд 1080p → 2160p)](items/title-versions-repacks.md) — По калибровке болей (2026-07-02) — не боль, из приоритета выпало +- [✨ Спаривать VobSub .idx+.sub и проставлять языковой суффикс субтитра](items/external-subtitles.md) — Привязка субтитр→серия уже работает; остались пары VobSub .idx+.sub и потеря Lang/Flags +- [✨ Переделать веб-UI в устанавливаемое PWA](items/web-ui-pwa.md) — текущий server-rendered UI функционален — PWA (устанавливаемое, удобное с телефона) это улучшение большого объёма, не блокер +- [✨ Править на ревью маппинг «файл → серия» и раскладывать вручную при провале LLM](items/review-mapping-editor.md) — правка S·E, «нумеровать подряд» и ручной режим при полном провале LLM были запланированы объёмом Ф5 и не заведены задачей — в ревью сегодня можно только подсказать текстом +- [✨ Заказать спекой крайние случаи именования: многофайловый фильм, редакции, двойная серия](items/naming-edge-cases.md) — стэкинг частей (part1/cd1), редакции [edition-…] и двойная серия SxxEyy-Eyy описаны нарративом, но в file-layout не заказаны — раскладка таких раздач не определена +- [✨ Брать у TVDB название на языке настройки и оригинальное название](items/tvdb-title-locale.md) — [general].language правит только TMDB и промпт LLM — TVDB отдаёт primary name, и при language=ru в карточку ревью и имя папки попадает 哪吒之魔童降世 вместо «Нэчжа» +- [🔬 Канон нумерации серий и порядок у провайдера тега](items/episode-numbering-canon.md) — Косметика/редкость: порядок просмотра ок, но у тайтлов со спорным порядком (Бибоп) Jellyfin подтягивает не те подписи серий, если канон файлов ≠ дефолтный порядок провайдера тега +- [🔬 Тексты и формат уведомлений в Telegram](items/telegram-messages-audit.md) — зонтичный проход по всем текстам бота: полнота карточек, единый язык, оформление; порождает под-задачи +- [🔬 guessit как сервис-спутник](items/guessit-sidecar.md) — go-ptn слабее питоновского guessit — если точности пред-парса не хватит, завернуть guessit в сервис-спутник рядом с бинарём +- [🔬 Многоступенчатая верификация привязки](items/multi-pass-verification.md) — несколько проходов распознавания с консенсусом подняли бы точность ценой стоимости/латентности — проработать, когда включать и как мерджить расхождения +- [🔬 Сила совпадения кандидата и пересмотр распознавания/матчинга](items/candidate-match-strength.md) — у кандидата метабазы нет метрики силы совпадения — список кандидатов на ревью нечем отсортировать по уверенности (сперва проработать процесс матчинга) +- [🔬 Сложные сериальные раздачи: все сезоны разом, паки, спецраскладки](items/complex-series-releases.md) — сложные раздачи (все сезоны разом, паки, смешанная нумерация) целостно не проработаны — распознавание/ревью/раскладка заточены под один сезон ## Инфраструктура -- [Агенты-ревьюверы качества (наименования, архитектура, конвенции, стиль)](items/quality-review-agents.md) — конвейер ревью переехал в плагин `av-dev-pipeline`; осталась калибровка проходов на этом проекте и ревьювер наименований (ждёт словарь единого языка) -- [Авторизация веб-UI (на будущее)](items/web-ui-auth.md) — для v1 решено без авторизации (доверенная LAN, опц. allowlist подсетей) — задел на случай, если понадобится защита -- [Бэкап SQLite](items/sqlite-backup.md) — architecture требует бекапить data-том, но стратегия не описана — сбой или редеплой стирают всё in-flight состояние (проще, пока БД маленькая) -- [Eval-харнес распознавания (корпус кейсов + метрика точности)](items/recognition-eval-harness.md) — смена модели или правка промпта распознавания сейчас вслепую — нет корпуса кейсов и метрики точности, регрессии не видно -- [Глубокий healthcheck и статус зависимостей](items/deep-healthcheck-dependencies.md) — /healthz проверяет только сам сервис — недоступность qBittorrent/LLM/метабазы видна лишь по застрявшим задачам, нет readiness и бейджа в UI -- [История переходов загрузки](items/download-transition-history.md) — хранится только текущий статус загрузки — разбор «как сюда попали» идёт по логам сервера, нет таблицы истории переходов -- [Кэш метабаз (и опционально LLM)](items/metadata-cache.md) — повторные и ретраящиеся прогоны бьют TMDB/TVDB/TVMaze одним запросом — кэш с TTL сэкономил бы лимиты и ускорил «Распознать заново» -- [НФТ: масштаб до 100 одновременных загрузок (потолок — 1000)](items/scale-100-downloads.md) — Зафиксировать в НФТ ориентир 100/1000 загрузок + аудит узких мест (SQLite, воркер, поллинг) -- [Шум ERROR фоновых циклов при недоступной зависимости](items/background-error-noise.md) — Остаток задачи логирования: ext.* ERROR-шторм при недоступном qBittorrent + эскалация устойчивого сбоя тика _(ревью Fable)_ -- [Ретеншн и очистка БД](items/db-retention-cleanup.md) — терминальные задачи и сырые ответы LLM копятся вечно — без авточистки список загрузок и БД деградируют по мере эксплуатации -- [Словарь единого языка (ubiquitous language)](items/ubiquitous-language-glossary.md) — наименования домена расходятся между спеками, UI и кодом — нет единого глоссария (на нём же стоит агент-ревьювер наименований) -- [[idea] Завершение загрузки через webhook](items/completion-webhook.md) — завершение сейчас ловим поллингом qBittorrent — webhook реагировал бы быстрее, но связывает нас с его конфигом (решим по опыту эксплуатации) -- [[idea] Кандидаты в конвенции кода](items/convention-candidates.md) — накоплен список кандидатов (внешние клиенты, конкурентность, тесты, CLI, время) — надо решить, что из них стало реальным трением, а что выдумано вперёд + +- [🧹 Откалибровать проходы ревью и завести ревьювер наименований](items/quality-review-agents.md) — конвейер ревью переехал в плагин `av-dev-pipeline`; осталась калибровка проходов на этом проекте и ревьювер наименований (ждёт словарь единого языка) +- [✨ Закрыть веб-UI авторизацией, когда доверенной LAN станет мало](items/web-ui-auth.md) — для v1 решено без авторизации (доверенная LAN, опц. allowlist подсетей) — задел на случай, если понадобится защита +- [🧹 Бэкапить SQLite по расписанию с ротацией](items/sqlite-backup.md) — architecture требует бекапить data-том, но стратегия не описана — сбой или редеплой стирают всё in-flight состояние (проще, пока БД маленькая) +- [🧹 Завести eval-харнес распознавания: корпус кейсов и метрику точности](items/recognition-eval-harness.md) — смена модели или правка промпта распознавания сейчас вслепую — нет корпуса кейсов и метрики точности, регрессии не видно +- [✨ Проверять в healthcheck доступность qBittorrent, LLM и метабаз и показывать её в UI](items/deep-healthcheck-dependencies.md) — /healthz проверяет только сам сервис — недоступность qBittorrent/LLM/метабазы видна лишь по застрявшим задачам, нет readiness и бейджа в UI +- [✨ Хранить историю переходов загрузки отдельной таблицей](items/download-transition-history.md) — хранится только текущий статус загрузки — разбор «как сюда попали» идёт по логам сервера, нет таблицы истории переходов +- [🧹 Кэшировать ответы метабаз с TTL (и опционально LLM)](items/metadata-cache.md) — повторные и ретраящиеся прогоны бьют TMDB/TVDB/TVMaze одним запросом — кэш с TTL сэкономил бы лимиты и ускорил «Распознать заново» +- [🐞 Не штормить ERROR при недоступном qBittorrent и эскалировать устойчивый сбой тика](items/background-error-noise.md) — Остаток задачи логирования: ext.* ERROR-шторм при недоступном qBittorrent + эскалация устойчивого сбоя тика _(ревью Fable)_ +- [✨ Чистить БД от терминальных задач и сырых ответов LLM старше срока хранения](items/db-retention-cleanup.md) — терминальные задачи и сырые ответы LLM копятся вечно — без авточистки список загрузок и БД деградируют по мере эксплуатации +- [🧹 Свести термины домена в словарь единого языка](items/ubiquitous-language-glossary.md) — наименования домена расходятся между спеками, UI и кодом — нет единого глоссария (на нём же стоит агент-ревьювер наименований) +- [🔬 Потолок нагрузки: 100 одновременных загрузок, план-максимум 1000](items/scale-100-downloads.md) — Зафиксировать в НФТ ориентир 100/1000 загрузок + аудит узких мест (SQLite, воркер, поллинг) +- [🔬 Завершение загрузки через webhook](items/completion-webhook.md) — завершение сейчас ловим поллингом qBittorrent — webhook реагировал бы быстрее, но связывает нас с его конфигом (решим по опыту эксплуатации) +- [🔬 Кандидаты в конвенции кода](items/convention-candidates.md) — накоплен список кандидатов (внешние клиенты, конкурентность, тесты, CLI, время) — надо решить, что из них стало реальным трением, а что выдумано вперёд diff --git a/docs/tasks/PLAN.md b/docs/tasks/PLAN.md deleted file mode 100644 index 254be78..0000000 --- a/docs/tasks/PLAN.md +++ /dev/null @@ -1,22 +0,0 @@ -# План - -Оглавление целей. Цель — файл `[goal]` в `items/`; её задачи -здесь **не перечисляются** — перечень даёт `tasks.py list --goal <слаг>`. -В первой секции («порядок») очередь значима и обосновывается -прозой; в остальных порядка нет — это тематические цели. - -## порядок - -Пусто. Фазы Ф0–Ф6 прежней дорожной карты (каркас, приём и трекинг, -распознавание, раскладка и ревью, метаданные, Telegram и UX, деплой) закрыты — -сквозной путь работает и развёрнут; закрытый шаг планом больше не является. -Что было сделано и когда — по архиву `openspec/changes/archive/`. -Следующая упорядоченная очередь появится, когда она понадобится. - -## темы -- [[goal] Точность распознавания](items/recognition-accuracy.md) — смена модели или правка промпта сегодня вслепую — нет ни метрики, ни способа переиспользовать уже сделанные человеком правки -- [[goal] Сложные раздачи](items/complex-releases.md) — типовая раздача раскладывается, а всё, что сложнее одного сезона одного тайтла, упирается в ручной разбор -- [[goal] Эксплуатационная прочность](items/operational-resilience.md) — сервис работает, но не переживает роста: база копится вечно, бэкапа нет, отказ зависимости виден только по застрявшим задачам -- [[goal] Интерфейсы приёма и ревью](items/ingest-and-review-interfaces.md) — путь «принести раздачу и подтвердить догадку» упирается в незакрытые куски интерфейсов, а не в логику -- [[goal] Целостность состояния и приёма](items/state-integrity.md) — известные окна рассинхрона и потери маркеров: каждое по отдельности самоисцеляется, вместе — источник необъяснимых состояний -- [[goal] Процесс и качество разработки](items/dev-process-quality.md) — наименования домена расходятся между спеками, UI и кодом, а конвейер ревью не откалиброван — растёт цена каждой следующей задачи diff --git a/docs/tasks/ROADMAP.md b/docs/tasks/ROADMAP.md new file mode 100644 index 0000000..2400483 --- /dev/null +++ b/docs/tasks/ROADMAP.md @@ -0,0 +1,34 @@ +# Роадмап + +Что приложение уже умеет и чего ещё не умеет. Цель — файл типа `goal` в +`items/`; её задачи здесь **не перечисляются** — перечень даёт +`tasks.py list --goal <слаг>`. В `Запланировано` очередь значима и +обосновывается прозой; в `Направлениях` порядка нет; `Сопровождение` — то, чем +держат проект, а не возможности приложения; в `Готово` строку с датой пишет +`close --implemented`. + +## Запланировано + +Пусто. Фазы Ф0–Ф6 прежней дорожной карты (каркас, приём и трекинг, +распознавание, раскладка и ревью, метаданные, Telegram и UX, деплой) закрыты — +сквозной путь работает и развёрнут; закрытый шаг планом больше не является. +Следующая упорядоченная очередь появится, когда она понадобится. + +## Направления + +- [🎯 Раздача узнаётся верно, и точность этого измерима](items/recognition-accuracy.md) — смена модели или правка промпта сегодня вслепую — нет ни метрики, ни способа переиспользовать уже сделанные человеком правки +- [🎯 Раскладывается не только типовая раздача](items/complex-releases.md) — типовая раздача раскладывается, а всё, что сложнее одного сезона одного тайтла, упирается в ручной разбор +- [🎯 Раздача приносится и подтверждается из любого транспорта](items/ingest-and-review-interfaces.md) — путь «принести раздачу и подтвердить догадку» упирается в незакрытые куски интерфейсов, а не в логику +- [🎯 По записи загрузки видно, как она сюда попала](items/state-integrity.md) — известные окна рассинхрона и потери маркеров: каждое по отдельности самоисцеляется, вместе — источник необъяснимых состояний + +## Сопровождение + +- [🎯 Сервис переживает рост и потерю тома](items/operational-resilience.md) — сервис работает, но не переживает роста: база копится вечно, бэкапа нет, отказ зависимости виден только по застрявшим задачам +- [🎯 Домен называется одинаково везде, ревью откалибровано](items/dev-process-quality.md) — наименования домена расходятся между спеками, UI и кодом, а конвейер ревью не откалиброван — растёт цена каждой следующей задачи + +## Готово + +Пусто. Фазы Ф0–Ф6 велись прозой и целями в роадмапе не числились, поэтому +строк с датами за ними нет; что было сделано и когда — по архиву +`openspec/changes/archive/`. Первую строку сюда впишет `close --implemented` +на первой достигнутой цели. diff --git a/docs/tasks/items/anime-absolute-numbering.md b/docs/tasks/items/anime-absolute-numbering.md index 76e5ad1..c660bae 100644 --- a/docs/tasks/items/anime-absolute-numbering.md +++ b/docs/tasks/items/anime-absolute-numbering.md @@ -1,6 +1,7 @@ -# Аниме с абсолютной нумерацией +# ✨ Пересчитывать абсолютную нумерацию аниме в SxxEyy -- **Секция:** ядро продукта +- **Тип:** feature +- **Категория:** Ядро продукта - **Зачем:** аниме со сквозной нумерацией (#137) не раскладывается в SxxEyy, который ждёт Jellyfin — нужен пересчёт абсолютной нумерации - **Теги:** goal:complex-releases diff --git a/docs/tasks/items/auto-link-confidence-gate.md b/docs/tasks/items/auto-link-confidence-gate.md index 50db457..302ab94 100644 --- a/docs/tasks/items/auto-link-confidence-gate.md +++ b/docs/tasks/items/auto-link-confidence-gate.md @@ -1,6 +1,7 @@ -# Confidence-гейт авто-раскладки: узаконить в спеке + сделать выключаемым (дефолт 0.7) +# ✨ Узаконить confidence-гейт авто-раскладки в спеке и сделать его выключаемым (дефолт 0.7) -- **Секция:** ядро продукта +- **Тип:** feature +- **Категория:** Ядро продукта - **Зачем:** Решено (B): гейт оставляем как доп. проверку на ревью — выключаемый порог, дефолт 0.85→0.7, записать в спеку - **Теги:** goal:recognition-accuracy @@ -35,7 +36,12 @@ вспомогательный сигнал». Добавить сценарий: матч + чистая валидация + согласованность, но `confidence` ниже порога → review. 4. **Конфиг-конвенция/дока:** описать ключ в `docs/conventions/config.md` (диапазон - [0,1], 0 = выкл, дефолт 0.7). + [0,1], 0 = выкл, дефолт 0.7) и поправить значение в таблице «Настройки с + числовым значением» [docs/database.md](../../database.md) — дом числа там, и + при смене дефолта оно разъедется первым. Формулировки инварианта в + `CLAUDE.md` и ложноположительного в `docs/review.md` уже приведены к + «единственным гейтом не является» (канон 4, 2026-08-06) — переписывать их + задача не должна. 5. **Тесты:** `decide` с `threshold=0` (гейт выключен, авто при чистых 1–3); confidence ниже/выше порога при выполненных 1–3. 6. Точное число порога откалибровать позже — для этого есть задача diff --git a/docs/tasks/items/background-error-noise.md b/docs/tasks/items/background-error-noise.md index 9bf00a7..bb275f3 100644 --- a/docs/tasks/items/background-error-noise.md +++ b/docs/tasks/items/background-error-noise.md @@ -1,6 +1,7 @@ -# Шум ERROR фоновых циклов при недоступной зависимости +# 🐞 Не штормить ERROR при недоступном qBittorrent и эскалировать устойчивый сбой тика -- **Секция:** инфраструктура +- **Тип:** fix +- **Категория:** Инфраструктура - **Зачем:** Остаток задачи логирования: ext.* ERROR-шторм при недоступном qBittorrent + эскалация устойчивого сбоя тика _(ревью Fable)_ - **Теги:** goal:operational-resilience diff --git a/docs/tasks/items/candidate-match-strength.md b/docs/tasks/items/candidate-match-strength.md index ca525a1..1e126dd 100644 --- a/docs/tasks/items/candidate-match-strength.md +++ b/docs/tasks/items/candidate-match-strength.md @@ -1,6 +1,7 @@ -# [idea] Сила совпадения кандидата и пересмотр распознавания/матчинга +# 🔬 Сила совпадения кандидата и пересмотр распознавания/матчинга -- **Секция:** ядро продукта +- **Тип:** research +- **Категория:** Ядро продукта - **Зачем:** у кандидата метабазы нет метрики силы совпадения — список кандидатов на ревью нечем отсортировать по уверенности (сперва проработать процесс матчинга) - **Теги:** goal:recognition-accuracy diff --git a/docs/tasks/items/catched-source-type-refresh.md b/docs/tasks/items/catched-source-type-refresh.md index d1be6a0..b897a09 100644 --- a/docs/tasks/items/catched-source-type-refresh.md +++ b/docs/tasks/items/catched-source-type-refresh.md @@ -1,6 +1,7 @@ -# `addReq` не пересобирается из свежего `source_type` перед `Add` (окно namer'а) +# 🐞 Пересобирать `addReq` из свежего `source_type` перед `Add` (окно namer'а) -- **Секция:** ядро продукта +- **Тип:** fix +- **Категория:** Ядро продукта - **Зачем:** При апгрейде magnet→.torrent в окне namer'а добавится magnet из устаревшего снимка; самоисцеляется через magnet_timeout→failed→Retry _(аудит 2026-07-17)_ - **Теги:** goal:state-integrity diff --git a/docs/tasks/items/completion-webhook.md b/docs/tasks/items/completion-webhook.md index 21b9f33..a142405 100644 --- a/docs/tasks/items/completion-webhook.md +++ b/docs/tasks/items/completion-webhook.md @@ -1,6 +1,7 @@ -# [idea] Завершение загрузки через webhook +# 🔬 Завершение загрузки через webhook -- **Секция:** инфраструктура +- **Тип:** research +- **Категория:** Инфраструктура - **Зачем:** завершение сейчас ловим поллингом qBittorrent — webhook реагировал бы быстрее, но связывает нас с его конфигом (решим по опыту эксплуатации) - **Теги:** goal:ingest-and-review-interfaces diff --git a/docs/tasks/items/complex-releases.md b/docs/tasks/items/complex-releases.md index 9392424..89d9f59 100644 --- a/docs/tasks/items/complex-releases.md +++ b/docs/tasks/items/complex-releases.md @@ -1,6 +1,7 @@ -# [goal] Сложные раздачи +# 🎯 Раскладывается не только типовая раздача -- **Секция:** темы +- **Тип:** goal +- **Секция:** Направления - **Зачем:** типовая раздача раскладывается, а всё, что сложнее одного сезона одного тайтла, упирается в ручной разбор - **Теги:** decomposed diff --git a/docs/tasks/items/complex-series-releases.md b/docs/tasks/items/complex-series-releases.md index fbb495b..cd384bc 100644 --- a/docs/tasks/items/complex-series-releases.md +++ b/docs/tasks/items/complex-series-releases.md @@ -1,6 +1,7 @@ -# [idea] Сложные сериальные раздачи: все сезоны разом, паки, спецраскладки +# 🔬 Сложные сериальные раздачи: все сезоны разом, паки, спецраскладки -- **Секция:** ядро продукта +- **Тип:** research +- **Категория:** Ядро продукта - **Зачем:** сложные раздачи (все сезоны разом, паки, смешанная нумерация) целостно не проработаны — распознавание/ревью/раскладка заточены под один сезон - **Теги:** goal:complex-releases diff --git a/docs/tasks/items/convention-candidates.md b/docs/tasks/items/convention-candidates.md index 2094e4d..9acb4cb 100644 --- a/docs/tasks/items/convention-candidates.md +++ b/docs/tasks/items/convention-candidates.md @@ -1,6 +1,7 @@ -# [idea] Кандидаты в конвенции кода +# 🔬 Кандидаты в конвенции кода -- **Секция:** Инфраструктура +- **Тип:** research +- **Категория:** Инфраструктура - **Зачем:** накоплен список кандидатов (внешние клиенты, конкурентность, тесты, CLI, время) — надо решить, что из них стало реальным трением, а что выдумано вперёд - **Теги:** goal:dev-process-quality diff --git a/docs/tasks/items/db-retention-cleanup.md b/docs/tasks/items/db-retention-cleanup.md index ac674d3..4bf0867 100644 --- a/docs/tasks/items/db-retention-cleanup.md +++ b/docs/tasks/items/db-retention-cleanup.md @@ -1,6 +1,7 @@ -# Ретеншн и очистка БД +# ✨ Чистить БД от терминальных задач и сырых ответов LLM старше срока хранения -- **Секция:** инфраструктура +- **Тип:** feature +- **Категория:** Инфраструктура - **Зачем:** терминальные задачи и сырые ответы LLM копятся вечно — без авточистки список загрузок и БД деградируют по мере эксплуатации - **Теги:** goal:operational-resilience diff --git a/docs/tasks/items/deep-healthcheck-dependencies.md b/docs/tasks/items/deep-healthcheck-dependencies.md index f021efc..657da5e 100644 --- a/docs/tasks/items/deep-healthcheck-dependencies.md +++ b/docs/tasks/items/deep-healthcheck-dependencies.md @@ -1,6 +1,7 @@ -# Глубокий healthcheck и статус зависимостей +# ✨ Проверять в healthcheck доступность qBittorrent, LLM и метабаз и показывать её в UI -- **Секция:** инфраструктура +- **Тип:** feature +- **Категория:** Инфраструктура - **Зачем:** /healthz проверяет только сам сервис — недоступность qBittorrent/LLM/метабазы видна лишь по застрявшим задачам, нет readiness и бейджа в UI - **Теги:** goal:operational-resilience diff --git a/docs/tasks/items/dev-process-quality.md b/docs/tasks/items/dev-process-quality.md index cdad725..8865937 100644 --- a/docs/tasks/items/dev-process-quality.md +++ b/docs/tasks/items/dev-process-quality.md @@ -1,6 +1,7 @@ -# [goal] Процесс и качество разработки +# 🎯 Домен называется одинаково везде, ревью откалибровано -- **Секция:** темы +- **Тип:** goal +- **Секция:** Сопровождение - **Зачем:** наименования домена расходятся между спеками, UI и кодом, а конвейер ревью не откалиброван — растёт цена каждой следующей задачи - **Теги:** decomposed diff --git a/docs/tasks/items/disc-image-releases.md b/docs/tasks/items/disc-image-releases.md index c201603..e25b3f8 100644 --- a/docs/tasks/items/disc-image-releases.md +++ b/docs/tasks/items/disc-image-releases.md @@ -1,6 +1,7 @@ -# Раздачи-копии диска (DVD/BluRay: VIDEO_TS/BDMV) +# ✨ Раскладывать раздачу-копию диска (VIDEO_TS/BDMV) каталогом целиком -- **Секция:** ядро продукта +- **Тип:** feature +- **Категория:** Ядро продукта - **Зачем:** раздача-образ диска (VIDEO_TS/BDMV) сейчас разбирается пофайлово вместо раскладки каталога целиком — редкий, но реальный случай - **Теги:** goal:complex-releases diff --git a/docs/tasks/items/dismiss-marker-lost.md b/docs/tasks/items/dismiss-marker-lost.md index 36b2911..be2b4ea 100644 --- a/docs/tasks/items/dismiss-marker-lost.md +++ b/docs/tasks/items/dismiss-marker-lost.md @@ -1,6 +1,7 @@ -# Веб-UI зовёт Cancel вместо Dismiss на не-терминальных → теряется `user_dismiss` +# 🐞 Не терять маркер `user_dismiss` при закрытии не-терминальной загрузки из веб-UI -- **Секция:** ядро продукта +- **Тип:** fix +- **Категория:** Ядро продукта - **Зачем:** Функционально ок (Cancel даёт cancelled), но маркер user_dismiss в error_code теряется; расхождение с буквой спеки _(аудит 2026-07-17)_ - **Теги:** goal:state-integrity diff --git a/docs/tasks/items/download-transition-history.md b/docs/tasks/items/download-transition-history.md index 69d6c78..c65dc89 100644 --- a/docs/tasks/items/download-transition-history.md +++ b/docs/tasks/items/download-transition-history.md @@ -1,6 +1,7 @@ -# История переходов загрузки +# ✨ Хранить историю переходов загрузки отдельной таблицей -- **Секция:** инфраструктура +- **Тип:** feature +- **Категория:** Инфраструктура - **Зачем:** хранится только текущий статус загрузки — разбор «как сюда попали» идёт по логам сервера, нет таблицы истории переходов - **Теги:** goal:state-integrity diff --git a/docs/tasks/items/episode-numbering-canon.md b/docs/tasks/items/episode-numbering-canon.md index 4e0997e..417d1fc 100644 --- a/docs/tasks/items/episode-numbering-canon.md +++ b/docs/tasks/items/episode-numbering-canon.md @@ -1,6 +1,7 @@ -# Согласование канона нумерации серий с провайдером тега +# 🔬 Канон нумерации серий и порядок у провайдера тега -- **Секция:** ядро продукта +- **Тип:** research +- **Категория:** Ядро продукта - **Зачем:** Косметика/редкость: порядок просмотра ок, но у тайтлов со спорным порядком (Бибоп) Jellyfin подтягивает не те подписи серий, если канон файлов ≠ дефолтный порядок провайдера тега - **Теги:** goal:complex-releases diff --git a/docs/tasks/items/external-subtitles.md b/docs/tasks/items/external-subtitles.md index dd39098..0548b8c 100644 --- a/docs/tasks/items/external-subtitles.md +++ b/docs/tasks/items/external-subtitles.md @@ -1,6 +1,7 @@ -# Внешние субтитры: пары VobSub и языковой суффикс +# ✨ Спаривать VobSub .idx+.sub и проставлять языковой суффикс субтитра -- **Секция:** ядро продукта +- **Тип:** feature +- **Категория:** Ядро продукта - **Зачем:** Привязка субтитр→серия уже работает; остались пары VobSub .idx+.sub и потеря Lang/Flags - **Теги:** goal:complex-releases diff --git a/docs/tasks/items/free-space-check-copy-fallback.md b/docs/tasks/items/free-space-check-copy-fallback.md index 388231f..3a9dadc 100644 --- a/docs/tasks/items/free-space-check-copy-fallback.md +++ b/docs/tasks/items/free-space-check-copy-fallback.md @@ -1,6 +1,7 @@ -# Проверка свободного места перед copy-fallback +# ✨ Проверять свободное место перед copy-fallback -- **Секция:** ядро продукта +- **Тип:** feature +- **Категория:** Ядро продукта - **Зачем:** copy-fallback при невозможности хардлинка может упереться в переполненный диск посреди раскладки — нет проверки места до копирования - **Теги:** goal:operational-resilience diff --git a/docs/tasks/items/guessit-sidecar.md b/docs/tasks/items/guessit-sidecar.md index 740cd83..a271242 100644 --- a/docs/tasks/items/guessit-sidecar.md +++ b/docs/tasks/items/guessit-sidecar.md @@ -1,6 +1,7 @@ -# [idea] guessit как сервис-спутник +# 🔬 guessit как сервис-спутник -- **Секция:** ядро продукта +- **Тип:** research +- **Категория:** Ядро продукта - **Зачем:** go-ptn слабее питоновского guessit — если точности пред-парса не хватит, завернуть guessit в сервис-спутник рядом с бинарём - **Теги:** goal:recognition-accuracy diff --git a/docs/tasks/items/infohash-identity-integrity.md b/docs/tasks/items/infohash-identity-integrity.md index de4a544..034044a 100644 --- a/docs/tasks/items/infohash-identity-integrity.md +++ b/docs/tasks/items/infohash-identity-integrity.md @@ -1,6 +1,7 @@ -# Идентичность инфохэшей: split v1/v2 одного торрента + крафт-магнет отравляет владение (F4, F5) +# 🐞 Связать v1/v2-хеши одного торрента и не доверять паре xt из магнета (F4, F5) -- **Секция:** ядро продукта +- **Тип:** fix +- **Категория:** Ядро продукта - **Зачем:** split v1/v2 идентичность и крафт-магнет отравляют владение инфохэшами _(ревью 2026-07-08)_ - **Теги:** goal:state-integrity diff --git a/docs/tasks/items/ingest-and-review-interfaces.md b/docs/tasks/items/ingest-and-review-interfaces.md index 63687eb..2d7326b 100644 --- a/docs/tasks/items/ingest-and-review-interfaces.md +++ b/docs/tasks/items/ingest-and-review-interfaces.md @@ -1,6 +1,7 @@ -# [goal] Интерфейсы приёма и ревью +# 🎯 Раздача приносится и подтверждается из любого транспорта -- **Секция:** темы +- **Тип:** goal +- **Секция:** Направления - **Зачем:** путь «принести раздачу и подтвердить догадку» упирается в незакрытые куски интерфейсов, а не в логику - **Теги:** decomposed diff --git a/docs/tasks/items/ingest-nits.md b/docs/tasks/items/ingest-nits.md index ca40a13..c19b988 100644 --- a/docs/tasks/items/ingest-nits.md +++ b/docs/tasks/items/ingest-nits.md @@ -1,6 +1,7 @@ -# Нити приёма: NoName в контексте, устаревшие комментарии, лог без причины, bencode-аллокации (N1, N3, N4, N5) +# 🧹 Закрыть мелочи приёма: NoName в контексте, устаревшие комментарии, лог без причины, bencode-аллокации (N1, N3, N4, N5) -- **Секция:** ядро продукта +- **Тип:** chore +- **Категория:** Ядро продукта - **Зачем:** косметика приёма: NoName в контексте, устаревшие комментарии, лог, bencode-аллокации _(ревью 2026-07-08)_ - **Теги:** goal:state-integrity diff --git a/docs/tasks/items/learn-from-user-corrections.md b/docs/tasks/items/learn-from-user-corrections.md index 706afd6..e3ed326 100644 --- a/docs/tasks/items/learn-from-user-corrections.md +++ b/docs/tasks/items/learn-from-user-corrections.md @@ -1,6 +1,7 @@ -# Обучение на правках человека (few-shot из прошлых ревью) +# ✨ Подмешивать прошлые правки человека в промпт распознавания (few-shot) -- **Секция:** ядро продукта +- **Тип:** feature +- **Категория:** Ядро продукта - **Зачем:** правки человека (матч/тип/нумерация) не переиспользуются — few-shot из прошлых ревью поднял бы точность на «своих» трекерах без смены модели - **Теги:** goal:recognition-accuracy diff --git a/docs/tasks/items/merge-incremental-redownload.md b/docs/tasks/items/merge-incremental-redownload.md index 5381a18..d10b69e 100644 --- a/docs/tasks/items/merge-incremental-redownload.md +++ b/docs/tasks/items/merge-incremental-redownload.md @@ -1,6 +1,7 @@ -# Раздачи с докачиванием (merge при повторном добавлении) +# ✨ Докладывать недостающие эпизоды merge-раскладкой при повторной заливке -- **Секция:** ядро продукта +- **Тип:** feature +- **Категория:** Ядро продукта - **Зачем:** повторная заливка сериала целиком должна доложить недостающие эпизоды merge-раскладкой, не трогая существующие ссылки — блокирует типовой сценарий свежих сериалов - **Теги:** goal:complex-releases diff --git a/docs/tasks/items/metadata-cache.md b/docs/tasks/items/metadata-cache.md index 4cacc57..252a391 100644 --- a/docs/tasks/items/metadata-cache.md +++ b/docs/tasks/items/metadata-cache.md @@ -1,6 +1,7 @@ -# Кэш метабаз (и опционально LLM) +# 🧹 Кэшировать ответы метабаз с TTL (и опционально LLM) -- **Секция:** инфраструктура +- **Тип:** chore +- **Категория:** Инфраструктура - **Зачем:** повторные и ретраящиеся прогоны бьют TMDB/TVDB/TVMaze одним запросом — кэш с TTL сэкономил бы лимиты и ускорил «Распознать заново» - **Теги:** goal:operational-resilience diff --git a/docs/tasks/items/multi-pass-verification.md b/docs/tasks/items/multi-pass-verification.md index 80122ab..55862c8 100644 --- a/docs/tasks/items/multi-pass-verification.md +++ b/docs/tasks/items/multi-pass-verification.md @@ -1,6 +1,7 @@ -# [idea] Многоступенчатая верификация привязки +# 🔬 Многоступенчатая верификация привязки -- **Секция:** ядро продукта +- **Тип:** research +- **Категория:** Ядро продукта - **Зачем:** несколько проходов распознавания с консенсусом подняли бы точность ценой стоимости/латентности — проработать, когда включать и как мерджить расхождения - **Теги:** goal:recognition-accuracy diff --git a/docs/tasks/items/naming-edge-cases.md b/docs/tasks/items/naming-edge-cases.md index ed4707c..26b9a1e 100644 --- a/docs/tasks/items/naming-edge-cases.md +++ b/docs/tasks/items/naming-edge-cases.md @@ -1,6 +1,7 @@ -# Крайние случаи именования: многофайловый фильм, редакции, двойная серия +# ✨ Заказать спекой крайние случаи именования: многофайловый фильм, редакции, двойная серия -- **Секция:** Ядро продукта +- **Тип:** feature +- **Категория:** Ядро продукта - **Зачем:** стэкинг частей (part1/cd1), редакции [edition-…] и двойная серия SxxEyy-Eyy описаны нарративом, но в file-layout не заказаны — раскладка таких раздач не определена - **Теги:** goal:complex-releases diff --git a/docs/tasks/items/notification-source-binding.md b/docs/tasks/items/notification-source-binding.md index b9317c6..a4e9f78 100644 --- a/docs/tasks/items/notification-source-binding.md +++ b/docs/tasks/items/notification-source-binding.md @@ -1,6 +1,7 @@ -# Привязка уведомлений к источнику в ботах (мульти-бот) +# ✨ Слать уведомления автору загрузки в его транспорт (мульти-бот) -- **Секция:** ядро продукта +- **Тип:** feature +- **Категория:** Ядро продукта - **Зачем:** пинги и ревью должен получать автор загрузки в своём транспорте — нет привязки загрузки к источнику/отправителю (нужно для мульти-бота) - **Теги:** goal:ingest-and-review-interfaces diff --git a/docs/tasks/items/operational-resilience.md b/docs/tasks/items/operational-resilience.md index 1ad4427..6075ccb 100644 --- a/docs/tasks/items/operational-resilience.md +++ b/docs/tasks/items/operational-resilience.md @@ -1,6 +1,7 @@ -# [goal] Эксплуатационная прочность +# 🎯 Сервис переживает рост и потерю тома -- **Секция:** темы +- **Тип:** goal +- **Секция:** Сопровождение - **Зачем:** сервис работает, но не переживает роста: база копится вечно, бэкапа нет, отказ зависимости виден только по застрявшим задачам - **Теги:** decomposed diff --git a/docs/tasks/items/quality-review-agents.md b/docs/tasks/items/quality-review-agents.md index 7e0b23f..94faeeb 100644 --- a/docs/tasks/items/quality-review-agents.md +++ b/docs/tasks/items/quality-review-agents.md @@ -1,6 +1,7 @@ -# Агенты-ревьюверы качества (наименования, архитектура, конвенции, стиль) +# 🧹 Откалибровать проходы ревью и завести ревьювер наименований -- **Секция:** инфраструктура +- **Тип:** chore +- **Категория:** Инфраструктура - **Зачем:** конвейер ревью переехал в плагин `av-dev-pipeline`; осталась калибровка проходов на этом проекте и ревьювер наименований (ждёт словарь единого языка) - **Теги:** goal:dev-process-quality diff --git a/docs/tasks/items/recognition-accuracy.md b/docs/tasks/items/recognition-accuracy.md index a10ffd0..429f314 100644 --- a/docs/tasks/items/recognition-accuracy.md +++ b/docs/tasks/items/recognition-accuracy.md @@ -1,6 +1,7 @@ -# [goal] Точность распознавания +# 🎯 Раздача узнаётся верно, и точность этого измерима -- **Секция:** темы +- **Тип:** goal +- **Секция:** Направления - **Зачем:** смена модели или правка промпта сегодня вслепую — нет ни метрики, ни способа переиспользовать уже сделанные человеком правки - **Теги:** decomposed diff --git a/docs/tasks/items/recognition-eval-harness.md b/docs/tasks/items/recognition-eval-harness.md index ea05c16..d100bad 100644 --- a/docs/tasks/items/recognition-eval-harness.md +++ b/docs/tasks/items/recognition-eval-harness.md @@ -1,6 +1,7 @@ -# Eval-харнес распознавания (корпус кейсов + метрика точности) +# 🧹 Завести eval-харнес распознавания: корпус кейсов и метрику точности -- **Секция:** инфраструктура +- **Тип:** chore +- **Категория:** Инфраструктура - **Зачем:** смена модели или правка промпта распознавания сейчас вслепую — нет корпуса кейсов и метрики точности, регрессии не видно - **Теги:** goal:recognition-accuracy diff --git a/docs/tasks/items/review-mapping-editor.md b/docs/tasks/items/review-mapping-editor.md index a24aa61..a10c883 100644 --- a/docs/tasks/items/review-mapping-editor.md +++ b/docs/tasks/items/review-mapping-editor.md @@ -1,6 +1,7 @@ -# Полный редактор маппинга «файл → серия» и ручной режим ревью +# ✨ Править на ревью маппинг «файл → серия» и раскладывать вручную при провале LLM -- **Секция:** Ядро продукта +- **Тип:** feature +- **Категория:** Ядро продукта - **Зачем:** правка S·E, «нумеровать подряд» и ручной режим при полном провале LLM были запланированы объёмом Ф5 и не заведены задачей — в ревью сегодня можно только подсказать текстом - **Теги:** goal:ingest-and-review-interfaces diff --git a/docs/tasks/items/scale-100-downloads.md b/docs/tasks/items/scale-100-downloads.md index 21b947a..2c94b45 100644 --- a/docs/tasks/items/scale-100-downloads.md +++ b/docs/tasks/items/scale-100-downloads.md @@ -1,6 +1,7 @@ -# НФТ: масштаб до 100 одновременных загрузок (потолок — 1000) +# 🔬 Потолок нагрузки: 100 одновременных загрузок, план-максимум 1000 -- **Секция:** инфраструктура +- **Тип:** research +- **Категория:** Инфраструктура - **Зачем:** Зафиксировать в НФТ ориентир 100/1000 загрузок + аудит узких мест (SQLite, воркер, поллинг) - **Теги:** goal:operational-resilience diff --git a/docs/tasks/items/sqlite-backup.md b/docs/tasks/items/sqlite-backup.md index 8a1ae94..1ecec9b 100644 --- a/docs/tasks/items/sqlite-backup.md +++ b/docs/tasks/items/sqlite-backup.md @@ -1,6 +1,7 @@ -# Бэкап SQLite +# 🧹 Бэкапить SQLite по расписанию с ротацией -- **Секция:** инфраструктура +- **Тип:** chore +- **Категория:** Инфраструктура - **Зачем:** architecture требует бекапить data-том, но стратегия не описана — сбой или редеплой стирают всё in-flight состояние (проще, пока БД маленькая) - **Теги:** goal:operational-resilience diff --git a/docs/tasks/items/sse-live-updates.md b/docs/tasks/items/sse-live-updates.md index e915df2..dc95513 100644 --- a/docs/tasks/items/sse-live-updates.md +++ b/docs/tasks/items/sse-live-updates.md @@ -1,6 +1,7 @@ -# Мгновенные обновления через SSE +# ✨ Слать живые обновления через SSE вместо htmx-поллинга -- **Секция:** ядро продукта +- **Тип:** feature +- **Категория:** Ядро продукта - **Зачем:** живые обновления на htmx-поллинге дают задержку и холостые запросы — SSE убрал бы то и другое (поллинг работает, поэтому улучшение, не блокер) - **Теги:** goal:ingest-and-review-interfaces diff --git a/docs/tasks/items/state-integrity.md b/docs/tasks/items/state-integrity.md index 337cbb1..53027f9 100644 --- a/docs/tasks/items/state-integrity.md +++ b/docs/tasks/items/state-integrity.md @@ -1,6 +1,7 @@ -# [goal] Целостность состояния и приёма +# 🎯 По записи загрузки видно, как она сюда попала -- **Секция:** темы +- **Тип:** goal +- **Секция:** Направления - **Зачем:** известные окна рассинхрона и потери маркеров: каждое по отдельности самоисцеляется, вместе — источник необъяснимых состояний - **Теги:** decomposed diff --git a/docs/tasks/items/telegram-messages-audit.md b/docs/tasks/items/telegram-messages-audit.md index fa8c4d0..6daded2 100644 --- a/docs/tasks/items/telegram-messages-audit.md +++ b/docs/tasks/items/telegram-messages-audit.md @@ -1,6 +1,7 @@ -# Ревью уведомлений в Telegram (аудит текстов и формата) +# 🔬 Тексты и формат уведомлений в Telegram -- **Секция:** ядро продукта +- **Тип:** research +- **Категория:** Ядро продукта - **Зачем:** зонтичный проход по всем текстам бота: полнота карточек, единый язык, оформление; порождает под-задачи - **Теги:** goal:ingest-and-review-interfaces diff --git a/docs/tasks/items/title-versions-repacks.md b/docs/tasks/items/title-versions-repacks.md index ed72246..7282cb1 100644 --- a/docs/tasks/items/title-versions-repacks.md +++ b/docs/tasks/items/title-versions-repacks.md @@ -1,6 +1,7 @@ -# Версии/качество одного тайтла (репаки, апгрейд 1080p → 2160p) +# ✨ Заменять разложенную версию тайтла новой (репаки, апгрейд 1080p → 2160p) -- **Секция:** ядро продукта +- **Тип:** feature +- **Категория:** Ядро продукта - **Зачем:** По калибровке болей (2026-07-02) — не боль, из приоритета выпало - **Теги:** goal:complex-releases diff --git a/docs/tasks/items/torrent-url-fetch.md b/docs/tasks/items/torrent-url-fetch.md index 65fb846..729c846 100644 --- a/docs/tasks/items/torrent-url-fetch.md +++ b/docs/tasks/items/torrent-url-fetch.md @@ -1,6 +1,7 @@ -# Фетч .torrent по URL — остаток «единого окна» +# ✨ Принимать ссылку на .torrent и скачивать файл самим (нужен SSRF-гард) -- **Секция:** ядро продукта +- **Тип:** feature +- **Категория:** Ядро продукта - **Зачем:** magnet и .torrent-файл приняты; остался фетч .torrent по URL (нужен SSRF-гард) - **Теги:** goal:ingest-and-review-interfaces diff --git a/docs/tasks/items/tvdb-title-locale.md b/docs/tasks/items/tvdb-title-locale.md index f3c0512..10ee466 100644 --- a/docs/tasks/items/tvdb-title-locale.md +++ b/docs/tasks/items/tvdb-title-locale.md @@ -1,6 +1,7 @@ -# Локаль TVDB: перевод названия и оригинальное название +# ✨ Брать у TVDB название на языке настройки и оригинальное название -- **Секция:** Ядро продукта +- **Тип:** feature +- **Категория:** Ядро продукта - **Зачем:** [general].language правит только TMDB и промпт LLM — TVDB отдаёт primary name, и при language=ru в карточку ревью и имя папки попадает 哪吒之魔童降世 вместо «Нэчжа» - **Теги:** goal:recognition-accuracy diff --git a/docs/tasks/items/ubiquitous-language-glossary.md b/docs/tasks/items/ubiquitous-language-glossary.md index c89b34f..f8860e9 100644 --- a/docs/tasks/items/ubiquitous-language-glossary.md +++ b/docs/tasks/items/ubiquitous-language-glossary.md @@ -1,6 +1,7 @@ -# Словарь единого языка (ubiquitous language) +# 🧹 Свести термины домена в словарь единого языка -- **Секция:** инфраструктура +- **Тип:** chore +- **Категория:** Инфраструктура - **Зачем:** наименования домена расходятся между спеками, UI и кодом — нет единого глоссария (на нём же стоит агент-ревьювер наименований) - **Теги:** goal:dev-process-quality diff --git a/docs/tasks/items/web-ui-auth.md b/docs/tasks/items/web-ui-auth.md index c32ff04..58a47be 100644 --- a/docs/tasks/items/web-ui-auth.md +++ b/docs/tasks/items/web-ui-auth.md @@ -1,6 +1,7 @@ -# Авторизация веб-UI (на будущее) +# ✨ Закрыть веб-UI авторизацией, когда доверенной LAN станет мало -- **Секция:** инфраструктура +- **Тип:** feature +- **Категория:** Инфраструктура - **Зачем:** для v1 решено без авторизации (доверенная LAN, опц. allowlist подсетей) — задел на случай, если понадобится защита - **Теги:** goal:ingest-and-review-interfaces diff --git a/docs/tasks/items/web-ui-pwa.md b/docs/tasks/items/web-ui-pwa.md index 866741a..191b274 100644 --- a/docs/tasks/items/web-ui-pwa.md +++ b/docs/tasks/items/web-ui-pwa.md @@ -1,6 +1,7 @@ -# Современный Web-UI как PWA +# ✨ Переделать веб-UI в устанавливаемое PWA -- **Секция:** ядро продукта +- **Тип:** feature +- **Категория:** Ядро продукта - **Зачем:** текущий server-rendered UI функционален — PWA (устанавливаемое, удобное с телефона) это улучшение большого объёма, не блокер - **Теги:** goal:ingest-and-review-interfaces diff --git a/openspec/specs/live-status/spec.md b/openspec/specs/live-status/spec.md index 3d97936..270a618 100644 --- a/openspec/specs/live-status/spec.md +++ b/openspec/specs/live-status/spec.md @@ -1,7 +1,12 @@ # live-status Specification ## Purpose -TBD - created by archiving change live-status. Update Purpose after archive. +Живая телеметрия раздач: единственный сэмплер — воркер, снимок держится в +памяти на тике поллинга и в БД не персистится. Состав телеметрии (прогресс, +скорость, ETA, размер; для сидирующих — рейтинг, сиды и пиры, отдано), узкий +контракт чтения транспортом и живое обновление интерфейса поллингом фрагментов. +Что именно рисуется на странице и в карточке — `web-ui`; откуда берутся сами +состояния загрузки — `download-tracking`. ## Requirements ### Requirement: Снимок живой телеметрии