Repository navigation
feat: move the standards baseline to Go 1.27 - #15
Conversation
Go 1.27 is the hard floor; a module on an older go directive skips what is marked newer, which the compiler, go vet (stdversion) and go fix enforce. One sentence in the router and the Cursor rule says so; no per-rule 1.26 variants. Re-read against the Go 1.27 release notes, the 1.27 blog posts (generic methods, goroutine leak profiles, HTTP/1 connection reuse), the JSON tutorial and the encoding/json migration notes, with fixer names taken from go1.27.3's `go tool fix help`: - go-idioms: Newer in Go 1.27 folded into the tables. errorsastype, atomictypes and slicesbackward ship in go fix; waitgroup is waitgroupgo; fmtappendf (golang/go#77581) and bloop (#74967) were withdrawn, so fmt.Fprintf leads (staticcheck QF1012) and b.Loop is converted by hand. CutLast is rewritten by golangci-lint's x/tools snapshot, not yet by the toolchain's stringscut. Adds generic methods, encoding/json/v2 for new code, std uuid, close-don't-drain HTTP bodies, url Clone, and omitzero over omitempty on scalar and pointer fields. - go-errors: %w on a *E whose E implements error (new vet check). - go-concurrency: goroutineleak profile as plain guidance, its blind spots and cost; goroutine labels now appear in crash tracebacks. - go-testing: synctest.Sleep, httptest.NewTestServer, go doc -ex, Config.Rand deprecation, b.Loop conversion caveat. - go-lint-setup: renamed or removed modernize analyzers in disable: only warn; keep formatters on one Go minor after the gofmt change. - revive citations move to revive-lint/revive. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviews against Go 1.27 and skips what is marked newer under an older go directive, matching the router. Stale modernization debt keeps go fix for real fixers and lists by-hand debt separately: cmp.Or chains and a drain before resp.Body.Close(), which Go 1.27's Transport does itself. Body 9,965 characters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GO_FLOOR_MINOR is 1.27 and the CI matrix runs 1.27.x, so the go-idioms Fixer column is checked against go1.27's `go tool fix help`. A next-minor leg is added once that Go version is released. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AGENTS.md, README.md and docs/install.md (go1.27.3) state the floor. docs/authoring.md records the 2026-10-27 re-read, tracks the release notes and golangci-lint's x/tools snapshot as mutable sources, and asks for a go fix -diff check before a new Fixer cell. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sebastian-iancu
left a comment
There was a problem hiding this comment.
Review
The floor move is sound, and the behavior I could run on go1.27.3 matches the new guidance: fixer names, %w of a *E, omitzero on a non-nil *time.Time, uuid.New / NewV7, httptest.NewTestServer, synctest.Sleep, url.Values.Clone versus maps.Clone, and the generic-method syntax. Three instruction bugs should be fixed before merge. None of them is a wrong API.
Important
1. The b.Loop withdrawal is tied to a timeout Go 1.24.2 already fixed. skills/go-testing/SKILL.md lines 40–43 (and the same story on the go-idioms row) say the automatic rewrite was withdrawn after it turned a benchmark that pauses the timer from seconds into a ten-minute timeout (golang/go#74967).
That timeout is the opening report. The thread then says it was the runtime bug in golang/go#72922, fixed in Go 1.24.2. The analyzer was disabled in December 2025 for two other reasons: a rewrite that leaves the timer stopped, and nanosecond-scale benchmarks skewing by as much as +65%. On go1.27.3 the stopped-timer case fails immediately (testing/benchmark.go: B.Loop called with timer stopped), rather than hanging. Keep "convert by hand, restructure a loop that pauses the timer, re-check the run time." Name that fatal and the skew.
2. The † legend says no row needs †. The CutLast row is that case. skills/go-idioms/SKILL.md lines 30–31 define † as a rewrite that exists only in the newer x/tools suite, then say no row needs it. Line 44 says stringscut rewrites CutLast through golangci-lint for now, and the 1.27 toolchain rewrites Index only.
The cell matches the toolchain. go tool fix help stringscut on go1.27.3 documents Index, IndexByte, and Split / SplitN(...)[0], not LastIndex. A † on this row would also fail scripts/validate.py, because stringscut is registered and † means the backticked name must be absent. docs/authoring.md already says a registered name can still lack a rewrite. Say that in the legend, and keep the narrower rewrite in the cell. "Rewrites Index only" is the right contrast with CutLast and the wrong inventory of stringscut.
3. The version-gate sentence gives the compiler work that stdversion does, and go-reviewer does not carry the sentence. skills/go-coding/SKILL.md lines 8–12 and rules/go-context.mdc lines 9–12 say the compiler, go vet's stdversion check, and go fix all follow the go directive. CHANGELOG.md says go-reviewer says the same thing.
Checked on go1.27.3, module line go 1.26:
go buildofstrings.CutLastsucceeded.go vetreportedstrings.CutLast requires go1.27 or later (module is go1.26).- The generic method
func (l List[E]) Map[R any](f func(E) R) List[R]failed the compiler:generic method requires go1.27 or later.
The compiler enforces language changes. stdversion (which go test runs) enforces standard-library symbols. agents/go-reviewer.md lines 25–26 only say "under an older go directive, skip what is marked newer." It never says what the mark is (Since cell or (1.NN) note) and never names the tools. The body is 9,962 characters, so the full sentence needs a cut elsewhere.
go-reviewer shape
The description is unchanged and still matches the agent pattern: "Use this agent when," three prose triggers, a pointer to "When to invoke," the not-for clause, model: inherit, color: cyan, and a tools: allowlist with no Write or Edit. The new modernization split is in the right dimension: go fix for real fixers, and by hand for cmp.Or chains and a drain before resp.Body.Close(). The opening version clause is the part that got compressed past the point where the agent can apply it.
Suggestions
url.Values.Cloneis in neither thego-idiomsdescription nor the router load row. Closing an HTTP body without draining it is on the router row only. Both are no-fixer forms, sogo fixwill not surface them.docs/install.mduses the same "the compiler,go vetandgo fix" wording as the router, without namingstdversion.skills/go-lint-setup/SKILL.mdcites the modernize-disable handling atblob/main. The authoring record pins revisions; this URL does not.- The
go-idiomsdescription's "or which fixer owns a rewrite" dropped the verb. "or asks which fixer owns a rewrite" restores it. The description is 72 words, inside the 50–75 guide. scripts/hooks-test.shchanging the fixture togo 1.27.0does not pin the floor. Session start only checks thatgo.modexists.
What holds up
go tool fix helpon go1.27.3 registerserrorsastype,atomictypes,slicesbackward,embedlit,unsafefuncs,waitgroupgo,stringscut, andomitzero. It does not registerfmtappendf,bloop, orwaitgroup.go vetreports%wof a*EwhoseEhas a value-receiverError, and does not report a pointer-receiver error.omitzerodrops a non-nil*time.Timeat the zero time;omitemptykeeps it. v2 marshals a nil slice and map as[]and{}.uuid.Newis version 4,uuid.NewV7is version 7, andurl.Values.Clonecopies each slice whilemaps.Cloneshares it.- The floor text matches across
AGENTS.md,README.md, anddocs/install.md. No skill, agent, or rule still says 1.26.4 or "flagged as hints." The revive URLs all point atrevive-lint/revive. GO_FLOOR_MINORand the CI matrix are both1.27. That pins fixer names. It cannot see that a registered fixer skips a rewrite, which is theCutLastcase, and authoring step 4 already says to rungo fix -difffor that.
Before merge
- Name the real
b.Loopfailure: the timer-stopped fatal, and timing skew. Drop the ten-minute timeout as the reason the rewrite was withdrawn. - Make the † legend agree with the
CutLastcell. - Split the version gate into language changes (the compiler) and standard-library symbols (
stdversion), and givego-reviewerthe same marks the router uses.
|
Addressed in e141814 and a31a435. Each point was checked on go1.27.3 first, and all three must-fix items held. 1.
The text now names both, and keeps "restructure a loop that pauses the timer, compare results and run time". 2. † legend vs the 3. Version gate. Fixed. Reproduced under
Suggestions:
Validator (Fixer column strict on go1.27.3, 22 cells), selftest, hook tests, |
Review on #15, each point checked on go1.27.3: - go-testing: the bloop rewrite was withdrawn (golang/go#74967) because a converted loop that pauses the timer fails with "B.Loop called with timer stopped" and b.Loop's overhead skewed nanosecond-scale benchmarks by up to 65%; the ten-minute timeout was a runtime bug fixed in 1.24.2. - go-idioms: the dagger legend covers a registered fixer that lacks a newer rewrite, which the CutLast cell states in words; that cell no longer says stringscut rewrites Index only. - Version gate (router, Cursor rule, go-idioms, AGENTS.md, install doc, changelog): the compiler rejects newer language features (generic methods under go 1.26 fail to build), go vet's stdversion newer standard-library symbols (strings.CutLast builds, vet reports it), and go fix offers only rewrites the directive allows. - go-idioms description restores "asks" and names url.Values.Clone and an undrained HTTP body; the router and rule load rows add url.Values. - go-lint-setup cites golangci-lint at a pinned commit, not a branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Names the marks (a Since cell or a (1.NN) note) and what enforces them: the compiler for language features, go vet's stdversion for standard-library symbols. Trimmed the skill list the router's table already carries, an example list and a rationale clause to stay under 10,000 characters (9,963). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Summary
Moves the plugin's standards baseline from Go 1.26.4+ (with Go 1.27 as hints) to Go 1.27+ as a hard floor. A module on an older
godirective skips what is marked newer. The compiler,go vet(stdversion, whichgo testruns by default from 1.27) andgo fixall follow the directive, so one sentence in the router, the Cursor rule andgo-reviewerreplaces any per-rule 1.26 variant. That matches the "no fallback branch" policy indocs/authoring.md.Sources: re-read per the refresh procedure in
docs/authoring.md:encoding/json/v2, and theencoding/json"Migrating to v2" notes.revive-lint/revive.Checked by running the tools, not by reading docs:
go tool fix helpregisterserrorsastype,atomictypesandslicesbackward, which were marked golangci-lint-only.waitgroupis nowwaitgroupgo, andfmtappendfis gone.fmtappendf(x/tools/go/analysis/passes/modernize: remove the fmtappendf modernizer golang/go#77581) andbloop(x/tools/gopls: modernize bloop: Can't use b.Loop() if loop contains b.StopTimer/b.StartTimer golang/go#74967) out of the suite.bloopwas marked as running in golangci-lint, which it no longer does.CutLast: go1.27.3'sstringscutrewritesIndextoCutbut notLastIndextoCutLast. x/tools v0.52.0, which golangci-lint v2.15.0 pins, does both.go fixgates rewrites on thegodirective (norange nundergo 1.21).go vetreportsstrings.CutLast requires go1.27 or later (module is go1.26).go vetreports%won a*EwhoseEimplementserror.omitzerodrops a non-nil*time.Timeat the zero time, whereomitemptykeeps it.Changes by file:
go-idioms:fmt.Fprintf(w, …)leads overfmt.Appendf(nil, …);b.Loopis converted by hand.CutLast,embedlitandunsafefuncsrows; generic methods;encoding/json/v2for new code, with v1 kept in existing code; the standard-libraryuuid; closing an HTTP response body without draining it;url.Values.Clone;omitzeroinstead ofomitemptyon scalar and pointer fields.go-errors:%wof a pointer to a value-receiver error type, andgo fixconvertingerrors.As.go-concurrency: thegoroutineleakprofile as plain guidance, with what it misses and its cost; goroutine labels now print in crash tracebacks.go-testing:synctest.Sleep,httptest.NewTestServer,go doc -ex, theConfig.Randdeprecation, and theb.Loopconversion caveat.go-lint-setup: amodernizedisable:entry namingfmtappendforwaitgrouponly warns now. Keep formatters on one Go minor, because 1.27'sgofmtre-spaces some files.go-idioms.go-reviewer: reviews on the 1.27 floor. By-hand modernization debt (cmp.Orchains, a body drain) is listed apart fromgo fixdebt. Body 9,965 characters.GO_FLOOR_MINORis1.27, and the matrix is1.27.x, so the Fixer-column check runs strictly there.AGENTS.md,README.md,docs/install.md(go1.27.3) anddocs/authoring.md. The revision record now tracks the release notes and golangci-lint's x/tools snapshot, and step 4 asks for ago fix -diffcheck before a new Fixer cell.Review: an independent
plugin-dev:skill-reviewerpass checked each changed claim against the downloaded sources, and its findings are applied. One kept item: theb.Loop"seconds into a ten-minute timeout" detail comes from golang/go#74967, which the reviewer wasn't given.The 0.8.0 version bump follows as its own release commit after merge.
Checklist
./scripts/validate.shpasses (orclaude plugin validate .if Python is unavailable): the Fixer column is checked strictly against go1.27.3 (22 cells)claude plugin validate .passes;python3 scripts/validate.py --check-links: 73 of 73 URLs resolve.cursor-plugin/plugin.jsonpath map kept in step with the components that exist: no rule added or renamed[Unreleased]; 0.8.0 at release🤖 Generated with Claude Code