Repository navigation
feat: usage-driven optimization and Go 1.27 support (v0.5.0) - #5
Merged
Merged
Conversation
…tion stub Moves the golangci-lint v2 config-schema, modernize/errorlint-adoption, and upgrade-breakage content from go-linting into go-lint-setup under a new "Adopting or debugging an existing config" heading, since go-lint-setup is the skill that actually gets routed to. go-linting becomes a three-line deprecation stub (removed in 0.6.0) rather than being deleted outright. Sweeps every other live go-linting pointer in the repo (router routing table and description, README, AGENTS.md inventory, the Cursor rule mirror, the reference lint config's header comment, and the go-errors/ go-idioms/go-explain/go-reviewer cross-references) to go-lint-setup, so the fold isn't undermined by stale internal links.
… trigger words, section nesting Fixes three review findings on the go-linting fold: the host-agnostic references/ path-resolution note was dropped along with go-linting and never replaced, so the new pointer to references/golangci.v2.yml's inline comments had no guidance on resolving that path from a user repo; the description lost golangci-lint fmt, linters.exclusions, and migrating a v1 config as live trigger words even though this skill now owns that content; and "Common breakage when bumping the pin" / "Adopting modernize" were nested under the "Discipline once adopted" sub-heading despite not being discipline bullets themselves.
…lint compat note, version-floor wording
…o-testing; AGENTS CI matrix note
…rename/drop, citations, fail-fast
…-fail self-tests; hooks-test in CI The PostToolUse nudge only ever echoed a plain line to stdout, which under Claude Code reaches the transcript, not the model's context — the nudge never actually surfaced. skill-nudge.sh now emits a hook systemMessage JSON object under Claude Code (detected via CLAUDE_PLUGIN_ROOT or a hook_event_name in the payload) and keeps the plain line for Cursor's afterFileEdit, still exiting 0 on every path with no jq/python. hooks-test.sh gains: two assertions proving each delivery path (systemMessage under Claude, plain line under Cursor); a can-fail self-test block proving chk_silent/run_case_absent actually fail on bad input, backing the CHANGELOG's existing "can-fail test control" claim; and a CI step so the suite runs on every PR instead of nowhere. 15/15 cases pass.
…up; drop go-linting from the adoption target A whole-branch review found the component inventories (AGENTS.md, README.md, docs/install.md) still describing a two-hook plugin, hooks-test.sh unreferenced from any doc or release step, and go-lint-setup's body opening with an unconditional "Write the config" despite now also answering adoption/debug questions. Fixes: - AGENTS.md, README.md, docs/install.md: list skill-nudge.sh as the third host-agnostic hook; README/AGENTS gain rows/bullets for scripts/hooks-test.sh and scripts/usage-report.py; docs/versioning.md's release step 2 and both Development command blocks now run ./scripts/hooks-test.sh beside validate.sh. - docs/testing.md: hooks-test.sh added to Validation; a Local triggering test for the nudge hook that requires confirming the model ACTS on the nudge, not only that a line appears; the adoption-target list drops the deprecated go-linting stub; the counted-events paragraph now names the report's two tables (every-event vs. distinct-sessions) and how the 50% target is read off the sessions one. - skills/go-lint-setup/SKILL.md: H1 and intro now name both the scaffold and adopt/debug halves, with one routing line ahead of the numbered steps so a debug question doesn't fall into "write a file"; description already covered both, left untouched. - Bundled minors: AGENTS.md's three "read-only" -> "report-only" go-reviewer mentions (0.4.1's own established wording); validate.py's error text and the golangci.v2.yml header corrected to name two config copies, not three.
The nudge grepped the whole edited file, so "this edit touches an error path" fired for any file that merely defines a sentinel somewhere — measured at 43% of non-test files in one real SDK, 65% in another. Under Claude Code the hook now classifies from the PostToolUse payload, which carries the new text (plus a few lines of context), and says "this edit"; Cursor sends only a path, so that host falls back to the whole file and the message says "this file". Volume was never the problem — the once-per-skill-per-session marker caps a session at three nudges — but the claim has to be true when it fires. Also drops /go-explain from the session banner ahead of the skill's removal, and adds two tests: an unrelated edit inside an error-heavy file stays silent, and a path-only payload still classifies from the file. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
go-linting shipped earlier in this branch as a deprecation stub pointing at go-lint-setup, which had absorbed its content. Delete it outright instead: a redirect that spends always-on description budget to say "load a different skill" earns less than the bytes it costs, and nothing in the tree pointed at it any more. go-explain goes with it. It answered one-shot lookups — "what's the modern way to do X" — which the focused skills already answer, with the same citations and in the context of the actual code; a usage scan recorded a single invocation in four weeks. Removing it leaves one routing story instead of two. The adoption script keeps both names in its known-skill list so a scan of older transcripts still resolves the events they produced. BREAKING CHANGE: /go-explain is gone — ask the topic and let the router load the focused skill. Prompts naming go-linting should name go-lint-setup. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eads it Descriptions are always-on context, so every word competes with the session's real work. Rewritten trigger-first with the redundant thematic restatements dropped and the concrete trigger tokens kept: 4,619 -> 4,101 characters across the seven surviving skills, 11% off, with no trigger removed. Two colons that YAML would have read as a nested mapping went with them (caught by validate.py). Adds a "Writing for the human" section to the router and a matching paragraph to go-reviewer's output format: anything a person reads — a PR description, a review comment, a question, a design choice put to them — states the effect before the mechanism and expands a Go term or drops it. Identifiers, commands and linter names stay verbatim; the prose around them is what has to be plain. Short, too: a few sentences per point, options plus a recommendation for a decision. Also drops the router's pointer to the removed /go-explain. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The section had grown to 94 lines of implementation narrative — which helper replaced which assertion idiom, which files a wording sweep touched. A changelog says what changed and why it matters; the rest is what the commits are for. 46 lines now, one short sentence per entry, plus a Removed section for go-linting and /go-explain. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… topic Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ck parity and inventories The Cursor mirror had drifted a release behind: it carried the topic table but neither the diff->skill mapping nor the minimum checklist, so a Cursor session got the index and none of the routing. It now carries both, plus the plain-English rule for anything a person reads. Two new checks in validate.py, each one closing a gap that was previously found only by reading two files side by side: - Hook parity — the same hooks/*.sh must be wired for the equivalent event on both hosts (SessionStart/sessionStart, PostToolUse/afterFileEdit), every wired script must exist and be executable, and no script may sit in the tree unwired. - Doc inventories — every shipped skill, agent and hook must be named where the docs claim to list them. One-directional on purpose, so tombstones for removed components stay legal. install.md is held to the hooks alone; it documents what they need on PATH and is not a skill catalogue. Each was run against a deliberately broken tree first: unwiring skill-nudge from Cursor, adding an undocumented skill, and clearing a hook's executable bit all produce the expected error. The inventory check immediately found a real gap — docs/testing.md had no triggering check for format-on-save — which is now added. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… claims
External review found the nudge still grepping the raw Claude payload. A real
PostToolUse payload carries old_string and a tool_response echo of nearby lines,
so deleting an fmt.Errorf — or editing beside one — still nudged go-errors. That
is the false positive the previous commit claimed to have removed. The hook now
extracts tool_input.new_string (Edit) and content (Write) and matches only those;
old_string and tool_response are excluded on purpose, and there is no fallback to
the file when the extraction is empty, since that would restore the bug.
The old fixtures could not see it: they sent new_string alone. Payload builders
now mirror production shape, and two independent cases — an edit that only
deletes an error path, and an edit beside one — fail against the old
implementation. Every case asserts exit 0 as well (Claude Code treats a non-zero
PostToolUse hook as failed, and $(...) was swallowing it), plus Write/content,
hook_event_name without CLAUDE_PLUGIN_ROOT, and the "this edit" / "this file"
wording. 24 cases.
Also from the review, each re-verified against its source:
- json/v2 said "Prefer ... for new/hot JSON paths". The 1.27 notes say the
opposite — v1 now runs on v2 underneath and "users are not required to
migrate". Reworded: keep v1; reach for v2 when you want its stricter semantics
or Options.
- embedlit was described as the t := T{}; t.x = v builder rewrite. It is the
promoted-field one: T{U: U{x: 1}} -> T{x: 1}.
- unsafefuncs was named in the prose but had no table row.
- stdversion was dated to 1.27. The check is older; 1.27 is when go test starts
running it by default.
- httptest.NewTestServer now carries its signature, which takes a testing.TB
that httptest.NewServer does not.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…testing checks
From the same external review, the cheaper half:
- README's lede said two hooks while the table listed three; AGENTS.md claimed a
Cursor `commands` path the manifest does not declare; docs/testing.md said CI
runs validate.sh when it calls validate.py directly, and listed neither new
check.
- gopls was pinned at v0.23's predecessor while the docs recommend Go 1.27.
v0.23.0 (July 2026) is the line that adds Go 1.27 support.
- The Cursor rule referenced ${CLAUDE_PLUGIN_ROOT}, which is Claude-only, in a
Cursor-only file.
- go-idioms triggered on "writes, reviews, or modernizes Go" — nearly every Go
turn. Narrowed to a rewritable construct or an explicit modernize/go fix ask.
- hooks-test.sh covers format-on-save for the first time: it reformats a messy
file, is idempotent, leaves non-Go files alone, and stays a silent exit-0 no-op
with no formatter on PATH. 28 cases, so "tests for both hooks" is now "all
three".
- The doc-inventory check matched substrings, so `go-test` would have been
satisfied by `go-testing`. Bounded now — which immediately caught two hooks my
own testing.md rewrite had dropped by name.
And the point behind those: `validate.py --selftest` rebuilds each structural
check's failure case in a temp tree and requires the check to catch it, then
requires it to go quiet once the defect is removed. Eight checks, run in CI. A
green validator over a valid tree never proved the checks still check.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Usage-driven optimization (v0.5.0)
A usage analysis of this plugin found the router loaded often but the focused skills it routes to
rarely followed, several skills with no concrete trigger words, and the two slash commands
effectively undiscovered. This release turns those findings into changes, shrinks the surface, and
adds Go 1.27 support (the floor stays Go 1.26.4+).
What changed
Routing
go-codinggains a Route, then load diff→skill table and a minimum checklist for when a secondskill load is not affordable; its description leads with the trigger.
go-layout,go-concurrencyandgo-testingdescriptions name the words a prompt or diffactually contains.
go-reviewer, and offers/go-lint-setuponly when the workspace has no golangci-lint config.
hooks/skill-nudge.sh, dual-host) names one matching skillafter a Go edit. Under Claude Code it matches only the text the edit adds —
new_string, orcontentfor a write — not the file, not the deleted text, and not the surrounding lines thepayload echoes back; Cursor passes a path alone, so there it matches the file and says so. Once
per skill per session, at most three per session. Delivered as a hook
systemMessageunderClaude Code, where the model actually sees it; a plain line under Cursor.
Smaller surface
go-lintingis removed, its content merged intogo-lint-setup(re-fronted as "scaffold, adopt,or debug").
/go-explainis removed: one-shot lookups are what the focused skills already do, with the samecitations and the code in view. One invocation in four weeks of transcripts.
Other
go-reviewer: anything a person reads — PR text, reviewfindings, a question — states the effect before the mechanism, in plain English, kept short.
does not inherit the parent session's skills).
scripts/usage-report.pymeasures skill and agent adoption from local transcripts;docs/testing.mddocuments what counts and the target.go-idioms(the newgo fixmodernizers, json/v2-backedencoding/json,generic methods,
stdversionby default), a golangci-lint ≥ v2.13.0 note, install docsrecommending 1.27.x on a 1.26.4+ floor, and a CI matrix over 1.26.x and 1.27.x.
rules/go-context.mdc(the Cursor mirror) is level with the router again: it had the topic tablebut neither the diff→skill mapping nor the minimum checklist, so a Cursor session got the index
and none of the routing.
scripts/hooks-test.sh: 28 bash tests over all three hooks, on production-shaped payloads,asserting exit status as well as output, with committed self-tests proving the silence
assertions can fail. Runs in CI on both Go legs.
scripts/validate.pygains two checks that previously needed a human reading two files side byside: hook parity (the same
hooks/*.shwired for the equivalent event on both hosts, eachexisting and executable, none left unwired) and doc inventories (every shipped skill, agent
and hook named where the docs claim to list them). Both were run against a deliberately broken
tree first. The inventory check found a real gap on its first run — no triggering check for
format-on-saveindocs/testing.md— now added.python3 scripts/validate.py --selftestrebuilds each structural check's failure case in atemporary tree, requires the check to catch it, then requires it to go quiet once the defect is
removed. Eight checks, run in CI: a green validator over a valid tree never proved the checks
still check.
Verification
./scripts/validate.sh,./scripts/hooks-test.sh(28/28),python3 scripts/validate.py --selftest(8/8),claude plugin validate .all green atthe branch head.
5,658 on
main— the surface costs less than the released version while carrying more triggerwords. (An intermediate commit on this branch peaked at 6,513.)
Local triggering tests in
docs/testing.md, including that the model acts on a nudge ratherthan the line merely appearing.
Addressed from the external review (2026-09-04)
old_stringand a
tool_responseecho, so deleting anfmt.Errorf— or editing beside one — still nudgedgo-errors. Fixed, and two fixtures now fail against the old implementation.encoding/json/v2no longer says"prefer" (the release notes say users are not required to migrate);
embedlitis thepromoted-field rewrite, not the builder one;
unsafefuncsgained its table row;stdversionisdated to when
go teststarts running it by default.httptest.NewTestServercarries itssignature.
commandsclaim in AGENTS, the CI line indocs/testing.md,the
goplspin (now v0.23.x, the Go 1.27 line), and the Claude-only variable in the Cursor rule.the checklist is explicitly the fallback for when opening a skill is not affordable, and stripping
Cursor back to a bare index gives that host less than it has today.
golangci-lint run --fixstays in the rule — it is editing guidance; the ban on in-place flags belongs to
go-reviewer,which has it. Live triggering on both hosts is still outstanding and still blocks the tag.
Notes for reviewers
lsexistence check that misfired whenever any one name was missing; a silence assertion that could
never fail; and a nudge printed to stdout, which under Claude Code reaches only the transcript and
never the model.
claimed "this edit touches an error path" for 43% of non-test files in one real Go SDK and 65% in
another, because most Go files define a sentinel somewhere.
"timeout": 5).🤖 Generated with Claude Code