evilution 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.beads/interactions.jsonl +13 -0
  3. data/.rubocop_todo.yml +0 -1
  4. data/CHANGELOG.md +32 -0
  5. data/README.md +19 -0
  6. data/lib/evilution/baseline.rb +5 -4
  7. data/lib/evilution/cli/parser/options_builder.rb +7 -0
  8. data/lib/evilution/compare/diff_extractor/evilution.rb +22 -0
  9. data/lib/evilution/compare/diff_extractor/mutant.rb +30 -0
  10. data/lib/evilution/compare/diff_extractor.rb +6 -0
  11. data/lib/evilution/compare/fingerprint.rb +15 -72
  12. data/lib/evilution/compare/line_normalizer.rb +72 -0
  13. data/lib/evilution/compare/normalizer.rb +17 -4
  14. data/lib/evilution/config/validators/profile.rb +11 -0
  15. data/lib/evilution/config.rb +40 -23
  16. data/lib/evilution/integration/crash_detector.rb +2 -2
  17. data/lib/evilution/integration/loading/source_evaluator.rb +6 -2
  18. data/lib/evilution/integration/minitest_crash_detector.rb +2 -2
  19. data/lib/evilution/integration/rspec/state_guard/object_space_example_groups.rb +11 -3
  20. data/lib/evilution/isolation/fork.rb +16 -11
  21. data/lib/evilution/isolation/in_process.rb +10 -6
  22. data/lib/evilution/mcp/info_tool.rb +0 -2
  23. data/lib/evilution/mcp/mutate_tool/progress_streamer.rb +5 -1
  24. data/lib/evilution/mcp/session_tool.rb +0 -2
  25. data/lib/evilution/mutation.rb +47 -27
  26. data/lib/evilution/mutator/base.rb +8 -8
  27. data/lib/evilution/mutator/operator/predicate_to_nil.rb +20 -0
  28. data/lib/evilution/mutator/registry.rb +20 -0
  29. data/lib/evilution/parallel/work_queue/channel/frame.rb +5 -1
  30. data/lib/evilution/parallel/work_queue/worker/loop.rb +1 -1
  31. data/lib/evilution/process_cleanup.rb +19 -0
  32. data/lib/evilution/reporter/html/baseline_keys.rb +1 -1
  33. data/lib/evilution/reporter/html/diff_formatter.rb +1 -1
  34. data/lib/evilution/reporter/html/escape.rb +1 -1
  35. data/lib/evilution/reporter/html/section.rb +1 -1
  36. data/lib/evilution/reporter/html/sections.rb +4 -2
  37. data/lib/evilution/reporter/html/stylesheet.rb +1 -1
  38. data/lib/evilution/reporter/html.rb +8 -3
  39. data/lib/evilution/reporter/suggestion/registry.rb +1 -5
  40. data/lib/evilution/reporter/suggestion/templates/generic.rb +1 -1
  41. data/lib/evilution/reporter/suggestion/templates/minitest.rb +349 -643
  42. data/lib/evilution/reporter/suggestion/templates/rspec.rb +351 -598
  43. data/lib/evilution/reporter/suggestion/templates.rb +6 -0
  44. data/lib/evilution/result/error_info.rb +20 -0
  45. data/lib/evilution/result/memory_stats.rb +20 -0
  46. data/lib/evilution/result/mutation_result.rb +30 -14
  47. data/lib/evilution/runner/baseline_runner.rb +1 -2
  48. data/lib/evilution/runner/diagnostics.rb +1 -2
  49. data/lib/evilution/runner/isolation_resolver.rb +1 -2
  50. data/lib/evilution/runner/mutation_executor/mutation_runner.rb +1 -3
  51. data/lib/evilution/runner/mutation_executor/neutralization_pipeline.rb +1 -2
  52. data/lib/evilution/runner/mutation_executor/neutralizer/baseline_failed.rb +3 -10
  53. data/lib/evilution/runner/mutation_executor/neutralizer/infra_error.rb +3 -10
  54. data/lib/evilution/runner/mutation_executor/neutralizer.rb +11 -0
  55. data/lib/evilution/runner/mutation_executor/result_cache.rb +1 -3
  56. data/lib/evilution/runner/mutation_executor/result_notifier.rb +1 -3
  57. data/lib/evilution/runner/mutation_executor/result_packer.rb +11 -9
  58. data/lib/evilution/runner/mutation_executor/strategy/parallel.rb +1 -3
  59. data/lib/evilution/runner/mutation_executor/strategy/sequential.rb +1 -3
  60. data/lib/evilution/runner/mutation_executor/strategy.rb +11 -0
  61. data/lib/evilution/runner/mutation_executor.rb +12 -20
  62. data/lib/evilution/runner/mutation_planner.rb +1 -2
  63. data/lib/evilution/runner/report_publisher.rb +1 -2
  64. data/lib/evilution/runner/subject_pipeline.rb +1 -2
  65. data/lib/evilution/runner.rb +33 -31
  66. data/lib/evilution/version.rb +1 -1
  67. data/lib/evilution.rb +1 -0
  68. data/script/memory_check +3 -1
  69. metadata +14 -3
  70. data/lib/evilution/reporter/html/namespace.rb +0 -11
