# Соглашения ## Git - Trunk-based: короткие ветки от `main`, merge через PR (squash). - Имена веток: `feat/wave-moods`, `fix/scan-rename`, `chore/ci-cache`. - Conventional Commits: `feat(wave): add running profile`, `fix(api): ...`. Скоупы: `api`, `agent`, `rec`, `web`, `mobile`, `core`, `i18n`, `ci`, `deploy`, `docs`. Breaking change — `!` и раздел `BREAKING CHANGE:`. - Изменение API = изменение `openapi.yaml` + регенерация клиентов в том же PR. - Существенные архитектурные решения — новый ADR в [decisions.md](decisions.md). ## Go (`services/api`) - `gofumpt`, `golangci-lint` (конфиг в репо). Ошибки оборачиваются `fmt.Errorf("...: %w", err)`; доменные ошибки — типизированные (`ErrForbidden`, `ErrNotFound`) и маппятся в problem+json в одном месте. - Контекст первым аргументом, без глобального состояния, зависимости через конструкторы (`NewService(repo, clock, ...)`). - `clock` и генератор ID — интерфейсы (детерминированные тесты). - SQL — только в `queries/*.sql` (sqlc), без ORM. ## Python (`services/recommender`) - `uv`, `ruff` (lint + format), `mypy --strict`, Pydantic v2 для схем. - Тяжёлые вычисления — `numpy`, без циклов Python по трекам. - Конфиги контекстов и весов — YAML, валидируются Pydantic при старте. ## TypeScript - `strict`, ESLint (flat config) + Prettier, без `any` (кроме сгенерированного кода). - Общая логика — в `packages/core`, платформенная — только адаптеры в `apps/*`. - Все строки UI — через i18n, хардкод текста ловит `eslint-plugin-i18next`. ## API - `snake_case` в JSON, курсорная пагинация, problem+json с `code`. - Новые поля — только дополнительные; удаление / переименование — через новую версию эндпоинта или депрекацию на один минорный релиз. ## Безопасность - Секреты только через env / docker secrets, никогда в репозитории. - Любой ввод, попадающий в команды (`mount`, `ffmpeg`), — только как отдельные аргументы `exec.Command`, без shell, с whitelist-валидацией. - Пути файлов — проверка, что итоговый путь внутри корня источника (`filepath.Rel` без `..`).