Go обновлён до 1.26, а расхождение версий теперь роняет гейт
- шаг go-version в task gate сверяет объявленную версию в go.mod, Dockerfile, CLAUDE.md и README.md; судит по репозиторию, go не зовёт, docker и сети не требует - заведена capability toolchain: до сих пор спеки нормировали только поведение сервиса, теперь и инструмент сборки. Причина и цена — в двух ADR - закрыт дефект 2026-08-12: образ на golang:1.24-alpine разошёлся с go.mod и перестал собираться, а восемь шагов гейта и шесть проходов ревью были зелёными
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
## Критерии приёмки
|
||||
|
||||
### От постановки
|
||||
|
||||
Перенесены дословно из записи задачи `tasks/items/go-1-26-upgrade.md`: закрытие
|
||||
задачи удалит файл, а критерии обязаны его пережить.
|
||||
|
||||
- Модуль, образ и документы называют одну версию Go. Оракул — `grep` по четырём
|
||||
местам: `go.mod`, `Dockerfile`, `CLAUDE.md`, `README.md`; все дают одно число.
|
||||
- Сборка на объявленной версии проходит. Оракул — `task image` и
|
||||
`CGO_ENABLED=0 go build ./...` на чистом дереве.
|
||||
- Расхождение версий роняет гейт. Оракул — прогон `task gate` на дереве, где
|
||||
версия в `Dockerfile` понижена на минор: шаг краснеет и называет оба числа.
|
||||
- Совпадение гейт не роняет, а сам шаг не требует docker и работает без сети.
|
||||
Оракул — `task gate` на неизменённом дереве и прогон с
|
||||
`DOCKER_HOST=/dev/null`.
|
||||
- Гейт зелёный целиком. Оракул — `task gate`.
|
||||
|
||||
### От рубрики ревью дизайна
|
||||
|
||||
Проход `review-rubric`, стадия ревью дизайна. Рубрика на род узла «проверочный
|
||||
шаг набора проверок, читающий разнородные источники».
|
||||
|
||||
- **Мутационный оракул на каждый источник.** Четыре прогона: по очереди понизить
|
||||
минор в `go.mod`, `Dockerfile`, `CLAUDE.md`, `README.md`. В каждом шаг краснеет
|
||||
и называет именно изменённый источник. Оракул — четыре прогона, четыре красных,
|
||||
четыре разных сообщения.
|
||||
- **Пустая выборка — отказ, а не согласие.** Четыре прогона: по очереди убрать
|
||||
строку версии из каждого источника. Каждый даёт отказ с именем этого источника.
|
||||
- **Исход — функция коммита, а не машины.** Оракул — прогон с `PATH`, из которого
|
||||
убран `go`: тот же код выхода и тот же вывод, что и при установленном `go`.
|
||||
- **Второе вхождение числа не проходит молча.** Оракул — дописать в `CLAUDE.md`
|
||||
второе «Go 1.25» и прогнать шаг: он краснеет, а не судит по первому совпадению.
|
||||
- **Патч и база образа свободны.** Оракул — два прогона: `golang:1.26.5-alpine` и
|
||||
`golang:1.26-bookworm` в `Dockerfile`, оба зелёные.
|
||||
- **Сообщение масштабируется на четыре места.** Оракул — прогон на дереве, где
|
||||
один источник разошёлся с тремя: вывод содержит четыре пары «место: число».
|
||||
- **Словарь кодов выхода объявлен.** Оракул — четыре прогона: сошлось, расхождение,
|
||||
лишний аргумент, файл источника убран — дают 0, 1, 2 и 3.
|
||||
- **Исход не зависит от рабочего каталога.** Оракул — прогон из корня и из
|
||||
подкаталога дают один код выхода.
|
||||
- **Шаг только читает.** Оракул — контрольная сумма дерева до и после прогона
|
||||
совпадает, два прогона подряд дают одинаковый вывод.
|
||||
- **Шаг не ходит в сеть.** Оракул — прогон под `strace -f -e
|
||||
trace=socket,connect,sendto,recvfrom`: ни одного сетевого вызова.
|
||||
- **Граница «чего шаг не проверяет» записана.** Оракул — раздел «Гейт» в
|
||||
`CLAUDE.md` содержит строку о том, что сборка образа в набор проверок
|
||||
по-прежнему не входит.
|
||||
- **Директива `toolchain` не проходит незамеченной.** Оракул — дописать
|
||||
`toolchain go1.27.0` в `go.mod` и прогнать шаг: он краснеет.
|
||||
|
||||
## 1. Шаг сверки версий
|
||||
|
||||
- [x] 1.1 Написать `scripts/check-go-version.sh` на POSIX `sh`: достать мажор и
|
||||
минор из директивы `go` в `go.mod`, из тега `FROM golang:` в `Dockerfile`, из
|
||||
строки стека в `CLAUDE.md` и из строки стека в `README.md`
|
||||
- [x] 1.2 Пути строить от корня репозитория, а не от текущего каталога
|
||||
- [x] 1.3 Не звать `go` ни в каком виде: ни `go mod edit`, ни `go list`, ни
|
||||
`go env`. Исход обязан быть функцией содержимого файлов
|
||||
- [x] 1.4 Тег образа разбирать по форме `golang:<мажор>.<минор>[.<патч>][-<база>]`
|
||||
— патч и база отбрасываются
|
||||
- [x] 1.5 Запретить директиву `toolchain` в `go.mod` — отказ с названием причины
|
||||
- [x] 1.6 Второе вхождение числа в одном источнике — отказ; для `Dockerfile` все
|
||||
`FROM golang:` обязаны давать одно число
|
||||
- [x] 1.7 Отказ при расхождении: сообщение печатает все четыре места и число
|
||||
каждого, а не одну пару
|
||||
- [x] 1.8 Отказ при ненайденном числе: место названо поимённо, «нечего
|
||||
сравнивать» исходом не считается
|
||||
- [x] 1.9 Коды выхода по словарю проекта: 0 сошлось, 1 расхождение, 2 ошибка
|
||||
употребления, 3 окружение
|
||||
- [x] 1.10 Не использовать GNU-only флаги (`grep -P`, `sed -E` с расширениями,
|
||||
`mapfile`); сделать скрипт исполняемым
|
||||
- [x] 1.11 Завести шаг `go-version` в `Taskfile.yml` по образцу соседних шагов,
|
||||
включая внятный отказ при отсутствии скрипта
|
||||
- [x] 1.12 Включить шаг в `gate`
|
||||
|
||||
## 2. Подъём версии до 1.26
|
||||
|
||||
- [x] 2.1 `go.mod`: директива `go 1.26.0`
|
||||
- [x] 2.2 `Dockerfile`: сборочный слой на `golang:1.26-alpine`
|
||||
- [x] 2.3 `CLAUDE.md`, раздел «Стек»: «Go 1.26» — ровно одно вхождение числа на
|
||||
файл, включая абзац из шага 3.1
|
||||
- [x] 2.4 `README.md`, раздел «Технологии»: завести строку версии Go в форме,
|
||||
которую находит скрипт
|
||||
- [x] 2.5 `go mod tidy` и проверка, что `go.sum` не разъехался и директива
|
||||
`toolchain` не появилась
|
||||
|
||||
## 3. Документы
|
||||
|
||||
- [x] 3.1 `CLAUDE.md`, раздел «Гейт»: новый шаг в перечне того, что красит
|
||||
безусловно; строка о том, что сборка образа в гейт по-прежнему не входит;
|
||||
словарь кодов выхода распространён на четвёртый проверочный шаг
|
||||
- [x] 3.2 `docs/review.md`: запись за 2026-08-12 получает строку о том, чем
|
||||
дефект закрыт
|
||||
- [x] 3.3 `docs/architecture.md`, преамбула: четвёртая capability в перечне и
|
||||
оговорка о том, что она нормирует не поведение сервиса (находка ревью B-2).
|
||||
Ссылка на спеку ставится шагом синка, после архивации: до неё файла нет и
|
||||
`docs.py check` краснеет битой ссылкой
|
||||
- [x] 3.4 `docs/conventions/README.md`, таблица «Механизировано»: строка про
|
||||
новое машинное правило (находка ревью N-2)
|
||||
|
||||
## 5. Отработка находок ревью кода
|
||||
|
||||
Отчёт триажа — `openspec/changes/go-1-26-upgrade/review/report.md`.
|
||||
|
||||
- [x] 5.1 N-3: нечитаемое место даёт код 3 с честным сообщением, а не «версия не
|
||||
названа» с кодом 1. Проверка читаемости стоит рядом с проверкой существования:
|
||||
подстановка команд в аргументе теряет код возврата, а в `read_doc` статус
|
||||
конвейера берётся от последней команды
|
||||
- [x] 5.2 N-4: цена отказа от `shellcheck` названа по замеру — один shell-скрипт,
|
||||
а не четыре. Правка в `CLAUDE.md` и в `design.md`
|
||||
- [x] 5.3 Подавление ложного `SC1007` в скрипте, чтобы заведение линтера потом
|
||||
стоило ровно одной строки шага. `shellcheck` на скрипте чист
|
||||
|
||||
## 6. B-1: правило множественности разведено по местам
|
||||
|
||||
Находка B-1 меняла требование, поэтому прошла через чекпоинт заново. Решение
|
||||
человека: развести правило по местам и сузить рамку до раздела.
|
||||
|
||||
- [x] 6.1 Требование: единственность — от раздела стека в документах, от
|
||||
сборочного образа — совпадение всех вхождений `FROM golang:`, требование модуля
|
||||
единственно по устройству файла
|
||||
- [x] 6.2 Пять новых сценариев: дубль в разделе, число вне раздела, два слоя с
|
||||
одной версией, два слоя с разными, пропавший раздел
|
||||
- [x] 6.3 Скрипт читает раздел (`## Стек` в памятке, `## Технологии` в README), а
|
||||
не файл целиком
|
||||
- [x] 6.4 Пропавший раздел — свой исход с именем раздела, а не «версия не названа»
|
||||
- [x] 6.5 Прогоны: история о прошлой версии вне раздела — зелено; пример команды в
|
||||
README вне раздела — зелено; дубль внутри раздела — красно; два слоя одной
|
||||
версии — зелено; два слоя разных — красно; раздела нет — красно с его именем
|
||||
- [x] 6.6 Регрессия прежней батареи: 8 мутаций по местам, `toolchain`, патч и база
|
||||
тега, `PATH` без `go`, подкаталог, коды 0/1/2/3 — все как прежде
|
||||
- [x] 6.7 `openspec validate --strict` и `task gate` зелёные
|
||||
|
||||
## 7. Отработка перепроверки спек после правки B-1
|
||||
|
||||
Целевой перепрогон прохода `specs`: правка коснулась ровно его темы. Прежняя
|
||||
находка S1 подтверждена закрытой по существу; три новые, все minor.
|
||||
|
||||
- [x] 7.1 F-2, ложное зелёное: раздел кончался только заголовком того же уровня, а
|
||||
заголовок внутри блока кода читался как настоящий — число доставалось из-за
|
||||
границы раздела. Граница определена в требовании и исполнена в коде: следующий
|
||||
заголовок того же или более высокого уровня, блок кода заголовков не даёт,
|
||||
хвостовые пробелы в заголовке не значат ничего
|
||||
- [x] 7.2 F-2: два сценария — заголовок раздела внутри блока кода, раздел закрыт
|
||||
заголовком верхнего уровня
|
||||
- [x] 7.3 F-3: отличие «сломанного окружения» от «пропавшей строки» записано
|
||||
требованием и сценарием, а не только кодом
|
||||
- [x] 7.4 F-1: таблица `design.md` велела правило, обратное принятой норме
|
||||
(«одно вхождение на файл»), и абзац-мотивировка стал фактически неверен —
|
||||
переписаны оба
|
||||
- [x] 7.5 Регрессия 21 прогоном: 8 мутаций по местам, `toolchain`, патч и база
|
||||
тега, два слоя одной и разных версий, дубль в разделе, история вне раздела,
|
||||
пропавший раздел, `PATH` без `go`, подкаталог, коды 0/1/2/3 — все совпали с
|
||||
ожиданием
|
||||
- [x] 7.6 `shellcheck` на скрипте чист
|
||||
|
||||
## 4. Проверка
|
||||
|
||||
- [x] 4.1 `CGO_ENABLED=0 go build ./...` и `go test ./...` на go1.26.5
|
||||
- [x] 4.2 `task image` собирает образ на `golang:1.26-alpine`
|
||||
- [x] 4.3 Четыре мутации по минору — по одной на источник; в каждой шаг краснеет
|
||||
и называет изменённый источник; дерево возвращается как было
|
||||
- [x] 4.4 Четыре мутации удалением строки версии — по одной на источник; в каждой
|
||||
отказ называет место
|
||||
- [x] 4.5 Прогон с `PATH` без `go`, прогон с `DOCKER_HOST=/dev/null`, прогон под
|
||||
`strace` без сетевых вызовов — исход тот же
|
||||
- [x] 4.6 Прогон из подкаталога — тот же код выхода
|
||||
- [x] 4.7 Прогоны на `golang:1.26.5-alpine` и `golang:1.26-bookworm` — зелёные
|
||||
- [x] 4.8 Прогон с дописанным `toolchain go1.27.0` — красный
|
||||
- [x] 4.9 Четыре прогона на коды выхода: 0, 1, 2, 3
|
||||
- [x] 4.10 Контрольная сумма дерева до и после прогона совпадает
|
||||
- [x] 4.11 `task gate` целиком зелёный
|
||||
Reference in New Issue
Block a user