Files
jellybit/CLAUDE.md
T
avandClaude Opus 4.8 c28745f369 backlog: подключить скилл как плагин av-dev-backlog
Скилл беклога переехал из глобального ~/.claude/skills в плагин av-dev-backlog
(маркетплейс av-dev-skills). Подключаем его на уровне проекта через
.claude/settings.json (extraKnownMarketplaces + enabledPlugins по git-URL),
чтобы был активен у всех, кто открывает репозиторий.

Правки в доках под новую раскладку:
- task-pipeline: убран зашитый путь к backlog.py (его больше нет) — проверка
  индекса идёт командой check самого скилла;
- CLAUDE.md: зафиксировано, что скилл backlog поставляется плагином и как
  вызывается.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 08:59:00 +03:00

177 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md
Памятка для работы над jellybit. Перед задачей прочитай также
[README.md](README.md), [BRIEF.md](BRIEF.md) и
[docs/specs/architecture.md](docs/specs/architecture.md). Разработка идёт
по **Spec Driven Development** через OpenSpec — см. раздел ниже.
## Что это
Связующий сервис qBittorrent ↔ Jellyfin: принимает торрент + контекст,
качает, распознаёт фильм/сериал (LLM + контекст + опц. метабазы) и
раскладывает файлы для Jellyfin хардлинками. Деплоится на домашний
медиа-сервер umbar (`/home/av/projects/private/umbar`) — туда копируется
готовый бинарь.
## Стек и принципы
- **Go**, один статический бинарь (`CGO_ENABLED=0`). Почему — см.
[ADR-2026-06-13-go-single-binary](docs/adr/ADR-2026-06-13-go-single-binary.md).
- **SQLite** как хранилище (чистый Go-драйвер `modernc.org/sqlite`).
- **Конфигурация — TOML**. **Логи — структурированный JSON** (`log/slog`).
- **Хардлинки, источник не трогаем** — qBittorrent продолжает раздачу,
диск не дублируется.
- **Единое ядро, тонкие транспорты** — вся логика приёма в use-case
`Ingest`; HTTP API, веб-UI и Telegram — лишь обёртки над ним.
- **Минимум компонентов** — в духе umbar, без зоопарка сервисов. Внешние
базы метаданных (TMDB/TVDB) опциональны, включаются конфигом.
## Инварианты (безопасность данных)
- **Источник неприкосновенен:** только `mkdir` / `link(2)` / `unlink`
своих ссылок; никогда не трогаем файлы под `paths.downloads`.
- **Целевой путь санитизируется** и проверяется, что он строго под
`paths.movies`/`series` (защита от traversal); существующее не
перезаписываем.
- **Выход LLM недоверенный** — безопасность на валидации пути, не на
промпте. Авто-раскладка только при подтверждённом матче в базе.
- **Секреты не попадают в логи** — пароли qBittorrent, API-ключи LLM/метабаз,
auth-заголовки. Подробнее — [docs/conventions/logging.md](docs/conventions/logging.md).
- **Запуск:** контейнер под `1000:1000`, в общей docker-сети (адресация
по именам), mount `/srv/media` (единая песочница) + data-том для
SQLite/конфига.
## Spec Driven Development (OpenSpec)
Изменения ведём через [OpenSpec](https://github.com/Fission-AI/OpenSpec)
(CLI `openspec`, v1.x). Сначала спецификация — потом код.
- `openspec/specs/<capability>/spec.md`**актуальные** capability-спеки:
что система делает сейчас. Capability — это поведение/домен (`ingest`,
`recognition`, `file-layout`, `review`, `notifications`), а не пакет кода.
- `openspec/changes/<id>/` — предлагаемое изменение: `proposal.md` (зачем и
что), `design.md` (как, для нетривиальных), дельта-спеки (`ADDED`/
`MODIFIED`/`REMOVED Requirements`), `tasks.md` (шаги). После реализации
change архивируется в `openspec/changes/archive/`, дельты вливаются в
`openspec/specs/`.
- `openspec/config.yaml` — язык и правила оформления спек (читай перед
написанием).
Поток работы — через слэш-команды `opsx:*` (канонический набор, его
поддерживает `openspec update`): `opsx:explore` (продумать), `opsx:propose`
(завести change), `opsx:apply` (реализовать tasks), `opsx:sync`/`opsx:archive`
(влить и архивировать). Skills `openspec-*` — то же, но предыдущего
поколения; для новой работы используем `opsx:*`.
Правила спек:
- Каждое `### Requirement` ОБЯЗАНО содержать литерал `SHALL` или `MUST`
иначе `openspec validate` падает.
- Структурные заголовки и ключевые слова — английские (`### Requirement:`,
`#### Scenario:`, `GIVEN/WHEN/THEN`, RFC 2119), остальной текст — русский.
- Сценарии — в формате `GIVEN/WHEN/THEN`.
- `openspec validate --strict` перед коммитом change.
Ревью (процесс, не артефакт): два чекпоинта — профиль `design` на предложении
(после design/specs, ДО кода) и ревью изменения после apply, до archive. Оба
идут через скилл `.claude/skills/review-pipeline`: детерминированный гейт
(`task gate`) → сверка с дельта-спеками в обе стороны → generative-проходы →
триаж с потолком 7 находок. Профиль (`quick`/`standard`/`deep`) выбирается по
факту изменения, правило — в скилле.
**Миграция:** capabilities постепенно переносятся из `docs/specs/` в
OpenSpec (пилот — `ingest`). До переноса источник истины по теме —
соответствующий файл в `docs/specs/`; перенесённое живёт в
`openspec/specs/`.
## Прочая документация
- `docs/specs/`**живые** спецификации целевого состояния (архитектурный
обзор + ещё не перенесённые в OpenSpec темы). Меняем по мере развития,
держим в соответствии с кодом.
- `docs/adr/`**неизменяемый** журнал решений, пишется постфактум,
хранит *почему*. Правила — [docs/adr/README.md](docs/adr/README.md).
- `docs/drafts/` — черновики: планы, идеи, ещё не принятые решения. Не
источник истины.
## Задачи и беклог
- **Единственный источник беклога — каталог [docs/backlog/](docs/backlog/README.md)**:
одна задача = один markdown-файл (`docs/backlog/<slug>.md`) + строка в индексе
[README.md](docs/backlog/README.md). Приоритеты: высокий/средний/низкий.
Работа с беклогом (заведение из диалога, разбор находок ревью/аудита, груминг,
приоритизация, декомпозиция, штурм идеи) — через скилл **`backlog`**; формат
файла, слага, индекса и кладбища он и держит, здесь не дублируем. Скилл
поставляется плагином `av-dev-backlog` (маркетплейс `av-dev-skills`, включён в
`.claude/settings.json`); вызов — `/av-dev-backlog:backlog`, свой скрипт
`backlog.py` он зовёт сам — путь к нему в проекте не зашиваем.
- **Источники задач:** диалог, инбокс Tududi и находки ревью. Отложенная находка
`review-pipeline` (реальная, но не для текущего мерджа) заводится задачей через
скилл `backlog` с тегом партии `review-ГГГГ-ММ-ДД` — так уже сделаны задачи
`review-*` в беклоге.
- **Проектные тонкости для скилла `backlog`:**
- каталог беклога — `docs/backlog/`, язык — русский, слаги — латиница;
- реализованная задача удаляется, её суть переезжает в `docs/specs`/`docs/adr`
(это делает пайплайн задачи на шаге 9, не скилл беклога);
- выкинутая без реализации уезжает строкой в `docs/backlog/CLOSED.md` (кладбище);
- спекулятивные задачи помечены `[idea]` в заголовке (тип — английское
ключевое слово idea/epic/task) — сперва штурм.
- **Tududi — только инбокс сырых идей** (проект `jellybit`, project_id 14).
Беклог там больше не ведём; идея из Tududi становится задачей, когда её
оформляют файлом в `docs/backlog/` через скилл `backlog`. Прежняя единая
`docs/backlog.md` доступна в истории git.
## Язык
- Документация, комментарии, сообщения коммитов — **русский**.
- Код и идентификаторы — английский.
## Команды
Запуск через [Task](https://taskfile.dev) (`task --list` — полный список):
- `task setup` — установка тулинга (golangci-lint + git-хуки lefthook)
- `task run` — локальный запуск (`go run ./cmd/jellybit --config ./config.toml`)
- `task build` — статический бинарь `linux/amd64` для сервера
- `task test` / `task lint` — тесты и golangci-lint
- `task gate` — детерминированный гейт ревью (build/vet/lint/test/race/покрытие
изменённых строк/миграции/секреты); блокирует опиниативные проходы ревью
- `task review:context` — карта проекта для архитектурного прохода ревью
- `task tidy``go mod tidy`
- `task image` — docker-образ из готового бинаря
Module path — `git.vakhrushev.me/av/jellybit`. Go 1.26, `CGO_ENABLED=0`.
Стек: `chi`, `sqlx` + `modernc.org/sqlite`, `goose` (миграции),
`pelletier/go-toml/v2`, `log/slog`.
## Конвенции кода
- Раскладка: `cmd/jellybit` (точка входа) + `internal/<пакет>` по
компонентам из [architecture.md](docs/specs/architecture.md).
- Механизируемое проверяет `task gate` (`.golangci.yml` + `internal/archrules`):
форма ошибок и логов, конфиг мимо env, время мимо `store.Now()`, AUTOINCREMENT
в миграциях, направление зависимостей ядро↔транспорты. Пересказывать эти
правила не нужно — гейт скажет точнее.
- Прозой остаётся то, что правилом не выражается, и читается в источнике:
[ошибки](docs/conventions/errors.md) (трансляция доменной ошибки на внешней
границе, sentinel против типизированной),
[логи](docs/conventions/logging.md) (уровень по адресату, единственный
логирующий чокпоинт, `ext.*`, что не логируем),
[конфиг](docs/conventions/config.md) (секреты рендерит деплой в файл `0600`,
самодокументируемый `config.example.toml`, валидация на старте),
[БД](docs/conventions/database.md) (время в UTC RFC 3339, TEXT ULID через
`internal/ident`, `ident.Parse` на входной границе).
- Миграции БД (goose, `internal/store/migrations`; SQL для DDL, Go — когда
нужен код) — при изменении структуры (таблица/столбец/индекс/связь) в том же
change обновляем ER-схему [docs/specs/database.md](docs/specs/database.md).
- Веб-UI на htmx — единый партиал = страница = фрагмент, ветвление по `isHTMX`,
деградация без JS, ошибка на htmx-пути = 200 + фрагмент, самозавершающийся
поллинг: [docs/conventions/web-ui.md](docs/conventions/web-ui.md).
Кросс-каттинг конвенции (как пишем код, а не что система делает) живут в
[docs/conventions/](docs/conventions/README.md) и не переносятся в OpenSpec.
Механизируемое там **не держим**: правило уезжает в `.golangci.yml` или в
`internal/archrules` и вычёркивается из прозы и из промптов ревью — процедура в
[references/promote.md](.claude/skills/review-pipeline/references/promote.md).
Прозой остаётся только то, что правилом не выражается.