diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-02 18:22:11 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-02 18:22:11 -0500 |
| commit | 3a06aff7eec20814f6b51b72691f4140668189c2 (patch) | |
| tree | 0dcfde239685ebeabea3ce941317f1dac5be8349 /languages/go/claude/rules/go.md | |
| parent | 0b07c15fb33ceaeec484dec9889c37098ec2e844 (diff) | |
| download | rulesets-3a06aff7eec20814f6b51b72691f4140668189c2.tar.gz rulesets-3a06aff7eec20814f6b51b72691f4140668189c2.zip | |
feat(go): build out the full Go language bundle
The Go bundle was coverage-slice-only. Because it shipped no rule files, sync-language-bundle.sh (which fingerprints a project's bundle by spotting one of its rule files in .claude/rules/) couldn't detect it, so the coverage slice it did ship never stayed in sync. Adding the rules is what makes the bundle sync-maintainable, which was the point.
Brought Go to the full tier, matching elisp:
- claude/rules/go.md and go-testing.md, the style and testing rules (table-driven tests, go test -race, errors.Is over message matching, how the coverage slice fits). These two are also the sync fingerprint.
- claude/hooks/validate-go.sh, a PostToolUse hook that runs gofmt and go vet on each edited .go file. go vet type-checks, so compile and syntax errors surface at edit time. It deliberately doesn't auto-run tests, since a package's tests can be slow or integration-tagged and shouldn't fire on every keystroke.
- claude/settings.json, Go permissions plus the hook wiring.
- githooks/pre-commit, a secret scan and a gofmt check on staged .go.
- CLAUDE.md, the seed.
validate-go.sh is TDD'd by scripts/tests/validate-go.bats: a clean file passes, gofmt and vet failures both block with the JSON payload, and non-go, missing, or empty paths are ignored. I updated install-lang.bats test 7, which asserted Go installs no CLAUDE.md, to check the full bundle instead. Verified with a real install into a throwaway project and a green make test.
Diffstat (limited to 'languages/go/claude/rules/go.md')
| -rw-r--r-- | languages/go/claude/rules/go.md | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/languages/go/claude/rules/go.md b/languages/go/claude/rules/go.md new file mode 100644 index 0000000..5415a6a --- /dev/null +++ b/languages/go/claude/rules/go.md @@ -0,0 +1,82 @@ +# Go Code Rules + +Applies to: `**/*.go` + +Go-specific style and structure. Pairs with `go-testing.md` for tests and the +generic `verification.md` / `commits.md` rules. When in doubt, defer to +[Effective Go](https://go.dev/doc/effective_go) and the +[Go Code Review Comments](https://go.dev/wiki/CodeReviewComments) wiki. + +## Formatting Is Not a Choice + +All code is `gofmt`-clean. The bundle's PostToolUse hook runs `gofmt` and `go +vet` on every edit and blocks on a violation, and the pre-commit hook re-checks +staged files. Don't hand-format; let the tool decide. Use `goimports` (or your +editor's organize-imports) to keep the import block grouped and pruned. + +## Errors + +- Return errors; don't panic across an API boundary. `panic` is for genuinely + unrecoverable, programmer-error states (a corrupt invariant), not for control + flow or expected failures. +- Wrap with context as an error travels up: `fmt.Errorf("load config %s: %w", + path, err)`. The `%w` verb preserves the chain for `errors.Is` / `errors.As`. +- Handle every error where it occurs. Don't assign to `_` to silence one unless + you can state why it's safe in a comment. +- Define sentinel errors (`var ErrNotFound = errors.New("not found")`) or typed + errors when callers need to branch on the failure; callers match with + `errors.Is` / `errors.As`, never on the message string. +- Check errors before using the other return value — the value is unspecified + when `err != nil`. + +## Naming and Shape + +- Package names are short, lowercase, no underscores or camelCase; the name is + part of every call site (`http.Client`, not `httputil.HTTPClient`). Avoid + stutter: `chat.Message`, not `chat.ChatMessage`. +- Exported identifiers carry a doc comment that begins with the identifier + name. Unexported helpers earn a comment when the why isn't obvious. +- Accept interfaces, return concrete types. Keep interfaces small — one or two + methods — and define them in the consuming package, not alongside the + implementation. +- Avoid naked returns except in very short functions; named returns that travel + more than a few lines hide what's being returned. + +## Concurrency + +- Pass `context.Context` as the first parameter (`ctx context.Context`) through + any call chain that does I/O or can block; honor cancellation. Never store a + context in a struct. +- A goroutine needs a defined lifecycle — know who stops it and how. Don't + launch one without a way for it to exit (a closed channel, a cancelled + context, a `sync.WaitGroup`). +- Share memory by communicating (channels) or guard it (`sync.Mutex`); don't do + both for the same state. Run concurrent code under `go test -race`. + +## Structure + +- Keep functions focused. A function that fetches, parses, decodes, and saves + is four functions; the test difficulty in `go-testing.md` is the tell. +- Zero values should be useful where you can manage it (`bytes.Buffer`, + `sync.Mutex` need no constructor). When construction must validate, provide a + `NewThing` that returns `(*Thing, error)`. +- Don't add an interface, generic, or abstraction for a single implementation. + Add it when the second caller arrives, not in anticipation. +- `defer` cleanup (`f.Close()`, `mu.Unlock()`) right after acquiring the + resource, so the release is visible next to the acquire. + +## Dependencies + +- Keep `go.mod` tidy: run `go mod tidy` after changing imports, commit the + resulting `go.mod` and `go.sum` together. +- Prefer the standard library. Pull in a dependency when it earns its + maintenance and supply-chain cost, not for a function you could write in a + few lines. + +## What Not to Do + +- Don't ignore a returned error to make the line shorter. +- Don't reach for `interface{}` / `any` when a concrete type or a small + generic constraint says what you mean. +- Don't refactor surrounding code while fixing a bug — keep the diff scoped. +- Don't add comments restating what the code already says; comment the why. |
