diff --git a/.av-dev.toml b/.av-dev.toml new file mode 100644 index 0000000..b42f74f --- /dev/null +++ b/.av-dev.toml @@ -0,0 +1,14 @@ +# Раскладка av-dev в этом проекте: версия и настройки проверок. +# Версия одна на всю раскладку — и на документы канона, и на каталог задач; +# по ней скилл av-dev:canon решает, какие записи журнала применять. + +version = 5 + +[docs] +migrations = "internal/store/migrations" +# Коммит, на котором в последний раз гоняли av-dev:doc-healthcheck (2026-09-02). +healthcheck_last = "fc9a3b406692620ba1503065c05166355b6387ab" + +[tasks] +dir = "tasks" +stage = "support" diff --git a/.claude/settings.json b/.claude/settings.json index 5bb4b24..10c8b62 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -2,9 +2,7 @@ "enabledPlugins": { "frontend-design@claude-plugins-official": true, "av-dev-git@av-dev-skills": true, - "av-dev-docs@av-dev-skills": true, - "av-dev-tasks@av-dev-skills": true, - "av-dev-code@av-dev-skills": true + "av-dev@av-dev-skills": true }, "extraKnownMarketplaces": { "av-dev-skills": { diff --git a/.golangci.yml b/.golangci.yml index 4cb02b2..a39d6b2 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -4,7 +4,7 @@ # staticcheck, unused. Сверх него включены линтеры, которые механизируют # конвенции из docs/conventions/*: то, что проверяет правило, не должно # оставаться прозой в конвенциях и в промптах ревью (процедура промоута — -# references/promote.md скилла av-dev-pipeline:review-pipeline; перечень уже +# references/promote.md скилла av-dev:code-review; перечень уже # механизированного — docs/conventions/README.md). version: "2" diff --git a/CLAUDE.md b/CLAUDE.md index a3214e4..23a8448 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -161,7 +161,7 @@ Go 1.26, один статический бинарь (`CGO_ENABLED=0`). Module другой работы: гейт один на все задачи. - **Приоритет — это порядок строк в [tasks/BACKLOG.md](tasks/BACKLOG.md).** Первая строка секции — то, что делают следующим. Порядок назначает человек на - груминге (`av-dev-tasks:groom`), машина его не выводит. + груминге (`av-dev:task-groom`), машина его не выводит. - **Ориентир по размеру порции разбора на груминге:** 5–8 задач. Ориентир, а не закон. - **Что такое «сделана»:** пайплайн задачи пройден целиком (спека → код → оба @@ -193,9 +193,10 @@ Go 1.26, один статический бинарь (`CGO_ENABLED=0`). Module `openspec validate --strict`. Ревью — два чекпоинта: ревью дизайна на предложении (после design/specs, ДО -кода) и ревью изменения после apply, до archive. Состав обоих выбирается по -метке задачи (`small` / `medium` / `large`), которую разметка ставит один раз -после propose. Настройка конвейера под проект и журнал дефектов — +кода) и ревью изменения после apply, до archive. Состав обоих постоянен и на +всякой задаче один и тот же; глубину по области даёт отдельный прогон +`av-dev:code-deep-review`, и зовёт его человек. Настройка конвейера под проект, +признаки для глубокого прогона и журнал дефектов — [docs/review.md](docs/review.md). ## Документация @@ -215,7 +216,7 @@ Go 1.26, один статический бинарь (`CGO_ENABLED=0`). Module - [docs/review.md](docs/review.md) — настройка конвейера ревью и журнал дефектов. - [tasks/](tasks/BACKLOG.md) — задачи и цели: одна запись = один файл в `items/` + строка в индексе, порядок строк = приоритет. Ведётся скиллом - `av-dev-tasks:tasks`, разбор беклога — `av-dev-tasks:groom`. + `av-dev:task-track`, разбор беклога — `av-dev:task-groom`. **Tududi** (проект `jellybit`, project_id 14) — только инбокс сырых идей. Идея становится задачей, когда её оформляют файлом в `tasks/items/`. diff --git a/README.md b/README.md index 2284817..758b98e 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,8 @@ Arr-стек (prowlarr/radarr/sonarr) плохо ложится на русск Рабочий прототип: сквозной путь приём → загрузка → распознавание → раскладка работает целиком, автоматически при уверенном результате либо через -подтверждение человеком. Что уже умеет и что дальше — -[tasks/ROADMAP.md](tasks/ROADMAP.md). +подтверждение человеком. Что уже умеет — `openspec/specs/`, что взято в очередь — +[tasks/BACKLOG.md](tasks/BACKLOG.md). ## Документация diff --git a/docs/.docs.json b/docs/.docs.json deleted file mode 100644 index df91dd2..0000000 --- a/docs/.docs.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "canon": 12, - "migrations": "internal/store/migrations" -} diff --git a/docs/adr/README.md b/docs/adr/README.md index b9e251b..f06cf64 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -1,17 +1,24 @@ # Журнал решений -Одна запись — одно решение. **ADR это промоут поверх архивного `design.md`**, -а не второе сочинение: запись цитирует решение и ссылается на -`openspec/changes/archive//design.md`. +Одна запись — одно решение. **ADR продвигает уже написанное решение, а не +сочиняет его заново**: запись цитирует решение и ссылается на источник — +`openspec/changes/archive//design.md`, а у решения, принятого разведкой без +изменения, на её записку. Главная ценность записи — сохранить **почему**: намерение и причинность. Это важнее аккуратности оформления и полноты остальных секций. +Решение старше OpenSpec либо принятое вне этого репозитория архивного +`design.md` не имеет по построению. Тогда поле **Источник** называет то, что +есть, — историю git, черновик, записку соседнего репозитория — и прямо говорит, +что архивного `design.md` нет. Запись без поля «Источник» вовсе — дефект; запись, +честно назвавшая отсутствие источника, — нет. + ## Когда заводить Верно одно из трёх: - + - **дорогой откат** — переделка стоит дороже переписывания одного файла; - **намеренный отказ** от очевидного подхода; - **пересмотр прежнего решения** — тогда у старой записи обязателен статус diff --git a/docs/adr/template.md b/docs/adr/template.md index 07b5672..b9f39b4 100644 --- a/docs/adr/template.md +++ b/docs/adr/template.md @@ -1,7 +1,8 @@ # Краткий заголовок решения - **Дата:** ГГГГ-ММ-ДД -- **Источник:** openspec/changes/archive//design.md +- **Источник:** openspec/changes/archive//design.md — либо записка разведки, + если решение принято без изменения + ## ГГГГ-ММ-ДД — <краткое последствие> [проскочил|пойман] - **Где:** путь:строка либо «конвейер, а не код» @@ -351,6 +326,9 @@ Go-сервиса и что здесь уже проскакивало. Устр удержания общего замка воркера (`280.450631ms` при задержке соседа `300ms`) - **Что меняем:** в «Триггеры метки», ось «незнакомое», добавлен пункт про новую поверхность поверх необратимой операции +- **Отменено 2026-09-02:** метка упразднена вместе с переходом на постоянный + состав прогона. Признак живёт в «Когда звать глубокое ревью» → «Области, + которые смотрят целиком» ## 2026-08-10 — три дефекта поштучного удаления жили незамеченными, пока рядом не появилась пачка [проскочил] @@ -374,6 +352,9 @@ Go-сервиса и что здесь уже проскакивало. Устр - **Что меняем:** три записи в беклоге со ссылкой на оракулы; триггер метки дополнен (см. запись выше), чтобы следующая поверхность над необратимой операцией шла сразу с доказательными проходами +- **Отменено 2026-09-02:** метки нет; вывод перенесён в «Когда звать глубокое + ревью» — удаление раздачи с файлами названо там областью, которую смотрят + целиком ## 2026-08-10 — тест остался зелёным навсегда, потому что проверял снятый атрибут [пойман] diff --git a/docs/security.md b/docs/security.md index 33e49ff..94f619f 100644 --- a/docs/security.md +++ b/docs/security.md @@ -50,10 +50,10 @@ REST API работают **без авторизации** осознанно; - **Правило:** компоненты санитизируются (убираются разделители пути, `..`, управляющие символы), финальный `filepath.Clean`-путь обязан быть **строго под** соответствующей библиотекой, иначе операция отклоняется. Проверка на - результате, а не на входе. Следом — длина: каждый компонент обязан помещаться в - 255 байт UTF-8, иначе задача уходит в `review`. Порядок значим: путь, вышедший - за песочницу, отклоняется как выход за библиотеку, а не как длинное имя, иначе - находка безопасности спряталась бы за косметической причиной. + результате, а не на входе. Длина компонента проверяется следом и только после + проверки принадлежности — порядок и исход нормированы + [file-layout](../openspec/specs/file-layout/spec.md), величина предела — + [database.md](database.md). - **Исходный путь** = `save_path` из qBittorrent + относительное имя файла из `/torrents/files`. Тоже недоверенный, но по нему мы только **читаем и линкуем**; писать в `paths.downloads` нельзя вообще. @@ -104,8 +104,8 @@ REST API работают **без авторизации** осознанно; - **Отказ в обслуживании изнутри контура.** Огромная раздача, тысяча файлов, бесконечный ответ LLM — это вопросы устойчивости и ресурсов ([architecture.md](architecture.md) → «Эксплуатация»), а не безопасности. - Тело ответа внешнего сервиса при этом читается с пределом: LLM — 8 MiB - (`internal/llm`), метабазы — 4 MiB (`internal/metadata`). + Тело ответа внешнего сервиса при этом читается с пределом — величины в + [database.md](database.md) → «Пределы, зашитые константой кода». - **Целостность содержимого медиафайлов.** Что в контейнере mkv — не наша забота. - **Цепочка поставки** — модули Go, базовый образ distroless, плагины тулинга. - **Приватность запросов к внешним сервисам.** Названия раздач уезжают в LLM и diff --git a/internal/archrules/arch_test.go b/internal/archrules/arch_test.go index cfd6a06..d7844a1 100644 --- a/internal/archrules/arch_test.go +++ b/internal/archrules/arch_test.go @@ -4,7 +4,7 @@ // Каждое правило здесь — бывшая строка прозаической конвенции: у него есть // детерминированный оракул, поэтому ему место в конвейере сборки, а не в // промпте ревью (процедура промоута — references/promote.md скилла -// av-dev-pipeline:review-pipeline). +// av-dev:code-review). package archrules import ( diff --git a/openspec/config.yaml b/openspec/config.yaml index 742a14f..d04cb90 100644 --- a/openspec/config.yaml +++ b/openspec/config.yaml @@ -32,8 +32,8 @@ context: | Пересказа этих документов здесь нет намеренно: второй дом факта расходится с первым молча, и заметно это становится в предложении, которое уже написано. - Ревью: правило выбора метки и состав проходов здесь не пересказываем — их дом - скилл av-dev-pipeline:review-pipeline, проектная настройка — docs/review.md. + Ревью: состав проходов здесь не пересказываем — их дом скилл + av-dev:code-review, проектная настройка — docs/review.md. Конвенции кода: механизированное проверяет `task gate`, прозой остаётся docs/conventions/. Ни состав шагов гейта, ни перечень конвенций здесь не diff --git a/scripts/gate.py b/scripts/gate.py index 2d8d3ff..5cb93aa 100755 --- a/scripts/gate.py +++ b/scripts/gate.py @@ -26,20 +26,19 @@ from pathlib import Path OUT_DIR = Path("tmp/gate") -# Скрипты трёх плагинов av-dev. Каждый проверяет своё и ни один не тянет -# остальных: без своего шага дрейф соответствующего каталога перестаёт ловиться -# молча. -# docs.py — раскладка docs/ против канона (av-dev-docs:canon) -# tasks.py — согласованность индексов каталога задач (av-dev-tasks:tasks) -# openspec.py — форма openspec/config.yaml (av-dev-code:openspec) +# Скрипты плагина av-dev. Каждый проверяет своё и ни один не тянет остальных: +# без своего шага дрейф соответствующего каталога перестаёт ловиться молча. +# docs.py — раскладка docs/ против канона (av-dev:canon) +# tasks.py — согласованность индексов каталога задач (av-dev:task-track) +# openspec.py — форма openspec/config.yaml (av-dev:code-openspec) # Путь берётся из каталога маркетплейса (он без хеша версии, в отличие от # cache/), переопределяется переменной: переустановка плагина не должна # требовать правки гейта. Если скрипта нет — шаг краснеет, а не пропускается: # молча пропущенная проверка даёт ложное ощущение проверенности. MARKETPLACE = Path.home() / ".claude/plugins/marketplaces/av-dev-skills" -DOCS_PY_DEFAULT = MARKETPLACE / "av-dev-docs/skills/canon/scripts/docs.py" -TASKS_PY_DEFAULT = MARKETPLACE / "av-dev-tasks/skills/tasks/scripts/tasks.py" -OPENSPEC_PY_DEFAULT = MARKETPLACE / "av-dev-code/skills/openspec/scripts/openspec.py" +DOCS_PY_DEFAULT = MARKETPLACE / "av-dev/skills/canon/scripts/docs.py" +TASKS_PY_DEFAULT = MARKETPLACE / "av-dev/skills/task-track/scripts/tasks.py" +OPENSPEC_PY_DEFAULT = MARKETPLACE / "av-dev/skills/code-openspec/scripts/openspec.py" OK, FAIL, WARN, SKIP = "OK", "FAIL", "WARN", "SKIP" @@ -219,17 +218,17 @@ def main() -> int: # а database.md нет» (отдельного шага er-schema поэтому больше нет). # База диффа обязательна: без неё сверка миграций со схемой не гоняется. plugin_step( - "canon", "DOCS_PY", DOCS_PY_DEFAULT, "av-dev-docs", + "canon", "DOCS_PY", DOCS_PY_DEFAULT, "av-dev", ["check", "--dir", ".", "--base", base], "раскладка docs/ разошлась с каноном", ) # --- Каталог задач --- - # Согласованность индексов с файлами items/: бездомная запись, строка без - # файла, порядок секций роадмапа, сырьё не в конце секции. docs.py этого не - # смотрит — каталог принадлежит другому плагину. + # Согласованность беклога с файлами items/: бездомная запись, строка без + # файла, стадия проекта, сырьё не в конце секции. docs.py этого не смотрит — + # каталог задач он не ведёт. plugin_step( - "tasks", "TASKS_PY", TASKS_PY_DEFAULT, "av-dev-tasks", + "tasks", "TASKS_PY", TASKS_PY_DEFAULT, "av-dev", ["check", "--dir", "tasks"], "индексы задач разошлись с items/", ) @@ -239,7 +238,7 @@ def main() -> int: # а работает как пустой. Ловит это только свой шаг: docs.py про config.yaml # молчит с канона 10. plugin_step( - "openspec", "OPENSPEC_PY", OPENSPEC_PY_DEFAULT, "av-dev-code", + "openspec", "OPENSPEC_PY", OPENSPEC_PY_DEFAULT, "av-dev", ["check", "--dir", "."], "openspec/config.yaml разошёлся с канонической формой", ) diff --git a/tasks/.tasks.json b/tasks/.tasks.json deleted file mode 100644 index f2fb1b5..0000000 --- a/tasks/.tasks.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "tasks": 1 -} diff --git a/tasks/BACKLOG.md b/tasks/BACKLOG.md index 944dd41..03c573b 100644 --- a/tasks/BACKLOG.md +++ b/tasks/BACKLOG.md @@ -1,12 +1,21 @@ # Беклог Что **можно взять**. Одна задача = один файл `items/.md` -+ строка здесь. Целей тут нет — они в [ROADMAP.md](ROADMAP.md): беклог — то, что -берут, роадмап — то, подо что берут. Ведётся скиллом `av-dev-tasks:tasks`. ++ строка здесь. Ведётся скиллом `av-dev:task-track`. -**Порядок строк внутри секции — это приоритет.** Первая строка секции — то, что -делают следующим. Порядок назначает человек на груминге -(`av-dev-tasks:groom`), машина его не выводит. + +Стадия проекта — **доработка** (`[tasks] stage = "support"`). +**Порядок строк внутри секции — важность:** первая строка это то, +что делают следующим. Назначает его человек на груминге, машина не +выводит. Секции — полки домена, смысла они не несут. Заводится по +одной, по мере появления; пустой беклог — нормальное состояние. + + +Одно место в очереди назначено не человеком, а типом: сырьё (`research` без +раздела «Вопрос») стоит в конце секции — его не берут. + +Тип записи стоит первым полем меты и решает, что у неё может быть: +✨ `feature` 🐞 `fix` 🧹 `chore` 🔬 `research` Секции «блокеры» здесь нет и не заводится: блокер — это состояние, оно живёт до ответа человека, а его следы — вопросами в файлах задач. @@ -41,7 +50,7 @@ ## Инфраструктура - [🐞 Тормозить опрос qBittorrent бэкоффом при недоступности и эскалировать устойчивый сбой](items/background-error-noise.md) — недоступный qBittorrent опрашивается каждые 5 с и даёт WARN на каждом тике: нужен экспоненциальный бэкофф до минутного потолка со сбросом по первому успеху и ERROR на устойчивой деградации -- [🧹 Откалибровать проходы ревью и завести ревьювер наименований](items/quality-review-agents.md) — конвейер ревью переехал в плагин `av-dev-pipeline`; осталась калибровка проходов на этом проекте и ревьювер наименований (ждёт словарь единого языка) +- [🧹 Откалибровать проходы ревью и завести ревьювер наименований](items/quality-review-agents.md) — конвейер ревью переехал в плагин `av-dev`; осталась калибровка проходов на этом проекте и ревьювер наименований (ждёт словарь единого языка) - [✨ Закрыть веб-UI авторизацией, когда доверенной LAN станет мало](items/web-ui-auth.md) — для v1 решено без авторизации (доверенная LAN, опц. allowlist подсетей) — задел на случай, если понадобится защита - [🧹 Бэкапить SQLite по расписанию с ротацией](items/sqlite-backup.md) — architecture требует бекапить data-том, но стратегия не описана — сбой или редеплой стирают всё in-flight состояние (проще, пока БД маленькая) - [✨ Проверять в healthcheck доступность qBittorrent, LLM и метабаз и показывать её в UI](items/deep-healthcheck-dependencies.md) — /healthz проверяет только сам сервис — недоступность qBittorrent/LLM/метабазы видна лишь по застрявшим задачам, нет readiness и бейджа в UI diff --git a/tasks/REJECTED.md b/tasks/REJECTED.md index 0a7ea53..c21af32 100644 --- a/tasks/REJECTED.md +++ b/tasks/REJECTED.md @@ -7,3 +7,10 @@ - 2026-08-06 `learn-from-user-corrections` — ✨ Подмешивать прошлые правки человека в промпт распознавания (few-shot). Причина: решено не собирать базу правок человека: направление — тюнинг автоматического распознавания без участия человека. Была секция: ядро продукта. - 2026-08-06 `recognition-eval-harness` — 🧹 Завести eval-харнес распознавания: корпус кейсов и метрику точности. Причина: решено не собирать размеченный корпус руками: направление — тюнинг автоматического распознавания без участия человека. Была секция: инфраструктура. +- 2026-09-02 `recognition-accuracy` — 🎯 Раздача узнаётся верно без подсказок человека. Причина: тип goal упразднён каноном раскладки 3; зонтик над точностью распознавания снят, работы живут строками беклога сами (tvdb-search-response-live-check, auto-link-confidence-gate, multi-pass-verification, candidate-match-strength, guessit-sidecar). Была секция: Направления. +- 2026-09-02 `complex-releases` — 🎯 Раскладывается не только типовая раздача. Причина: тип goal упразднён каноном раскладки 3; зонтик над нетиповыми раздачами снят, работы живут строками беклога сами (anime-absolute-numbering, disc-image-releases, merge-incremental-redownload, external-subtitles, naming-edge-cases, complex-series-releases, episode-numbering-canon, title-versions-repacks). Была секция: Направления. +- 2026-09-02 `ingest-and-review-interfaces` — 🎯 Раздача приносится и подтверждается из любого транспорта. Причина: тип goal упразднён каноном раскладки 3; зонтик над приёмом и ревью снят, работы живут строками беклога сами (torrent-url-fetch, review-mapping-editor, notification-source-binding, sse-live-updates, web-ui-pwa, web-ui-auth, telegram-messages-audit, completion-webhook). Была секция: Направления. +- 2026-09-02 `state-integrity` — 🎯 По записи загрузки видно, как она сюда попала. Причина: тип goal упразднён каноном раскладки 3; зонтик над целостностью состояния снят, работы живут строками беклога сами (delete-checks-active-infohash-owner, delete-leaves-stale-done, download-transition-history, infohash-identity-integrity). Была секция: Направления. +- 2026-09-02 `operational-resilience` — 🎯 Сервис переживает рост и потерю тома. Причина: тип goal упразднён каноном раскладки 3; зонтик над эксплуатационной устойчивостью снят, работы живут строками беклога сами (sqlite-backup, db-retention-cleanup, deep-healthcheck-dependencies, metadata-cache, free-space-check-copy-fallback, scale-100-downloads). Была секция: Сопровождение. +- 2026-09-02 `dev-process-quality` — 🎯 Домен называется одинаково везде, ревью откалибровано. Причина: тип goal упразднён каноном раскладки 3; зонтик над качеством процесса снят, работы живут строками беклога сами (ubiquitous-language-glossary, quality-review-agents, tests-convention, convention-candidates). Была секция: Сопровождение. +- 2026-09-02 `bulk-download-management` — 🎯 Загрузки удаляются пачкой, а не по одной. Причина: тип goal упразднён каноном раскладки 3; групповое удаление раздач в беклог не берётся — по решению человека при повышении раскладки заводится заново, когда уборка десятка раздач станет болью. Была секция: Направления. diff --git a/tasks/ROADMAP.md b/tasks/ROADMAP.md deleted file mode 100644 index 9ee488c..0000000 --- a/tasks/ROADMAP.md +++ /dev/null @@ -1,35 +0,0 @@ -# Роадмап - -Что приложение уже умеет и чего ещё не умеет. Цель — файл типа `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/bulk-download-management.md) — удаление раздачи с файлами доступно только по одной кнопке на странице одной загрузки — уборка десятка раздач превращается в десяток заходов - -## Сопровождение - -- [🎯 Сервис переживает рост и потерю тома](items/operational-resilience.md) — сервис работает, но не переживает роста: база копится вечно, бэкапа нет, отказ зависимости виден только по застрявшим задачам -- [🎯 Домен называется одинаково везде, ревью откалибровано](items/dev-process-quality.md) — наименования домена расходятся между спеками, UI и кодом, а конвейер ревью не откалиброван — растёт цена каждой следующей задачи - -## Готово - -Пусто. Фазы Ф0–Ф6 велись прозой и целями в роадмапе не числились, поэтому -строк с датами за ними нет; что было сделано и когда — по архиву -`openspec/changes/archive/`. Первую строку сюда впишет `close --implemented` -на первой достигнутой цели. diff --git a/tasks/items/anime-absolute-numbering.md b/tasks/items/anime-absolute-numbering.md index a49a290..dd71e22 100644 --- a/tasks/items/anime-absolute-numbering.md +++ b/tasks/items/anime-absolute-numbering.md @@ -3,7 +3,6 @@ - **Тип:** feature - **Категория:** Ядро продукта — уступила верх четырём мелочам с готовыми оракулами: сама задача жива и остаётся первой фичей ядра, но стоит дороже и в ближайший заход не влезает - **Зачем:** аниме со сквозной нумерацией (#137) не раскладывается в SxxEyy, который ждёт Jellyfin — нужен пересчёт абсолютной нумерации -- **Теги:** goal:complex-releases Релизы аниме часто нумеруют серии сквозным числом (#137) без сезонов, а Jellyfin ждёт SxxEyy. Нужен пересчёт абсолютной нумерации в сезон/серию — надёжнее всего через TVDB (там есть absolute order). Отдельный крайний случай распознавания; на стороне ревью — веб-хелпер «absolute → S·E». diff --git a/tasks/items/auto-link-confidence-gate.md b/tasks/items/auto-link-confidence-gate.md index 4991f49..3b15dcf 100644 --- a/tasks/items/auto-link-confidence-gate.md +++ b/tasks/items/auto-link-confidence-gate.md @@ -3,7 +3,6 @@ - **Тип:** feature - **Категория:** Ядро продукта — тот же internal/recognize, что и первая задача — окно открыто; решение принято 2026-07-08, осталось снять пере-применение дефолта, понизить 0.85→0.7 и записать в спеку - **Зачем:** Решено (B): гейт оставляем как доп. проверку на ревью — выключаемый порог, дефолт 0.85→0.7, записать в спеку -- **Теги:** goal:recognition-accuracy Аудит спек↔код (2026-07-03) нашёл расхождение: спека recognition считает `confidence` вспомогательным сигналом (условия авто — только матч в базе + diff --git a/tasks/items/bulk-download-management.md b/tasks/items/bulk-download-management.md deleted file mode 100644 index 1acfbe4..0000000 --- a/tasks/items/bulk-download-management.md +++ /dev/null @@ -1,23 +0,0 @@ -# 🎯 Загрузки удаляются пачкой, а не по одной - -- **Тип:** goal -- **Секция:** Направления -- **Зачем:** удаление раздачи с файлами доступно только по одной кнопке на странице одной загрузки — уборка десятка раздач превращается в десяток заходов - -Ради чего: разложенные раздачи копятся, и убирают их обычно скопом — после -просмотра сезона, при чистке диска, после серии неудачных заливок. Сегодня -удаление живёт только в danger-секции страницы одной загрузки, поэтому уборка -десяти раздач стоит десяти проходов «список → карточка → подтверждение». - -При этом удаление с файлами необратимо: оно зовёт `torrents/delete` с -`deleteFiles=true`, а гард последней копии там выключен сознательно -(инвариант «Источник неприкосновенен», исключение 1). Групповой режим обязан -сделать уборку дешевле, не сделав ошибку дешевле. - -## Завершение - -Достигнута, когда человек убирает любое число раздач одним проходом: выбирает -их в списке, один раз подтверждает удаление по перечню, где каждая раздача -названа поимённо, и видит поимённый результат — что снесено, что отказало и -почему. Ни одна раздача не сносится без того, чтобы человек увидел её в -подтверждении. diff --git a/tasks/items/candidate-match-strength.md b/tasks/items/candidate-match-strength.md index 1e126dd..293f54b 100644 --- a/tasks/items/candidate-match-strength.md +++ b/tasks/items/candidate-match-strength.md @@ -3,7 +3,6 @@ - **Тип:** research - **Категория:** Ядро продукта - **Зачем:** у кандидата метабазы нет метрики силы совпадения — список кандидатов на ревью нечем отсортировать по уверенности (сперва проработать процесс матчинга) -- **Теги:** goal:recognition-accuracy У кандидата метабазы нет метрики силы совпадения (metadata_candidate хранит provider/id/title/year/url), решение «авто vs review» — по правилу «единственный сильный матч + валидация», не по числу. Для ревью: список кандидатов нечем отсортировать/подсветить по уверенности. Идея — ввести на этапе матча силу совпадения кандидата (точное совпадение названия+года vs частичное) для сортировки и подсказки в UI. Шире — продумать сам процесс распознавания и матчинга: границы «разбор LLM / поиск в базе / сверка», что храним у кандидата, как считаем и показываем уверенность. diff --git a/tasks/items/completion-webhook.md b/tasks/items/completion-webhook.md index a142405..44ceded 100644 --- a/tasks/items/completion-webhook.md +++ b/tasks/items/completion-webhook.md @@ -3,7 +3,6 @@ - **Тип:** research - **Категория:** Инфраструктура - **Зачем:** завершение сейчас ловим поллингом qBittorrent — webhook реагировал бы быстрее, но связывает нас с его конфигом (решим по опыту эксплуатации) -- **Теги:** goal:ingest-and-review-interfaces Сейчас завершение ловим поллингом qBittorrent раз в несколько секунд. Альтернатива: «Run external program on torrent completion» в qBittorrent дёргает эндпоинт jellybit. Реагирует быстрее, но связывает нас с конфигом qBittorrent. diff --git a/tasks/items/complex-releases.md b/tasks/items/complex-releases.md deleted file mode 100644 index 89d9f59..0000000 --- a/tasks/items/complex-releases.md +++ /dev/null @@ -1,12 +0,0 @@ -# 🎯 Раскладывается не только типовая раздача - -- **Тип:** goal -- **Секция:** Направления -- **Зачем:** типовая раздача раскладывается, а всё, что сложнее одного сезона одного тайтла, упирается в ручной разбор -- **Теги:** decomposed - -Ради чего: сериальные паки, докачивание, аниме со сквозной нумерацией, образы дисков и внешние субтитры — это ровно тот контент, ради которого проект и заводился вместо arr-стека. - -## Завершение - -Достигнута, когда сериальный пак, докачивание недостающих серий, аниме со сквозной нумерацией, образ диска и внешние субтитры раскладываются без ручного вмешательства в файлы на диске — либо честно уходят в ревью с названной причиной, а не молча кладутся неверно. diff --git a/tasks/items/complex-series-releases.md b/tasks/items/complex-series-releases.md index 477399d..daf7c4f 100644 --- a/tasks/items/complex-series-releases.md +++ b/tasks/items/complex-series-releases.md @@ -3,7 +3,6 @@ - **Тип:** research - **Категория:** Ядро продукта - **Зачем:** сложные раздачи (все сезоны разом, паки, смешанная нумерация) целостно не проработаны — распознавание/ревью/раскладка заточены под один сезон -- **Теги:** goal:complex-releases Обычный случай — один сезон (его номер видно глазами и сверяем на ревью — под это сделана сводка сезонов). Но в редких заказах раздача сложнее: все сезоны сериала разом, пак нескольких сезонов, смешанная нумерация, вложенные папки сезонов, разнобойные имена файлов. Сейчас PlanFile.Season задаётся на каждом файле (мультисезон в принципе выразим), но целостно эти сценарии не проработаны: как надёжно распознать, как показать на ревью, как разложить и как стыкуется со сходимостью папки и merge-докачиванием. Решить, что поддерживаем явно, а что уводим в ревью как «сложную раскладку». diff --git a/tasks/items/convention-candidates.md b/tasks/items/convention-candidates.md index 6c8936e..fcf1278 100644 --- a/tasks/items/convention-candidates.md +++ b/tasks/items/convention-candidates.md @@ -3,7 +3,6 @@ - **Тип:** research - **Категория:** Инфраструктура - **Зачем:** накоплен список кандидатов (внешние клиенты, конкурентность, тесты, CLI, время) — надо решить, что из них стало реальным трением, а что выдумано вперёд -- **Теги:** goal:dev-process-quality Список копился в черновике `docs/drafts/conventions-backlog.md` (удалён при переводе на канон, текст в истории git) под правилом «пишем по мере реального @@ -46,7 +45,7 @@ - **Время.** Явный TZ всегда, хранение и логи в UTC. Уже частично в `CLAUDE.md` и `conventions/logging.md`, а `time.Now` вне `store` запрещён линтером — этот пункт, вероятно, закрыт и подлежит вычёркиванию. -- **Язык вывода связан с мапперами.** Провенанс — ревью `tvdb-title-locale` +- **Язык вывода связан с мапперами.** Откуда — ревью `tvdb-title-locale` (2026-08-07, [отчёт триажа](../../openspec/changes/archive/2026-08-07-tvdb-title-locale/review/report.md), находка R2 прохода `architecture`). Язык вывода живёт в пяти местах четырёх diff --git a/tasks/items/db-retention-cleanup.md b/tasks/items/db-retention-cleanup.md index 4bf0867..b2e8b18 100644 --- a/tasks/items/db-retention-cleanup.md +++ b/tasks/items/db-retention-cleanup.md @@ -3,7 +3,6 @@ - **Тип:** feature - **Категория:** Инфраструктура - **Зачем:** терминальные задачи и сырые ответы LLM копятся вечно — без авточистки список загрузок и БД деградируют по мере эксплуатации -- **Теги:** goal:operational-resilience Терминальные задачи (done/cancelled/failed/reverted), их попытки recognition с сырыми ответами LLM и metadata_candidate копятся вечно — БД и список загрузок распухают и становятся нечитаемыми. Нужна авточистка старше N дней (настройка в [storage] или [worker]) и/или ручное удаление. Маленькая задача, но без неё интерфейс деградирует по мере эксплуатации. diff --git a/tasks/items/deep-healthcheck-dependencies.md b/tasks/items/deep-healthcheck-dependencies.md index 657da5e..3b62462 100644 --- a/tasks/items/deep-healthcheck-dependencies.md +++ b/tasks/items/deep-healthcheck-dependencies.md @@ -3,7 +3,6 @@ - **Тип:** feature - **Категория:** Инфраструктура - **Зачем:** /healthz проверяет только сам сервис — недоступность qBittorrent/LLM/метабазы видна лишь по застрявшим задачам, нет readiness и бейджа в UI -- **Теги:** goal:operational-resilience /healthz проверяет только сам сервис. Если qBittorrent, LLM или метабаза недоступны — узнаёшь лишь по застрявшим задачам. Нужна readiness-проверка ключевых зависимостей и отражение их состояния в UI (бейдж «qBittorrent недоступен»), чтобы причина простоя была видна сразу. diff --git a/tasks/items/delete-checks-active-infohash-owner.md b/tasks/items/delete-checks-active-infohash-owner.md index bf600d7..5f794a4 100644 --- a/tasks/items/delete-checks-active-infohash-owner.md +++ b/tasks/items/delete-checks-active-infohash-owner.md @@ -3,7 +3,6 @@ - **Тип:** fix - **Категория:** Ядро продукта - **Зачем:** удаление старой закрытой задачи уничтожает файлы живой загрузки с тем же инфохэшем; воспроизведено падающим тестом на ревью bulk-delete-page -- **Теги:** goal:state-integrity `Delete` зовёт `torrents/delete` с `deleteFiles=true` по хешам своей записи и не спрашивает, не владеет ли этим инфохэшем другая **активная** загрузка. Человек diff --git a/tasks/items/delete-leaves-stale-done.md b/tasks/items/delete-leaves-stale-done.md index 7389837..b654e50 100644 --- a/tasks/items/delete-leaves-stale-done.md +++ b/tasks/items/delete-leaves-stale-done.md @@ -3,7 +3,6 @@ - **Тип:** fix - **Категория:** Ядро продукта - **Зачем:** при недоступном qBittorrent задача весь простой соседа показывает done, хотя тайтла в Jellyfin уже нет: сверка падает на первом шаге и до коррекции не доходит -- **Теги:** goal:state-integrity `Delete` снимает библиотечные ссылки раньше, чем зовёт `qbt.Delete`. Если сосед недоступен, локальный шаг проходит, внешний падает, и задача остаётся в `done`. diff --git a/tasks/items/dev-process-quality.md b/tasks/items/dev-process-quality.md deleted file mode 100644 index 8865937..0000000 --- a/tasks/items/dev-process-quality.md +++ /dev/null @@ -1,12 +0,0 @@ -# 🎯 Домен называется одинаково везде, ревью откалибровано - -- **Тип:** goal -- **Секция:** Сопровождение -- **Зачем:** наименования домена расходятся между спеками, UI и кодом, а конвейер ревью не откалиброван — растёт цена каждой следующей задачи -- **Теги:** decomposed - -Ради чего: это не поведение продукта, а то, чем он делается. Единый словарь, калибровка проходов ревью и разбор накопленных кандидатов в конвенции — вложение в скорость всех остальных целей. - -## Завершение - -Достигнута, когда домен называется одинаково в спеках, коде и интерфейсе, а конвейер ревью откалиброван на журнале реальных дефектов, а не на догадках о том, что он ловит. diff --git a/tasks/items/disc-image-releases.md b/tasks/items/disc-image-releases.md index f949742..401f9cd 100644 --- a/tasks/items/disc-image-releases.md +++ b/tasks/items/disc-image-releases.md @@ -3,7 +3,6 @@ - **Тип:** feature - **Категория:** Ядро продукта - **Зачем:** раздача-образ Blu-ray разбирается пофайлово и едет в библиотеку россыпью .m2ts, а Jellyfin умеет такой каталог целиком -- **Теги:** goal:complex-releases Иногда для очень редкого фильма качается не один видеофайл, а полная копия диска — каталог `BDMV/` (Blu-ray) или `VIDEO_TS/` (DVD). Раскладка сегодня diff --git a/tasks/items/download-transition-history.md b/tasks/items/download-transition-history.md index c65dc89..1b47e61 100644 --- a/tasks/items/download-transition-history.md +++ b/tasks/items/download-transition-history.md @@ -3,7 +3,6 @@ - **Тип:** feature - **Категория:** Инфраструктура - **Зачем:** хранится только текущий статус загрузки — разбор «как сюда попали» идёт по логам сервера, нет таблицы истории переходов -- **Теги:** goal:state-integrity Сохранять полную историю переходов состояний загрузки (что/когда/почему/кто инициировал — воркер, человек, сверка), а не только текущее состояние. Сейчас по задаче виден лишь актуальный статус, разбор «как мы сюда попали» идёт по логам сервера. Отдельная таблица истории даёт лог переходов в карточке/расширенной информации и фундамент для метрик длительности стадий. Естественно ложится на собственный идентификатор загрузки и уже реализованный экран /download/{id}. diff --git a/tasks/items/episode-numbering-canon.md b/tasks/items/episode-numbering-canon.md index 417d1fc..565772f 100644 --- a/tasks/items/episode-numbering-canon.md +++ b/tasks/items/episode-numbering-canon.md @@ -3,7 +3,6 @@ - **Тип:** research - **Категория:** Ядро продукта - **Зачем:** Косметика/редкость: порядок просмотра ок, но у тайтлов со спорным порядком (Бибоп) Jellyfin подтягивает не те подписи серий, если канон файлов ≠ дефолтный порядок провайдера тега -- **Теги:** goal:complex-releases Косметика и редкий случай: порядок просмотра не страдает (файлы уже пронумерованы канонически и лежат по порядку), разъезжаются только подписи серий diff --git a/tasks/items/external-subtitles.md b/tasks/items/external-subtitles.md index 0548b8c..9898c13 100644 --- a/tasks/items/external-subtitles.md +++ b/tasks/items/external-subtitles.md @@ -3,7 +3,6 @@ - **Тип:** feature - **Категория:** Ядро продукта - **Зачем:** Привязка субтитр→серия уже работает; остались пары VobSub .idx+.sub и потеря Lang/Flags -- **Теги:** goal:complex-releases Базовая привязка субтитр→серия для сериала уже работает: `layout.PlanFile` несёт `Season/Episode`, а `seriesDst` именует субтитр по стему эпизода diff --git a/tasks/items/free-space-check-copy-fallback.md b/tasks/items/free-space-check-copy-fallback.md index 3a9dadc..d7c8bc7 100644 --- a/tasks/items/free-space-check-copy-fallback.md +++ b/tasks/items/free-space-check-copy-fallback.md @@ -3,7 +3,6 @@ - **Тип:** feature - **Категория:** Ядро продукта - **Зачем:** copy-fallback при невозможности хардлинка может упереться в переполненный диск посреди раскладки — нет проверки места до копирования -- **Теги:** goal:operational-resilience Когда хардлинк невозможен (EXDEV/ENOTSUP/…), layout копирует файл, дублируя место на диске. На забитом диске это упрётся в полку посреди раскладки. Перед копированием проверять доступное место и при нехватке внятно уходить в failed с понятной причиной, а не падать на полпути. diff --git a/tasks/items/guessit-sidecar.md b/tasks/items/guessit-sidecar.md index a271242..640d64d 100644 --- a/tasks/items/guessit-sidecar.md +++ b/tasks/items/guessit-sidecar.md @@ -3,7 +3,6 @@ - **Тип:** research - **Категория:** Ядро продукта - **Зачем:** go-ptn слабее питоновского guessit — если точности пред-парса не хватит, завернуть guessit в сервис-спутник рядом с бинарём -- **Теги:** goal:recognition-accuracy go-ptn слабее питоновского guessit. Если точности пред-парса не хватит — завернуть guessit в крошечный HTTP-сервис (один файл, поставляется рядом с бинарём jellybit) и спрашивать его на шаге пред-парса. Сохраняет «доставку копированием»: два файла вместо одного. diff --git a/tasks/items/infohash-identity-integrity.md b/tasks/items/infohash-identity-integrity.md index 5d2acf7..be97ab5 100644 --- a/tasks/items/infohash-identity-integrity.md +++ b/tasks/items/infohash-identity-integrity.md @@ -3,7 +3,6 @@ - **Тип:** research - **Категория:** Ядро продукта — сырьё, а не дефект: тело само не решает — «change по модели доверия/идентичности либо задокументировать как ограничение», и критерии приёмки писать не из чего; идёт на штурм, взять нельзя - **Зачем:** две находки ревью 2026-07-08 упираются в нерешённую модель идентичности: связывать ли v1- и v2-хеши одного торрента и что делать с парой xt, которую qBittorrent не подтверждал — merge, supersede или ограничение в документе -- **Теги:** goal:state-integrity Ревью Fable 2026-07-08 (приём). Две связанные находки о доверии к парам xt в magnet (предпосылки к F1). diff --git a/tasks/items/ingest-and-review-interfaces.md b/tasks/items/ingest-and-review-interfaces.md deleted file mode 100644 index 2d7326b..0000000 --- a/tasks/items/ingest-and-review-interfaces.md +++ /dev/null @@ -1,12 +0,0 @@ -# 🎯 Раздача приносится и подтверждается из любого транспорта - -- **Тип:** goal -- **Секция:** Направления -- **Зачем:** путь «принести раздачу и подтвердить догадку» упирается в незакрытые куски интерфейсов, а не в логику -- **Теги:** decomposed - -Ради чего: приём и ревью — единственные места, где система встречается с человеком. Здесь копятся незакрытые куски: фетч по URL, редактор маппинга, привязка уведомлений к автору, латентность обновлений. - -## Завершение - -Достигнута, когда любой из поддержанных источников принимается одним действием из любого транспорта, а ревью позволяет довести план до применимого состояния без ухода в другой инструмент. diff --git a/tasks/items/merge-incremental-redownload.md b/tasks/items/merge-incremental-redownload.md index d10b69e..2fa1944 100644 --- a/tasks/items/merge-incremental-redownload.md +++ b/tasks/items/merge-incremental-redownload.md @@ -3,7 +3,6 @@ - **Тип:** feature - **Категория:** Ядро продукта - **Зачем:** повторная заливка сериала целиком должна доложить недостающие эпизоды merge-раскладкой, не трогая существующие ссылки — блокирует типовой сценарий свежих сериалов -- **Теги:** goal:complex-releases Свежий сериал раздают по мере выхода: торрент с 5 из 10 эпизодов позже перезаливают целиком, пользователь добавляет раздачу повторно. Новая загрузка приходит в ту же папку за счёт правила сходимости, а раскладка становится merge — доложить только недостающее. Существующие пути не трогаем (never-overwrite, владение у старой загрузки), новые кладём (владеет новая). Split-ownership сезона принят как норма per-path модели; обе раздачи сидируют независимо. diff --git a/tasks/items/metadata-cache.md b/tasks/items/metadata-cache.md index 252a391..075aef7 100644 --- a/tasks/items/metadata-cache.md +++ b/tasks/items/metadata-cache.md @@ -3,7 +3,6 @@ - **Тип:** chore - **Категория:** Инфраструктура - **Зачем:** повторные и ретраящиеся прогоны бьют TMDB/TVDB/TVMaze одним запросом — кэш с TTL сэкономил бы лимиты и ускорил «Распознать заново» -- **Теги:** goal:operational-resilience Повторные и ретраящиеся прогоны распознавания бьют TMDB/TVDB/TVMaze одним и тем же запросом. Кэш ответов с TTL экономит лимиты API и ускоряет «Распознать заново»/«Уточнить». При желании — кэш ответов LLM по хешу входа (но он менее полезен, т.к. вход меняется подсказками). diff --git a/tasks/items/multi-pass-verification.md b/tasks/items/multi-pass-verification.md index 55862c8..ea42ba1 100644 --- a/tasks/items/multi-pass-verification.md +++ b/tasks/items/multi-pass-verification.md @@ -3,7 +3,6 @@ - **Тип:** research - **Категория:** Ядро продукта - **Зачем:** несколько проходов распознавания с консенсусом подняли бы точность ценой стоимости/латентности — проработать, когда включать и как мерджить расхождения -- **Теги:** goal:recognition-accuracy Несколько раз извлекать данные из раздачи и контекста разными промптами, искать в метабазах, затем сводить результаты в общий вердикт (голосование/консенсус) — выше точность ценой нескольких вызовов LLM и запросов к базам. Проработать: когда включать, как мерджить расхождения, стоимость/латентность. diff --git a/tasks/items/naming-edge-cases.md b/tasks/items/naming-edge-cases.md index 1da1822..e77bd75 100644 --- a/tasks/items/naming-edge-cases.md +++ b/tasks/items/naming-edge-cases.md @@ -3,7 +3,6 @@ - **Тип:** feature - **Категория:** Ядро продукта - **Зачем:** стэкинг частей (part1/cd1), редакции [edition-…] и двойная серия SxxEyy-Eyy описаны нарративом, но в file-layout не заказаны — раскладка таких раздач не определена -- **Теги:** goal:complex-releases Целевые имена для типового фильма и типового сезона заказаны [file-layout](../../openspec/specs/file-layout/spec.md). Крайние случаи там diff --git a/tasks/items/notification-source-binding.md b/tasks/items/notification-source-binding.md index a4e9f78..21a1e19 100644 --- a/tasks/items/notification-source-binding.md +++ b/tasks/items/notification-source-binding.md @@ -3,7 +3,6 @@ - **Тип:** feature - **Категория:** Ядро продукта - **Зачем:** пинги и ревью должен получать автор загрузки в своём транспорте — нет привязки загрузки к источнику/отправителю (нужно для мульти-бота) -- **Теги:** goal:ingest-and-review-interfaces Уведомления и запросы подтверждения должен получать тот, кто прислал загрузку: автор сообщения о новой раздаче — адресат пингов и ревью по ней. Транспортов-ботов может быть несколько (Telegram, в перспективе Matrix и др.); каждый адресует «своему» отправителю. Веб-интерфейс остаётся единым для всех и точкой правды по функциональности (боты — тонкие адаптеры над тем же ядром). Нужно: хранить у загрузки источник/транспорт и идентификатор отправителя, маршрутизировать пинги по нему. diff --git a/tasks/items/operational-resilience.md b/tasks/items/operational-resilience.md deleted file mode 100644 index 6075ccb..0000000 --- a/tasks/items/operational-resilience.md +++ /dev/null @@ -1,12 +0,0 @@ -# 🎯 Сервис переживает рост и потерю тома - -- **Тип:** goal -- **Секция:** Сопровождение -- **Зачем:** сервис работает, но не переживает роста: база копится вечно, бэкапа нет, отказ зависимости виден только по застрявшим задачам -- **Теги:** decomposed - -Ради чего: сегодня всё держится на том, что загрузок мало и всё рядом работает. Ретеншена нет, бэкапа нет, глубокого healthcheck нет, поведение под сотней загрузок не мерялось. - -## Завершение - -Достигнута, когда база не растёт бесконечно, состояние переживает потерю тома, отказ любой зависимости виден владельцу раньше, чем по застрявшим задачам, и поведение под сотней одновременных загрузок измерено, а не предположено. diff --git a/tasks/items/quality-review-agents.md b/tasks/items/quality-review-agents.md index a10ab44..9118fc6 100644 --- a/tasks/items/quality-review-agents.md +++ b/tasks/items/quality-review-agents.md @@ -2,8 +2,7 @@ - **Тип:** chore - **Категория:** Инфраструктура — уступила первую строку: ready не проходит, а часть про ревьювер наименований ждёт словарь единого языка -- **Зачем:** конвейер ревью переехал в плагин `av-dev-pipeline`; осталась калибровка проходов на этом проекте и ревьювер наименований (ждёт словарь единого языка) -- **Теги:** goal:dev-process-quality +- **Зачем:** конвейер ревью переехал в плагин `av-dev`; осталась калибровка проходов на этом проекте и ревьювер наименований (ждёт словарь единого языка) Набор проходов ревью поверх ревью-процесса из CLAUDE.md. Развивает ревью-процесс OpenSpec в сторону воспроизводимых автопроверок, не заменяя человеческое ревью. @@ -49,7 +48,7 @@ OpenSpec в сторону воспроизводимых автопроверо оптикой не выделен: зависит от задачи «Словарь единого языка», без глоссария проверять не по чему. Завести после неё. - **Калибровка проходов** по процедуре `references/calibration.md` скилла - `av-dev-pipeline:review-pipeline` — ни один проход ещё не замерен инъекцией. + `av-dev:code-review` — ни один проход ещё не замерен инъекцией. До замера ничего не удаляем и промпты не правим. - **Проходы не сообщают свой потолок находок.** На прогоне `tvdb-title-locale` (2026-08-07) о потолке промолчали четверо из шести — `autotests`, `specs`, diff --git a/tasks/items/recognition-accuracy.md b/tasks/items/recognition-accuracy.md deleted file mode 100644 index fb06326..0000000 --- a/tasks/items/recognition-accuracy.md +++ /dev/null @@ -1,14 +0,0 @@ -# 🎯 Раздача узнаётся верно без подсказок человека - -- **Тип:** goal -- **Секция:** Направления -- **Зачем:** распознавание ошибается молча и правдоподобно, а смена модели или правка промпта идёт вслепую — сдвига точности не видно ни до, ни после -- **Теги:** decomposed - -Ради чего: распознавание — единственное место, где система может ошибиться молча и правдоподобно. Сегодня о его точности судят по впечатлению, и сдвиг от смены модели или правки промпта заметен только задним числом. - -Размеченный корпус и обучение на правках человека из этой цели исключены сознательно (`REJECTED.md`, 2026-08-06): базу руками не собираем, точность поднимаем тюнингом автоматического распознавания. - -## Завершение - -Достигнута, когда решение auto/review опирается на измеримую силу совпадения с записью метабазы, а не на самооценку модели, и доля раздач, ушедших в ревью или поправленных после авто-раскладки, видна по рабочему потоку и не растёт от версии к версии. diff --git a/tasks/items/review-mapping-editor.md b/tasks/items/review-mapping-editor.md index 948b9aa..11ab7bd 100644 --- a/tasks/items/review-mapping-editor.md +++ b/tasks/items/review-mapping-editor.md @@ -3,7 +3,6 @@ - **Тип:** feature - **Категория:** Ядро продукта - **Зачем:** правка S·E, «нумеровать подряд» и ручной режим при полном провале LLM были запланированы объёмом Ф5 и не заведены задачей — в ревью сегодня можно только подсказать текстом -- **Теги:** goal:ingest-and-review-interfaces Когда распознавание разложило файлы по сериям неверно, единственный путь — подсказать текстом и перераспознать. Точечно поправить номер серии у одного diff --git a/tasks/items/scale-100-downloads.md b/tasks/items/scale-100-downloads.md index 2c94b45..ba62a9e 100644 --- a/tasks/items/scale-100-downloads.md +++ b/tasks/items/scale-100-downloads.md @@ -3,7 +3,6 @@ - **Тип:** research - **Категория:** Инфраструктура - **Зачем:** Зафиксировать в НФТ ориентир 100/1000 загрузок + аудит узких мест (SQLite, воркер, поллинг) -- **Теги:** goal:operational-resilience Потолок по нагрузке нигде не зафиксирован: воркер, поллинг qBittorrent, пул LLM-вызовов и запись в SQLite спроектированы «на глаз». Записать в НФТ целевой ориентир — архитектура держит до 100 одновременных загрузок в работе (приём → распознавание → раскладка), план-максимум — 1000. Сама запись требования дешева и высокоценна: задаёт рамку для решений ниже. Отдельно (дороже) — аудит узких мест: одиночное соединение SQLite и сериализация записи, конкурентность воркера и лимит параллельных распознаваний, частота/стоимость поллинга и дедуп при наплыве. diff --git a/tasks/items/sqlite-backup.md b/tasks/items/sqlite-backup.md index 1ecec9b..a2b608e 100644 --- a/tasks/items/sqlite-backup.md +++ b/tasks/items/sqlite-backup.md @@ -3,7 +3,6 @@ - **Тип:** chore - **Категория:** Инфраструктура - **Зачем:** architecture требует бекапить data-том, но стратегия не описана — сбой или редеплой стирают всё in-flight состояние (проще, пока БД маленькая) -- **Теги:** goal:operational-resilience architecture.md требует «бекапить data-том», но как — не описано. Без понятной стратегии сбой или редеплой стирают всё in-flight состояние. Зафиксировать решение и реализовать: периодический VACUUM INTO в /data/backups по расписанию (с ротацией) либо потоковая репликация (litestream). Лучше сделать, пока БД маленькая. diff --git a/tasks/items/sse-live-updates.md b/tasks/items/sse-live-updates.md index dc95513..61bc65f 100644 --- a/tasks/items/sse-live-updates.md +++ b/tasks/items/sse-live-updates.md @@ -3,7 +3,6 @@ - **Тип:** feature - **Категория:** Ядро продукта - **Зачем:** живые обновления на htmx-поллинге дают задержку и холостые запросы — SSE убрал бы то и другое (поллинг работает, поэтому улучшение, не блокер) -- **Теги:** goal:ingest-and-review-interfaces Живые обновления прогресса сейчас на htmx-поллинге (фаза 2 веб-UI) — просто и работает, но с задержкой в интервал опроса и холостыми запросами. Перевести динамический контент (прогресс загрузки, смена статуса, раздача) на Server-Sent Events, чтобы обновления приходили почти мгновенно и без лишнего поллинга. Поллинг работает, поэтому это улучшение, а не блокер; SSE — один долгоживущий ответ на соединение, ложится на server-rendered UI без тяжёлого фронтенда. diff --git a/tasks/items/state-integrity.md b/tasks/items/state-integrity.md deleted file mode 100644 index 53027f9..0000000 --- a/tasks/items/state-integrity.md +++ /dev/null @@ -1,12 +0,0 @@ -# 🎯 По записи загрузки видно, как она сюда попала - -- **Тип:** goal -- **Секция:** Направления -- **Зачем:** известные окна рассинхрона и потери маркеров: каждое по отдельности самоисцеляется, вместе — источник необъяснимых состояний -- **Теги:** decomposed - -Ради чего: состояние загрузки — то, по чему судят обо всём остальном. Накопились известные щели: окно namer'а, идентичность split v1/v2, потеря маркера dismiss, отсутствие истории переходов. - -## Завершение - -Достигнута, когда по записи загрузки можно ответить «как она сюда попала», ни один известный сегодня путь не оставляет состояние, которое не объясняется историей переходов, и идентичность раздачи не подделывается входом. diff --git a/tasks/items/telegram-messages-audit.md b/tasks/items/telegram-messages-audit.md index d193f7f..7a21ec6 100644 --- a/tasks/items/telegram-messages-audit.md +++ b/tasks/items/telegram-messages-audit.md @@ -3,7 +3,6 @@ - **Тип:** research - **Категория:** Ядро продукта - **Зачем:** зонтичный проход по всем текстам бота: полнота карточек, единый язык, оформление; порождает под-задачи -- **Теги:** goal:ingest-and-review-interfaces Зонтичная задача: пройтись по всем исходящим уведомлениям и запросам подтверждения бота, выправить формулировки, состав данных и оформление. Тексты формируются в diff --git a/tasks/items/tests-convention.md b/tasks/items/tests-convention.md index 2c6cf47..c0b8772 100644 --- a/tasks/items/tests-convention.md +++ b/tasks/items/tests-convention.md @@ -24,7 +24,7 @@ **Два кандидата пришли из ревью `tvdb-title-locale`** (2026-08-07, [отчёт триажа](../../openspec/changes/archive/2026-08-07-tvdb-title-locale/review/report.md) -→ «Promote candidates»), оба с провенансом прохода, а не из головы: +→ «Promote candidates»), оба с названным проходом, а не из головы: - **один стенд чужого API на пакет.** В `tvdb_test.go` завелись два фейка одного и того же API. Когда форма ответа поменяется по факту разведки, забытый diff --git a/tasks/items/title-versions-repacks.md b/tasks/items/title-versions-repacks.md index 7282cb1..81c2713 100644 --- a/tasks/items/title-versions-repacks.md +++ b/tasks/items/title-versions-repacks.md @@ -3,7 +3,6 @@ - **Тип:** feature - **Категория:** Ядро продукта - **Зачем:** По калибровке болей (2026-07-02) — не боль, из приоритета выпало -- **Теги:** goal:complex-releases По калибровке болей (2026-07-02) — не боль, из приоритета выпало. Сосуществование версий доступно уже сейчас (Jellyfin multi-version, другой целевой путь), коллизия на тот же путь штатно уходит в review. Явный replace (undo старого хардлинка → lay нового → супересид владения путём) — отдельный change, если/когда станет болью. diff --git a/tasks/items/torrent-url-fetch.md b/tasks/items/torrent-url-fetch.md index 302f0f7..f62c6ad 100644 --- a/tasks/items/torrent-url-fetch.md +++ b/tasks/items/torrent-url-fetch.md @@ -3,7 +3,6 @@ - **Тип:** feature - **Категория:** Ядро продукта - **Зачем:** magnet и .torrent-файл приняты; остался фетч .torrent по URL (нужен SSRF-гард) -- **Теги:** goal:ingest-and-review-interfaces Приём magnet и `.torrent`-файла уже реализован: ветка `TorrentData → torrent.Parse` (`internal/ingest/ingest.go`), файл-пикер в веб-форме (`web/templates/index.html`), diff --git a/tasks/items/tvdb-search-response-live-check.md b/tasks/items/tvdb-search-response-live-check.md index 5e8c406..a5a64ea 100644 --- a/tasks/items/tvdb-search-response-live-check.md +++ b/tasks/items/tvdb-search-response-live-check.md @@ -3,7 +3,6 @@ - **Тип:** research - **Категория:** Ядро продукта — дешевле всех и проверяет только что сделанное: при иной форме ответа TheTVDB локализация молча уходит в фолбэк, а гейт зелёный - **Зачем:** форма ответа поиска TheTVDB принята по swagger 4.7.10 и живым прогоном не подтверждена — при иной форме разбор молча уходит в фолбэк, гейт зелёный, локализованное название не работает -- **Теги:** goal:recognition-accuracy Задача `tvdb-title-locale` научила клиент TVDB брать локализованное название из блока переводов ответа `/search` и заполнять `OriginalTitle` primary name'ом. Но diff --git a/tasks/items/ubiquitous-language-glossary.md b/tasks/items/ubiquitous-language-glossary.md index f8860e9..76dea0f 100644 --- a/tasks/items/ubiquitous-language-glossary.md +++ b/tasks/items/ubiquitous-language-glossary.md @@ -3,7 +3,6 @@ - **Тип:** chore - **Категория:** Инфраструктура - **Зачем:** наименования домена расходятся между спеками, UI и кодом — нет единого глоссария (на нём же стоит агент-ревьювер наименований) -- **Теги:** goal:dev-process-quality Свести термины домена в один глоссарий, чтобы пользователь, документация, код и агент говорили на одном языке: загрузка, раздача, распознавание, матч, кандидат, раскладка, источник/цель, хардлинк, ревью, переход состояния и т.д. — русский термин, английский идентификатор в коде, краткое определение. Сейчас наименования расходятся между спеками, UI и кодом. Глоссарий — источник истины по именам; на нём же строится агент-ревьювер наименований. diff --git a/tasks/items/web-ui-auth.md b/tasks/items/web-ui-auth.md index 58a47be..2fd7f8b 100644 --- a/tasks/items/web-ui-auth.md +++ b/tasks/items/web-ui-auth.md @@ -3,7 +3,6 @@ - **Тип:** feature - **Категория:** Инфраструктура - **Зачем:** для v1 решено без авторизации (доверенная LAN, опц. allowlist подсетей) — задел на случай, если понадобится защита -- **Теги:** goal:ingest-and-review-interfaces Решено для v1: без авторизации в доверенной LAN, опц. allowlist подсетей (http.trusted_subnets) — как умеет qBittorrent. Если понадобится защита: токен/Basic в самом приложении или вынос за reverse-proxy с аутентификацией. diff --git a/tasks/items/web-ui-pwa.md b/tasks/items/web-ui-pwa.md index 191b274..470d960 100644 --- a/tasks/items/web-ui-pwa.md +++ b/tasks/items/web-ui-pwa.md @@ -3,7 +3,6 @@ - **Тип:** feature - **Категория:** Ядро продукта - **Зачем:** текущий server-rendered UI функционален — PWA (устанавливаемое, удобное с телефона) это улучшение большого объёма, не блокер -- **Теги:** goal:ingest-and-review-interfaces Переделать веб-интерфейс в современное PWA-приложение (устанавливаемое, отзывчивое, удобное с телефона). Текущий server-rendered UI функционален, поэтому это улучшение, а не блокер; большой объём работы.