Files
transcriber/openspec/changes/archive/2026-08-12-go-1-26-upgrade/tasks.md
T
av 09228f23d8 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 и
  перестал собираться, а восемь шагов гейта и шесть проходов ревью были зелёными
2026-08-12 10:57:50 +03:00

174 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Критерии приёмки
### От постановки
Перенесены дословно из записи задачи `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` целиком зелёный