Skip to content

feat(toolchain): add Go ecosystem batch entry + format hook plugin - #908

Closed
kyle-sexton wants to merge 1 commit into
mainfrom
feat/832-go-coverage-batch
Closed

feat(toolchain): add Go ecosystem batch entry + format hook plugin#908
kyle-sexton wants to merge 1 commit into
mainfrom
feat/832-go-coverage-batch

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes Brief item 2 in docs/topics/lint-static-analysis-gaps/PLAN.md (PR #829): Go coverage in
both lanes.

  • Batch ecosystem (plugins/toolchain/reference/ecosystems/go.yaml): golangci-lint run ./...
    for lint (package-scope by design — golangci-lint's own FAQ/architecture rules out per-file use,
    so it always applies with no opt-in gate), gofmt -l/-w <files> for the zero-config format leg,
    and a default go-mod-tidy gate (go mod tidy -diff, non-mutating). /toolchain:check and
    /toolchain:lint now cover go (build + test + lint + format), with a golang alias.
    govulncheck is intentionally not part of the default (its vulnerability-database fetch and
    scan time don't fit the batch entry's contract) — documented as a consumer-added gates entry.
  • go-format hook plugin (new, plugins/go-format/): a PostToolUse hook that runs
    gofmt -w unconditionally on every .go edit.

Formatter choice — pick-for-the-problem field survey (gofmt vs goimports vs gofumpt vs
golangci-lint fmt): gofmt is the only candidate safe to run unconditionally on every edit.
goimports removes unreferenced imports — the same hazard ruff-format's --unfixable F401 guard
exists to prevent for Python, but Go imports have no per-tool "unfixable" escape hatch, so
goimports would delete an import added one edit before the code that uses it. gofumpt
(mvdan.cc/gofumpt) is third-party and stricter-than-canonical, an opinion this plugin does not
ship unconditionally. golangci-lint fmt requires an explicit formatters.enable in the repo's
own golangci-lint v2 config — verified empirically against golangci-lint v2.12.2 source
(pkg/config/config.go's NewDefault leaves Formatters.Enable empty with no config file) and
by running it in a throwaway repo (golangci-lint fmt --diff exits 0 with no output — a silent
no-op) that it cannot serve as a zero-config unconditional default either. Import-organizing and
stricter formatting stay available as an opt-in through the batch ecosystem's golangci-lint fmt
route once a repo configures it (documented in go.yaml's opt-in field).

Per-plugin version bumps: toolchain 0.6.0 → 0.7.0 (CHANGELOG entry added); go-format ships as
a new 0.1.0 plugin (CHANGELOG seeded). Both registered in .claude-plugin/marketplace.json, the
root README catalog (regenerated via scripts/generate-catalog.mjs), and the go-format telemetry
data schema registered in docs/conventions/hook-telemetry/README.md's Implementers table.

Fix

  • plugins/toolchain/reference/ecosystems/go.yaml — new bundled default ecosystem file.
  • plugins/toolchain/skills/check/context/go.md — new gotchas/reference file.
  • plugins/toolchain/skills/check/SKILL.md, skills/lint/SKILL.md — added go to the covered
    ecosystem enumerations, argument-hints, per-project walking notes, and the lint/format
    two-column table.
  • plugins/go-format/ — new hook plugin: hooks/go-format.sh (mirrors ruff-format's shell
    shape; unconditional per typos-format's pattern since gofmt has no config surface),
    hooks/go-format.test.sh (35-case black-box contract test), hooks/hook-utils.sh (synced
    verbatim from lib/hook-utils.sh), hooks/hooks.json, skills/setup/SKILL.md (mirrors
    typos-format's check/apply contract — gofmt is a standalone toolchain binary, no per-repo
    dependency-manager install path).
  • docs/conventions/hook-telemetry/data/go-format.schema.json — new per-hook telemetry data
    schema, registered in the Implementers table.
  • .claude-plugin/marketplace.json, root README.md — new go-format catalog entry.

Verification

Ran locally (Go 1.26.5, golangci-lint v2.12.2, gofmt, shellcheck 0.11.0, shfmt v3.13.1, jq
1.8.2, node v24.17.0, claude CLI 2.1.217 all present on this machine):

$ bash plugins/go-format/hooks/go-format.test.sh
PASS=35 FAIL=0

$ shellcheck -x -S warning plugins/go-format/hooks/go-format.sh plugins/go-format/hooks/go-format.test.sh
$ shfmt -d plugins/go-format/hooks/go-format.sh plugins/go-format/hooks/go-format.test.sh
(clean, no output)

$ check-jsonschema --schemafile docs/conventions/ecosystem-commands/ecosystem.schema.json plugins/toolchain/reference/ecosystems/go.yaml
ok -- validation done

$ check-jsonschema --check-metaschema docs/conventions/hook-telemetry/data/go-format.schema.json
ok -- validation done
$ check-jsonschema --schemafile docs/conventions/hook-telemetry/data/go-format.schema.json <example-instance>
ok -- validation done

$ claude plugin validate plugins/go-format/
$ claude plugin validate plugins/toolchain/
$ claude plugin validate --strict .
✔ Validation passed (all three)

$ bash scripts/sync-hook-utils.sh --check
All 12 plugin copies match lib/hook-utils.sh.

$ bash scripts/check-changelog-parity.sh --check
$ bash scripts/check-changelog-parity.sh --check-bump origin/main
(both pass)

$ bash scripts/check-cross-plugin-source-drift.sh --check
No unregistered or drifted cross-plugin source clusters found.

$ bash scripts/check-silent-skips.sh
No silent prerequisite skips found in hook entry scripts.

$ bash scripts/check-orphaned-fixtures.sh   # exit 0
$ node scripts/validate-plugin-contracts.mjs
Plugin contracts validated: 37 setup skills and 1942 plugin files checked.

$ node scripts/generate-catalog.mjs --check
Catalog is in sync with the manifests.

$ bash scripts/check-skill-leaf-names.sh    # go-format's setup skill listed correctly
$ bash scripts/check-skill-portability.sh origin/main
No unexcused coupling tokens in 2 skill file(s).

$ bash scripts/check-changed-skills.sh origin/main
3 skill(s) checked, 0 failed.

$ markdownlint-cli2 <all new/changed .md files>
Summary: 0 error(s)

End-to-end sanity check of the ecosystem's exact commands against a throwaway go.mod repo
(build-cmd, test-cmd, check-cmd's golangci-lint run ./... and gofmt -l, the go-mod-tidy gate,
and golangci-lint fmt --diff's documented zero-config no-op) — all behaved as documented in
go.yaml.

Not run: the fleet-wide scripts/run-plugin-tests.sh (every *.test.sh across all ~50 plugins)
was not executed in full — only the new go-format.test.sh (directly relevant) and the targeted
gates above, which is what CI actually gates on for this class of change.

Related

Source issue: #832. Spec: docs/topics/lint-static-analysis-gaps/PLAN.md Brief item 2 (from #829).

Closes #832

Claude-Session: https://claude.ai/code/session_01K1V3gkrfSf75isB8MiDy3o

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com

Adds Go coverage in both lanes per docs/topics/lint-static-analysis-gaps/PLAN.md
Brief item 2: a `go` batch ecosystem default (golangci-lint v2 lint, gofmt
check/format, a go-mod-tidy gate) in the toolchain plugin, and a new go-format
hook plugin for fast per-file formatting on edit.

Formatter selection followed a pick-for-the-problem field survey across
gofmt/goimports/gofumpt/golangci-lint fmt: gofmt is the only candidate safe to
run unconditionally on every edit (goimports deletes unreferenced imports with
no unfixable-style escape hatch; gofumpt is third-party and stricter-than-
canonical; golangci-lint fmt runs zero formatters with no repo config,
verified empirically against golangci-lint v2.12.2). Import-organizing and
stricter formatting stay available as an opt-in through the batch
ecosystem's golangci-lint fmt route once a repo configures it.

toolchain bumped 0.6.0 -> 0.7.0; go-format ships as a new 0.1.0 plugin.

Closes #832

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@kyle-sexton

Copy link
Copy Markdown
Contributor Author

Superseded by #910 — same issue (#832), independently and more completely implemented by another actor (full green CI including both review passes, non-draft, richer design rationale). This lane's claim on #832 has been released; see the issue comment for the cross-actor-collision context (second occurrence this session of the pattern tracked in #920). Closing as this lane's own redundant work, not touching #910.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automated Opened by automation.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(go): Go coverage — batch ecosystem entry + format hook plugin

1 participant