docs: инструменты гейта переехали в свой дом — docs/autotests.md
- перечень «Механизировано» и то, что осталось прозой, снято из конвенций: они про то, как писать код, а не про инструменты, которые его читают - новый документ — дом темы ревью autotests, с границами: семантика гейта остаётся в CLAUDE.md, журнал дефектов и вопросы по темам — в review.md - вопрос ревью о суждении по готовому ответу сужен до того, что машина не проверяет: до ответа мимо recorder
This commit is contained in:
@@ -105,6 +105,9 @@ task gate # весь набор проверок разом
|
|||||||
|
|
||||||
- **Команда целиком:** `task gate`. База диффа — переменная `BASE`, по умолчанию
|
- **Команда целиком:** `task gate`. База диффа — переменная `BASE`, по умолчанию
|
||||||
`origin/master`; переопределяется `task gate BASE=<rev>`.
|
`origin/master`; переопределяется `task gate BASE=<rev>`.
|
||||||
|
- **Какое правило чем проверяется** — [docs/autotests.md](docs/autotests.md), дом
|
||||||
|
темы `autotests`. Здесь семантика гейта, там перечень правил и место настройки
|
||||||
|
каждого; перечень здесь не повторяется.
|
||||||
- **Где логи шагов:** вывод команды, отдельного файла нет.
|
- **Где логи шагов:** вывод команды, отдельного файла нет.
|
||||||
- **Что означает каждый исход:** ненулевой код любого шага роняет гейт. У
|
- **Что означает каждый исход:** ненулевой код любого шага роняет гейт. У
|
||||||
`docs.py check`, `tasks.py check`, `openspec.py check` и
|
`docs.py check`, `tasks.py check`, `openspec.py check` и
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# Автопроверки
|
||||||
|
|
||||||
|
Чем машина судит код: перечень свойств, доведённых до проверки, и место, где
|
||||||
|
каждое настроено. Документ — дом темы ревью `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`), ни архитектурные тесты-сканеры. Это следующий шаг
|
||||||
|
переноса в правило: свойство, оставшееся прозой, проверяет человек на каждом
|
||||||
|
ревью заново.
|
||||||
@@ -6,7 +6,8 @@
|
|||||||
|
|
||||||
**Прозой остаётся только то, что не выражается правилом.** Свойство, ставшее
|
**Прозой остаётся только то, что не выражается правилом.** Свойство, ставшее
|
||||||
правилом линтера или тестом-сканером, отсюда **удаляется** и переезжает в
|
правилом линтера или тестом-сканером, отсюда **удаляется** и переезжает в
|
||||||
перечень «Механизировано» ниже. Причина: файл на несколько сотен строк
|
перечень «Механизировано» документа [../autotests.md](../autotests.md) — дома
|
||||||
|
инструментов, которые читают код. Причина: файл на несколько сотен строк
|
||||||
размазывает внимание по тривиальному — и модель, и человек добросовестно
|
размазывает внимание по тривиальному — и модель, и человек добросовестно
|
||||||
проверят именование и не дойдут до формы решения.
|
проверят именование и не дойдут до формы решения.
|
||||||
|
|
||||||
@@ -47,30 +48,12 @@ htmx, а здесь решено делать SPA — и перенесённы
|
|||||||
однофайловые компоненты, таблица маршрутов, состояние в экране, одна обёртка
|
однофайловые компоненты, таблица маршрутов, состояние в экране, одна обёртка
|
||||||
над `fetch`, показ ошибок и состояний списка.
|
над `fetch`, показ ошибок и состояний списка.
|
||||||
|
|
||||||
## Механизировано
|
## Что из этого проверяет машина
|
||||||
|
|
||||||
Проверяется командами из [CLAUDE.md](../../CLAUDE.md); прозой не дублируется и в
|
Перечень правил, доведённых до проверки, и место настройки каждого — в
|
||||||
промптах ревью не пересказывается.
|
[../autotests.md](../autotests.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`), ни архитектурные тесты-сканеры. Это следующий шаг переноса в правило: свойство,
|
|
||||||
оставшееся прозой, проверяет человек на каждом ревью заново.
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
**Механизировано:** одно — сверка изменённого шага схемы с
|
**Механизировано:** одно — сверка изменённого шага схемы с
|
||||||
[../database.md](../database.md), шаг гейта `docs.py check`
|
[../database.md](../database.md), шаг гейта `docs.py check`
|
||||||
([README.md](README.md), «Механизировано»). Под прочие пункты ни правила
|
([../autotests.md](../autotests.md), «Механизировано»). Под прочие пункты ни правила
|
||||||
линтера, ни теста-сканера в transcriber нет.
|
линтера, ни теста-сканера в transcriber нет.
|
||||||
|
|
||||||
## Первичные ключи — ULID, не автоинкремент
|
## Первичные ключи — ULID, не автоинкремент
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ OpenSpec.
|
|||||||
|
|
||||||
**Механизировано:** ничего из перечисленного ниже. `forbidigo` в `.golangci.yml`
|
**Механизировано:** ничего из перечисленного ниже. `forbidigo` в `.golangci.yml`
|
||||||
включён, но правило у него одно и о другом — чем судят ответ в проверках
|
включён, но правило у него одно и о другом — чем судят ответ в проверках
|
||||||
([README.md](README.md), «Механизировано»); `sloglint` не заведён, и ни один
|
([../autotests.md](../autotests.md), «Механизировано»); `sloglint` не заведён, и ни один
|
||||||
пункт этой записи правилом не выражен.
|
пункт этой записи правилом не выражен.
|
||||||
|
|
||||||
## Принципы
|
## Принципы
|
||||||
|
|||||||
+8
-2
@@ -8,6 +8,10 @@
|
|||||||
Разделы ниже заполнены наперёд по коду и правятся по итогам прогонов: «Типовые
|
Разделы ниже заполнены наперёд по коду и правятся по итогам прогонов: «Типовые
|
||||||
ложноположительные» первым прогоном уже пользовались.
|
ложноположительные» первым прогоном уже пользовались.
|
||||||
|
|
||||||
|
Дом темы `autotests` — [autotests.md](autotests.md): что уже проверяет машина и
|
||||||
|
что из этого спрашивать больше не нужно. Вопросы ниже — то, чего машина не
|
||||||
|
проверяет.
|
||||||
|
|
||||||
### Типовые узлы
|
### Типовые узлы
|
||||||
|
|
||||||
Рода узлов проекта и проверяемые свойства к каждому.
|
Рода узлов проекта и проверяемые свойства к каждому.
|
||||||
@@ -141,8 +145,10 @@
|
|||||||
- `autotests`: покрыт ли изменённый шаг конвейера хоть одним тестом — сегодня
|
- `autotests`: покрыт ли изменённый шаг конвейера хоть одним тестом — сегодня
|
||||||
тестов два файла, и оба мимо конвейера.
|
тестов два файла, и оба мимо конвейера.
|
||||||
- `autotests`: судит ли проверка ответа по готовому ответу, а не по изменяемому
|
- `autotests`: судит ли проверка ответа по готовому ответу, а не по изменяемому
|
||||||
состоянию обработчика — класс всплывал трижды, последний раз 2026-08-12 на
|
состоянию обработчика — **только там, где ответ идёт мимо recorder**, через
|
||||||
уборке носителя состояния входа.
|
свой `http.ResponseWriter`. Обращение к живой карте recorder'а с
|
||||||
|
2026-08-12 роняет гейт правилом линтера
|
||||||
|
([autotests.md](autotests.md), «Механизировано»), и спрашивать о нём не нужно.
|
||||||
- `security`: не открылась ли снова поверхность, которую приносит хранилище, —
|
- `security`: не открылась ли снова поверхность, которую приносит хранилище, —
|
||||||
собственная регистрация, вход по паролю, одноразовый код, восстановление
|
собственная регистрация, вход по паролю, одноразовый код, восстановление
|
||||||
доступа, продление сессии. Всё это приходит включённым и закрывается нами
|
доступа, продление сессии. Всё это приходит включённым и закрывается нами
|
||||||
|
|||||||
Reference in New Issue
Block a user