- Раскладка docs/ приведена к канону 2: заведены passport/architecture/ database/security/review и research; docs/specs, drafts, backlog, review/ и BRIEF.md разобраны и удалены, беклог переехал в docs/tasks (34 задачи, 6 целей, слаги на английский). - Нарративы specs удалены как дубли openspec-спек после поимённой сверки; остаток заведён задачами (редактор маппинга ревью, крайние случаи именования), отказ от сущности title промоутнут в ADR. - Проектные копии агентов и скиллов ревью/пайплайна удалены в пользу плагинов av-dev-pm и av-dev-pipeline; в task gate добавлен шаг canon вместо er-schema.
54 lines
4.7 KiB
Markdown
54 lines
4.7 KiB
Markdown
# Конвенции кода
|
||
|
||
Как мы пишем код — в отличие от `openspec/specs/`, который описывает, что
|
||
система делает, и от [../architecture.md](../architecture.md), который
|
||
описывает, как она сложена.
|
||
|
||
**Прозой остаётся только то, что не выражается правилом.** Свойство, ставшее
|
||
правилом линтера или тестом-сканером, отсюда **удаляется** и переезжает в
|
||
перечень «Механизировано» ниже. Причина: файл на несколько сотен строк
|
||
размазывает внимание по тривиальному — и модель, и человек добросовестно
|
||
проверят именование и не дойдут до формы решения. Процедура промоута —
|
||
`references/promote.md` скилла `av-dev-pipeline:review-pipeline`.
|
||
|
||
Обоснование «почему именно так» живёт в [../adr/](../adr/README.md); инварианты
|
||
с severity — в [CLAUDE.md](../../CLAUDE.md).
|
||
|
||
## Записи
|
||
|
||
- [logging.md](logging.md) — логирование: уровень по адресату, единственный
|
||
логирующий чекпоинт, поля, `ext.*`, что не логируем.
|
||
- [errors.md](errors.md) — ошибки: stdlib, обёртка `%w`, `errors.Is`/`As`,
|
||
трансляция доменной ошибки на внешней границе, sentinel против типизированной.
|
||
- [config.md](config.md) — конфигурация: TOML, секреты рендерит деплой в файл
|
||
`0600`, самодокументируемый `config.example.toml`, валидация на старте.
|
||
- [database.md](database.md) — БД и идентификаторы: время в UTC RFC 3339, TEXT
|
||
ULID через `internal/ident`, `ident.Parse` на входной границе, естественные
|
||
ключи у деталей.
|
||
- [web-ui.md](web-ui.md) — веб-UI на htmx: единый партиал = страница = фрагмент,
|
||
ветвление по `isHTMX`, деградация без JS, ошибка на htmx-пути = 200 +
|
||
фрагмент, самозавершающийся поллинг, вендоринг и кэш статики.
|
||
|
||
## Механизировано
|
||
|
||
Проверяется `task gate`; прозой не дублируется и в промптах ревью не
|
||
пересказывается.
|
||
|
||
| Правило | Где механизировано |
|
||
| --- | --- |
|
||
| `msg` лога — константная категория, данные в полях, единый стиль ключ-значение | `.golangci.yml` → `sloglint` (`static-msg`, `kv-only`, `no-mixed-args`) |
|
||
| В stdout напрямую не пишем (`fmt.Print*`) | `.golangci.yml` → `forbidigo` |
|
||
| Конфигурация только из TOML, `os.Getenv` для конфига не используем | `.golangci.yml` → `forbidigo` |
|
||
| Время только через `store.Now()` — `time.Now` запрещён вне `internal/{ident,store}` | `.golangci.yml` → `forbidigo` |
|
||
| Сравнение ошибок через `errors.Is`/`As`, не `==` и не приведением типа | `.golangci.yml` → `errorlint` |
|
||
| Ошибки — только stdlib (`github.com/pkg/errors`, `cockroachdb/errors` запрещены) | `.golangci.yml` → `depguard` |
|
||
| Опечатки в тексте | `.golangci.yml` → `misspell` |
|
||
| Транспорты не зависят друг от друга | `internal/archrules` → `TestТранспортыНеЗависятДругОтДруга` |
|
||
| Ядро не зависит от транспортов | `internal/archrules` → `TestЯдроНеЗависитОтТранспортов` |
|
||
| Миграции без `AUTOINCREMENT` и без серверного времени | `internal/archrules` → `TestМиграцииБезAutoincrementИСерверногоВремени` |
|
||
| Ошибки не матчатся по тексту сообщения | `internal/archrules` → `TestОшибкиНеМатчатсяПоТексту` |
|
||
| Покрытие изменённых строк, секреты в диффе, миграция без правки `database.md` | `scripts/gate.py`, `scripts/diff-coverage.py`, `docs.py check` |
|
||
|
||
Непойманное место механизации означает, что проход по конвенциям будет
|
||
добросовестно проверять уже проверенное.
|