docs: канон поднят с версии 7 до 12

- каталог задач переехал в tasks/ в корне, спринт упразднён — приоритет
  теперь порядок строк в BACKLOG.md, четыре задачи набора вернулись в беклог
- гейт: путь docs.py переведён на av-dev-docs вместо снесённого av-dev-pm,
  добавлены шаги tasks.py check и openspec.py check
- относительные ссылки внутри задач и ссылки из docs/ на задачи починены
This commit is contained in:
av
2026-08-09 19:09:53 +03:00
parent 9a624d4e13
commit c5d62d76ee
54 changed files with 109 additions and 79 deletions
+13 -8
View File
@@ -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/`.
## Конвенции кода
+2 -2
View File
@@ -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`.
+1 -1
View File
@@ -1,4 +1,4 @@
{
"canon": 7,
"canon": 12,
"migrations": "internal/store/migrations"
}
@@ -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).
+1 -1
View File
@@ -172,7 +172,7 @@ Jellyfin указывают на `movies`/`series`, а не на корень
Места, где устройство знаемо тонкое: не дефекты, а принятые пока пробелы. Здесь
только адрес и одна фраза — что именно не сделано; работа под каждым живёт
задачей в [tasks/BACKLOG.md](tasks/BACKLOG.md). Список нужен ревью: правка,
задачей в [tasks/BACKLOG.md](../tasks/BACKLOG.md). Список нужен ревью: правка,
попавшая в такую область, стоит дороже, чем выглядит.
| Область | Чего нет сегодня | Задача |
+1 -1
View File
@@ -1,7 +1,7 @@
# Паспорт проекта
Зачем это и для кого. [architecture.md](architecture.md) отвечает «как
устроено», [tasks/ROADMAP.md](tasks/ROADMAP.md) — «что уже умеет», паспорт —
устроено», [tasks/ROADMAP.md](../tasks/ROADMAP.md) — «что уже умеет», паспорт —
«зачем и для кого».
## Цель
+2 -2
View File
@@ -252,8 +252,8 @@ Go-сервиса и что здесь уже проскакивало. Устр
**Ориентир частоты.** `medium` закрывает большинство задач, `large` рассчитана
на 5–10% и приходится на крупную функциональность, а не на уборку: задача типа
`chore` или `fix`, собранная из нитей прошлого ревью, идёт в `small` или
`medium`, даже когда трогает файл из перечней выше. `large` чаще одной задачи на
спринт означает ошибку в критерии, а не спринт из сложных задач.
`medium`, даже когда трогает файл из перечней выше. `large` чаще одной задачи из
десяти означает ошибку в критерии, а не полосу сложных задач подряд.
### Недоступно проверке
-14
View File
@@ -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» вместо карточки ревью, где это чинится подсказкой
+64 -26
View File
@@ -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 разошёлся с канонической формой",
)
# --- Секреты ---
# Гоняем всегда: секрет утекает из любого файла, не только из кода.
@@ -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, время) — надо решить, что из них стало реальным трением, а что выдумано вперёд
@@ -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) — переписывать их
@@ -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`). Язык вывода живёт в пяти местах четырёх
пакетов, и теста, связывающего множество кодов языка с мапперами, нет:
забытая ветка молча даст английский вывод вместо ошибки. Кандидат в правило
@@ -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) — перевод доменной ошибки на внешней
границе.
- Проверка длины стоит **до** первой операции с файловой системой. **Оракул:**
чтение диффа на ревью; тест на пустоту каталога цели его подтверждает.
@@ -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).
## Воспроизведение
@@ -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).
## Критерии приёмки
@@ -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), «Словарь единого языка».
@@ -22,7 +22,7 @@
в вебе, из бота идёт эскалация по deep-link.
Смежное: превью раскладки и единый список источников совпадения уже есть
([review](../../../openspec/specs/review/spec.md)), так что задача про
([review](../../openspec/specs/review/spec.md)), так что задача про
редактирование плана, а не про его показ.
## Критерии приёмки
@@ -26,4 +26,4 @@
Итог аудита — конкретные под-задачи (эта их порождает). Проход дешёвый, при желании
приоритет можно поднять.
Связано: `internal/tgbot`, [review](../../../openspec/specs/review/spec.md).
Связано: `internal/tgbot`, [review](../../openspec/specs/review/spec.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` — только если что-то из решённого
выражается правилом, а не прозой.
@@ -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`.
@@ -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`: