Class: Moult::Gate::Policy

Inherits:
Object
  • Object
show all
Defined in:
lib/moult/gate/policy.rb

Overview

The explicit, recorded set of thresholds the gate enforces — the realisation of "Clean as You Code" for Moult. A Policy is a plain value object; the thresholds it carries are serialized into every gate report so the verdict is auditable (never a hidden heuristic) and reproducible.

The DEFAULTS are judgement-based heuristics in the spirit of the health knees — calibrated against a real corpus (Moult's own codebase: a default must let a clean, well-tested gem pass). They are pinned in test/test_gate_policy.rb; drift is a bug. Teams override them via the gate: section of .moult.yml.

Constant Summary collapse

DEFAULTS =
{
  # A dead-code candidate on changed lines at or above this confidence fails
  # the gate. Public symbols base well below this; the rule bites freshly
  # added unused private methods — the canonical "new dead code" smell.
  dead_code_max_confidence: 0.8,

  # The highest boundary severity allowed to appear in a changed file. With
  # "medium", a new HIGH-severity packwerk violation (dependency/layer) fails.
  boundary_max_severity: "medium",

  # A changed method whose ABC complexity exceeds this ceiling fails the gate.
  complexity_ceiling: 30.0,

  # A clone group touching the diff whose flay mass exceeds this fails. Set to
  # roughly a fully duplicated ~10-line method: below it lies idiomatic
  # parallelism (sibling guard clauses, similar small methods) that a clean
  # codebase legitimately has, so a lower bar produces noise, not signal.
  duplication_max_mass: 100,

  # Path prefixes excluded from gating. Test/spec code is legitimately
  # repetitive (parallel cases, shared setup), so — like SonarQube and
  # CodeScene — the gate judges production code. Findings under these prefixes
  # are dropped from every rule. Override to [] to gate everything.
  exclude_paths: ["test", "spec"]
}.freeze
KEYS =
DEFAULTS.keys.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dead_code_max_confidence:, boundary_max_severity:, complexity_ceiling:, duplication_max_mass:, exclude_paths:, source:) ⇒ Policy

Returns a new instance of Policy.



47
48
49
50
51
52
53
54
55
# File 'lib/moult/gate/policy.rb', line 47

def initialize(dead_code_max_confidence:, boundary_max_severity:,
  complexity_ceiling:, duplication_max_mass:, exclude_paths:, source:)
  @dead_code_max_confidence = dead_code_max_confidence
  @boundary_max_severity = boundary_max_severity
  @complexity_ceiling = complexity_ceiling
  @duplication_max_mass = duplication_max_mass
  @exclude_paths = exclude_paths
  @source = source
end

Instance Attribute Details

#boundary_max_severityObject (readonly)

Returns the value of attribute boundary_max_severity.



44
45
46
# File 'lib/moult/gate/policy.rb', line 44

def boundary_max_severity
  @boundary_max_severity
end

#complexity_ceilingObject (readonly)

Returns the value of attribute complexity_ceiling.



44
45
46
# File 'lib/moult/gate/policy.rb', line 44

def complexity_ceiling
  @complexity_ceiling
end

#dead_code_max_confidenceObject (readonly)

Returns the value of attribute dead_code_max_confidence.



44
45
46
# File 'lib/moult/gate/policy.rb', line 44

def dead_code_max_confidence
  @dead_code_max_confidence
end

#duplication_max_massObject (readonly)

Returns the value of attribute duplication_max_mass.



44
45
46
# File 'lib/moult/gate/policy.rb', line 44

def duplication_max_mass
  @duplication_max_mass
end

#exclude_pathsObject (readonly)

Returns the value of attribute exclude_paths.



44
45
46
# File 'lib/moult/gate/policy.rb', line 44

def exclude_paths
  @exclude_paths
end

#sourceObject (readonly)

Returns the value of attribute source.



44
45
46
# File 'lib/moult/gate/policy.rb', line 44

def source
  @source
end

Class Method Details

.defaultPolicy

The pinned defaults, recorded with source "default".

Returns:



60
61
62
# File 'lib/moult/gate/policy.rb', line 60

def default
  load({}, source: "default")
end

.load(overrides, source:) ⇒ Policy

Merge a (string- or symbol-keyed) overrides hash onto DEFAULTS. Unknown keys are ignored so a stray .moult.yml entry can't silently weaken the gate.

Parameters:

  • overrides (Hash)
  • source (String)

    provenance for the report (e.g. ".moult.yml")

Returns:



69
70
71
# File 'lib/moult/gate/policy.rb', line 69

def load(overrides, source:)
  new(**DEFAULTS.merge(sanitize(overrides)), source: source)
end

Instance Method Details

#excluded?(path) ⇒ Boolean

Is path (root-relative) outside the gate's scope — i.e. under an excluded prefix like test/ or spec/?

Returns:

  • (Boolean)


85
86
87
88
# File 'lib/moult/gate/policy.rb', line 85

def excluded?(path)
  segment = path.to_s.split("/").first
  exclude_paths.include?(segment)
end

#to_hObject

The auditable record of every applied threshold.



91
92
93
94
95
96
97
98
99
100
# File 'lib/moult/gate/policy.rb', line 91

def to_h
  {
    source: source,
    dead_code_max_confidence: dead_code_max_confidence,
    boundary_max_severity: boundary_max_severity,
    complexity_ceiling: complexity_ceiling,
    duplication_max_mass: duplication_max_mass,
    exclude_paths: exclude_paths
  }
end