8.1 KiB
CLAUDE.md
Памятка для работы над jellybit. Перед задачей прочитай также README.md, BRIEF.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. - 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.
- Запуск: контейнер под
1000:1000, в общей docker-сети (адресация по именам), mount/srv/media(единая песочница) + data-том для SQLite/конфига.
Spec Driven Development (OpenSpec)
Изменения ведём через 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/specs, ДО кода; ревью кода после apply, до archive); тривиальная — одного прохода по коду достаточно.
Миграция: capabilities постепенно переносятся из docs/specs/ в
OpenSpec (пилот — ingest). До переноса источник истины по теме —
соответствующий файл в docs/specs/; перенесённое живёт в
openspec/specs/.
Прочая документация
docs/specs/— живые спецификации целевого состояния (архитектурный обзор + ещё не перенесённые в OpenSpec темы). Меняем по мере развития, держим в соответствии с кодом.docs/adr/— неизменяемый журнал решений, пишется постфактум, хранит почему. Правила — docs/adr/README.md.docs/drafts/— черновики: планы, идеи, ещё не принятые решения. Не источник истины.
Язык
- Документация, комментарии, сообщения коммитов — русский.
- Код и идентификаторы — английский.
Команды
Запуск через Task (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-linttask tidy—go mod tidytask 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. - Ошибки оборачиваем с контекстом (
fmt.Errorf("...: %w", err)). - Логирование только через
slog, безfmt.Println— уровни, обязательные поля и что не логировать см. docs/conventions/logging.md. - Время — всегда с явным TZ (сервер в
Europe/Moscow).
Кросс-каттинг конвенции (как пишем код, а не что система делает) живут в docs/conventions/ и не переносятся в OpenSpec.