Contributing to Guardrails¶
Thanks for helping make Claude's work better. Bug reports, ideas, docs and code are all welcome.
Ground rules¶
- Be kind. This project follows the Code of Conduct.
- For anything bigger than a small fix, open an issue first so we can agree on the approach.
- Security issues go through SECURITY.md, not public issues.
Development setup¶
You need Go (see go.mod for the version), golangci-lint
v2, and ideally gofumpt and
govulncheck.
git clone https://github.com/rossedman/guardrails
cd guardrails
make test # go test -race ./...
make lint # golangci-lint run
make gate # build the binary and run its own gate on itself
make harden # mutation-test and fuzz what this branch changed
To try your build inside Claude Code, point the plugin launcher at it:
Then add your checkout as a local marketplace and install from it:
The bar¶
Guardrails holds itself to its own gate, and CI enforces it:
go test -race ./...passes on Linux and macOS.- Total coverage stays at or above 90%, with no package below 80%.
golangci-lint runis clean. No//nolintwithout a specific linter and a reason.- Code is gofumpt-formatted.
Tests should assert behavior, not just execute code; make harden
mutation-tests your changes to prove it. Prefer table-driven
tests. Hooks are pure functions from stdin JSON to stdout JSON; the engine's
golden files live in internal/engine/testdata and are regenerated with
go test ./internal/engine -update.
Adding a language module¶
- Create
internal/module/<lang>implementingmodule.Module(Match,Fix,Check,Gate,Tools). - Register it in
engine.New. - Add its config block to
internal/configandschema.json(the config tests fail if the schema and defaults drift apart). - Add tests with
runner.Fake, plus a real-tool integration test that skips when the tool is missing. - Document it in
docs/and add a rule file underinternal/assets/rules/.
Commit messages¶
Use Conventional Commits:
feat:, fix:, docs:, test:, chore:. Release notes are generated
from them.
Releasing¶
Maintainers:
- Set the version everywhere it's pinned (the plugin manifest, which the
launcher uses to pick the binary, the install snippets in the README and
docs, and the changelog's Unreleased heading), then open a PR:
make version VERSION=X.Y.Z. - Merge to
main, then tag the merge commit with the same version:git tag vX.Y.Z && git push origin vX.Y.Z. - The release workflow verifies the plugin version matches the tag and runs GoReleaser. A tag pushed without step 1 fails that check; bump, merge, and tag again.
Regenerating the graphics¶
The README header, social preview and terminal image are HTML pages in
docs/assets/src/, styled with the design tokens in brand.css (the site
uses the same tokens in docs/assets/site/landing.css). Render them to PNG
with headless Chrome or Chromium: