diff --git a/CLAUDE.md b/CLAUDE.md index 378079a..c401a11 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -117,7 +117,7 @@ Go 1.26, один статический бинарь (`CGO_ENABLED=0`). Module - Ничего не гоняется против **живого** qBittorrent, LLM и метабаз: интеграционные тесты за env-гейтами, запускает человек вручную. - Качество распознавания гейтом не проверяется вовсе и проверяться не будет: - размеченный корпус решено не собирать (`docs/tasks/REJECTED.md`, + размеченный корпус решено не собирать (`tasks/REJECTED.md`, 2026-08-06). Сдвиг точности виден только по рабочему потоку. ## Запреты @@ -149,9 +149,14 @@ Go 1.26, один статический бинарь (`CGO_ENABLED=0`). Module исключения инварианта выше); снятие последней копии данных; правка уже применённой миграции; `git push --force`; удаление или перезапись файла в библиотеке Jellyfin, которого мы не создавали. -- **Общий станок** — покрасневший `task gate` на `master` врывается в - замороженный спринт: пока он красный, ни одна задача не считается сделанной. -- **Ориентир по размеру спринта:** 5–8 задач. Ориентир, а не закон. +- **Что считается сломанным:** покрасневший `task gate` на `master`. Пока он + красный, ни одна задача не считается сделанной, и чинится он вперёд любой + задачи — станок общий. +- **Приоритет — это порядок строк в [tasks/BACKLOG.md](tasks/BACKLOG.md).** + Первая строка секции — то, что делают следующим. Порядок назначает человек на + груминге (`av-dev-tasks:groom`), машина его не выводит. +- **Ориентир по размеру порции разбора на груминге:** 5–8 задач. Ориентир, а не + закон. - **Что такое «сделана»:** пайплайн задачи пройден целиком (спека → код → оба чекпоинта ревью → archive) и критерии приёмки проверены поимённо. @@ -201,12 +206,12 @@ Go 1.26, один статический бинарь (`CGO_ENABLED=0`). Module - [docs/research/](docs/research/README.md) — наблюдения за чужими форматами. - [docs/adr/](docs/adr/README.md) — журнал решений, неизменяемый. - [docs/review.md](docs/review.md) — настройка конвейера ревью и журнал дефектов. -- [docs/tasks/](docs/tasks/BACKLOG.md) — задачи и цели: одна запись = один файл - в `items/` + строка в индексе. Ведётся скиллом `av-dev-pm:tasks`, ритуал - спринта — `av-dev-pm:session`. +- [tasks/](tasks/BACKLOG.md) — задачи и цели: одна запись = один файл + в `items/` + строка в индексе, порядок строк = приоритет. Ведётся скиллом + `av-dev-tasks:tasks`, разбор беклога — `av-dev-tasks:groom`. **Tududi** (проект `jellybit`, project_id 14) — только инбокс сырых идей. Идея -становится задачей, когда её оформляют файлом в `docs/tasks/items/`. +становится задачей, когда её оформляют файлом в `tasks/items/`. ## Конвенции кода diff --git a/README.md b/README.md index 7d0a004..73333d8 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/ROADMAP.md](docs/tasks/ROADMAP.md). +ссылке — в планах. Что дальше — [tasks/ROADMAP.md](tasks/ROADMAP.md). ## Документация @@ -64,7 +64,7 @@ REST API, веб-UI, Telegram-бот и CLI (`jellybit add`). [веб-UI](docs/conventions/web-ui.md). - [docs/adr/](docs/adr/README.md) — журнал решений (почему так), неизменяемый. - [docs/research/](docs/research/README.md) — наблюдения за чужими форматами. -- [docs/tasks/](docs/tasks/BACKLOG.md) — задачи и цели. +- [tasks/](tasks/BACKLOG.md) — задачи и цели. Раскладка документации задана каноном av-dev и проверяется шагом `canon` в `task gate`. diff --git a/docs/.pm.json b/docs/.pm.json index 0fa3929..df91dd2 100644 --- a/docs/.pm.json +++ b/docs/.pm.json @@ -1,4 +1,4 @@ { - "canon": 7, + "canon": 12, "migrations": "internal/store/migrations" } diff --git a/docs/adr/ADR-2026-08-07-tvdb-locale-reads-response.md b/docs/adr/ADR-2026-08-07-tvdb-locale-reads-response.md index 774c795..d2ce52c 100644 --- a/docs/adr/ADR-2026-08-07-tvdb-locale-reads-response.md +++ b/docs/adr/ADR-2026-08-07-tvdb-locale-reads-response.md @@ -53,7 +53,7 @@ - Критерий приёмки задачи «запрос поиска содержит параметр языка» выполнен быть не может и отменён этим решением. Расхождение вынесено вопросом человеку — - разведка [tvdb-search-response-live-check](../tasks/items/tvdb-search-response-live-check.md). + разведка [tvdb-search-response-live-check](../../tasks/items/tvdb-search-response-live-check.md). - **Решение опирается на документацию, а не на замер.** Семантика параметра и форма блока переводов живым API не подтверждены — [research/tvdb-search-translations.md](../research/tvdb-search-translations.md). diff --git a/docs/architecture.md b/docs/architecture.md index cd4aca7..e46a8cc 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -172,7 +172,7 @@ Jellyfin указывают на `movies`/`series`, а не на корень Места, где устройство знаемо тонкое: не дефекты, а принятые пока пробелы. Здесь только адрес и одна фраза — что именно не сделано; работа под каждым живёт -задачей в [tasks/BACKLOG.md](tasks/BACKLOG.md). Список нужен ревью: правка, +задачей в [tasks/BACKLOG.md](../tasks/BACKLOG.md). Список нужен ревью: правка, попавшая в такую область, стоит дороже, чем выглядит. | Область | Чего нет сегодня | Задача | diff --git a/docs/passport.md b/docs/passport.md index 92813d1..e85e864 100644 --- a/docs/passport.md +++ b/docs/passport.md @@ -1,7 +1,7 @@ # Паспорт проекта Зачем это и для кого. [architecture.md](architecture.md) отвечает «как -устроено», [tasks/ROADMAP.md](tasks/ROADMAP.md) — «что уже умеет», паспорт — +устроено», [tasks/ROADMAP.md](../tasks/ROADMAP.md) — «что уже умеет», паспорт — «зачем и для кого». ## Цель diff --git a/docs/review.md b/docs/review.md index 2ffccb6..63ba255 100644 --- a/docs/review.md +++ b/docs/review.md @@ -252,8 +252,8 @@ Go-сервиса и что здесь уже проскакивало. Устр **Ориентир частоты.** `medium` закрывает большинство задач, `large` рассчитана на 5–10% и приходится на крупную функциональность, а не на уборку: задача типа `chore` или `fix`, собранная из нитей прошлого ревью, идёт в `small` или -`medium`, даже когда трогает файл из перечней выше. `large` чаще одной задачи на -спринт означает ошибку в критерии, а не спринт из сложных задач. +`medium`, даже когда трогает файл из перечней выше. `large` чаще одной задачи из +десяти означает ошибку в критерии, а не полосу сложных задач подряд. ### Недоступно проверке diff --git a/docs/tasks/SPRINT.md b/docs/tasks/SPRINT.md deleted file mode 100644 index 4c6a29a..0000000 --- a/docs/tasks/SPRINT.md +++ /dev/null @@ -1,14 +0,0 @@ -# Спринт - -- **Цель:** [🎯 Раздача узнаётся верно без подсказок человека](items/recognition-accuracy.md) -- **Начат:** 2026-08-06 -- **Спринт:** `2026-08-06` - -Урожай спринта поднимается `tasks.py list --tag sprint:2026-08-06` — это первая порция переоценки на сессии. - -## Набор - -- [✨ Узаконить confidence-гейт авто-раскладки в спеке и сделать его выключаемым (дефолт 0.7)](items/auto-link-confidence-gate.md) — Решено (B): гейт оставляем как доп. проверку на ревью — выключаемый порог, дефолт 0.85→0.7, записать в спеку -- [🐞 Тормозить опрос qBittorrent бэкоффом при недоступности и эскалировать устойчивый сбой](items/background-error-noise.md) — недоступный qBittorrent опрашивается каждые 5 с и даёт WARN на каждом тике: нужен экспоненциальный бэкофф до минутного потолка со сбросом по первому успеху и ERROR на устойчивой деградации -- [🐞 Санитизировать название из метабазы перед подстановкой в план](items/metadata-title-sanitize.md) — plan.Title = match.Title подставляется ПОСЛЕ sanitizePlan — название из TMDB/TVDB уезжает в имя каталога Jellyfin дословно, с невидимыми символами и гомоглифами, и авто-раскладка это пропускает -- [🐞 Отправлять на ревью раздачу с непомещающимся именем вместо отказа](items/long-title-to-review.md) — название длиннее ~237 байт роняет раскладку в failed с текстом системной ошибки: пользователь видит «file name too long» вместо карточки ревью, где это чинится подсказкой diff --git a/scripts/gate.py b/scripts/gate.py index 13a631c..2d8d3ff 100755 --- a/scripts/gate.py +++ b/scripts/gate.py @@ -26,16 +26,20 @@ from pathlib import Path OUT_DIR = Path("tmp/gate") -# docs.py скилла av-dev-pm:canon — проверка раскладки docs/ против канона. +# Скрипты трёх плагинов av-dev. Каждый проверяет своё и ни один не тянет +# остальных: без своего шага дрейф соответствующего каталога перестаёт ловиться +# молча. +# docs.py — раскладка docs/ против канона (av-dev-docs:canon) +# tasks.py — согласованность индексов каталога задач (av-dev-tasks:tasks) +# openspec.py — форма openspec/config.yaml (av-dev-code:openspec) # Путь берётся из каталога маркетплейса (он без хеша версии, в отличие от -# cache/), переопределяется переменной DOCS_PY: переустановка плагина не должна +# cache/), переопределяется переменной: переустановка плагина не должна # требовать правки гейта. Если скрипта нет — шаг краснеет, а не пропускается: # молча пропущенная проверка даёт ложное ощущение проверенности. -DOCS_PY_DEFAULT = ( - Path.home() - / ".claude/plugins/marketplaces/av-dev-skills" - / "av-dev-pm/skills/canon/scripts/docs.py" -) +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" OK, FAIL, WARN, SKIP = "OK", "FAIL", "WARN", "SKIP" @@ -92,6 +96,34 @@ def step(name: str, cmd: list[str], hint: str = "", env: dict[str, str] | None = return ok +def plugin_step( + name: str, var: str, default: Path, plugin: str, args: list[str], hint: str +) -> None: + """Шаг, который гоняет скрипт плагина av-dev. + + Отсутствие скрипта краснеет, а не пропускается: молча пропущенная проверка + неотличима от пройденной. Коды выхода у всех трёх скриптов общие: 0 сошлось, + 1 дрейф, 2 ошибка употребления, 3 окружение, 4 внутренний сбой. Различать 1 + и остальные обязательно — дрейф чинится правкой проекта, прочее нет. + """ + script = Path(os.environ.get(var) or default) + if not script.is_file(): + record(FAIL, name, f"{script.name} не найден: {script} — переустанови" + f" плагин {plugin} или задай путь переменной {var}") + return + log = OUT_DIR / f"{name}.log" + proc = subprocess.run( + ["python3", str(script), *args], capture_output=True, text=True, + ) + log.write_text(proc.stdout + proc.stderr, encoding="utf-8") + if proc.returncode == 0: + record(OK, name) + elif proc.returncode == 1: + record(FAIL, name, f"{hint} → {log}") + else: + record(FAIL, name, f"{script.name} не отработал (код {proc.returncode}) → {log}") + + def main() -> int: OUT_DIR.mkdir(parents=True, exist_ok=True) base = base_rev(sys.argv) @@ -186,25 +218,31 @@ def main() -> int: # нетронутые плейсхолдеры и две сверки с кодом — включая «миграция изменена, # а database.md нет» (отдельного шага er-schema поэтому больше нет). # База диффа обязательна: без неё сверка миграций со схемой не гоняется. - docs_py = Path(os.environ.get("DOCS_PY") or DOCS_PY_DEFAULT) - if not docs_py.is_file(): - record(FAIL, "canon", f"docs.py не найден: {docs_py} — переустанови плагин" - " av-dev-pm или задай путь переменной DOCS_PY") - else: - log = OUT_DIR / "canon.log" - proc = subprocess.run( - ["python3", str(docs_py), "check", "--dir", ".", "--base", base], - capture_output=True, text=True, - ) - log.write_text(proc.stdout + proc.stderr, encoding="utf-8") - if proc.returncode == 0: - record(OK, "canon") - elif proc.returncode == 1: - record(FAIL, "canon", f"раскладка docs/ разошлась с каноном → {log}") - else: - # 2 — ошибка употребления, 3 — окружение, 4 — внутренний сбой. - # Это не дрейф документации, и чинится оно не правкой docs/. - record(FAIL, "canon", f"docs.py не отработал (код {proc.returncode}) → {log}") + plugin_step( + "canon", "DOCS_PY", DOCS_PY_DEFAULT, "av-dev-docs", + ["check", "--dir", ".", "--base", base], + "раскладка docs/ разошлась с каноном", + ) + + # --- Каталог задач --- + # Согласованность индексов с файлами items/: бездомная запись, строка без + # файла, порядок секций роадмапа, сырьё не в конце секции. docs.py этого не + # смотрит — каталог принадлежит другому плагину. + plugin_step( + "tasks", "TASKS_PY", TASKS_PY_DEFAULT, "av-dev-tasks", + ["check", "--dir", "tasks"], + "индексы задач разошлись с items/", + ) + + # --- Форма настройки OpenSpec --- + # Незаменённый пример в openspec/config.yaml читается как настроенный файл, + # а работает как пустой. Ловит это только свой шаг: docs.py про config.yaml + # молчит с канона 10. + plugin_step( + "openspec", "OPENSPEC_PY", OPENSPEC_PY_DEFAULT, "av-dev-code", + ["check", "--dir", "."], + "openspec/config.yaml разошёлся с канонической формой", + ) # --- Секреты --- # Гоняем всегда: секрет утекает из любого файла, не только из кода. diff --git a/docs/tasks/BACKLOG.md b/tasks/BACKLOG.md similarity index 87% rename from docs/tasks/BACKLOG.md rename to tasks/BACKLOG.md index 16acce5..12da4fb 100644 --- a/docs/tasks/BACKLOG.md +++ b/tasks/BACKLOG.md @@ -25,6 +25,9 @@ - [✨ Править на ревью маппинг «файл → серия» и раскладывать вручную при провале LLM](items/review-mapping-editor.md) — правка S·E, «нумеровать подряд» и ручной режим при полном провале LLM были запланированы объёмом Ф5 и не заведены задачей — в ревью сегодня можно только подсказать текстом - [✨ Заказать спекой крайние случаи именования: многофайловый фильм, редакции, двойная серия](items/naming-edge-cases.md) — стэкинг частей (part1/cd1), редакции [edition-…] и двойная серия SxxEyy-Eyy описаны нарративом, но в file-layout не заказаны — раскладка таких раздач не определена - [🔬 Форма ответа поиска TheTVDB и семантика параметра language](items/tvdb-search-response-live-check.md) — форма ответа поиска TheTVDB принята по swagger 4.7.10 и живым прогоном не подтверждена — при иной форме разбор молча уходит в фолбэк, гейт зелёный, локализованное название не работает +- [✨ Узаконить confidence-гейт авто-раскладки в спеке и сделать его выключаемым (дефолт 0.7)](items/auto-link-confidence-gate.md) — Решено (B): гейт оставляем как доп. проверку на ревью — выключаемый порог, дефолт 0.85→0.7, записать в спеку +- [🐞 Отправлять на ревью раздачу с непомещающимся именем вместо отказа](items/long-title-to-review.md) — название длиннее ~237 байт роняет раскладку в failed с текстом системной ошибки: пользователь видит «file name too long» вместо карточки ревью, где это чинится подсказкой +- [🐞 Санитизировать название из метабазы перед подстановкой в план](items/metadata-title-sanitize.md) — plan.Title = match.Title подставляется ПОСЛЕ sanitizePlan — название из TMDB/TVDB уезжает в имя каталога Jellyfin дословно, с невидимыми символами и гомоглифами, и авто-раскладка это пропускает - [🔬 Канон нумерации серий и порядок у провайдера тега](items/episode-numbering-canon.md) — Косметика/редкость: порядок просмотра ок, но у тайтлов со спорным порядком (Бибоп) Jellyfin подтягивает не те подписи серий, если канон файлов ≠ дефолтный порядок провайдера тега - [🔬 Тексты и формат уведомлений в Telegram](items/telegram-messages-audit.md) — зонтичный проход по всем текстам бота: полнота карточек, единый язык, оформление; порождает под-задачи - [🔬 guessit как сервис-спутник](items/guessit-sidecar.md) — go-ptn слабее питоновского guessit — если точности пред-парса не хватит, завернуть guessit в сервис-спутник рядом с бинарём @@ -43,6 +46,7 @@ - [✨ Чистить БД от терминальных задач и сырых ответов LLM старше срока хранения](items/db-retention-cleanup.md) — терминальные задачи и сырые ответы LLM копятся вечно — без авточистки список загрузок и БД деградируют по мере эксплуатации - [🧹 Свести термины домена в словарь единого языка](items/ubiquitous-language-glossary.md) — наименования домена расходятся между спеками, UI и кодом — нет единого глоссария (на нём же стоит агент-ревьювер наименований) - [🧹 Разобрать кандидатов по тестам и записать конвенцию](items/tests-convention.md) — как пишем тесты, не записано нигде: пункт «Тесты» в convention-candidates не пересматривали, он обещает фикстуры в testdata/, которых в проекте нет — а трение накопилось (четыре внешних клиента, fakeStore с инъекцией ошибок, env-гейты, флаки-прогон, diff-coverage) +- [🐞 Тормозить опрос qBittorrent бэкоффом при недоступности и эскалировать устойчивый сбой](items/background-error-noise.md) — недоступный qBittorrent опрашивается каждые 5 с и даёт WARN на каждом тике: нужен экспоненциальный бэкофф до минутного потолка со сбросом по первому успеху и ERROR на устойчивой деградации - [🔬 Потолок нагрузки: 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/REJECTED.md b/tasks/REJECTED.md similarity index 100% rename from docs/tasks/REJECTED.md rename to tasks/REJECTED.md diff --git a/docs/tasks/ROADMAP.md b/tasks/ROADMAP.md similarity index 100% rename from docs/tasks/ROADMAP.md rename to tasks/ROADMAP.md diff --git a/docs/tasks/items/anime-absolute-numbering.md b/tasks/items/anime-absolute-numbering.md similarity index 100% rename from docs/tasks/items/anime-absolute-numbering.md rename to tasks/items/anime-absolute-numbering.md diff --git a/docs/tasks/items/auto-link-confidence-gate.md b/tasks/items/auto-link-confidence-gate.md similarity index 99% rename from docs/tasks/items/auto-link-confidence-gate.md rename to tasks/items/auto-link-confidence-gate.md index db19f60..c7c0524 100644 --- a/docs/tasks/items/auto-link-confidence-gate.md +++ b/tasks/items/auto-link-confidence-gate.md @@ -37,7 +37,7 @@ согласованность, но `confidence` ниже порога → review. 4. **Конфиг-конвенция/дока:** описать ключ в `docs/conventions/config.md` (диапазон [0,1], 0 = выкл, дефолт 0.7) и поправить значение в таблице «Настройки с - числовым значением» [docs/database.md](../../database.md) — дом числа там, и + числовым значением» [docs/database.md](../../docs/database.md) — дом числа там, и при смене дефолта оно разъедется первым. Формулировки инварианта в `CLAUDE.md` и ложноположительного в `docs/review.md` уже приведены к «единственным гейтом не является» (канон 4, 2026-08-06) — переписывать их diff --git a/docs/tasks/items/background-error-noise.md b/tasks/items/background-error-noise.md similarity index 100% rename from docs/tasks/items/background-error-noise.md rename to tasks/items/background-error-noise.md diff --git a/docs/tasks/items/candidate-match-strength.md b/tasks/items/candidate-match-strength.md similarity index 100% rename from docs/tasks/items/candidate-match-strength.md rename to tasks/items/candidate-match-strength.md diff --git a/docs/tasks/items/completion-webhook.md b/tasks/items/completion-webhook.md similarity index 100% rename from docs/tasks/items/completion-webhook.md rename to tasks/items/completion-webhook.md diff --git a/docs/tasks/items/complex-releases.md b/tasks/items/complex-releases.md similarity index 100% rename from docs/tasks/items/complex-releases.md rename to tasks/items/complex-releases.md diff --git a/docs/tasks/items/complex-series-releases.md b/tasks/items/complex-series-releases.md similarity index 100% rename from docs/tasks/items/complex-series-releases.md rename to tasks/items/complex-series-releases.md diff --git a/docs/tasks/items/convention-candidates.md b/tasks/items/convention-candidates.md similarity index 97% rename from docs/tasks/items/convention-candidates.md rename to tasks/items/convention-candidates.md index a1b2ae7..6c8936e 100644 --- a/docs/tasks/items/convention-candidates.md +++ b/tasks/items/convention-candidates.md @@ -48,7 +48,7 @@ пункт, вероятно, закрыт и подлежит вычёркиванию. - **Язык вывода связан с мапперами.** Провенанс — ревью `tvdb-title-locale` (2026-08-07, - [отчёт триажа](../../../openspec/changes/archive/2026-08-07-tvdb-title-locale/review/report.md), + [отчёт триажа](../../openspec/changes/archive/2026-08-07-tvdb-title-locale/review/report.md), находка R2 прохода `architecture`). Язык вывода живёт в пяти местах четырёх пакетов, и теста, связывающего множество кодов языка с мапперами, нет: забытая ветка молча даст английский вывод вместо ошибки. Кандидат в правило diff --git a/docs/tasks/items/db-retention-cleanup.md b/tasks/items/db-retention-cleanup.md similarity index 100% rename from docs/tasks/items/db-retention-cleanup.md rename to tasks/items/db-retention-cleanup.md diff --git a/docs/tasks/items/deep-healthcheck-dependencies.md b/tasks/items/deep-healthcheck-dependencies.md similarity index 100% rename from docs/tasks/items/deep-healthcheck-dependencies.md rename to tasks/items/deep-healthcheck-dependencies.md diff --git a/docs/tasks/items/dev-process-quality.md b/tasks/items/dev-process-quality.md similarity index 100% rename from docs/tasks/items/dev-process-quality.md rename to tasks/items/dev-process-quality.md diff --git a/docs/tasks/items/disc-image-releases.md b/tasks/items/disc-image-releases.md similarity index 100% rename from docs/tasks/items/disc-image-releases.md rename to tasks/items/disc-image-releases.md diff --git a/docs/tasks/items/download-transition-history.md b/tasks/items/download-transition-history.md similarity index 100% rename from docs/tasks/items/download-transition-history.md rename to tasks/items/download-transition-history.md diff --git a/docs/tasks/items/episode-numbering-canon.md b/tasks/items/episode-numbering-canon.md similarity index 100% rename from docs/tasks/items/episode-numbering-canon.md rename to tasks/items/episode-numbering-canon.md diff --git a/docs/tasks/items/external-subtitles.md b/tasks/items/external-subtitles.md similarity index 100% rename from docs/tasks/items/external-subtitles.md rename to tasks/items/external-subtitles.md diff --git a/docs/tasks/items/free-space-check-copy-fallback.md b/tasks/items/free-space-check-copy-fallback.md similarity index 100% rename from docs/tasks/items/free-space-check-copy-fallback.md rename to tasks/items/free-space-check-copy-fallback.md diff --git a/docs/tasks/items/guessit-sidecar.md b/tasks/items/guessit-sidecar.md similarity index 100% rename from docs/tasks/items/guessit-sidecar.md rename to tasks/items/guessit-sidecar.md diff --git a/docs/tasks/items/infohash-identity-integrity.md b/tasks/items/infohash-identity-integrity.md similarity index 100% rename from docs/tasks/items/infohash-identity-integrity.md rename to tasks/items/infohash-identity-integrity.md diff --git a/docs/tasks/items/ingest-and-review-interfaces.md b/tasks/items/ingest-and-review-interfaces.md similarity index 100% rename from docs/tasks/items/ingest-and-review-interfaces.md rename to tasks/items/ingest-and-review-interfaces.md diff --git a/docs/tasks/items/long-title-to-review.md b/tasks/items/long-title-to-review.md similarity index 95% rename from docs/tasks/items/long-title-to-review.md rename to tasks/items/long-title-to-review.md index 6a01362..b0731d0 100644 --- a/docs/tasks/items/long-title-to-review.md +++ b/tasks/items/long-title-to-review.md @@ -3,7 +3,6 @@ - **Тип:** fix - **Категория:** Ядро продукта - **Зачем:** название длиннее ~237 байт роняет раскладку в failed с текстом системной ошибки: пользователь видит «file name too long» вместо карточки ревью, где это чинится подсказкой -- **Теги:** sprint:2026-08-06 Длина компонента пути ограничена файловой системой (255 байт на имя, минус расширение и суффиксы — практический потолок около 237). Название такой длины @@ -20,7 +19,7 @@ есть и его надо переиспользовать, а не изобретать. Провенанс: ревью изменения `tvdb-title-locale`, находка AD4 — -[отчёт триажа](../../../openspec/changes/archive/2026-08-07-tvdb-title-locale/review/report.md). +[отчёт триажа](../../openspec/changes/archive/2026-08-07-tvdb-title-locale/review/report.md). Понижена там не по доказательности, а по принадлежности к диффу: путь этим изменением не тронут. @@ -59,7 +58,7 @@ n=250 → Apply err=… file name too long, results=0 **Оракул:** тот же тест — проверяет, что каталог цели пуст. - Причина, показанная человеку, не содержит текста системной ошибки. **Оракул:** утверждение теста на текст причины плюс конвенция - [errors.md](../../conventions/errors.md) — перевод доменной ошибки на внешней + [errors.md](../../docs/conventions/errors.md) — перевод доменной ошибки на внешней границе. - Проверка длины стоит **до** первой операции с файловой системой. **Оракул:** чтение диффа на ревью; тест на пустоту каталога цели его подтверждает. diff --git a/docs/tasks/items/merge-incremental-redownload.md b/tasks/items/merge-incremental-redownload.md similarity index 100% rename from docs/tasks/items/merge-incremental-redownload.md rename to tasks/items/merge-incremental-redownload.md diff --git a/docs/tasks/items/metadata-cache.md b/tasks/items/metadata-cache.md similarity index 100% rename from docs/tasks/items/metadata-cache.md rename to tasks/items/metadata-cache.md diff --git a/docs/tasks/items/metadata-title-sanitize.md b/tasks/items/metadata-title-sanitize.md similarity index 97% rename from docs/tasks/items/metadata-title-sanitize.md rename to tasks/items/metadata-title-sanitize.md index be0e0be..ccee22c 100644 --- a/docs/tasks/items/metadata-title-sanitize.md +++ b/tasks/items/metadata-title-sanitize.md @@ -3,7 +3,6 @@ - **Тип:** fix - **Категория:** Ядро продукта - **Зачем:** plan.Title = match.Title подставляется ПОСЛЕ sanitizePlan — название из TMDB/TVDB уезжает в имя каталога Jellyfin дословно, с невидимыми символами и гомоглифами, и авто-раскладка это пропускает -- **Теги:** sprint:2026-08-06 Название, пришедшее из метабазы, подставляется в план **после** того, как план прошёл санитизацию: `plan.Title = match.Title` стоит ниже `sanitizePlan`. @@ -22,7 +21,7 @@ а не `critical`. Провенанс: ревью изменения `tvdb-title-locale`, находка 2 — -[отчёт триажа](../../../openspec/changes/archive/2026-08-07-tvdb-title-locale/review/report.md). +[отчёт триажа](../../openspec/changes/archive/2026-08-07-tvdb-title-locale/review/report.md). ## Воспроизведение diff --git a/docs/tasks/items/multi-pass-verification.md b/tasks/items/multi-pass-verification.md similarity index 100% rename from docs/tasks/items/multi-pass-verification.md rename to tasks/items/multi-pass-verification.md diff --git a/docs/tasks/items/naming-edge-cases.md b/tasks/items/naming-edge-cases.md similarity index 94% rename from docs/tasks/items/naming-edge-cases.md rename to tasks/items/naming-edge-cases.md index 26b9a1e..1da1822 100644 --- a/docs/tasks/items/naming-edge-cases.md +++ b/tasks/items/naming-edge-cases.md @@ -6,7 +6,7 @@ - **Теги:** goal:complex-releases Целевые имена для типового фильма и типового сезона заказаны -[file-layout](../../../openspec/specs/file-layout/spec.md). Крайние случаи там +[file-layout](../../openspec/specs/file-layout/spec.md). Крайние случаи там не заказаны: до перевода на канон они жили разделом «Крайние случаи» нарратива `docs/specs/jellyfin-layout.md` (удалён, текст в истории git) как намерение, а не как требование. Значит, что делает код в этих случаях, без чтения кода @@ -24,7 +24,7 @@ - **Двойная серия в одном файле** — `… SxxEyy-Eyy`. - **Спецвыпуски** — `Season 00`. Сперва проверить, не покрыты ли уже требованием «Роли файлов на краях раздачи» в - [recognition](../../../openspec/specs/recognition/spec.md). + [recognition](../../openspec/specs/recognition/spec.md). ## Критерии приёмки diff --git a/docs/tasks/items/notification-source-binding.md b/tasks/items/notification-source-binding.md similarity index 100% rename from docs/tasks/items/notification-source-binding.md rename to tasks/items/notification-source-binding.md diff --git a/docs/tasks/items/operational-resilience.md b/tasks/items/operational-resilience.md similarity index 100% rename from docs/tasks/items/operational-resilience.md rename to tasks/items/operational-resilience.md diff --git a/docs/tasks/items/quality-review-agents.md b/tasks/items/quality-review-agents.md similarity index 91% rename from docs/tasks/items/quality-review-agents.md rename to tasks/items/quality-review-agents.md index f3c1aaf..da689db 100644 --- a/docs/tasks/items/quality-review-agents.md +++ b/tasks/items/quality-review-agents.md @@ -21,7 +21,7 @@ OpenSpec в сторону воспроизводимых автопроверо профили `quick`/`standard`/`deep`/`design`, контракт находок, границы покрытия, храповик «находка → конвенция → правило → удаление», журнал проскочивших дефектов и процедура калибровки. Подробности — ADR -[ADR-2026-07-23-review-pipeline-generative](../../adr/ADR-2026-07-23-review-pipeline-generative.md). +[ADR-2026-07-23-review-pipeline-generative](../../docs/adr/ADR-2026-07-23-review-pipeline-generative.md). Открытый вопрос «дробить ли проход по конвенциям на узкие оптики» закрыт: **не дробим** — декорреляция внимания без декорреляции суждения почти не @@ -32,14 +32,14 @@ OpenSpec в сторону воспроизводимых автопроверо Проектные копии агентов (`.claude/agents/jellybit-review-*`) и скиллов (`review-pipeline`, `task-pipeline`, `task-batch`) удалены в пользу плагина `av-dev-pipeline`. Проектная специфика теперь приходит из документов канона — -[docs/review.md](../../review.md): типовые узлы, ложноположительные, вопросы по +[docs/review.md](../../docs/review.md): типовые узлы, ложноположительные, вопросы по темам, триггеры метки, недоступное проверке. Два прохода плагин при этом **упразднил**, и это надо помнить: - `idiom` — поимённая сверка со стайлгайдами языка не задаётся теперь ни одним проходом; куда переселены способные части и почему класс признан обратимым — - [docs/review.md](../../review.md) → «Перестали проверять сознательно». + [docs/review.md](../../docs/review.md) → «Перестали проверять сознательно». - `negative` — вопрос «что опытный человек отсюда удалил бы» вошёл в `architecture` вторым обязательным. @@ -58,10 +58,10 @@ OpenSpec в сторону воспроизводимых автопроверо больше нет» неотличимо от «больше не поместилось» — то есть ровно тот сигнал, ради которого потолок и заведён. Первый замер калибровки стоит начать с этого: дефект прогона виден без инъекции. -- **Заполнить журнал дефектов** в [docs/review.md](../../review.md) случаями, +- **Заполнить журнал дефектов** в [docs/review.md](../../docs/review.md) случаями, которые уже проскочили ревью, — они станут первыми пробами калибровки. - **Решить судьбу упразднённых проходов:** нужен ли проекту свой `idiom` поверх плагина, или записи в «Недоступно проверке» достаточно. Связано: CLAUDE.md (ревью-процесс, конвенции), -[docs/conventions/](../../conventions/README.md), «Словарь единого языка». +[docs/conventions/](../../docs/conventions/README.md), «Словарь единого языка». diff --git a/docs/tasks/items/recognition-accuracy.md b/tasks/items/recognition-accuracy.md similarity index 100% rename from docs/tasks/items/recognition-accuracy.md rename to tasks/items/recognition-accuracy.md diff --git a/docs/tasks/items/review-mapping-editor.md b/tasks/items/review-mapping-editor.md similarity index 96% rename from docs/tasks/items/review-mapping-editor.md rename to tasks/items/review-mapping-editor.md index a10c883..948b9aa 100644 --- a/docs/tasks/items/review-mapping-editor.md +++ b/tasks/items/review-mapping-editor.md @@ -22,7 +22,7 @@ в вебе, из бота идёт эскалация по deep-link. Смежное: превью раскладки и единый список источников совпадения уже есть -([review](../../../openspec/specs/review/spec.md)), так что задача про +([review](../../openspec/specs/review/spec.md)), так что задача про редактирование плана, а не про его показ. ## Критерии приёмки diff --git a/docs/tasks/items/scale-100-downloads.md b/tasks/items/scale-100-downloads.md similarity index 100% rename from docs/tasks/items/scale-100-downloads.md rename to tasks/items/scale-100-downloads.md diff --git a/docs/tasks/items/sqlite-backup.md b/tasks/items/sqlite-backup.md similarity index 100% rename from docs/tasks/items/sqlite-backup.md rename to tasks/items/sqlite-backup.md diff --git a/docs/tasks/items/sse-live-updates.md b/tasks/items/sse-live-updates.md similarity index 100% rename from docs/tasks/items/sse-live-updates.md rename to tasks/items/sse-live-updates.md diff --git a/docs/tasks/items/state-integrity.md b/tasks/items/state-integrity.md similarity index 100% rename from docs/tasks/items/state-integrity.md rename to tasks/items/state-integrity.md diff --git a/docs/tasks/items/telegram-messages-audit.md b/tasks/items/telegram-messages-audit.md similarity index 96% rename from docs/tasks/items/telegram-messages-audit.md rename to tasks/items/telegram-messages-audit.md index 6daded2..d193f7f 100644 --- a/docs/tasks/items/telegram-messages-audit.md +++ b/tasks/items/telegram-messages-audit.md @@ -26,4 +26,4 @@ Итог аудита — конкретные под-задачи (эта их порождает). Проход дешёвый, при желании приоритет можно поднять. -Связано: `internal/tgbot`, [review](../../../openspec/specs/review/spec.md). +Связано: `internal/tgbot`, [review](../../openspec/specs/review/spec.md). diff --git a/docs/tasks/items/tests-convention.md b/tasks/items/tests-convention.md similarity index 96% rename from docs/tasks/items/tests-convention.md rename to tasks/items/tests-convention.md index 709a7b0..2c6cf47 100644 --- a/docs/tasks/items/tests-convention.md +++ b/tasks/items/tests-convention.md @@ -3,7 +3,6 @@ - **Тип:** chore - **Категория:** Инфраструктура - **Зачем:** как пишем тесты, не записано нигде: пункт «Тесты» в convention-candidates не пересматривали, он обещает фикстуры в testdata/, которых в проекте нет — а трение накопилось (четыре внешних клиента, fakeStore с инъекцией ошибок, env-гейты, флаки-прогон, diff-coverage) -- **Теги:** sprint:2026-08-06 В `docs/conventions/` пять записей, и ни одна не про тесты. Знание о том, как они здесь устроены, живёт в головах и в самих файлах: фикстуры чужих форматов @@ -24,7 +23,7 @@ то есть «что краснеет», а не «что стоит проверять». **Два кандидата пришли из ревью `tvdb-title-locale`** (2026-08-07, -[отчёт триажа](../../../openspec/changes/archive/2026-08-07-tvdb-title-locale/review/report.md) +[отчёт триажа](../../openspec/changes/archive/2026-08-07-tvdb-title-locale/review/report.md) → «Promote candidates»), оба с провенансом прохода, а не из головы: - **один стенд чужого API на пакет.** В `tvdb_test.go` завелись два фейка одного @@ -40,7 +39,7 @@ - `docs/conventions/tests.md` — новый файл записи конвенции; - `docs/conventions/README.md` — строка в индексе «Записи» и, возможно, строки в таблице «Механизировано»; -- `docs/tasks/items/convention-candidates.md` — пункт «Тесты» уходит из списка +- `tasks/items/convention-candidates.md` — пункт «Тесты» уходит из списка кандидатов; - `.golangci.yml` и `internal/archrules` — только если что-то из решённого выражается правилом, а не прозой. diff --git a/docs/tasks/items/title-versions-repacks.md b/tasks/items/title-versions-repacks.md similarity index 100% rename from docs/tasks/items/title-versions-repacks.md rename to tasks/items/title-versions-repacks.md diff --git a/docs/tasks/items/torrent-url-fetch.md b/tasks/items/torrent-url-fetch.md similarity index 93% rename from docs/tasks/items/torrent-url-fetch.md rename to tasks/items/torrent-url-fetch.md index e8df799..302f0f7 100644 --- a/docs/tasks/items/torrent-url-fetch.md +++ b/tasks/items/torrent-url-fetch.md @@ -17,6 +17,6 @@ это (magnet / ссылка на .torrent / .torrent-файл / сообщение бота). Сейчас текстовое поле идёт только через `magnet.Parse`. -Связано: [architecture.md](../../architecture.md) → «Внешние границы и форматы» +Связано: [architecture.md](../../docs/architecture.md) → «Внешние границы и форматы» (`source_type = magnet|torrent|url` уже в схеме), пакет `ingest`, архив change `torrent-file-ingest`. diff --git a/docs/tasks/items/tvdb-search-response-live-check.md b/tasks/items/tvdb-search-response-live-check.md similarity index 96% rename from docs/tasks/items/tvdb-search-response-live-check.md rename to tasks/items/tvdb-search-response-live-check.md index d0b3afc..0663c1b 100644 --- a/docs/tasks/items/tvdb-search-response-live-check.md +++ b/tasks/items/tvdb-search-response-live-check.md @@ -3,14 +3,14 @@ - **Тип:** research - **Категория:** Ядро продукта - **Зачем:** форма ответа поиска TheTVDB принята по swagger 4.7.10 и живым прогоном не подтверждена — при иной форме разбор молча уходит в фолбэк, гейт зелёный, локализованное название не работает -- **Теги:** goal:recognition-accuracy, sprint:2026-08-06 +- **Теги:** goal:recognition-accuracy Задача `tvdb-title-locale` научила клиент TVDB брать локализованное название из блока переводов ответа `/search` и заполнять `OriginalTitle` primary name'ом. Но живым прогоном форма ответа не сверялась: `CLAUDE.md` → «Запреты» запрещает ходить в боевые метабазы из отладочных прогонов и расходовать лимиты ключа. Форма взята из публичной документации (swagger TheTVDB v4, версия `4.7.10`) и -записана в [docs/research/tvdb-search-translations.md](../../research/tvdb-search-translations.md) +записана в [docs/research/tvdb-search-translations.md](../../docs/research/tvdb-search-translations.md) как **условие, а не замер**. Разведка нужна потому, что ошибка предположения **не наблюдаема**: разбор уйдёт в @@ -27,7 +27,7 @@ ## Куда ляжет ответ -- [docs/research/tvdb-search-translations.md](../../research/tvdb-search-translations.md): +- [docs/research/tvdb-search-translations.md](../../docs/research/tvdb-search-translations.md): предположения заменяются наблюдениями с датой прогона, а строка «живым прогоном не подтверждено» — результатом. Условие пересмотра там уже записано. - Решение по трём развилкам, оставшимся от `tvdb-title-locale`: diff --git a/docs/tasks/items/ubiquitous-language-glossary.md b/tasks/items/ubiquitous-language-glossary.md similarity index 100% rename from docs/tasks/items/ubiquitous-language-glossary.md rename to tasks/items/ubiquitous-language-glossary.md diff --git a/docs/tasks/items/web-ui-auth.md b/tasks/items/web-ui-auth.md similarity index 100% rename from docs/tasks/items/web-ui-auth.md rename to tasks/items/web-ui-auth.md diff --git a/docs/tasks/items/web-ui-pwa.md b/tasks/items/web-ui-pwa.md similarity index 100% rename from docs/tasks/items/web-ui-pwa.md rename to tasks/items/web-ui-pwa.md