Module: Moult::Boundaries::Severity
- Defined in:
- lib/moult/boundaries/severity.rb
Overview
The per-finding model for architecture boundaries — this slice's realisation
of Moult's protected per-finding API. Unlike dead code, a packwerk violation
is not a probabilistic guess: packwerk resolved the constant via Zeitwerk and
verified it crosses a declared boundary, so the honest grade here is a
SEVERITY classification, not a confidence. We never manufacture a fake 1.0
confidence (which would carry no information); the finding's confidence is
null and Severity.classify assigns a severity by violation kind instead.
This keeps the humility invariant in a different register: we still never overstate. A "severity" says how architecturally significant the kind of boundary crossing is — it does not assert the code is wrong, only that packwerk recorded a declared-boundary violation of that kind.
Severity.classify is a pure function of the violation type — no IO, no packwerk objects — so it is pinned against hand-built inputs exactly like ABC, the coverage Resolver, and the duplication Confidence model. Drift is a bug.
Defined Under Namespace
Classes: Assessment, Reason
Constant Summary collapse
- CATEGORY =
"architecture_boundary"- SCALE =
The ordered severity scale (least → most architecturally significant).
%w[low medium high].freeze
- SEVERITY =
Pinned severity per packwerk violation type. Dependency and layer crossings break the declared dependency graph — the core architectural contract — so they rank highest. Privacy/visibility/folder_privacy are reaches past a package's public surface: real violations, but a narrower contract, so medium. An unrecognised type degrades to
low(we never drop it). { "dependency" => "high", "layer" => "high", "privacy" => "medium", "visibility" => "medium", "folder_privacy" => "medium" }.freeze
- DEFAULT_SEVERITY =
"low"- SEVERITY_WEIGHT =
Numeric weight per severity, consumed by the health composite to turn a set of violations into a per-file badness burden. Pinned alongside SEVERITY so the health boundaries component stays deterministic.
{"high" => 1.0, "medium" => 0.6, "low" => 0.3}.freeze
Class Method Summary collapse
Class Method Details
.classify(violation_type:) ⇒ Assessment
64 65 66 67 |
# File 'lib/moult/boundaries/severity.rb', line 64 def classify(violation_type:) severity = SEVERITY.fetch(violation_type, DEFAULT_SEVERITY) Assessment.new(severity: severity, reasons: [Reason.new(rule: :"#{violation_type}_violation", detail: detail_for(violation_type, severity))]) end |
.detail_for(violation_type, severity) ⇒ Object
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/moult/boundaries/severity.rb', line 69 def detail_for(violation_type, severity) case violation_type when "dependency" "references a constant in a package this one does not declare a dependency on (#{severity})" when "layer" "depends across a declared architecture layer boundary (#{severity})" when "privacy" "references another package's private (non-public) constant (#{severity})" when "visibility" "references a package that does not list this one as visible_to (#{severity})" when "folder_privacy" "references a nested package outside the allowed folder scope (#{severity})" else "recorded packwerk boundary violation of an unrecognised type (#{severity})" end end |