From e19f842b22a141206ade86b56247ada5ff2279ef Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 1 Jul 2026 09:31:14 +0000 Subject: [PATCH 1/2] feat(skills): retarget Go standards baseline to Go 1.26 (1.25+ compatible) Move the standards baseline from Go 1.25 to Go 1.26 (1.26.4+) while keeping version-gated guidance valid for 1.25 modules via the existing Since/go.mod gating in go-idioms. - go-idioms: add new(expr) idiom (1.26) and self-referencing generics note; frame go fix ./... as the canonical modernizer runner on 1.26. - go-testing: add testing/cryptotest (1.26); note synctest.Run removal in 1.26. - go-concurrency: add runtime/pprof goroutineleak profile (1.26). - go-coding, go-linting, go-explain, go-reviewer, rules/go-context.mdc: bump baseline to 1.26 and clarify go fix vs golangci-lint modernize. - docs/install.md, README.md: min toolchain Go 1.26.x (1.26.4+); tarball and gopls (v0.22.x) pins updated. - Bump both manifests to 0.3.0; add CHANGELOG 0.3.0 section; update AGENTS.md. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_016DwDkMcyVnq9RqzJK7CHKu --- .claude-plugin/plugin.json | 2 +- .cursor-plugin/plugin.json | 2 +- AGENTS.md | 2 +- CHANGELOG.md | 15 +++++++++++++++ README.md | 2 +- agents/go-reviewer.md | 7 ++++--- docs/install.md | 26 +++++++++++++------------- rules/go-context.mdc | 6 +++--- skills/go-coding/SKILL.md | 6 +++--- skills/go-concurrency/SKILL.md | 7 +++++-- skills/go-explain/SKILL.md | 5 +++-- skills/go-idioms/SKILL.md | 28 ++++++++++++++++++---------- skills/go-linting/SKILL.md | 6 +++++- skills/go-testing/SKILL.md | 11 ++++++++--- 14 files changed, 81 insertions(+), 44 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index e14a947..01bd009 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "go-coding", - "version": "0.2.1", + "version": "0.3.0", "description": "Idiomatic Go coding standards for AI assistants — formatting, errors, concurrency, testing, layout.", "author": { "name": "Cadasto B.V.", diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json index 96dc804..56068c8 100644 --- a/.cursor-plugin/plugin.json +++ b/.cursor-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "go-coding", - "version": "0.2.1", + "version": "0.3.0", "description": "Idiomatic Go coding standards for AI assistants — formatting, errors, concurrency, testing, layout.", "author": { "name": "Cadasto B.V.", diff --git a/AGENTS.md b/AGENTS.md index 0cc41c5..795aab5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,7 +6,7 @@ This file provides guidance to AI coding assistants (Claude Code, Cursor, and co The **Go Coding Plugin** is an AI plugin by Cadasto B.V. that teaches AI coding assistants **idiomatic Go coding standards** — formatting, naming, error handling, concurrency, testing, and project layout — through skills, commands, agents, hooks, and Cursor rules. It targets **both Claude Code and Cursor** from a single shared component set. -> **Current status — v0.2.0.** A complete dual-host (Claude Code + Cursor) Go-standards set that validates clean (`./scripts/validate.sh` + `claude plugin validate .`): the auto-invoked `go-coding` **router** skill; the focused standards skills `go-errors`, `go-concurrency`, `go-testing`, `go-idioms`, `go-linting`, `go-layout`; the read-only `go-reviewer` agent; the user-invoked `/go-explain` and `/go-lint-setup` skills; a shipped `references/golangci.v2.yml`; the `rules/go-context.mdc` Cursor rule; and host-agnostic `session-start` + `format-on-save` hooks. Do not assume a file is present because it is documented here — check first. +> **Current status — v0.3.0.** A complete dual-host (Claude Code + Cursor) Go-standards set, baselined on **Go 1.26** (1.26.4+) with version-gated guidance that still holds for 1.25 modules, that validates clean (`./scripts/validate.sh` + `claude plugin validate .`): the auto-invoked `go-coding` **router** skill; the focused standards skills `go-errors`, `go-concurrency`, `go-testing`, `go-idioms`, `go-linting`, `go-layout`; the read-only `go-reviewer` agent; the user-invoked `/go-explain` and `/go-lint-setup` skills; a shipped `references/golangci.v2.yml`; the `rules/go-context.mdc` Cursor rule; and host-agnostic `session-start` + `format-on-save` hooks. Do not assume a file is present because it is documented here — check first. ## Domain Context diff --git a/CHANGELOG.md b/CHANGELOG.md index d316228..e55b4e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,21 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Keep a Changelog: https://keepachangelog.com/en/1.1.0/ - Semantic Versioning: https://semver.org/spec/v2.0.0.html +## [0.3.0] - 2026-07-01 + +Retargets the standards baseline to **Go 1.26** (1.26.4+) while keeping version-gated guidance valid for 1.25 modules. + +### Added +- Skills: `go-idioms` — `new(expr)` row (Go 1.26 pointer/optional-field initialization) and a note on self-referencing generic type parameters. +- Skills: `go-testing` — `testing/cryptotest.SetGlobalRandom` for deterministic crypto tests (Go 1.26); note that the pre-1.25 `GOEXPERIMENT=synctest` (`synctest.Run`) API was removed in Go 1.26. +- Skills: `go-concurrency` — experimental `goroutineleak` profile in `runtime/pprof` (Go 1.26) as a toolchain-native complement to `goleak`. + +### Changed +- Skills / agent / Cursor rule: standards baseline moved from Go 1.25 to **Go 1.26** in `go-coding`, `go-idioms`, `go-linting`, `go-explain`, the `go-reviewer` agent, and `rules/go-context.mdc`; all keep working against 1.25 modules via the `Since`/`go.mod` version-gating already in `go-idioms`. +- Skills: `go-idioms`, `go-linting`, `go-coding`, `rules/go-context.mdc` — frame `go fix ./...` as the canonical modernizer runner on Go 1.26 (rewritten atop the analysis framework), with `golangci-lint --enable-only=modernize` for CI reproducibility and older toolchains. +- Docs: `docs/install.md` and `README.md` — minimum host toolchain raised to **Go 1.26.x** (1.26.4+); tarball example updated to `go1.26.4`; `gopls` pin moved to `v0.22.x` (the line that adds Go 1.26 support). +- Manifests: `version` → `0.3.0` in both `.claude-plugin/plugin.json` and `.cursor-plugin/plugin.json`. + ## [0.2.1] - 2026-06-18 ### Added diff --git a/README.md b/README.md index 1d6b571..ac91ca9 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Or from a local working copy: `claude plugin add /path/to/go-coding-plugin`. **Cursor**: add this repository as a plugin (Settings → Plugins). See [`docs/install.md`](docs/install.md) for both hosts. -**Prerequisites** — the plugin installs without a Go toolchain, but its hooks and enforcement guidance expect **Go 1.25.x** plus `gofmt`, `gofumpt`, `goimports`, and `gopls` on the host `PATH`. See [Host toolchain (minimal requirements)](docs/install.md#host-toolchain-minimal-requirements) for what each tool drives and copy-paste install commands. +**Prerequisites** — the plugin installs without a Go toolchain, but its hooks and enforcement guidance expect **Go 1.26.x** (minimum 1.26.4; still works against 1.25 modules) plus `gofmt`, `gofumpt`, `goimports`, and `gopls` on the host `PATH`. See [Host toolchain (minimal requirements)](docs/install.md#host-toolchain-minimal-requirements) for what each tool drives and copy-paste install commands. ## Component surface diff --git a/agents/go-reviewer.md b/agents/go-reviewer.md index bdc9a17..587735c 100644 --- a/agents/go-reviewer.md +++ b/agents/go-reviewer.md @@ -43,7 +43,7 @@ tools: - Bash --- -You are **go-reviewer**, a reviewer of idiomatic, correct Go (Go 1.25, golangci-lint v2). You supply +You are **go-reviewer**, a reviewer of idiomatic, correct Go (Go 1.26, works with 1.25+; golangci-lint v2). You supply the judgment a linter cannot — the bugs and smells that survive `gofmt`, `go vet`, and `golangci-lint`. You are **read-only**: you report findings, you never edit code. @@ -94,8 +94,9 @@ the judgment a linter cannot — the bugs and smells that survive `gofmt`, `go v non-atomic reads of those fields); `sync.Mutex`/`WaitGroup` copied by value; a map written concurrently without a lock; check-then-act races. - **Stale modernization debt** — code `modernize`/`go fix` would rewrite (range-int, `min`/`max`, - `slices`/`maps`, `strings.Cut`, `cmp.Or`, pre-1.22 loop-var copies). Low severity; point at - `golangci-lint run --enable-only=modernize`. + `slices`/`maps`, `strings.Cut`, `cmp.Or`, pre-1.22 loop-var copies; on Go 1.26 modules also + pointer-helper temps that `new(expr)` replaces). Low severity; point at `go fix ./...` (Go 1.26) or + `golangci-lint run --enable-only=modernize`. Gate suggestions on the module's `go.mod` version. - **slog hot-path waste** — building a per-call logger instead of `logger.With(...)`; formatting or allocating before a level check; key-value variadic on a hot path instead of `slog.LogAttrs`. diff --git a/docs/install.md b/docs/install.md index ec7e35b..9ad021c 100644 --- a/docs/install.md +++ b/docs/install.md @@ -41,51 +41,51 @@ Add this repository as a plugin (Cursor **Settings → Plugins**, via Git URL or ## Host toolchain (minimal requirements) -Installing the plugin itself needs no Go toolchain — it is pure Markdown + JSON. But its **enforcement** layer only delivers value when the standard Go tools are on the host `PATH`: the `format-on-save` hook shells out to a formatter, the golangci-lint v2 reference config lists `gofumpt`/`goimports` as formatters, and the recommended official `gopls-lsp` plugin (`@claude-plugins-official`) drives `gopls`. The plugin targets **Go 1.25** + golangci-lint v2. +Installing the plugin itself needs no Go toolchain — it is pure Markdown + JSON. But its **enforcement** layer only delivers value when the standard Go tools are on the host `PATH`: the `format-on-save` hook shells out to a formatter, the golangci-lint v2 reference config lists `gofumpt`/`goimports` as formatters, and the recommended official `gopls-lsp` plugin (`@claude-plugins-official`) drives `gopls`. The plugin targets **Go 1.26** + golangci-lint v2, and its version-gated idiom guidance still works against 1.25 modules. At minimum the host should provide: | Tool | Provided by | Used for | If missing | |------|-------------|----------|------------| -| **Go 1.25.x** | [go.dev/dl](https://go.dev/dl/) / package manager | everything; satisfies `go.mod` `go 1.25.x` | no toolchain at all | +| **Go 1.26.x** (min 1.26.4) | [go.dev/dl](https://go.dev/dl/) / package manager | everything; satisfies `go.mod` `go 1.26.x` (and 1.25 modules); `go fix ./...` runs the modernizers | no toolchain at all | | **`gofmt`** | the Go distribution | `format-on-save.sh` fallback (`gofmt -w -s`) | n/a — always ships with Go | | **`gofumpt`** | `go install` | `format-on-save.sh` primary (`gofumpt -w`), stricter gofmt superset | hook degrades to `gofmt` | | **`goimports`** | `go install` | `goimports` formatter in the golangci-lint v2 config (import grouping/pruning) | import-group formatting skipped | -| **`gopls`** (v0.21.1) | `go install` | the `gopls-lsp` plugin (defs/refs/diagnostics/rename/vulncheck) | no code intelligence | +| **`gopls`** (v0.22.x) | `go install` | the `gopls-lsp` plugin (defs/refs/diagnostics/rename/vulncheck) | no code intelligence | ### Install / upgrade Go (official tarball, Linux) -Pick the latest **1.25.x** patch from and the build matching your platform (`linux-amd64` shown): +Pick the latest **1.26.x** patch (1.26.4 or newer) from and the build matching your platform (`linux-amd64` shown): ```bash -# replace the version with the current latest 1.25.x patch -curl -fLO https://go.dev/dl/go1.25.11.linux-amd64.tar.gz +# replace the version with the current latest 1.26.x patch (1.26.4+) +curl -fLO https://go.dev/dl/go1.26.4.linux-amd64.tar.gz sudo rm -rf /usr/local/go # remove any prior install (don't overlay) -sudo tar -C /usr/local -xzf go1.25.11.linux-amd64.tar.gz +sudo tar -C /usr/local -xzf go1.26.4.linux-amd64.tar.gz export PATH=$PATH:/usr/local/go/bin # add to your shell profile if not already present -go version # → go version go1.25.11 linux/amd64 +go version # → go version go1.26.4 linux/amd64 ``` -> macOS/Windows or a package manager (Homebrew `go`, `winget`, distro packages) work equally well — the only requirement is that `go version` reports **1.25.x**. `gofmt` is included in every Go distribution, so nothing extra is needed for the hook's fallback path. +> macOS/Windows or a package manager (Homebrew `go`, `winget`, distro packages) work equally well — the only requirement is that `go version` reports **1.26.x** (1.26.4+). `gofmt` is included in every Go distribution, so nothing extra is needed for the hook's fallback path. ### Install the supporting tools -`go install` drops binaries in `$(go env GOPATH)/bin` (default `~/go/bin`) — make sure that directory is on your `PATH`. Run these **after** Go is in place so they compile against your 1.25 toolchain: +`go install` drops binaries in `$(go env GOPATH)/bin` (default `~/go/bin`) — make sure that directory is on your `PATH`. Run these **after** Go is in place so they compile against your 1.26 toolchain: ```bash go install mvdan.cc/gofumpt@latest # stricter gofmt superset (hook primary) go install golang.org/x/tools/cmd/goimports@latest # import grouping / pruning -go install golang.org/x/tools/gopls@v0.21.1 # language server for the gopls-lsp plugin (pinned: v0.21.1, verified against Go 1.25.x) +go install golang.org/x/tools/gopls@v0.22.0 # language server for the gopls-lsp plugin (pinned: v0.22.x — the gopls line that adds Go 1.26 support; use @latest for the newest patch) ``` Verify: ```bash -go version # → 1.25.x +go version # → 1.26.x (1.26.4+) command -v gofmt # ships with Go (in GOROOT/bin) gofumpt --version command -v goimports # goimports has no --version flag -gopls version # → golang.org/x/tools/gopls v0.21.1 +gopls version # → golang.org/x/tools/gopls v0.22.x ``` These are **host-only** dev tools; the plugin still works without them (the format hook degrades to `gofmt`, then to a silent no-op). Full-tree `golangci-lint` runs separately — often in a pinned container — so it does not depend on these host binaries. diff --git a/rules/go-context.mdc b/rules/go-context.mdc index 0d683ed..fc4fd5e 100644 --- a/rules/go-context.mdc +++ b/rules/go-context.mdc @@ -1,5 +1,5 @@ --- -description: Go coding standards — idiomatic Go (Go 1.25, golangci-lint v2) for .go files. Mirrors the go-coding router skill for Cursor. +description: Go coding standards — idiomatic Go (Go 1.26, works with 1.25+; golangci-lint v2) for .go files. Mirrors the go-coding router skill for Cursor. globs: ["**/*.go"] alwaysApply: false --- @@ -17,10 +17,10 @@ This Cursor rule mirrors the `go-coding` router skill — apply it when editing |---|---|---| | Formatting | `gofmt`/`gofumpt` (+ `goimports`) — machine-enforced | — | | Static analysis / bugs | `go vet ./...`, `golangci-lint run` | `go-linting` | -| Modern idioms | `golangci-lint run --enable-only=modernize`, `go fix ./...` | `go-idioms` | +| Modern idioms | `go fix ./...` (Go 1.26 runs the modernizers natively), or `golangci-lint run --enable-only=modernize` | `go-idioms` | | Errors | `golangci-lint run --enable-only=errorlint` | `go-errors` | | Concurrency | `go test -race ./...`, `go vet ./...` | `go-concurrency` | -| Testing | `go test -race ./...`; `testing/synctest` (1.25) for time/concurrency | `go-testing` | +| Testing | `go test -race ./...`; `testing/synctest` (since 1.25) for time/concurrency | `go-testing` | | Layout | judgment | `go-layout` | Ground every judgment call in a cited source — Effective Go, Go Code Review Comments, the Google diff --git a/skills/go-coding/SKILL.md b/skills/go-coding/SKILL.md index 43df66b..749a9fc 100644 --- a/skills/go-coding/SKILL.md +++ b/skills/go-coding/SKILL.md @@ -1,6 +1,6 @@ --- name: go-coding -description: Go coding-standards router and entry point for idiomatic Go (Go 1.25, golangci-lint v2). This skill should be used when a Go task spans multiple areas, is unspecified, or the question is which tool or standard applies — it routes each topic to the deterministic tool (gofmt/gofumpt, go vet, golangci-lint v2 + modernize, go test -race), to the gopls-lsp plugin for code intelligence, and then to the focused go-* skill that owns it. For a single, already-identified topic prefer that skill directly (errors → go-errors, concurrency → go-concurrency, testing → go-testing, idioms/modernization → go-idioms, linter config → go-linting, layout → go-layout). Not for non-Go languages or domain/business rules. +description: Go coding-standards router and entry point for idiomatic Go (Go 1.26, works with 1.25+; golangci-lint v2). This skill should be used when a Go task spans multiple areas, is unspecified, or the question is which tool or standard applies — it routes each topic to the deterministic tool (gofmt/gofumpt, go vet, go fix / golangci-lint v2 modernize, go test -race), to the gopls-lsp plugin for code intelligence, and then to the focused go-* skill that owns it. For a single, already-identified topic prefer that skill directly (errors → go-errors, concurrency → go-concurrency, testing → go-testing, idioms/modernization → go-idioms, linter config → go-linting, layout → go-layout). Not for non-Go languages or domain/business rules. --- # go-coding — Go standards router @@ -19,10 +19,10 @@ Two principles from the project research drive it: |---|---|---| | Formatting | `gofmt -l` / `gofumpt -l` (+ `goimports`) — machine-enforced, non-negotiable | — | | Static analysis / likely bugs | `go vet ./...`, `golangci-lint run` | `go-linting` | -| Modern idioms (range-int, `min`/`max`, `slices`/`maps`, `wg.Go`, `strings.Cut`) | `golangci-lint run --enable-only=modernize`, or `go fix ./...` | `go-idioms` | +| Modern idioms (range-int, `min`/`max`, `slices`/`maps`, `wg.Go`, `strings.Cut`, `new(expr)`) | `go fix ./...` (Go 1.26 applies the full modernizer suite natively), or `golangci-lint run --enable-only=modernize` on older toolchains | `go-idioms` | | Errors (`%w`, `errors.Is`/`As`, `errors.Join`, sentinel/typed) | `golangci-lint run --enable-only=errorlint` | `go-errors` | | Concurrency (goroutine leaks, ctx lifecycle, atomics) | `go test -race ./...`, `go vet ./...` | `go-concurrency` | -| Testing (table-driven, `t.Parallel`, `B.Loop`, `testing/synctest`) | `go test -race ./...`; use `testing/synctest` (stable in 1.25) for time/concurrency tests | `go-testing` | +| Testing (table-driven, `t.Parallel`, `B.Loop`, `testing/synctest`) | `go test -race ./...`; use `testing/synctest` (stable since 1.25) for time/concurrency tests | `go-testing` | | Project layout (`internal/`, start-flat) | judgment — see sources below | `go-layout` | | Code intelligence (defs/refs/diagnostics/rename/vulncheck) | install the **`gopls-lsp`** plugin | — | diff --git a/skills/go-concurrency/SKILL.md b/skills/go-concurrency/SKILL.md index a9010e1..57580b3 100644 --- a/skills/go-concurrency/SKILL.md +++ b/skills/go-concurrency/SKILL.md @@ -7,6 +7,8 @@ description: Idiomatic, leak-free Go concurrency. This skill should be used when Deterministic backstop: `go test -race ./...`, `go vet ./...` (catches copylocks, lost cancel), and `go.uber.org/goleak`. The race detector is the source of truth — run it before reasoning. +On **Go 1.26+** the runtime also ships an experimental `goroutineleak` profile in `runtime/pprof` +that reports leaked goroutines — a toolchain-native complement to `goleak` for leak hunts. ## Rules @@ -28,11 +30,12 @@ and `go.uber.org/goleak`. The race detector is the source of truth — run it be a concurrent map write panics; `-race` catches it. - **Channels:** close on the *send* side, never the receive side; a `nil` channel blocks forever (useful for disabling a `select` arm, a bug everywhere else). -- **Testing time/concurrency:** use **`testing/synctest`** (stable in Go 1.25) — fake clock + +- **Testing time/concurrency:** use **`testing/synctest`** (stable since Go 1.25) — fake clock + deterministic scheduling. See `go-testing`. ## Sources -- synctest — ; Go 1.25 release notes — +- synctest — ; Go 1.25/1.26 release notes — +- `goroutineleak` profile (Go 1.26, experimental) — - Code Review Comments (Goroutine Lifetimes, Contexts) — - Uber Go Style Guide (Concurrency) — diff --git a/skills/go-explain/SKILL.md b/skills/go-explain/SKILL.md index 4df9161..70422f5 100644 --- a/skills/go-explain/SKILL.md +++ b/skills/go-explain/SKILL.md @@ -18,8 +18,9 @@ Cover, in a few lines: 4. **Source** — cite one authoritative reference: Effective Go, Go Code Review Comments, the Google or Uber Go style guide, a `go.dev/blog` post, or `pkg.go.dev`. -Tailor to the repo's Go version when a `go.mod` is present — don't recommend a Go 1.25 idiom for an -older module. For a fuller treatment, route to the matching skill: `go-errors`, `go-concurrency`, +Tailor to the repo's Go version when a `go.mod` is present — don't recommend a Go 1.26 idiom (e.g. +`new(expr)`) for a module pinned to 1.25 or older. For a fuller treatment, route to the matching +skill: `go-errors`, `go-concurrency`, `go-testing`, `go-idioms`, `go-linting`, or `go-layout`. Keep it tight — this is a lookup, not a lecture. If `$ARGUMENTS` is empty, ask what to explain. diff --git a/skills/go-idioms/SKILL.md b/skills/go-idioms/SKILL.md index 1f57668..ce61f5d 100644 --- a/skills/go-idioms/SKILL.md +++ b/skills/go-idioms/SKILL.md @@ -1,24 +1,29 @@ --- name: go-idioms -description: Modern idiomatic Go (the `modernize` linter set). This skill should be used when the user writes, reviews, or modernizes Go and wants current-version idioms — range-over-int, `min`/`max`, `slices`/`maps`, `strings.Cut`, `cmp.Or`, `sync.OnceFunc`, iterators, `slog.LogAttrs`, dropped loop-var copies. Framed so advice equals tooling (`golangci-lint --enable-only=modernize` / `go fix`). Errors→`go-errors`, concurrency→`go-concurrency`, tests→`go-testing`. Not for golangci-lint configuration (use `go-linting`) or non-Go languages. +description: Modern idiomatic Go (the `modernize` analyzer set). This skill should be used when the user writes, reviews, or modernizes Go and wants current-version idioms — range-over-int, `min`/`max`, `slices`/`maps`, `strings.Cut`, `cmp.Or`, `sync.OnceFunc`, iterators, `slog.LogAttrs`, `new(expr)` (Go 1.26), dropped loop-var copies. Framed so advice equals tooling (`go fix ./...` on Go 1.26, or `golangci-lint --enable-only=modernize`). Errors→`go-errors`, concurrency→`go-concurrency`, tests→`go-testing`. Not for golangci-lint configuration (use `go-linting`) or non-Go languages. --- # go-idioms — modern Go (modernize) -**Advice == tooling.** The `modernize` analyzer (in `golang.org/x/tools`, the same engine as gopls -and `go fix`) flags and usually auto-fixes everything below. Run it, don't hand-audit: +**Advice == tooling.** The `modernize` analyzers flag and usually auto-fix everything below. Run +them, don't hand-audit. As of **Go 1.26** the rewritten `go fix` is the canonical runner — it ships +the full modernizer suite in the toolchain itself: ``` -golangci-lint run --enable-only=modernize --fix # or: go fix ./... +go fix ./... # Go 1.26+: applies the built-in modernizers +golangci-lint run --enable-only=modernize --fix # any toolchain (same analyzers, via golangci-lint) ``` -This skill explains *why* and catches what review notices before the linter runs. **Check `go.mod` -first** — don't apply a Go 1.25 idiom to a repo pinned older. +Both draw on the same `golang.org/x/tools` engine as gopls, so their fixes agree. This skill +explains *why* and catches what review notices before the tool runs. **Check `go.mod` first** — +gate each idiom on the module's Go version (the `Since` column below); don't apply a Go 1.26 idiom to +a repo pinned to 1.25 or older. ## Prefer → over (since) | Prefer | Over | Since | |---|---|---| +| `new(expr)` — e.g. `Field: new(30)`, `new(int64(req.Limit))` | a named temp + `&tmp`, or a `ptr[T](v)` helper, for optional/pointer fields | 1.26 | | `for i := range n` | `for i := 0; i < n; i++` | 1.22 | | `min(a, b)` / `max(a, b)` builtins | hand-rolled helpers | 1.21 | | *(drop)* `x := x` loop-var copy | pre-1.22 capture workaround | 1.22 | @@ -33,13 +38,16 @@ first** — don't apply a Go 1.25 idiom to a repo pinned older. | `for b.Loop()` | `for i := 0; i < b.N; i++` → `go-testing` | 1.24 | | typed `atomic.Int64` | bare-int `atomic.Add*` → `go-concurrency` | 1.19 | -Idioms are a moving target — let the linter (pinned to the repo's toolchain) be the source of -truth so advice never drifts from the user's `go fix`. +Idioms are a moving target — let the tool (pinned to the repo's toolchain) be the source of +truth so advice never drifts from the user's `go fix`. Go 1.26 also lifts the ban on a generic type +referencing itself in its own type-parameter list (e.g. `type Adder[A Adder[A]] interface{ Add(A) A }`), +so self-referential constraints no longer need a workaround — but that's a hand-written pattern, not +something a modernizer rewrites. ## Sources - `modernize` — -- `go fix` / tool evolution — ; range-over-func — -- `slog` — ; Go 1.21–1.25 release notes +- `go fix` (rewritten in 1.26) — ; range-over-func — +- `slog` — ; Go 1.21–1.26 release notes (`new(expr)`, self-ref generics — ) --- *Decomposition inspired by [`samber/cc-skills-golang`](https://github.com/samber/cc-skills-golang) (MIT © 2026 Samuel Berthe); rules grounded in the sources above.* diff --git a/skills/go-linting/SKILL.md b/skills/go-linting/SKILL.md index dfd0f89..887b95e 100644 --- a/skills/go-linting/SKILL.md +++ b/skills/go-linting/SKILL.md @@ -44,7 +44,11 @@ formatters: - **Pin the version in CI** for reproducibility (the Cadasto Go repos pin `v2.11.4`); a version drift silently changes the rule set. - `modernize` is the single highest-leverage linter — it operationalizes most `go-idioms` rules on - the same engine as gopls/`go fix`, so the plugin's advice stays consistent with the toolchain. + the same engine as gopls/`go fix`, so the plugin's advice stays consistent with the toolchain. As + of **Go 1.26** the rewritten `go fix ./...` runs that same modernizer suite from the toolchain + itself; keep `modernize` in golangci-lint for CI reproducibility and to cover toolchains older + than 1.26. Use golangci-lint built with a Go version ≥ the module's toolchain (Go 1.26 support + landed in the golangci-lint releases built with 1.26). - Adopt the shipped reference config `references/golangci.v2.yml`, or run `/go-lint-setup` to scaffold it into a repo (it won't overwrite an existing config without asking). diff --git a/skills/go-testing/SKILL.md b/skills/go-testing/SKILL.md index 4389557..d1ad33a 100644 --- a/skills/go-testing/SKILL.md +++ b/skills/go-testing/SKILL.md @@ -18,19 +18,24 @@ Deterministic backstop: `go test -race ./...` (always, in CI), `go test -bench`, - **`-race` is non-negotiable** for any code touching goroutines; wire it into CI. - **Goroutine-leak detection:** `go.uber.org/goleak` — `goleak.VerifyTestMain(m)` or per-test `defer goleak.VerifyNone(t)`. -- **`testing/synctest` (stable Go 1.25) is the default for time/concurrency tests** — timeouts, +- **`testing/synctest` (stable since Go 1.25) is the default for time/concurrency tests** — timeouts, tickers, retries, `context` cancellation. It runs the bubble on a *fake clock* with deterministic scheduling, so "5-second" waits complete in microseconds and flakiness disappears. Wrap with `synctest.Test(t, func(t *testing.T){ … })`; `synctest.Wait()` blocks until every goroutine in the - bubble is durably blocked. Reach for it instead of `time.Sleep`-based polling. + bubble is durably blocked. Reach for it instead of `time.Sleep`-based polling. (The pre-1.25 + `GOEXPERIMENT=synctest` API — `synctest.Run` — was removed in Go 1.26; use the stable `synctest.Test`.) - **Fuzzing** (`func FuzzX(f *testing.F)`) for parsers, codecs, and anything consuming untrusted bytes. **Golden files** (an `-update` flag writing `testdata/*.golden`) for large structured output. +- **Deterministic crypto tests (Go 1.26):** `testing/cryptotest.SetGlobalRandom` pins a + deterministic randomness source for the test's duration — reach for it instead of hand-injecting a + custom `io.Reader` when testing code that draws from `crypto/rand`. - **Assertions:** stdlib + small helpers (`t.Helper()`) is often enough; `testify` is fine — match the repo, don't mix styles. ## Sources - synctest — ; `testing.B.Loop` — -- `testing` package — ; Go 1.22/1.24/1.25 release notes +- `testing` package — ; Go 1.22/1.24/1.25/1.26 release notes — +- `testing/cryptotest` (Go 1.26) — - `go.uber.org/goleak` — --- From d0a7c22aefae395c0f870feb77ae93c5071b7144 Mon Sep 17 00:00:00 2001 From: Sebastian Iancu Date: Wed, 1 Jul 2026 12:07:07 +0200 Subject: [PATCH 2/2] fix(skills): tighten Go 1.26 guidance per PR review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - go-concurrency: note the goroutineleak profile needs GOEXPERIMENT=goroutineleakprofile at build time - go-testing: SetGlobalRandom(t, seed) is process-global — flag that it can't run under t.Parallel() - go-idioms: distinguish the newexpr auto-fixed ptr[T](v) helper from the review-catch tmp+&tmp inline form Co-Authored-By: Claude Opus 4.8 (1M context) --- skills/go-concurrency/SKILL.md | 3 ++- skills/go-idioms/SKILL.md | 2 +- skills/go-testing/SKILL.md | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/skills/go-concurrency/SKILL.md b/skills/go-concurrency/SKILL.md index 57580b3..dbc2c8b 100644 --- a/skills/go-concurrency/SKILL.md +++ b/skills/go-concurrency/SKILL.md @@ -8,7 +8,8 @@ description: Idiomatic, leak-free Go concurrency. This skill should be used when Deterministic backstop: `go test -race ./...`, `go vet ./...` (catches copylocks, lost cancel), and `go.uber.org/goleak`. The race detector is the source of truth — run it before reasoning. On **Go 1.26+** the runtime also ships an experimental `goroutineleak` profile in `runtime/pprof` -that reports leaked goroutines — a toolchain-native complement to `goleak` for leak hunts. +that reports leaked goroutines — a toolchain-native complement to `goleak` for leak hunts (enable it +with `GOEXPERIMENT=goroutineleakprofile` at build time). ## Rules diff --git a/skills/go-idioms/SKILL.md b/skills/go-idioms/SKILL.md index ce61f5d..f0e835e 100644 --- a/skills/go-idioms/SKILL.md +++ b/skills/go-idioms/SKILL.md @@ -23,7 +23,7 @@ a repo pinned to 1.25 or older. | Prefer | Over | Since | |---|---|---| -| `new(expr)` — e.g. `Field: new(30)`, `new(int64(req.Limit))` | a named temp + `&tmp`, or a `ptr[T](v)` helper, for optional/pointer fields | 1.26 | +| `new(expr)` — e.g. `Field: new(30)`, `new(int64(req.Limit))` | a `ptr[T](v)` helper (auto-rewritten by the `newexpr` fixer) or a hand-written `tmp := v; &tmp`, for optional/pointer fields | 1.26 | | `for i := range n` | `for i := 0; i < n; i++` | 1.22 | | `min(a, b)` / `max(a, b)` builtins | hand-rolled helpers | 1.21 | | *(drop)* `x := x` loop-var copy | pre-1.22 capture workaround | 1.22 | diff --git a/skills/go-testing/SKILL.md b/skills/go-testing/SKILL.md index d1ad33a..a83458b 100644 --- a/skills/go-testing/SKILL.md +++ b/skills/go-testing/SKILL.md @@ -26,9 +26,10 @@ Deterministic backstop: `go test -race ./...` (always, in CI), `go test -bench`, `GOEXPERIMENT=synctest` API — `synctest.Run` — was removed in Go 1.26; use the stable `synctest.Test`.) - **Fuzzing** (`func FuzzX(f *testing.F)`) for parsers, codecs, and anything consuming untrusted bytes. **Golden files** (an `-update` flag writing `testdata/*.golden`) for large structured output. -- **Deterministic crypto tests (Go 1.26):** `testing/cryptotest.SetGlobalRandom` pins a +- **Deterministic crypto tests (Go 1.26):** `testing/cryptotest.SetGlobalRandom(t, seed)` pins a deterministic randomness source for the test's duration — reach for it instead of hand-injecting a - custom `io.Reader` when testing code that draws from `crypto/rand`. + custom `io.Reader` when testing code that draws from `crypto/rand`. It's process-global, so it + can't run inside a `t.Parallel()` test (or one with a parallel ancestor). - **Assertions:** stdlib + small helpers (`t.Helper()`) is often enough; `testify` is fine — match the repo, don't mix styles.