Development record
The pre-push format gate documented in AGENTS.md is a bare gofmt -l ., which reads whatever Go toolchain the developer has rather than the one CI pins. go.mod declares go 1.26.7 and every CI leg pins go-version 1.26.7, but gofmt from go 1.27 re-indents a multi-value return whose operands are composite literals, so on a 1.27 machine gofmt -l . reports internal/core/ahoy/remote.go against an unmodified checkout of main. The failure is a trap rather than a nuisance: the developer is told the tree is unformatted, reformats the file to satisfy the documented gate, and pushes a file that CI's own gofmt then reports as unformatted in the other direction. Neither direction is detectable from the gate's output, which names a file and says nothing about which toolchain judged it. Fix candidates, none chosen here: pin the gate to the go.mod toolchain (go run mvdan.cc/gofumpt or go1.26.7 gofmt via the toolchain directive), have the Makefile compare the running gofmt's version against go.mod and refuse loudly on a mismatch rather than emitting a filename, or move the format gate into make preflight where the toolchain is already asserted. Detector: on a machine whose default gofmt is newer than the go.mod toolchain, the format gate over an unmodified checkout must either pass or refuse by naming the version skew, never name a file. Surfaced while clearing the site-composer bug-fix branch, where the finding cost two subagents a detour each.
The pre-push format gate documented in AGENTS.md is a bare gofmt -l ., which reads whatever Go toolchain the developer has rather than the one CI pins. go.mod declares go 1.26.7 and every CI leg pins go-version 1.26.7, but gofmt from go 1.27 re-indents a multi-value return whose operands are composite literals, so on a 1.27 machine gofmt -l . reports internal/core/ahoy/remote.go against an unmodified checkout of main. The failure is a trap rather than a nuisance: the developer is told the tree is unformatted, reformats the file to satisfy the documented gate, and pushes a file that CI's own gofmt then reports as unformatted in the other direction. Neither direction is detectable from the gate's output, which names a file and says nothing about which toolchain judged it. Fix candidates, none chosen here: pin the gate to the go.mod toolchain (go run mvdan.cc/gofumpt or go1.26.7 gofmt via the toolchain directive), have the Makefile compare the running gofmt's version against go.mod and refuse loudly on a mismatch rather than emitting a filename, or move the format gate into make preflight where the toolchain is already asserted. Detector: on a machine whose default gofmt is newer than the go.mod toolchain, the format gate over an unmodified checkout must either pass or refuse by naming the version skew, never name a file. Surfaced while clearing the site-composer bug-fix branch, where the finding cost two subagents a detour each.
Grounds
- pursued: one gate resolved through the declared toolchain cannot disagree with itself across machines, so the false positive on a newer gofmt is gone for good; a fmt-check that still names a file on an unmodified checkout, or a CI job that formats differently from the target, would show it wrong.
Frontmatter
| id | iss-2609081953452204 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | minor |
.abcd/work/issues/resolved/iss-2609081953452204-gofmt-gate-reports-a-false-positive-on-a.md
open on GitHub ↗ · commit history ↗
mentions1
- iss-2609261850045839make preflight runs its Go steps (go build, go vet, go test, the race lane, and every go run and go test its prerequisites make) on the go found on PATH, which is go1.27.1 on this machine, while CI builds and tests with the toolchain go.mod declares (go 1.26.7) through setup-go's go-version-file. iss-2609081953452204 closed exactly this skew for gofmt alone, by resolving the format gate through the declared toolchain; the build and test half was left on PATH. On 2026-09-26 pull request 728 failed CI on a test whose assertion depended on the go 1.27 encoding/json error wording, after a clean local preflight: the push gate judged the tree with a toolchain CI does not use, so a green preflight vouched for nothing on that point. Fix: run preflight's Go steps under the declared toolchain, resolved by the same resolver the format gate uses rather than a second one, and refuse loudly, naming the skew, when it cannot be fetched, exactly as the format gate refuses. Detector: a test holding every Go step under preflight to the declared toolchain.