Skip to content

Feat/rule cards explain#37

Merged
SaschaOnTour merged 7 commits into
mainfrom
feat/rule-cards-explain
Jun 13, 2026
Merged

Feat/rule cards explain#37
SaschaOnTour merged 7 commits into
mainfrom
feat/rule-cards-explain

Conversation

@SaschaOnTour

Copy link
Copy Markdown
Owner

No description provided.

…in <RULE-ID>

One card per catalog rule (id, title, detects, why, fix, suppress, config)
in src/domain/rule_cards/; the SARIF rules table renders from it (sync test
pins set equality) and --explain now resolves a rule id case-insensitively
to its card before falling back to the file-path architecture diagnostics.

cli/explain tests moved into tests/ (directory form) so the production-only
unwrap/expect pattern rule exempts them like every other test module.
--explain with an argument that is no rule id, not 'allow', and not a
readable file now prints the three explain modes with copyable examples
(and where rule ids come from) instead of stopping at the bare OS error.
An argument that matches a qual:allow target name (the real flailing-agent
guess: --explain boilerplate) gets a hint naming its dimension and the
allow guide. Clap help for --explain now names all three modes.
The findings epilogue now names both next steps — rule cards
(rustqual --explain <RULE-ID>) and suppression syntax (--explain allow) —
and renders last, so it is what survives an agent's '| tail' truncation.
Boilerplate entries in the compact findings/summary view carry the
registry title (BP-009 Struct update boilerplate) instead of the bare id.
…cabulary

New field declaring the project's trivial-Display house idioms (fixed
vocabulary: write_str, write_char, write_macro, delegation; default empty).
Validation runs at startup next to weights/suppression: an unknown entry is
a config error naming the offender and the valid vocabulary — declared
policy must parse or error, never silently mean something else. Not yet
consumed by BP-002 (that lands with the semantic-matching slice).
…cy gate

BP-002 now matches what the fmt body MEANS, not which syntax it uses: any
branch-free body consisting only of formatter write ops (write!/writeln!,
f.write_str, f.write_char, Display::fmt delegation), multi-statement
included — so the observed evasion (one write! split into two write_char
statements) is the same finding, and the semantically identical write_str
newtype form no longer slips through unmatched.

[boilerplate].accepted_display_idioms gates findings as declared policy: a
body whose every used idiom is accepted is silent, any other trivial form
keeps firing — the rule re-targets to house-idiom consistency enforcement
instead of going quiet. The suggestion (and the BP-002 rule card) always
names all three fix forms regardless of workspace deps: derive
(derive_more::Display under suggest_crates), the accepted-idiom config, and
a local macro_rules! as the dependency-free DRY option.

Behavior change: projects with hand-rolled write_str/write_char/delegation
Displays get new BP-002 findings until they pick a form (minor bump follows
in the release slice).
CHANGELOG entry for the self-explaining-CLI + semantic-BP-002 release;
accepted_display_idioms documented in book/reference-configuration.md,
rustqual.toml, and the BP-002 rows of reference-rules.md / code-reuse.md;
reference-rules.md points at --explain <RULE-ID>; CLAUDE.md records the
rule-card and BP-002 design decisions. Version bump to 1.6.0 (two new
features + a finding-set expansion — not a patch).
…t target the formatter

Two review findings. (1) Findings print dynamic hierarchical ids
(architecture/pattern/<name>, architecture/trait_contract/<check>) and the
footer says --explain <RULE-ID> — find_rule_card now resolves an id to its
longest registered prefix card, while ids with their own card
(architecture/layer/unmatched, the call_parity checks) keep matching
exactly. (2) classify_macro only counts write!/writeln! as the write_macro
idiom when the first macro argument IS the formatter (recovered via the
shared macro_tokens helper) — write!(self.buf, …) writes somewhere else:
real logic, neither trivial nor blessable via accepted_display_idioms.
Copilot AI review requested due to automatic review settings June 13, 2026 00:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes rustqual “self-explaining” by introducing a centralized rule-card registry and routing --explain to either suppression guidance, rule cards, or per-file architecture diagnostics; it also upgrades BP-002 (trivial Display) detection to semantic matching with a validated config “house-idiom” gate.

