Беклог теперь ведётся пользовательским скиллом `backlog` (заведение из диалога, разбор находок ревью, груминг, приоритизация, декомпозиция, штурм). CLAUDE.md делегирует ему формат и держит только проектные тонкости; добавлены источники задач (диалог, Tududi, находки ревью) и кладбище `docs/backlog/CLOSED.md` для выкинутого без реализации. - task-pipeline: шаги 1 и 9 больше не описывают формат сами, ссылаются на скилл; шаг 9 гоняет `backlog.py check` после удаления файла задачи. - review-pipeline: отложенная реальная находка (не для текущего мерджа) заводится задачей через скилл с тегом партии review-ГГГГ-ММ-ДД. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
174 lines
13 KiB
Markdown
174 lines
13 KiB
Markdown
# 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`**; формат
|
||
файла, слага, индекса и кладбища он и держит, здесь не дублируем.
|
||
- **Источники задач:** диалог, инбокс 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).
|
||
Прозой остаётся только то, что правилом не выражается.
|