build: the tool is built with Go 1.27 now, and six formats change bytes - #37
Merged
Conversation
Raises the toolchain in go.mod and GO_VERSION in both workflows from
1.26.7 to 1.27.0, rewrites the eleven pinned generator cases and all
seven pinned standard library paths that moved, and bumps two tools that
cannot read Go 1.27 at all.
This is a breaking change under D11. Sizes are unchanged, every size that
worked still works, and the same sizes are reachable - what moved is the
compressed data. Affected: targz, png, docx, xlsx, pptx, ico when it
holds a png, and zip when compression is asked for. zip left alone is
untouched because its default stores rather than compresses. The other
seventeen formats are byte for byte what they were.
The cause is compress/flate, measured with a probe that imports none of
this project: all thirty two combinations of input and level differ
between the two releases. At level zero the change is framing rather than
algorithm - the block closing a stream went from five bytes to two, a
constant three - and above it the compressor itself behaves differently.
The reason for moving is not that 1.27 is better. This machine builds
other projects that are already on it, and GOTOOLCHAIN belongs to the
account rather than to a project, so the two were taking it in turns and
a guard went red on formats nobody had touched. Staying meant a check
before every command, forever. Decision by the owner.
TAR.GZ could not be produced at all under 1.27 before this. Its size is
arithmetic rather than a measurement, because compressing twice to learn
a length would make a preview cost what a run costs, and that arithmetic
carried the gzip framing as three constants. It measures them now, at
first use, from four compressions, and checks the model against a third
block and against the empty stream before trusting it. A later release
can move these bytes again but can no longer stop the format being
written. That repair is byte neutral under 1.26.7 - nine of nine against
a binary from main - so every byte that moved here belongs to the
compiler and not to it.
Two tools had to move with the compiler, and neither was a finding about
this code. staticcheck v0.7.0 refuses the standard library outright
("export data version 4"), and govulncheck v1.1.4 PANICS, which reads
like a security result on a gate named for vulnerabilities. Both are at
their newest, v0.8.1 and v1.7.0, and both are clean - govulncheck finds
no vulnerability in 1.27.0, which was worth knowing for a release with no
patches behind it yet.
Two minimums moved and the site says so: png 73 to 74 B, with 75-82 and
85 now the sizes it cannot produce, and targz 1052 to 1049 B, next
reachable 1051, gap at 1050. Measured with the probe that exists for that
table rather than derived, because two numbers in it were once wrong from
being derived.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI found this, not the migration: three jobs went red on a change that had passed a targeted local run, and one of the three was a latent fault in compression rather than anything Go 1.27 did. A 256 KiB archive at compression best would not settle. Traced: the filler came down five or six bytes a round while the file sat at 262147 and 262148, wobbling by one. The filler is a tar entry and a tar entry is padded to a whole 512 byte block, so handing back fewer than 512 bytes changes WHICH bytes the archive carries and not HOW MANY - what comes out of gzip then moves by a byte either way. The walk assumed a slope of one and was stepping across a staircase. That was true from the day compression landed. Go 1.27 only moved which sizes sit on a step edge, which is why it looked like the compiler broke it. So it gives back a whole block on an overshoot now: that lands under the target, and the gzip extra field closes the rest exactly, because the field adds precisely what it is given. Two rounds instead of never. Checked over 90 consecutive sizes in four bands at all three levels. The other two were mine and both were the guards being right. sync.OnceValues in framing.go was new concurrency, which this project asks to be declared. Declaring it would have been the wrong answer: the registry works out this format's minimum during init, which asks for the framing anyway, so nothing was ever lazy about it. A package level variable is initialised once, before any goroutine exists, and needs no lock. The guard found real over-engineering rather than a missing entry. And a semicolon in a comment, which rule 13 does not take. Adds tools/probes/targzsettle, which is how the staircase was seen, and a mutation for the block step. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🔴 This is a breaking change under
D11. It needs a major version bump, which is the owner's call and is not in this branch.Raises the toolchain from 1.26.7 to 1.27.0 in
go.modand in both workflows, rewrites the pinned hashes that moved, and bumps two tools that cannot read Go 1.27 at all.What changes for someone using the tool
Sizes are unchanged. Every size that worked still works, the same sizes are reachable, and every reader that took these files still takes them. What moved is the compressed data inside them.
targz,png,docx,xlsx,pptx,icowhen it holds a png,zipwhen compression is asked forzipleft alone (its default stores), and the other 17 formats, byte for byteTwo minimums moved and the site says so:
png73 → 74 B (75-82 and 85 are now the unreachable band) andtargz1052 → 1049 B (next reachable 1051, gap at 1050). Measured withtools/probes/reachable-sizes.pyrather than derived - two numbers in that table were once wrong precisely from being derived.Why move at all
Not because 1.27 is better. This machine builds other projects already on it, and
GOTOOLCHAINbelongs to the account rather than to a project, so the two kept taking it in turns - a byte guard went red on formats nobody had touched, andgo versionsettled it rather than reading the diff. Staying meant a check before every command, forever. Decision by the owner; written up indocs/GO-127-MIGRATION.mdandO169.TAR.GZ could not be produced at all under 1.27
Every size was refused: the generator came out three bytes short of its plan. The size of a
.tar.gzis arithmetic rather than a measurement - compressing twice to learn a length would make--dry-runcost what a run costs - and that arithmetic carried the gzip framing as constants. Go 1.27 changed one: the block that closes a level zero stream went from five bytes to two.It measures the framing now, at first use, from four compressions, and checks the model against a third block and against the empty stream before trusting it. A later release can move these bytes again but can no longer stop the format from being written.
Two properties made this safe to land as one commit:
main. So every byte that moved in this branch belongs to the compiler, not to the repair, and the golden diff is auditable.Two tools had to move, and neither was a finding about this code
staticcheckv0.7.0 → v0.8.1. The old one refuses the standard library outright:export data version 4 is greater than maximum supported version 2.govulncheckv1.1.4 → v1.7.0. The old one panics (panic: Intinsidegolang.org/x/tools), which reads like a security result on a gate named for vulnerabilities. The new one is clean and finds no vulnerability in 1.27.0 - worth knowing for a release with no patches behind it yet.gofmtfrom 1.27 reindents exactly one file,internal/gui/window/generate.go, and that is included.What was checked
targzruns again: 11 of 51 and 7 of 7targzproduced at every size tried, exact to the byte; 312 shapes through the arithmetic guardpreflightnon-test checks, 9 of 9, including the two new tool versionsFull analysis, and what it got wrong before the work started, in
docs/GO-127-MIGRATION.md.🤖 Generated with Claude Code