Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Add and validate custom Go analysis linters in gh-aw.
.claude/skills/github-go-linters/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-17 | ✗→✓ | ▲ Improved | -42% | 0% |
| case-01 | ✗→✓ | ▲ Improved | -25% | 0% |
| case-02 | ✗→✓ | ▲ Improved | -52% | 0% |
| case-03 | ✗→✓ | ▲ Improved | -37% | 0% |
| case-04 | ✗→✓ | ▲ Improved | -47% | 0% |
Use this guide when adding a new custom Go analysis linter in this repository.
For PR-driven linter generation (derive a rule from a specific pull request pattern), use .github/skills/pr-to-go-linter/SKILL.md.
pkg/linters/<linter-name>/.Analyzer).analysistest with fixtures under testdata/src/....cmd/linters/main.go so it runs via the multichecker binary.go test ./pkg/linters/<linter-name>/...go build ./cmd/lintersmake golint-custommake golint-custom builds cmd/linters and runs it against ./cmd/... and ./pkg/....
For linters that flag micro-optimizations (allocation/perf rules), only apply them on lines that tests actually exercise — "hot paths" — rather than on dead or rarely-executed code where the optimization brings no measurable benefit. Use the shared pkg/linters/internal/coverage package:
-hot-threshold flag in init() (not as a var initializer,to avoid an Analyzer/run/flag initialization cycle):
go var hotThreshold int
func init() { hotThreshold = coverage.RegisterHotThresholdFlag(Analyzer) }
coverage.ShouldApply:go if !coverage.ShouldApply(pass, node.Pos(), *hotThreshold) { return }
coverage.ShouldApply is permissive by default: when no coverage profile is loaded via the GH_AW_LINT_COVERAGE_PROFILE environment variable, or when hot-threshold is 0, it always returns true, preserving pre-coverage-aware behavior. Only wire this into linters whose fix has a genuine performance rationale (extra allocations, O(n²) behavior, etc.) — purely readability/style linters should not be coverage-gated.
bashgo test -covermode=count -coverprofile=/tmp/coverage.out ./... export GH_AW_LINT_COVERAGE_PROFILE=/tmp/coverage.out make golint-custom
This profile is read once per linter-runner process. To lint only a specific subtree, scope the go test and golint-custom commands to the same package path.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-17 | fail→pass | 9,475 | 1,437 | -85% | 1 | 1 | 0% | 1,559 | 899 | -42% | 0 | 0 | — |
case-01 | fail→pass | 15,845 | 7,523 | -53% | 1 | 1 | 0% | 2,948 | 2,223 | -25% | 0 | 0 | — |
case-02 | fail→pass | 16,989 | 4,834 | -72% | 1 | 1 | 0% | 3,134 | 1,517 | -52% | 0 | 0 | — |
case-03 | fail→pass | 9,884 | 2,486 | -75% | 1 | 1 | 0% | 1,672 | 1,059 | -37% | 0 | 0 | — |
case-04 | fail→pass | 10,874 | 2,142 | -80% | 1 | 1 | 0% | 1,669 | 887 | -47% | 0 | 0 | — |
case-05 | pass→pass | 9,311 | 3,614 | -61% | 1 | 1 | 0% | 1,571 | 1,261 | -20% | 0 | 0 | — |
case-06 | fail→pass | 3,321 | 2,537 | -24% | 1 | 1 | 0% | 502 | 1,015 | +102% | 0 | 0 | — |
case-07 | fail→pass | 7,199 | 1,969 | -73% | 1 | 1 | 0% | 1,124 | 958 | -15% | 0 | 0 | — |
case-08 | pass→pass | 10,661 | 1,398 | -87% | 1 | 1 | 0% | 1,659 | 873 | -47% | 0 | 0 | — |
case-09 | pass→pass | 8,942 | 2,073 | -77% | 1 | 1 | 0% | 1,320 | 917 | -31% | 0 | 0 | — |
case-10 | fail→pass | 13,971 | 7,723 | -45% | 1 | 1 | 0% | 2,056 | 1,896 | -8% | 0 | 0 | — |
case-11 | pass→pass | 7,597 | 2,924 | -62% | 1 | 1 | 0% | 1,074 | 1,092 | +2% | 0 | 0 | — |
case-12 | pass→pass | 6,877 | 2,773 | -60% | 1 | 1 | 0% | 1,049 | 1,038 | -1% | 0 | 0 | — |
case-13 | pass→pass | 8,937 | 2,495 | -72% | 1 | 1 | 0% | 1,339 | 946 | -29% | 0 | 0 | — |
case-14 | pass→pass | 6,528 | 2,413 | -63% | 1 | 1 | 0% | 934 | 990 | +6% | 0 | 0 | — |
case-15 | pass→fail | 8,993 | 5,570 | -38% | 1 | 1 | 0% | 1,623 | 1,649 | +2% | 0 | 0 | — |
case-16 | pass→pass | 3,777 | 1,231 | -67% | 1 | 1 | 0% | 645 | 819 | +27% | 0 | 0 | — |
case-18 | fail→pass | 14,375 | 3,041 | -79% | 1 | 1 | 0% | 2,086 | 1,130 | -46% | 0 | 0 | — |
case-19 | pass→pass | 14,197 | 7,430 | -48% | 1 | 1 | 0% | 2,451 | 1,869 | -24% | 0 | 0 | — |
case-20 | pass→pass | 12,923 | 8,138 | -37% | 1 | 1 | 0% | 1,962 | 2,031 | +4% | 0 | 0 | — |
case-21 | fail→pass | 15,430 | 7,007 | -55% | 1 | 1 | 0% | 1,972 | 1,933 | -2% | 0 | 0 | — |
case-22 | pass→pass | 4,177 | 3,593 | -14% | 1 | 1 | 0% | 619 | 1,237 | +100% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +41 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
| Model | Method | Date | Lift |
|---|---|---|---|
| gemini-3.6-flash | verified | 8/9/2026 | +55% |
Other measured skills in the registry, with their headline benchmark lift.