diff --git a/docs/conventions/README.md b/docs/conventions/README.md index eecd036..01babf9 100644 --- a/docs/conventions/README.md +++ b/docs/conventions/README.md @@ -48,10 +48,9 @@ htmx, а здесь решено делать SPA — и перенесённы - [web-ui.md](web-ui.md) — веб-UI: Vue 3 с Vite и статикой в бинарнике, однофайловые компоненты, таблица маршрутов, состояние в экране, одна обёртка над `fetch`, показ ошибок и состояний списка. -- [go-linters.md](go-linters.md) — линтеры и механизированные проверки: лестница - механизации, два круга (pre-commit и гейт), перечень правил и подавлений, - порядок заведения нового правила. Про инструменты, а не про то, как писать - тесты. +- [go-linters.md](go-linters.md) — линтеры и механизированные проверки: два круга + (pre-commit и гейт), перечень правил и подавлений, порядок заведения нового + правила. Про инструменты, а не про то, как писать тесты. ## Что из этого проверяет машина diff --git a/docs/conventions/go-linters.md b/docs/conventions/go-linters.md index 07176a7..1685abc 100644 --- a/docs/conventions/go-linters.md +++ b/docs/conventions/go-linters.md @@ -15,11 +15,11 @@ исходники. Пока язык у проекта один, и запись названа по нему. Появится второй — у него -будет своя запись, а лестница и два круга останутся общими. +будет своя запись, а два круга останутся общими. -Устройство ниже **переносимо**: разделы «Лестница механизации», «Два круга» и -«Как заводят новое правило» — не особенность transcriber и переносятся в другой -Go-проект как есть. Своё здесь — перечень правил и подавлений. +Устройство ниже **переносимо**: разделы «Два круга» и «Как заводят новое +правило» — не особенность transcriber и переносятся в другой Go-проект как есть. +Своё здесь — перечень правил и подавлений. ## Границы: где что живёт @@ -48,37 +48,6 @@ Go-проект как есть. Своё здесь — перечень пра механики и мало пользы. Долгом это больше не числится — задача `migrations-step-norm-and-tests` закрыта без реализации. -## Лестница механизации - -Свойство поднимается по ступеням, и ступень выбирают не по вкусу, а по тому, -чем свойство выражается. Верхняя ступень дешевле нижней в эксплуатации и дороже -в заведении, поэтому прыгать через ступень без нужды не надо. - -1. **Проза конвенции.** Свойство названо словами, проверяет человек на каждом - ревью заново. Это ступень по умолчанию и худшая из всех: она стоит внимания - каждого прогона и молча перестаёт работать, когда внимание кончилось. -2. **Настройка готового линтера.** Свойство совпало с чужим правилом — - включается строкой в `.golangci.yml`. Дешевле всего; ограничение в том, что - правило чужое и говорит о том, о чём его написали. -3. **Запрет по имени** (`forbidigo`, `depguard`). Свойство выражается через «эту - функцию/пакет тут звать нельзя». Дешёво и точно, но требует **единой точки**, - куда запрещённое переносят: запрет без дома оставляет код без способа сделать - нужное. -4. **Тест-сканер исходников** (`internal/archrules`). Свойство — о структуре, а - не о вызове: направление зависимостей, согласованность двух перечней, - отсутствие идиомы. Пишется руками на `go/parser` или регулярном выражении, - зато читается как тест и ломается заметно. -5. **Свой шаг проверки** (`scripts/`, шаги `Taskfile.yml`). Свойство выходит за - пределы кода на Go: версия инструмента, форма `Dockerfile`, раскладка - документов. Дороже всех — у шага появляется своя норма и свои тесты. - -Ступень, выбранная неверно, видна сразу. Запрет по имени, обходимый одной -лишней строкой, — на деле правило четвёртой ступени, оформленное как правило -третьей: так было с правилом о -заголовках ответа, которое сначала запретило текст `\.Header\(\)\.Get`, а -обходилось присваиванием в переменную. Правило переписано на суждение **по типу -приёмника** (`analyze-types`), и это уже настоящая третья ступень. - ## Два круга: pre-commit и гейт Проверки идут двумя кругами, и круг выбирается по цене прогона. @@ -199,8 +168,8 @@ Go-проект как есть. Своё здесь — перечень пра остаётся то, чему нет ни готового правила, ни детерминированного оракула: уровень лога по адресату, единая логирующая точка на доменной границе, словарь имён полей, канонический вид идентификатора, естественные ключи у деталей. -Свойство, оставшееся прозой, проверяет человек на каждом ревью заново — это и -есть первая ступень лестницы, и подъём с неё всегда выигрыш. +Свойство, оставшееся прозой, проверяет человек на каждом ревью заново, и правило, +снявшее с него эту работу, всегда выигрыш. Названы поимённо и **остатки правил** — то, что правило не ловит и потому осталось человеку: @@ -242,8 +211,9 @@ Go-проект как есть. Своё здесь — перечень пра Порядок один и тот же, и последние два шага пропускать нельзя. -1. **Найти дом.** Ступень лестницы выбирается по тому, чем свойство - выражается, а не по тому, что проще включить. +1. **Найти дом.** Дом выбирается по тому, чем свойство выражается, а не по тому, + что проще включить: настройка готового линтера, запрет по имени, тест-сканер + исходников или свой шаг набора проверок. 2. **Написать причину рядом.** Правило без причины снимают при первом же неудобстве: тот, кто снимает, не знает, что оно ловило. 3. **Починить находки, а не подавить.** Подавление годится, когда правило diff --git a/docs/review.md b/docs/review.md index 2d5b5b4..f110347 100644 --- a/docs/review.md +++ b/docs/review.md @@ -69,22 +69,11 @@ - изменённое место покрыто хоть одним **проходящим** тестом. Тест, который никогда не был зелёным, обнуляет сигнал всего пакета: настоящий отказ в нём - становится неотличим от привычного шума (журнал, запись 2026-08-10); -- проверка **способна упасть**. Утверждение, разбирающее ответ в ту же - структуру, чьи теги и составляют проверяемый контракт, меняется вместе с ним - и никогда не ловит поломку; такое судят по сырому виду ответа. Признак ищется - мутацией: сломай проверяемое свойство и убедись, что тест краснеет (журнал, - запись 2026-08-11); -- **то же и об оракуле критерия приёмки, не только о тесте.** Критерий, чей - единственный оракул — молчание линтера, годится ровно тогда, когда линтер - краснеет на **всех** негодных реализациях; проверяется той же мутацией. - Прецедент: «отказ `Close` не теряется молча» принимался молчанием `errcheck`, - а тот пропускал `_ = conn.Close()` — реализацию, теряющую отказ целиком - (журнал, запись 2026-08-11 про недостижимую норму; закрыто - [решением](adr/ADR-2026-08-11-errcheck-check-blank.md)); -- **требование без сценария не имеет оракула** и потому не может быть нарушено - заметно. Норма, которую нечем уронить, расходится с кодом молча — и расходится - тем вернее, чем убедительнее написана (журнал, запись 2026-08-11). + становится неотличим от привычного шума (журнал, запись 2026-08-10). + +Свойств о годности самих проверок здесь больше нет: требования мутировать тест, +оракул критерия приёмки и норму сняты 2026-08-13 решением владельца — проверка +над проверкой стоит внимания каждого прогона и отвечает редко. ### Типовые ложноположительные @@ -357,10 +346,10 @@ API и имя не откатываются обратной правкой по которого писали. Мутация была, но одна — нужна была по одной на каждую форму - **Что меняем:** правило судит по типу приёмника (`analyze-types`, `httptest.ResponseRecorder.Header` и `.HeaderMap`) и ловит все шесть форм; - проверено мутацией по каждой. Отсюда же строка в - docs/conventions/go-linters.md, «Лестница механизации»: запрет по имени, - обходимый лишней строкой, требует ступени тест-сканера, хотя выглядит запретом - по имени + проверено мутацией по каждой. Урок записи: запрет по имени, обходимый лишней + строкой, свойства не держит — такому свойству нужен тест-сканер. Строка об этом + стояла в `docs/conventions/go-linters.md`, разделе «Лестница механизации»; + раздел снят 2026-08-13, урок остался здесь ## 2026-08-12 — закрыли поверхность так, что войти не мог никто [пойман ревью]