Changes:

  • Add a domain::rule_cards registry as the single source of truth for rule IDs/titles/metadata, and derive SARIF rules output from it (with a sync test).
  • Expand --explain into a 3-mode dispatcher (allow guide, <RULE-ID> rule card, or <file.rs> diagnostics) and update text reporter footer to point to both next steps.
  • Make BP-002 semantic (branch-free, write-only fmt bodies across multiple idioms), and add [boilerplate].accepted_display_idioms with fail-loud validation.

Reviewed changes

Copilot reviewed 38 out of 39 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/lib.rs Switch --explain handling to a centralized dispatcher.
src/domain/tests/rule_cards.rs Add tests pinning rule-card lookup semantics (case-insensitive, prefix fallback, uniqueness).
src/domain/tests/mod.rs Register new rule_cards test module.
src/domain/rule_cards/mod.rs Introduce rule-card registry + lookup/prefix resolution helpers.
src/domain/rule_cards/architecture.rs Add rule cards for Architecture rule families and call-parity checks.
src/domain/rule_cards/boilerplate.rs Add boilerplate (BP-*) rule cards, including BP-002 policy/config details.
src/domain/rule_cards/complexity.rs Add Complexity (CX-*, A20) rule cards.
src/domain/rule_cards/coupling.rs Add Coupling (CP-*) rule cards.
src/domain/rule_cards/dry.rs Add DRY (DRY-*) rule cards and suppression/config guidance.
src/domain/rule_cards/governance.rs Add suppression governance (SUP-, ORPHAN-) rule cards.
src/domain/rule_cards/iosp.rs Add IOSP rule card.
src/domain/rule_cards/srp.rs Add SRP rule cards.
src/domain/rule_cards/structural.rs Add structural check cards (BTC/SLM/NMS/OI/SIT/DEH/IET).
src/domain/rule_cards/tq.rs Add Test Quality (TQ-*) rule cards.
src/domain/mod.rs Export rule_cards from domain.
src/cli/mod.rs Update --explain help text/value_name to document all modes.
src/cli/explain.rs Implement dispatch_explain, rule-card rendering, and fallback guidance; keep suppression guide testable.
src/cli/explain/tests.rs Remove old flat test module file (moved to nested module).
src/cli/explain/tests/mod.rs Add tests for suppression guide + rule card rendering + fallback messaging.
src/app/setup.rs Add config validation for boilerplate display-idiom vocabulary.
src/adapters/report/text/mod.rs Update footer to point at both --explain <RULE-ID> and --explain allow (tail-proof).
src/adapters/report/text/tests/root.rs Add test ensuring footer hints render after findings and include both explain paths.
src/adapters/report/findings_list/categories.rs Enrich boilerplate compact detail with rule-card title (BP-009 <title>).
src/adapters/report/tests/findings_list_categories.rs Update expected boilerplate detail string to include registry title.
src/adapters/report/sarif/rules.rs Build SARIF rules table from rule-card registry instead of hardcoded list.
src/adapters/report/sarif/tests/rules.rs Add sync test enforcing SARIF rule IDs equal rule-card IDs.
src/adapters/config/sections.rs Add accepted_display_idioms to boilerplate config + fixed vocabulary constant.
src/adapters/config/mod.rs Implement validate_boilerplate fail-loud validation for idiom vocabulary.
src/adapters/config/tests/root.rs Add tests covering boilerplate validation behavior + parsing defaults.
src/adapters/analyzers/dry/boilerplate/trivial_display.rs Rework BP-002 to semantic detection + idiom classification + policy gate + improved suggestion.
src/adapters/analyzers/dry/boilerplate/tests/root/mod.rs Register new BP-002 semantic test module.
src/adapters/analyzers/dry/boilerplate/tests/root/bp002_semantics.rs Add comprehensive tests for BP-002 semantic matching and policy gating.
rustqual.toml Document BP-002 semantic behavior and accepted-display-idioms config.
CHANGELOG.md Add 1.6.0 release notes describing rule cards, explain modes, and BP-002 changes.
Cargo.toml Bump crate version to 1.6.0.
Cargo.lock Update lockfile version entry to 1.6.0.
book/reference-rules.md Document --explain <RULE-ID> and refine BP-002 catalog entry.
book/reference-configuration.md Document accepted_display_idioms semantics and vocabulary.
book/code-reuse.md Update BP-002 description to semantic matching + policy gating.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@SaschaOnTour SaschaOnTour merged commit 9e31911 into main Jun 13, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants