go-cli-static-format-gate
Solution written to ~/go-cli-static-format-gate.md (495 lines, self-contained).
The working directory had no repo, so I reconstructed the failure mode in a scratch module, then implemented and empirically verified the fix.
Root cause: "gofmt-clean" has no executable definition in a typical Go repo. go build, go vet, and go test are all indifferent to formatting, so rot accumulates behind green checks.
Fix: one in-repo package internal/fmtcheck is the single check-only definition; three surfaces invoke it with the byte-identical command:
- TestGofmt (no testing.Short() skip) → caught by plain go test ./...
- Makefile target → go test -count=1 -run TestGofmt ./internal/fmtcheck
- CI step → same exact string, verified identical with diff
| Claim | Result |
|---|---|
Baseline build/vet/test green with gofmt rot |
✅ reproduced |
Gate catches rot, testdata/ skipped |
✅ reports only cmd/app/main.go |
go test -short ./... still enforces |
✅ fails on rot |
Root located via go.mod walk-up, not cwd |
✅ test runs in internal/fmtcheck, still finds cmd/app |
| Parse errors not double-reported | ✅ gate passes, go build reports the syntax error |
format.Source ≡ gofmt binary |
✅ 5,597 files, 0 divergences (Go stdlib) |
Unsorted imports / legacy // +build / trailing WS / misaligned comments |
✅ all flagged by both |
| Makefile vs CI command | ✅ byte-identical |
The document includes full copy-pasteable source, the Makefile/CI snippets, a regression test (TestCheckSemantics) locking in the two non-obvious rules, the differential harness source, and adaptation steps for an existing module (only change needed: the module path in the test import).
# Evidence - Problem class: go-cli-static-format-gate - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-15T12:03:46.150Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Go repo where gofmt is not gated: build/vet/test all green while formatting rot accumulates. Fix proven at coding-hermes/boardctl BT-028 (commit 5ff8e2e): create a tiny in-repo package (internal/fmtcheck) that is the SINGLE check-only definition of gofmt-clean, then have three surfaces invoke that one checker so they cannot drift: a Makefile target (go test -count=1 -run TestGofmt ./internal/fmtcheck), a CI step with the byte-identical command, and the test itself picked up by plain `go test ./...` with NO testing.Short() skip so a -short CI test step still enforces it. Two non-obvious findings worth caching: (1) in Go 1.26 go/format.Source output is byte-identical to the gofmt binary on real code - a differential harness over 39 repo files found 0 divergences and probes for unsorted imports, legacy // +build-only tags, trailing whitespace and misaligned field comments were ALL flagged, so an in-process checker is a legitimate substitute for `gofmt -l` and needs no external binary; (2) format.Source returns an error on unparseable files, so treat a parse error as NOT-a-formatting-finding and leave it to build/vet, otherwise the gate double-reports compile errors. Also skip directories named testdata (fixtures may hold intentionally unformatted snippets) and locate the repo root by walking up to go.mod instead of trusting the cwd.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "go-cli-static-format-gate", "provider": "openrouter", "solved_at": "2026-09-15T12:03:46.150Z", "version": ""}Solution written to ~/go-cli-static-format-gate.md (495 lines, self-contained).
The working directory had no repo, so I reconstructed the failure mode in a scratch module, then implemented and empirically verified the fix.
Root cause: "gofmt-clean" has no executable definition in a typical Go repo. go build, go vet, and go test are all indifferent to formatting, so rot accumulates behind green checks.
Fix: one in-repo package internal/fmtcheck is the single check-only definition; three surfaces invoke it with the byte-identical command:
- TestGofmt (no testing.Short() skip) → caught by plain go test ./...
- Makefile target → go test -count=1 -run TestGofmt ./internal/fmtcheck
- CI step → same exact string, verified identical with diff
| Claim | Result |
|---|---|
Baseline build/vet/test green with gofmt rot |
✅ reproduced |
Gate catches rot, testdata/ skipped |
✅ reports only cmd/app/main.go |
go test -short ./... still enforces |
✅ fails on rot |
Root located via go.mod walk-up, not cwd |
✅ test runs in internal/fmtcheck, still finds cmd/app |
| Parse errors not double-reported | ✅ gate passes, go build reports the syntax error |
format.Source ≡ gofmt binary |
✅ 5,597 files, 0 divergences (Go stdlib) |
Unsorted imports / legacy // +build / trailing WS / misaligned comments |
✅ all flagged by both |
| Makefile vs CI command | ✅ byte-identical |
The document includes full copy-pasteable source, the Makefile/CI snippets, a regression test (TestCheckSemantics) locking in the two non-obvious rules, the differential harness source, and adaptation steps for an existing module (only change needed: the module path in the test import).
# Evidence - Problem class: go-cli-static-format-gate - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-15T12:03:46.150Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Go repo where gofmt is not gated: build/vet/test all green while formatting rot accumulates. Fix proven at coding-hermes/boardctl BT-028 (commit 5ff8e2e): create a tiny in-repo package (internal/fmtcheck) that is the SINGLE check-only definition of gofmt-clean, then have three surfaces invoke that one checker so they cannot drift: a Makefile target (go test -count=1 -run TestGofmt ./internal/fmtcheck), a CI step with the byte-identical command, and the test itself picked up by plain `go test ./...` with NO testing.Short() skip so a -short CI test step still enforces it. Two non-obvious findings worth caching: (1) in Go 1.26 go/format.Source output is byte-identical to the gofmt binary on real code - a differential harness over 39 repo files found 0 divergences and probes for unsorted imports, legacy // +build-only tags, trailing whitespace and misaligned field comments were ALL flagged, so an in-process checker is a legitimate substitute for `gofmt -l` and needs no external binary; (2) format.Source returns an error on unparseable files, so treat a parse error as NOT-a-formatting-finding and leave it to build/vet, otherwise the gate double-reports compile errors. Also skip directories named testdata (fixtures may hold intentionally unformatted snippets) and locate the repo root by walking up to go.mod instead of trusting the cwd.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "go-cli-static-format-gate", "provider": "openrouter", "solved_at": "2026-09-15T12:03:46.150Z", "version": ""}