# Автопроверки Чем машина судит код: перечень свойств, доведённых до проверки, и место, где каждое настроено. Документ — дом темы ревью `autotests`: проход, которому эта тема досталась, читает его, а не перечисляет инструменты по памяти. Тема заведена 2026-08-13. Прежде перечень лежал разделом «Механизировано» в [conventions/README.md](conventions/README.md), и это был чужой дом: конвенции говорят, **как писать код**, а здесь речь об инструментах, которые его читают. ## Границы дома Что здесь есть и чего здесь нет — чтобы факт не жил в двух местах: - **семантика гейта** — команда целиком, база диффа, словарь кодов выхода, что красит безусловно, чего в гейте намеренно нет и кто тогда обязан это гонять — в [CLAUDE.md](../CLAUDE.md), раздел «Гейт». Здесь это не повторяется: у гейта один дом, и он у памятки, потому что её читают прежде работы; - **как писать код** — [conventions/](conventions/README.md). Свойство, ставшее правилом, оттуда удаляется и попадает в перечень ниже; обратный перенос запрещён — правило, оставшееся ещё и прозой, проверяют дважды; - **настройка конвейера ревью и журнал дефектов** — [review.md](review.md). Оттуда берутся вопросы по темам, и перечень ниже говорит этим вопросам, чего спрашивать уже не нужно; - **поведение сервиса** — нормативные спеки `openspec/specs/`. У шага сверки версий Go поведение нормировано отдельно, спекой [toolchain](../openspec/specs/toolchain/spec.md): это единственная проверка проекта, у которой есть своя capability. Домов настройки четыре: `.golangci.yml` — линтеры и форматтер, `lefthook.yml` — проверки на pre-commit, `Taskfile.yml` — шаги гейта и их обёртки, `scripts/` — единственный собственный скрипт проверки. Скрипты `docs.py`, `tasks.py` и `openspec.py` живут вне репозитория, в плагинах, и Taskfile знает их путями. ## Механизировано Проверяется командами из [CLAUDE.md](../CLAUDE.md); прозой не дублируется и в промптах ревью не пересказывается. | Правило | Где механизировано | | --- | --- | | Сравнение ошибок через `errors.Is` и `errors.As`, не `==` и не приведением типа | `.golangci.yml` → `errorlint` | | Непроверенное возвращаемое значение ошибки | `.golangci.yml` → `errcheck`, включая присваивание в `_` (`check-blank`). Отказ, который решено не проверять, объявляют в `exclude-functions` поимённо — там сегодня `defer Close` и `send` | | Проверка судит ответ по готовому ответу (`Result()`), а не по живой карте заголовков обработчика | `.golangci.yml` → `forbidigo` с `analyze-types`, находки только в `*_test.go`. Судит по типу приёмника (`httptest.ResponseRecorder`), поэтому ловит любую форму: цепочкой, через переменную, по индексу карты, обходом, полем `HeaderMap`. Остаётся ревью проверка, идущая мимо recorder — через свой `http.ResponseWriter` | | Форматирование исходников | `.golangci.yml` → `gofmt` | | Подозрительные конструкции языка | `.golangci.yml` → `govet`, `staticcheck`, `ineffassign`, `unused` | | Секреты в коммите | `lefthook.yml` → `gitleaks git --staged` | | Достижимая из кода уязвимость в зависимостях | `Taskfile.yml` → шаг `vulns` (`govulncheck ./...`) | | Раскладка документов, битые ссылки, изменённый шаг схемы без правки `database.md` | `docs.py check`; каталог шагов задаёт ключ `migrations` в `docs/.docs.json` | | Одно число версии Go в `go.mod`, `Dockerfile`, `CLAUDE.md` и `README.md` | `Taskfile.yml` → шаг `go-version` (`scripts/check-go-version.sh`) | Не названное здесь место механизации означает, что проход по конвенциям будет добросовестно проверять уже проверенное. ## Что остаётся прозой **Из перечисленного в записях конвенций правилом выражено одно** — сравнение ошибок через `errors.Is` и `errors.As` (`errorlint`, строка таблицы выше). Прозой остаётся всё прочее: ни константный `msg` лога (`sloglint`), ни запрет `fmt.Print*` и `os.Getenv` (`forbidigo` заведён, но правило у него одно — о том, чем судят ответ в проверках; этих двух запретов в нём нет), ни запрет сторонних пакетов ошибок (`depguard`), ни архитектурные тесты-сканеры. Это следующий шаг переноса в правило: свойство, оставшееся прозой, проверяет человек на каждом ревью заново.