Files
jellybit/docs/conventions
av d081ef1d30 ingest: закрыты мелочи приёма — вырожденное имя, контракт Result, корреляция add
- имя раздачи нормализуется на границе разбора: вырожденное `-`
  (metainfo.NoName) даёт пустое имя, пробельное схлопывается — сентинел больше
  не доходит ни до контекста распознавания, ни до source_ref, ни до подсказки
  вывода имени
- контракт «на любом пути ошибки приёма результат нулевой» объявлен в ingest и
  удерживается структурно; три транспорта перестали обещать идентификатор,
  которого нет, и коррелируют отказ по request_id
- scoped-логгер загрузки ставится до вызова внешнего сервиса в семи командах
  воркера — записи об отказе qBittorrent и метабаз получили download_id
  и infohash; граница разбора bencode записана в docs/research
2026-08-06 18:20:12 +03:00
..

Конвенции кода

Как мы пишем код — в отличие от openspec/specs/, который описывает, что система делает, и от ../architecture.md, который описывает, как она сложена.

Прозой остаётся только то, что не выражается правилом. Свойство, ставшее правилом линтера или тестом-сканером, отсюда удаляется и переезжает в перечень «Механизировано» ниже. Причина: файл на несколько сотен строк размазывает внимание по тривиальному — и модель, и человек добросовестно проверят именование и не дойдут до формы решения. Процедура промоута — references/promote.md скилла av-dev-pipeline:review-pipeline.

Обоснование «почему именно так» живёт в ../adr/; инварианты с severity — в CLAUDE.md.

Записи

  • logging.md — логирование: уровень по адресату, единственный логирующий чекпоинт, поля, ext.*, что не логируем.
  • errors.md — ошибки: stdlib, обёртка %w, errors.Is/As, трансляция доменной ошибки на внешней границе, sentinel против типизированной.
  • config.md — конфигурация: TOML, секреты рендерит деплой в файл 0600, самодокументируемый config.example.toml, валидация на старте.
  • database.md — БД и идентификаторы: время в UTC RFC 3339, TEXT ULID через internal/ident, ident.Parse на входной границе, естественные ключи у деталей.
  • web-ui.md — веб-UI на htmx: единый партиал = страница = фрагмент, ветвление по isHTMX, деградация без JS, ошибка на htmx-пути = 200 + фрагмент, самозавершающийся поллинг, вендоринг и кэш статики.

Механизировано

Проверяется task gate; прозой не дублируется и в промптах ревью не пересказывается.

Правило Где механизировано
msg лога — константная категория, данные в полях, единый стиль ключ-значение .golangci.ymlsloglint (static-msg, kv-only, no-mixed-args)
В stdout напрямую не пишем (fmt.Print*) .golangci.ymlforbidigo
Конфигурация только из TOML, os.Getenv для конфига не используем .golangci.ymlforbidigo
Время только через store.Now()time.Now запрещён вне internal/{ident,store} .golangci.ymlforbidigo
Сравнение ошибок через errors.Is/As, не == и не приведением типа .golangci.ymlerrorlint
Ошибки — только stdlib (github.com/pkg/errors, cockroachdb/errors запрещены) .golangci.ymldepguard
Опечатки в тексте .golangci.ymlmisspell
Транспорты не зависят друг от друга internal/archrulesTestТранспортыНеЗависятДругОтДруга
Ядро не зависит от транспортов internal/archrulesTestЯдроНеЗависитОтТранспортов
Миграции без AUTOINCREMENT и без серверного времени internal/archrulesTestМиграцииБезAutoincrementИСерверногоВремени
Ошибки не матчатся по тексту сообщения internal/archrulesTestОшибкиНеМатчатсяПоТексту
Покрытие изменённых строк, секреты в диффе, миграция без правки database.md scripts/gate.py, scripts/diff-coverage.py, docs.py check

Непойманное место механизации означает, что проход по конвенциям будет добросовестно проверять уже проверенное.