v0.2.0
gruff-php 0.2.0 - strict gating, better triage, cleaner config
Released: 2026-05-28
Install: composer require --dev blundergoat/gruff-php:^0.2
Full changelog: CHANGELOG.md
Headline
analyse now fails CI on any finding by default, configs must declare schemaVersion:, and a new per-rule triage surface (list-rules <ruleId>, branch-review deltas, output-volume hint) makes large result sets browsable instead of overwhelming.
Highlights
- Strict CI gating out of the box.
analyse --fail-ondefault lowerederror → advisory. A clean CI run no longer hides warning- or advisory-tier findings - they exit 1 by default. Pass--fail-on erroror setminimumSeverity.analyse: errorin.gruff-php.yamlto restore the old behaviour. - Per-command threshold config.
minimumSeverity:block in.gruff-php.yamllets you pin exit-code policy per command (analyse | report | dashboard) once instead of remembering--fail-onon every invocation. Precedence: CLI flag > YAML > binary default. - Visibility without scoring.
rules.<id>.excludeFromScore: truekeeps a rule running and surfaces its findings in every report, but those findings stop tanking the composite score. Distinct fromenabled: false, which silences the rule entirely. Useful when a team has decided a rule is informational but still wants to see what it catches. list-rules <ruleId>detail view. A new positional arg renders the rule's defaults, escape-hatch config paths (rules.<id>.options.*,enabled,excludeFromScore), and catalogued false-positive shapes. Typos suggest near matches and exit 2. Pass--format=jsonfor structured triage tooling.- Triage surface for large scans. Branch-review reporters render "Top 5 improved / Top 5 regressed" rules before the composite score.
analyse --format=textadds a footer hint pointing atsummarywhen findings exceed 50.Finding::stableIdentityis a line-shift-resilient sibling tofingerprintfor diff tooling.
Breaking Changes
Five user-visible breaks. Each requires action:
1. .gruff-php.yaml requires schemaVersion:
# Before - 0.1.4 and earlier
minimumPhpVersion: 8.3
rules:
size.file-length: ...
# After - 0.2.0
schemaVersion: gruff-php.config.v0.1
minimumPhpVersion: 8.3
rules:
size.file-length: ...Configs missing the key fail to load with: Config key "schemaVersion" is required. Add 'schemaVersion: gruff-php.config.v0.1' to the top of .gruff-php.yaml, or regenerate with 'gruff-php init --force'.
Migration effort: one line per config file, or one gruff-php init --force invocation (preserves your existing paths.ignore, custom rule tunings, and allowlists).
2. analyse --fail-on default lowered error → advisory
CI jobs running gruff-php analyse with no --fail-on flag now exit 1 on any advisory finding that previously exited 0. The rationale is the cross-port "show everything, fail on anything for gating commands" philosophy.
# Option A: restore the previous gate
minimumSeverity:
analyse: error
# Option B: opt into the new default explicitly (no-op, but documents intent)
minimumSeverity:
analyse: advisoryOr pass --fail-on error on every CI invocation.
Migration effort: zero if you already pass --fail-on explicitly. Otherwise, decide whether new CI behaviour matches your policy and pin the threshold either way.
3. JSON output schemas bumped to v2
Both gruff-php analyse --format=json and gruff-php summary --format=json advertise gruff.analysis.v2 / gruff.summary.v2 and have renamed per-severity keys:
- "findings": { "advisories": 12, "warnings": 3, "errors": 1 }
+ "findings": { "advisory": 12, "warning": 3, "error": 1 }Pillar and file-score payloads use the same singular names everywhere.
Migration effort: update consumer parsers (key names + the schemaVersion literal). No backward-compatible aliases - v1 consumers must update.
4. naming.parameter-type-name rule retired
Adopters with a rules.naming.parameter-type-name block in their config will hit Unknown rule id "naming.parameter-type-name". at load time.
# Remove this entire block from your .gruff-php.yaml
rules:
naming.parameter-type-name:
enabled: trueThe rule produced too many domain-DTO false positives without a reliable signal. The gruff-py port retires the sibling rule in lockstep. PHP naming-rule count drops 12 → 11. Rationale and reversibility plan in ADR-014.
Migration effort: delete the block. Findings disappear from baselines and reports automatically.
5. waste.one-line-method defaults tightened
New defaults match gruff-php's own self-tuning:
# Old (implicit) # New (implicit)
minInFileCallers: 0 minInFileCallers: 2
namedAlternativeFactoryExempt: false namedAlternativeFactoryExempt: trueMost projects will see fewer findings (named factory pairs like Money::fromCents() / Money::fromDollars() and same-file wrappers stop reporting). Projects that pinned the old defaults explicitly can drop the pin or keep it.
Migration effort: zero. If your scoring suddenly improves, that's why.
Other Notable Changes
modernisation.phpdoc-mixed-overuseaccepts precise envelope shapes.array{entries: list<array<string, mixed>>, total: int|null, complete: bool}no longer fires (one or more non-mixed sibling fields signals the leaf-mixed is intentional). Loose shapes still fire:array<string|int, mixed>,Collection<mixed>,array{value: mixed}.- Per-rule remediation messages name their escape hatch. Findings now include "If this is intentional, add it to
rules.<id>.options.<key>in.gruff-php.yaml" so users can act on a finding without grepping for the right config path. initscaffolds default accepted abbreviations. Fresh.gruff-php.yamlfiles no longer floodnaming.abbreviation-allowlistwith universal tokens (id,url,db, etc.).- HTML and Markdown reporters render pillar tables. Per-severity columns instead of single-count rows; easier to read at a glance.
Upgrade
# 1. Update Composer
composer update blundergoat/gruff-php --with-dependencies
# 2. Pick one: regenerate config (preserves your tunings)
vendor/bin/gruff-php init --force
# 2'. Or edit by hand - add this line at the top of .gruff-php.yaml
# schemaVersion: gruff-php.config.v0.1
# 3. Confirm version
vendor/bin/gruff-php --version
# → gruff-php 0.2.0After upgrade, run a scan to see what shifts. Use the new triage flow:
# Big scan? See the per-rule breakdown
vendor/bin/gruff-php summary
# Drill into one rule's defaults + escape hatches
vendor/bin/gruff-php list-rules naming.identifier-qualityIf a CI job that passed on 0.1.x now fails, you are seeing the lowered --fail-on default. Pick your policy: pass --fail-on error on the command, or set minimumSeverity.analyse: error in .gruff-php.yaml. Either is fine; the YAML form documents intent for future maintainers.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
Config key "schemaVersion" is required. |
Pre-0.2.0 config | Add schemaVersion: gruff-php.config.v0.1 to top of file, or run gruff-php init --force. |
Unknown rule id "naming.parameter-type-name" |
Retired rule referenced | Delete the rules.naming.parameter-type-name: block. |
| CI fails where it passed before | Lowered analyse --fail-on default |
Pass --fail-on error or set minimumSeverity.analyse: error. |
JSON consumer misses advisories/warnings/errors keys |
Schema bumped to v2 (singular keys) | Update parser; new keys are advisory/warning/error. |
Config key "minimumSeverity.summary" is rejected |
Non-gating command name | Use analyse, report, or dashboard - summary and init don't gate exit code. |