@@ -9,7 +9,10 @@ class Evilution::Integration::RSpec::StateGuard::ObjectSpaceExampleGroups
9
9
  groups = Set.new
10
10
  ObjectSpace.each_object(Class) do |klass|
11
11
  groups << klass.object_id if klass < ::RSpec::Core::ExampleGroup
12
- rescue TypeError # rubocop:disable Lint/SuppressedException
12
+ rescue TypeError
13
+ # ObjectSpace iteration may surface partially-initialized or anonymous
14
+ # classes whose `<` comparison raises. Skipping them is safe — they
15
+ # cannot be ExampleGroup descendants we need to track.
13
16
  end
14
17
  groups
15
18
  end
@@ -23,13 +26,18 @@ class Evilution::Integration::RSpec::StateGuard::ObjectSpaceExampleGroups
23
26
 
24
27
  klass.constants(false).each do |const|
25
28
  klass.send(:remove_const, const)
26
- rescue NameError # rubocop:disable Lint/SuppressedException
29
+ rescue NameError
30
+ # Constant may have been removed concurrently (e.g. via autoload
31
+ # reload) between #constants(false) and #remove_const. Best-effort
32
+ # cleanup — nothing to do if it's already gone.
27
33
  end
28
34
 
29
35
  klass.instance_variables.each do |ivar|
30
36
  klass.remove_instance_variable(ivar)
31
37
  end
32
- rescue TypeError # rubocop:disable Lint/SuppressedException
38
+ rescue TypeError
39
+ # Same defensive case as #snapshot: skip classes whose `<` raises
40
+ # mid-iteration.
33
41
  end
34
42
  end
35
43
  end
@@ -5,6 +5,7 @@ require "tmpdir"
5
5
  require_relative "../memory"
6
6
  require_relative "../temp_dir_tracker"
7
7
  require_relative "../child_output"
8
+ require_relative "../process_cleanup"
8
9
 
9
10
  require_relative "../isolation"
10
11
 
@@ -49,13 +50,13 @@ class Evilution::Isolation::Fork
49
50
  read_io&.close
50
51
  write_io&.close
51
52
  ensure_reaped(pid)
52
- restore_original_source(mutation)
53
+ restore_original_source
53
54
  FileUtils.rm_rf(sandbox_dir) if sandbox_dir
54
55
  end
55
56
 
56
57
  private
57
58
 
58
- def restore_original_source(mutation) # rubocop:disable Lint/UnusedMethodArgument
59
+ def restore_original_source
59
60
  Evilution::TempDirTracker.cleanup_all
60
61
  end
61
62
 
@@ -88,7 +89,7 @@ class Evilution::Isolation::Fork
88
89
  if data.empty?
89
90
  { timeout: false, passed: false, error: "empty result from child" }
90
91
  else
91
- { timeout: false }.merge(Marshal.load(data)) # rubocop:disable Security/MarshalLoad
92
+ { timeout: false }.merge(Marshal.load(data))
92
93
  end
