Skip to content

build: the oldest compiler this module admits is the one CI builds with - #70

Merged
donislawdev merged 1 commit into
mainfrom
stability/build-floor
Sep 6, 2026
Merged

build: the oldest compiler this module admits is the one CI builds with#70
donislawdev merged 1 commit into
mainfrom
stability/build-floor

Conversation

@donislawdev

Copy link
Copy Markdown
Owner

Stability report finding T3, accepted in full. The owner decided on 2026-09-06 to take the compatibility cost.

What was wrong

go.mod declared go 1.26.5 as a floor while CI and the toolchain line both said 1.27.0. The floor is the only one of those the compiler enforces, so the gap was reachable:

GOTOOLCHAIN=go1.26.8 go build -tags noasm ./cmd/tfg   -> succeeds
./tfg version                                          -> 0.3.0-rc1

That binary writes 0.3.0-rc1 into every manifest and produces different bytes for png, docx and targz than the release of that name, because Go 1.27 changed compress/flate - eleven of the fifty one pinned cases and all seven pinned standard library paths moved when this project went to 1.27.0.

The diagnosis was already in place and works: a manifest records the toolchain under tool.go, so two runs that disagree can be told apart afterwards. Nothing made anybody look.

What changed

Raising the floor makes Go itself refuse that build. The cost is real and stated rather than hidden, in go.mod and in the changelog: nobody on Go 1.26 can build from source any more. Downloadable binaries and go install are unaffected.

There is no toolchain directive left to compare against, and that is measured rather than overlooked. Go refuses a module whose toolchain directive is not newer than its go directive - once the floor met the pin, the build answered updates to go.mod needed until the directive came out. Verified in three runs: floor plus directive refuses, directive removed builds, comment survives either way. The thirty nine lines recording four toolchain decisions moved above the go line, because the directive was never the valuable part.

The pin now lives in GO_VERSION in the three workflows, and the new guard holds the floor against every one of them and against the sentence in README.md - the copy somebody reads before they have the repository.

Bytes were checked, not assumed

go mod tidy moved three modules from the indirect block to the direct one, so this was measured rather than reasoned about:

before after
module graph (go list -m all) 69 69, identical
packages cmd/tfg links 200 200, identical
go.sum untouched

TestStandardLibraryOutputHasNotDrifted, TestOurOwnGeneratorsHaveNotDrifted and TestGeneratingTwiceGivesTheSameBytes are all green. D11 holds.

Guard

TestTheBuildFloorIsThePinnedToolchainAndTheReadmeSaysSo - proven red on the real defect before the fix, then green after. Two mutations, both caught: the README going back to an older floor, and CI building with a compiler older than the floor.

It asks for equality rather than "the floor is at least the pin", deliberately: comparing versions means arithmetic on version strings, and that is exactly where this class of defect hides.

What it does not prove: that 1.27.0 is a good version or that it exists. It proves only that three places name the same one.

Full suite green, preflight --quick green on all 12 checks.

🤖 Generated with Claude Code

The "go" line in go.mod was a floor of 1.26.5 while CI and the toolchain line
both said 1.27.0. A floor is the only one of those the compiler enforces, so
the gap was reachable: GOTOOLCHAIN=go1.26.8 built this module, the binary
answered 0.3.0-rc1, wrote 0.3.0-rc1 into every manifest, and produced
different bytes for png, docx and targz than the release of that name. Go
1.27 changed compress/flate, which is why - eleven of the fifty one pinned
cases and all seven pinned standard library paths moved when this project
went to 1.27.0.

The diagnosis was already there and works: a manifest records the toolchain
under tool.go, so two runs that disagree can be told apart. Nothing made
anybody look.

Raising the floor means Go itself refuses that build. The cost is stated
rather than hidden, in go.mod and in the changelog: nobody on Go 1.26 can
build this from source any more. Downloadable binaries and "go install" are
unaffected.

There is no toolchain directive left to compare against, and that is measured
rather than overlooked. Go refuses a module whose toolchain directive is not
newer than its go directive, so once the floor met the pin the build answered
"updates to go.mod needed" until the directive came out. The thirty nine lines
recording four toolchain decisions moved above the go line instead - the
directive was never the valuable part. The pin now lives in GO_VERSION in the
three workflows, and the new guard holds the floor against every one of them
and against the sentence in README.md, which is the copy somebody reads before
they have the repository.

Bytes were checked rather than assumed, because go mod tidy moved three
modules from the indirect block to the direct one: the module graph is
identical at 69, the package set cmd/tfg links is identical at 200, go.sum is
untouched, and the three byte stability guards are green.

Two mutations, both caught.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@donislawdev
donislawdev merged commit fe2fbdb into main Sep 6, 2026
18 checks passed
@donislawdev
donislawdev deleted the stability/build-floor branch September 6, 2026 18:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant