gruff-go

gruff-go is an opinionated code-quality scanner for Go, built to govern AI-generated code. Its primary use is as a coding-agent hook: it forces an agent to produce code a human who didn't write it can read, review, and trust. It reads Go packages, scores findings across quality pillars, and emits reports for terminals, CI annotations, SARIF consumers, static HTML, and a local dashboard.
Mission
gruff exists to make AI-generated code something a human can trust. Its primary use is a coding-agent hook: when an agent writes code, gruff is the gate that forces output a reviewer who didn't write it can actually sign off on. It optimises for three things:
- Legible enough to verify — a reviewer can confirm the code does what was asked.
- Secure where the eye fails — it catches the security classes human review reliably misses.
- Tested for real, not padded — it forces high-signal tests and rejects low-signal test ceremony.
The framing it encodes: you are a coding agent, and a human who didn't write this code has to read, review, and trust it. That is also why doc comments are required even on a private one-liner — not as ceremony, but because coding agents routinely produce code that superficially works while misunderstanding the requirement. Forcing the agent to state intent, usage, contract, and failure behaviour in prose gives the reviewer something to check the implementation against; a mismatch between the doc comment and the code is a signal the change needs a deeper look.
gruff is heuristic static analysis, not a proof: it can create the artifact a reviewer checks (a doc comment, an assertion) but cannot verify that artifact is truthful. Run it beside go vet, staticcheck, govulncheck, tests, and human review — as the forcing function that makes that review tractable, not a replacement for it.
Status At A Glance
| Field |
Value |
| Release line |
Published 0.4.0 package line |
| Runtime |
Go 1.25+ |
| Module |
github.com/blundergoat/gruff-go |
| Binary |
gruff-go |
| Rule catalogue |
83 rules across 11 pillars; 70 enabled by default |
| Primary config |
.gruff-go.yaml |
| Analysis schema |
gruff.analysis.v2 |
| Baseline schema |
gruff-go.baseline.v0.1 |
| Severity gate |
--min-severity with advisory, warning, error |
| Dashboard |
127.0.0.1:8765 by default |
Requirements
- Go
1.25 or newer, matching go.mod.
- Git only for changed-region scans (
--since, --diff, or the legacy --diff-base).
- No runtime dependencies outside the Go standard library.
The project-pinned install flow uses Go's tool support, introduced before this module's current Go requirement. The binary itself still requires Go 1.25+.
Install
Install as a project-pinned dev tool:
go get -tool github.com/blundergoat/gruff-go/cmd/gruff-go@v0.4.0
go tool gruff-go init
go tool gruff-go summary .
From a source checkout:
git clone https://github.com/blundergoat/gruff-go.git
cd gruff-go
go build -o ./bin/gruff-go ./cmd/gruff-go
./bin/gruff-go --help
Linux, macOS, and Windows archives are attached to each GitHub Release. Releases include checksums.txt.
Quick Start
# Create the project config.
go tool gruff-go init
# Review the current finding mix.
go tool gruff-go summary .
# Inspect the current module.
go tool gruff-go analyse .
# Raise the failure floor while exploring an existing codebase.
go tool gruff-go analyse --min-severity error .
# Emit SARIF for code scanning.
go tool gruff-go analyse --format sarif --min-severity error . > gruff.sarif
# Generate a fresh-start baseline.
go tool gruff-go analyse --generate-baseline gruff-baseline.json .
# Start the local dashboard.
go tool gruff-go dashboard --project .
Go's standard flag package stops parsing flags at the first non-flag argument. Put every --flag before path arguments.
Commands
| Command |
Purpose |
analyse |
Run rules over the supplied paths and emit a report. |
summary |
Print a compact score, per-pillar counts, top rules, and top files. |
report |
Render static HTML or JSON to stdout or --output <file>. |
baseline |
Run a scan and write the current findings to a baseline file. |
init |
Generate a default .gruff-go.yaml. |
check-ignore |
Report whether paths.ignore / gitignore would exclude given paths, and why. |
list-rules |
Print rule metadata as text or JSON. |
dashboard |
Serve the local browser dashboard. |
list, help |
Show command lists and command-specific help. |
completion |
Print a shell completion script. |
Run go tool gruff-go help <command> for command-specific flags.
go tool gruff-go analyse --format <fmt> accepts:
| Format |
Use it for |
text |
Human terminal output. |
json |
Full gruff.analysis.v2 report. |
summary-json |
Compact CI digest without the full finding list. |
sarif |
SARIF 2.1.0 for code scanning. |
github |
GitHub Actions workflow annotations. |
html |
Self-contained inspection report. |
markdown |
CI-ready Markdown summary for PR comments or job summaries. |
go tool gruff-go report --format <fmt> accepts html and json. See docs/output-formats.md for schema details and HTML flags.
Exit Codes
| Code |
Meaning |
0 |
No finding met --min-severity, and no fatal diagnostic occurred. |
1 |
At least one finding met --min-severity. |
2 |
Invalid input or a fatal diagnostic such as config, parse, baseline, path, or diff failure. |
--min-severity defaults to advisory (every finding fails). Pass warning for moderate gating or error for the strict gate. Go uses --min-severity where the other gruff implementations use --fail-on; both names work on the CLI as of v0.1.1.
CI Usage
Generic CI command:
go tool gruff-go analyse --format github --min-severity warning .
SARIF upload jobs can use:
go tool gruff-go analyse --format sarif --min-severity error . > gruff-go.sarif
For incremental rollout, generate a baseline first, commit it after review, then run with --baseline gruff-baseline.json. See docs/ci-integration.md for GitHub Actions and GitLab examples.
Configuration
gruff-go auto-loads .gruff-go.yaml from the project root unless --config <path> or --no-config is supplied. Config validation fails closed on unknown keys, unknown rule IDs, unknown pillars, and invalid thresholds.
paths:
ignore:
- "vendor/"
- "internal/generated/"
allowlists:
acceptedAbbreviations: ["ID", "HTTP", "JSON", "AST"]
secretPreviews: [] # path globs where redacted secret previews may be shown
selection:
excludeRules: []
excludePillars: []
rules:
complexity.cyclomatic:
threshold: 15
severity: error
naming.package-underscore:
enabled: true
See docs/configuration.md for the full schema and validation rules.
Rules And Pillars
The current checkout contains 83 rules across 11 pillars. 70 rules are enabled by default; the 13 opt-in rules are convention-only naming/modernisation checks, parser-only dead-code candidates, the entropy/PII/PHI sensitive-data detectors, and the static-analysis-redundant test candidate.
| Pillar |
Rules |
complexity |
3 |
dead-code |
6 |
design |
1 |
documentation |
5 |
maintainability |
6 |
modernisation |
2 |
naming |
8 |
security |
24 |
sensitive-data |
16 |
size |
3 |
test-quality |
9 |
See docs/rules.md for rule IDs, severities, thresholds, and remediation guidance.
list-rules reports the effective rule state after applying project config. Use go tool gruff-go list-rules --no-config to inspect built-in defaults.
Baselines And Changed-Code Scans
Baselines suppress reviewed findings by fingerprint without disabling rules:
go tool gruff-go analyse --generate-baseline gruff-baseline.json .
go tool gruff-go analyse --baseline gruff-baseline.json .
Changed-region scans use Git only when requested:
go tool gruff-go analyse --format json --changed-ranges "3-3,8-10" src/foo.go
go tool gruff-go analyse --format json --since HEAD src/foo.go
git diff | go tool gruff-go analyse --format json --diff -
--diff also accepts working-tree, staged, unstaged, or a base ref. JSON output keeps the normal findings array and adds suppressedCount when changed-region filtering is active. The older --diff-base flag remains supported as a base-ref alias.
Display filters such as --include-pillars, --exclude-rules, and --include-rules reduce report noise without changing which rules execute.
Dashboard
go tool gruff-go dashboard --project .
# Open http://127.0.0.1:8765/ in a browser.
The dashboard binds to loopback by default and refuses public hosts unless --allow-public is supplied. It has no authentication; treat the bind address as the safety boundary. See docs/dashboard.md for the security model, postMessage protocol, and scan timeout behavior.
In polyglot repositories, remember that gruff-go, gruff-php, and gruff-py all default to port 8765; use --port when running multiple dashboards at the same time.
Trust Boundary
Default scans are local source inspections. gruff-go parses Go source and selected text/config files; it does not execute target code, run tests, call package build scripts, query vulnerability feeds, or replace type-aware tools. Git is invoked only for explicit diff scans. Sensitive-data previews are redacted before they reach terminal, JSON, SARIF, GitHub, or HTML output.
Stability Contract
While gruff is pre-1.0, the CLI surface, rule IDs, finding fingerprints, baseline identity, and schemas named in this README are compatibility-sensitive within a patch series. Breaking changes to those surfaces land in a minor release and are called out in CHANGELOG.md. Rule precision is calibrated by dogfooding this repository and by scanning a separate Go service corpus before release.
How It Compares
| Tool |
Relationship |
go vet |
Type-aware checks for suspicious Go constructs. Run it before or beside gruff-go. |
staticcheck |
Deeper semantic linting. gruff-go focuses on scoring, baselines, reports, and project-level quality signals. |
govulncheck |
Vulnerability-feed-backed dependency and call-path checks. gruff-go does not query vulnerability feeds. |
gofmt / go fmt |
Formatting only. gruff-go does not format code. |
| Code review and tests |
Still required; gruff findings are deterministic review prompts, not runtime proof. |
Development
go test ./...
go vet ./...
make check
make check is the release gate together with a dogfood scan that must return grade A with zero findings on this repository. Read CONTRIBUTING.md for workflow and test conventions.
Documentation
Author
Built by Matthew Hansen.
License
MIT