93
94
  else
94
95
  terminate_child(pid)
@@ -113,7 +114,7 @@ class Evilution::Isolation::Fork
113
114
  end
114
115
 
115
116
  def terminate_child(pid)
116
- ::Process.kill("TERM", pid) rescue nil # rubocop:disable Style/RescueModifier
117
+ Evilution::ProcessCleanup.safe_kill("TERM", pid)
117
118
  _, status = ::Process.waitpid2(pid, ::Process::WNOHANG)
118
119
  return if status
119
120
 
@@ -121,8 +122,8 @@ class Evilution::Isolation::Fork
121
122
  _, status = ::Process.waitpid2(pid, ::Process::WNOHANG)
122
123
  return if status
123
124
 
124
- ::Process.kill("KILL", pid) rescue nil # rubocop:disable Style/RescueModifier
125
- ::Process.wait(pid) rescue nil # rubocop:disable Style/RescueModifier
125
+ Evilution::ProcessCleanup.safe_kill("KILL", pid)
126
+ Evilution::ProcessCleanup.safe_wait(pid)
126
127
  end
127
128
 
128
129
  def classify_status(result)
@@ -143,11 +144,15 @@ class Evilution::Isolation::Fork
143
144
  status: status,
144
145
  duration: duration,
145
146
  test_command: result[:test_command],
146
- child_rss_kb: result[:child_rss_kb],
147
- parent_rss_kb: parent_rss_kb,
148
- error_message: result[:error],
149
- error_class: result[:error_class],
150
- error_backtrace: result[:error_backtrace]
147
+ memory: Evilution::Result::MemoryStats.from_fields(
148
+ child_rss_kb: result[:child_rss_kb],
149
+ parent_rss_kb: parent_rss_kb
150
+ ),
151
+ error: Evilution::Result::ErrorInfo.from_fields(
152
+ message: result[:error],
153
+ klass: result[:error_class],
154
+ backtrace: result[:error_backtrace]
155
+ )
151
156
  )
152
157
  end
153
158
  end
@@ -80,12 +80,16 @@ class Evilution::Isolation::InProcess
80
80
  status: status,
81
81
  duration: duration,
82
82
  test_command: result[:test_command],
83
- child_rss_kb: rss_after,
84
- memory_delta_kb: memory_delta_kb,
85
- parent_rss_kb: rss_before,
86
- error_message: result[:error],
87
- error_class: result[:error_class],
88
- error_backtrace: result[:error_backtrace]
83
+ memory: Evilution::Result::MemoryStats.from_fields(
84
+ child_rss_kb: rss_after,
85
+ memory_delta_kb: memory_delta_kb,
86
+ parent_rss_kb: rss_before
87
+ ),
88
+ error: Evilution::Result::ErrorInfo.from_fields(
89
+ message: result[:error],
90
+ klass: result[:error_class],
91
+ backtrace: result[:error_backtrace]
92
+ )
89
93
  )
90
94
  end
91
95
  end
@@ -70,7 +70,6 @@ class Evilution::MCP::InfoTool < MCP::Tool
70
70
  )
71
71
 
72
72
  class << self
73
- # rubocop:disable Lint/UnusedMethodArgument
74
73
  def call(server_context:, action: nil, files: nil, target: nil, spec: nil, integration: nil, skip_config: nil)
75
74
  return ResponseFormatter.error("config_error", "action is required") unless action
76
75
  return ResponseFormatter.error("config_error", "unknown action: #{action}") unless ACTIONS.key?(action)
@@ -84,7 +83,6 @@ class Evilution::MCP::InfoTool < MCP::Tool
84
83
  rescue Evilution::Error => e
85
84
  ResponseFormatter.error_for(e)
86
85
  end
87
- # rubocop:enable Lint/UnusedMethodArgument
88
86
  end
89
87
  end
90
88
 
@@ -10,15 +10,19 @@ module Evilution::MCP::MutateTool::ProgressStreamer
10
10
 
11
11
  suggestion = Evilution::Reporter::Suggestion.new(suggest_tests: true, integration: integration)
12
12
  survivor_index = 0
13
+ disabled = false
13
14
 
14
15
  proc do |result|
15
16
  next unless result.survived?
17
+ next if disabled
16
18
 
17
19
  begin
18
20
  survivor_index += 1
19
21
  detail = build_suggestion_detail(result.mutation, suggestion)
20
22
  server_context.report_progress(survivor_index, message: ::JSON.generate(detail))
21
- rescue StandardError # rubocop:disable Lint/SuppressedException
23
+ rescue StandardError => e
24
+ warn "[evilution] progress stream disabled after error: #{e.class}: #{e.message}"
25
+ disabled = true
22
26
  end
23
27
  end
24
28
  end
@@ -51,7 +51,6 @@ class Evilution::MCP::SessionTool < MCP::Tool
51
51
  VALID_ACTIONS = %w[list show diff].freeze
52
52
 
53
53
  class << self
54
- # rubocop:disable Lint/UnusedMethodArgument
55
54
  def call(server_context:, action: nil, results_dir: nil, limit: nil, path: nil, base: nil, head: nil)
56
55
  return error_response("config_error", "action is required") unless action
57
56
  return error_response("config_error", "unknown action: #{action}") unless VALID_ACTIONS.include?(action)
@@ -62,7 +61,6 @@ class Evilution::MCP::SessionTool < MCP::Tool
62
61
  when "diff" then diff_action(base: base, head: head, results_dir: results_dir)
63
62
  end
64
63
  end
65
- # rubocop:enable Lint/UnusedMethodArgument
66
64
 
67
65
  private
68
66
 
@@ -1,30 +1,53 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "diff/lcs"
4
+ require_relative "../evilution"
4
5
 
5
6
  class Evilution::Mutation
6
- attr_reader :subject, :operator_name, :original_source,
7
- :mutated_source, :original_slice, :mutated_slice,
8
- :file_path, :line, :column, :parse_status
9
-
10
- # rubocop:disable Metrics/ParameterLists
11
- def initialize(subject:, operator_name:, original_source:, mutated_source:,
12
- file_path:, line:, column: 0, original_slice: nil, mutated_slice: nil,
13
- parse_status: :ok)
14
- # rubocop:enable Metrics/ParameterLists
7
+ Sources = Data.define(:original, :mutated)
8
+ Slice = Data.define(:original, :mutated)
9
+ Location = Data.define(:file_path, :line, :column)
10
+
11
+ attr_reader :subject, :operator_name, :parse_status, :location
12
+
13
+ def initialize(subject:, operator_name:, sources:, location:, slice: nil, parse_status: :ok)
15
14
  @subject = subject
16
15
  @operator_name = operator_name
17
- @original_source = original_source
18
- @mutated_source = mutated_source
19
- @original_slice = original_slice
20
- @mutated_slice = mutated_slice
21
- @file_path = file_path
22
- @line = line
23
- @column = column
16
+ @sources = sources
17
+ @location = location
18
+ @slice = slice
24
19
  @parse_status = parse_status
25
20
  @diff = nil
26
21
  end
27
22
 
23
+ def original_source
24
+ @sources&.original
25
+ end
26
+
27
+ def mutated_source
28
+ @sources&.mutated
29
+ end
30
+
31
+ def original_slice
32
+ @slice&.original
33
+ end
34
+
35
+ def mutated_slice
36
+ @slice&.mutated
37
+ end
38
+
39
+ def file_path
40
+ @location.file_path
41
+ end
42
+
43
+ def line
44
+ @location.line
45
+ end
46
+
47
+ def column
48
+ @location.column
49
+ end
50
+
28
51
  def unparseable?
29
52
  @parse_status == :unparseable
30
53
  end
@@ -41,8 +64,11 @@ class Evilution::Mutation
41
64
 
42
65
  def strip_sources!
43
66
  diff # ensure diff is cached before clearing sources
44
- @original_source = nil
45
- @mutated_source = nil
67
+ @sources = nil
68
+ end
69
+
70
+ def to_s
71
+ "#{operator_name}: #{file_path}:#{line}"
46
72
  end
47
73
 
48
74
  private
@@ -67,10 +93,10 @@ class Evilution::Mutation
67
93
  end
68
94
 
69
95
  def compute_unified_diff
70
- return nil if @original_slice.nil? || @mutated_slice.nil?
96
+ return nil if @slice.nil?
71
97
 
72
- original_lines = @original_slice.lines
73
- mutated_lines = @mutated_slice.lines
98
+ original_lines = @slice.original.lines
99
+ mutated_lines = @slice.mutated.lines
74
100
  body = ::Diff::LCS.sdiff(original_lines, mutated_lines).map { |c| format_sdiff_change(c) }.join("\n")
75
101
  [
76
102
  "--- a/#{file_path}",
@@ -88,10 +114,4 @@ class Evilution::Mutation
88
114
  when "!" then "-#{change.old_element.chomp}\n+#{change.new_element.chomp}"
89
115
  end
90
116
  end
91
-
92
- public
93
-
94
- def to_s
95
- "#{operator_name}: #{file_path}:#{line}"
96
- end
97
117
  end
@@ -45,14 +45,14 @@ class Evilution::Mutator::Base < Prism::Visitor
45
45
  @mutations << Evilution::Mutation.new(
46
46
  subject: @subject,
47
47
  operator_name: self.class.operator_name,
48
- original_source: @file_source,
49
- mutated_source: mutated_source,
50
- original_slice: original_slice,
51
- mutated_slice: mutated_slice,
52
- parse_status: surgery.status,
53
- file_path: @subject.file_path,
54
- line: node.location.start_line,
55
- column: node.location.start_column
48
+ sources: Evilution::Mutation::Sources.new(original: @file_source, mutated: mutated_source),
49
+ slice: Evilution::Mutation::Slice.new(original: original_slice, mutated: mutated_slice),
50
+ location: Evilution::Mutation::Location.new(
51
+ file_path: @subject.file_path,
52
+ line: node.location.start_line,
53
+ column: node.location.start_column
54
+ ),
55
+ parse_status: surgery.status
56
56
  )
57
57
  end
58
58
 
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../operator"
4
+
5
+ class Evilution::Mutator::Operator::PredicateToNil < Evilution::Mutator::Base
6
+ def visit_call_node(node)
7
+ if node.name.to_s.end_with?("?")
8
+ loc = node.location
9
+
10
+ add_mutation(
11
+ offset: loc.start_offset,
12
+ length: loc.length,
13
+ replacement: "nil",
14
+ node: node
15
+ )
16
+ end
17
+
18
+ super
19
+ end
20
+ end
@@ -3,6 +3,26 @@
3
3
  require_relative "../mutator"
4
4
 
5
5
  class Evilution::Mutator::Registry
6
+ STRICT_EXTRA_OPERATORS = [
7
+ Evilution::Mutator::Operator::PredicateToNil
8
+ ].freeze
9
+
10
+ def self.for_profile(profile)
11
+ unless profile.is_a?(Symbol) || profile.is_a?(String)
12
+ raise ArgumentError, "unknown profile: #{profile.inspect} (expected :default or :strict)"
13
+ end
14
+
15
+ case profile.to_sym
16
+ when :default then default
17
+ when :strict
18
+ registry = default
19
+ STRICT_EXTRA_OPERATORS.each { |op| registry.register(op) }
20
+ registry
21
+ else
22
+ raise ArgumentError, "unknown profile: #{profile.inspect} (expected :default or :strict)"
23
+ end
24
+ end
25
+
6
26
  def self.default
7
27
  registry = new
8
28
  [
@@ -10,12 +10,16 @@ module Evilution::Parallel::WorkQueue::Channel::Frame
10
10
  [payload.bytesize].pack("N") + payload
11
11
  end
12
12
 
13
+ # Marshal.load is safe here: payload originates from a sibling worker the
14
+ # parent itself forked, transferred over a private pipe inside our process
15
+ # tree. No external/untrusted input ever reaches this code. See
16
+ # .rubocop.yml (Security/MarshalLoad) for the full rationale.
13
17
  def decode(header, payload)
14
18
  return nil if header.nil? || header.bytesize < 4
15
19
 
16
20
  length = header.unpack1("N")
17
21
  return nil if payload.nil? || payload.bytesize < length
18
22
 
19
- Marshal.load(payload) # rubocop:disable Security/MarshalLoad
23
+ Marshal.load(payload)
20
24
  end
21
25
  end
@@ -36,7 +36,7 @@ module Evilution::Parallel::WorkQueue::Worker::Loop
36
36
  result = block.call(item)
37
37
  elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0
38
38
  Evilution::Parallel::WorkQueue::Channel.write(res_io, [index, :ok, result])
39
- rescue Exception => e # rubocop:disable Lint/RescueException
39
+ rescue StandardError, ScriptError, SystemStackError, NoMemoryError, SecurityError => e
40
40
  elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0
41
41
  Evilution::Parallel::WorkQueue::Channel.write(res_io, [index, :error, e])
42
42
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "version"
4
+
5
+ module Evilution::ProcessCleanup
6
+ module_function
7
+
8
+ def safe_kill(signal, pid)
9
+ ::Process.kill(signal, pid)
10
+ rescue Errno::ESRCH
11
+ nil
12
+ end
13
+
14
+ def safe_wait(pid)
15
+ ::Process.wait(pid)
16
+ rescue Errno::ECHILD
17
+ nil
18
+ end
19
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "namespace"
3
+ require_relative "../html"
4
4
 
5
5
  class Evilution::Reporter::HTML::BaselineKeys
6
6
  def initialize(baseline)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "namespace"
3
+ require_relative "../html"
4
4
  require_relative "escape"
5
5
 
6
6
  module Evilution::Reporter::HTML::DiffFormatter
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "cgi"
4
- require_relative "namespace"
4
+ require_relative "../html"
5
5
 
6
6
  module Evilution::Reporter::HTML::Escape
7
7
  module_function
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "erb"
4
- require_relative "namespace"
4
+ require_relative "../html"
5
5
  require_relative "escape"
6
6
 
7
7
  class Evilution::Reporter::HTML::Section
@@ -1,4 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "namespace"
4
- require_relative "section"
3
+ require_relative "../html"
4
+
5
+ module Evilution::Reporter::HTML::Sections
6
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "namespace"
3
+ require_relative "../html"
4
4
 
5
5
  module Evilution::Reporter::HTML::Stylesheet
6
6
  PATH = File.expand_path("assets/style.css", __dir__)
@@ -2,11 +2,16 @@
2
2
 
3
3
  require_relative "../reporter"
4
4
  require_relative "suggestion"
5
- require_relative "html/escape"
6
- require_relative "html/baseline_keys"
7
- require_relative "html/report"
8
5
 
9
6
  class Evilution::Reporter::HTML
7
+ autoload :Escape, "evilution/reporter/html/escape"
8
+ autoload :BaselineKeys, "evilution/reporter/html/baseline_keys"
9
+ autoload :Section, "evilution/reporter/html/section"
10
+ autoload :Sections, "evilution/reporter/html/sections"
11
+ autoload :Stylesheet, "evilution/reporter/html/stylesheet"
12
+ autoload :DiffFormatter, "evilution/reporter/html/diff_formatter"
13
+ autoload :Report, "evilution/reporter/html/report"
14
+
10
15
  def initialize(baseline: nil, integration: :rspec)
11
16
  @suggestion = Evilution::Reporter::Suggestion.new(integration: integration)
12
17
  @baseline = baseline
@@ -1,13 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../suggestion"
4
-
5
- # rubocop:disable Style/OneClassPerFile
6
- module Evilution::Reporter::Suggestion::Templates
7
- end
4
+ require_relative "templates"
8
5
 
9
6
  class Evilution::Reporter::Suggestion::Registry
10
- # rubocop:enable Style/OneClassPerFile
11
7
  def self.default
12
8
  return @default if @default
13
9
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../registry"
3
+ require_relative "../templates"
4
4
 
5
5
  module Evilution::Reporter::Suggestion::Templates::Generic
6
6
  GENERIC_ENTRIES = {