evilution 0.24.0 → 0.26.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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/.beads/interactions.jsonl +210 -0
  3. data/.claude/prompts/architect.md +14 -1
  4. data/.claude/skills/create-issue/SKILL.md +55 -0
  5. data/CHANGELOG.md +51 -0
  6. data/README.md +80 -4
  7. data/exe/evil +6 -0
  8. data/lib/evilution/ast/constant_names.rb +34 -0
  9. data/lib/evilution/ast/source_surgeon.rb +15 -1
  10. data/lib/evilution/cli/commands/compare.rb +68 -0
  11. data/lib/evilution/cli/parser/command_extractor.rb +2 -1
  12. data/lib/evilution/cli/parser/options_builder.rb +21 -1
  13. data/lib/evilution/cli/printers/compare.rb +159 -0
  14. data/lib/evilution/cli.rb +1 -0
  15. data/lib/evilution/compare/categorizer.rb +109 -0
  16. data/lib/evilution/compare/detector.rb +21 -0
  17. data/lib/evilution/compare/fingerprint.rb +83 -0
  18. data/lib/evilution/compare/invalid_input.rb +12 -0
  19. data/lib/evilution/compare/normalizer.rb +106 -0
  20. data/lib/evilution/compare/record.rb +16 -0
  21. data/lib/evilution/compare.rb +6 -0
  22. data/lib/evilution/config.rb +165 -3
  23. data/lib/evilution/example_filter.rb +143 -0
  24. data/lib/evilution/integration/base.rb +4 -155
  25. data/lib/evilution/integration/crash_detector.rb +5 -2
  26. data/lib/evilution/integration/loading/concern_state_cleaner.rb +49 -0
  27. data/lib/evilution/integration/loading/constant_pinner.rb +24 -0
  28. data/lib/evilution/integration/loading/mutation_applier.rb +52 -0
  29. data/lib/evilution/integration/loading/redefinition_recovery.rb +54 -0
  30. data/lib/evilution/integration/loading/source_evaluator.rb +15 -0
  31. data/lib/evilution/integration/loading/syntax_validator.rb +19 -0
  32. data/lib/evilution/integration/loading.rb +6 -0
  33. data/lib/evilution/integration/minitest.rb +10 -5
  34. data/lib/evilution/integration/minitest_crash_detector.rb +5 -2
  35. data/lib/evilution/integration/rspec.rb +82 -7
  36. data/lib/evilution/isolation/fork.rb +25 -0
  37. data/lib/evilution/load_path/subpath_resolver.rb +25 -0
  38. data/lib/evilution/load_path.rb +4 -0
  39. data/lib/evilution/mcp/info_tool.rb +77 -5
  40. data/lib/evilution/mcp/mutate_tool/config_builder.rb +20 -0
  41. data/lib/evilution/mcp/mutate_tool/error_payload.rb +17 -0
  42. data/lib/evilution/mcp/mutate_tool/option_parser.rb +54 -0
  43. data/lib/evilution/mcp/mutate_tool/progress_streamer.rb +37 -0
  44. data/lib/evilution/mcp/mutate_tool/report_trimmer.rb +31 -0
  45. data/lib/evilution/mcp/mutate_tool/survived_enricher.rb +52 -0
  46. data/lib/evilution/mcp/mutate_tool.rb +34 -186
  47. data/lib/evilution/mutation.rb +43 -3
  48. data/lib/evilution/mutator/base.rb +39 -1
  49. data/lib/evilution/mutator/operator/argument_nil_substitution.rb +5 -1
  50. data/lib/evilution/mutator/operator/argument_removal.rb +5 -1
  51. data/lib/evilution/parallel/work_queue.rb +149 -31
  52. data/lib/evilution/parallel_db_warning.rb +68 -0
  53. data/lib/evilution/reporter/cli.rb +37 -11
  54. data/lib/evilution/reporter/html/assets/style.css +17 -0
  55. data/lib/evilution/reporter/html/sections/file_section.rb +15 -0
  56. data/lib/evilution/reporter/html/sections/neutral_details.rb +25 -0
  57. data/lib/evilution/reporter/html/sections/unparseable_details.rb +25 -0
  58. data/lib/evilution/reporter/html/sections/unresolved_details.rb +25 -0
  59. data/lib/evilution/reporter/html/templates/file_section.html.erb +3 -0
  60. data/lib/evilution/reporter/html/templates/neutral_details.html.erb +14 -0
  61. data/lib/evilution/reporter/html/templates/summary_cards.html.erb +3 -0
  62. data/lib/evilution/reporter/html/templates/unparseable_details.html.erb +11 -0
  63. data/lib/evilution/reporter/html/templates/unresolved_details.html.erb +11 -0
  64. data/lib/evilution/reporter/json.rb +8 -2
  65. data/lib/evilution/reporter/suggestion/diff_helpers.rb +28 -0
  66. data/lib/evilution/reporter/suggestion/registry.rb +64 -0
  67. data/lib/evilution/reporter/suggestion/templates/generic.rb +55 -0
  68. data/lib/evilution/reporter/suggestion/templates/minitest.rb +659 -0
  69. data/lib/evilution/reporter/suggestion/templates/rspec.rb +613 -0
  70. data/lib/evilution/reporter/suggestion.rb +8 -1327
  71. data/lib/evilution/result/mutation_result.rb +5 -1
  72. data/lib/evilution/result/summary.rb +13 -1
  73. data/lib/evilution/runner/baseline_runner.rb +23 -2
  74. data/lib/evilution/runner/isolation_resolver.rb +12 -1
  75. data/lib/evilution/runner/mutation_executor.rb +83 -13
  76. data/lib/evilution/runner/subject_pipeline.rb +18 -8
  77. data/lib/evilution/runner.rb +6 -0
  78. data/lib/evilution/source_ast_cache.rb +39 -0
  79. data/lib/evilution/spec_ast_cache.rb +166 -0
  80. data/lib/evilution/spec_resolver.rb +6 -1
  81. data/lib/evilution/spec_selector.rb +39 -0
  82. data/lib/evilution/temp_dir_tracker.rb +23 -3
  83. data/lib/evilution/version.rb +1 -1
  84. data/script/memory_check +7 -5
  85. metadata +46 -5
  86. data/lib/evilution/mcp/session_diff_tool.rb +0 -63
  87. data/lib/evilution/mcp/session_list_tool.rb +0 -50
  88. data/lib/evilution/mcp/session_show_tool.rb +0 -57
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: evilution
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.24.0
4
+ version: 0.26.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Kiselev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-04-14 00:00:00.000000000 Z
11
+ date: 2026-04-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: diff-lcs
@@ -56,6 +56,7 @@ description: Evilution is a mutation testing tool for Ruby. It validates test su
56
56
  email:
57
57
  - denis.kiselyov@gmail.com
58
58
  executables:
59
+ - evil
59
60
  - evilution
60
61
  extensions: []
61
62
  extra_rdoc_files: []
@@ -71,6 +72,7 @@ files:
71
72
  - ".claude/prompts/devops.md"
72
73
  - ".claude/prompts/tests.md"
73
74
  - ".claude/settings.json"
75
+ - ".claude/skills/create-issue/SKILL.md"
74
76
  - CHANGELOG.md
75
77
  - CODE_OF_CONDUCT.md
76
78
  - LICENSE.txt
@@ -83,9 +85,11 @@ files:
83
85
  - docs/ast_pattern_syntax.md
84
86
  - docs/isolation.md
85
87
  - docs/mutation_density_benchmark.md
88
+ - exe/evil
86
89
  - exe/evilution
87
90
  - lib/evilution.rb
88
91
  - lib/evilution/ast.rb
92
+ - lib/evilution/ast/constant_names.rb
89
93
  - lib/evilution/ast/inheritance_scanner.rb
90
94
  - lib/evilution/ast/parser.rb
91
95
  - lib/evilution/ast/pattern.rb
@@ -99,6 +103,7 @@ files:
99
103
  - lib/evilution/cli.rb
100
104
  - lib/evilution/cli/command.rb
101
105
  - lib/evilution/cli/commands.rb
106
+ - lib/evilution/cli/commands/compare.rb
102
107
  - lib/evilution/cli/commands/environment_show.rb
103
108
  - lib/evilution/cli/commands/init.rb
104
109
  - lib/evilution/cli/commands/mcp.rb
@@ -119,6 +124,7 @@ files:
119
124
  - lib/evilution/cli/parser/options_builder.rb
120
125
  - lib/evilution/cli/parser/stdin_reader.rb
121
126
  - lib/evilution/cli/printers.rb
127
+ - lib/evilution/cli/printers/compare.rb
122
128
  - lib/evilution/cli/printers/environment.rb
123
129
  - lib/evilution/cli/printers/session_detail.rb
124
130
  - lib/evilution/cli/printers/session_diff.rb
@@ -127,6 +133,13 @@ files:
127
133
  - lib/evilution/cli/printers/tests_list.rb
128
134
  - lib/evilution/cli/printers/util_mutation.rb
129
135
  - lib/evilution/cli/result.rb
136
+ - lib/evilution/compare.rb
137
+ - lib/evilution/compare/categorizer.rb
138
+ - lib/evilution/compare/detector.rb
139
+ - lib/evilution/compare/fingerprint.rb
140
+ - lib/evilution/compare/invalid_input.rb
141
+ - lib/evilution/compare/normalizer.rb
142
+ - lib/evilution/compare/record.rb
130
143
  - lib/evilution/config.rb
131
144
  - lib/evilution/disable_comment.rb
132
145
  - lib/evilution/equivalent.rb
@@ -139,6 +152,7 @@ files:
139
152
  - lib/evilution/equivalent/heuristic/method_body_nil.rb
140
153
  - lib/evilution/equivalent/heuristic/noop_source.rb
141
154
  - lib/evilution/equivalent/heuristic/void_context.rb
155
+ - lib/evilution/example_filter.rb
142
156
  - lib/evilution/git.rb
143
157
  - lib/evilution/git/changed_files.rb
144
158
  - lib/evilution/hooks.rb
@@ -147,19 +161,31 @@ files:
147
161
  - lib/evilution/integration.rb
148
162
  - lib/evilution/integration/base.rb
149
163
  - lib/evilution/integration/crash_detector.rb
164
+ - lib/evilution/integration/loading.rb
165
+ - lib/evilution/integration/loading/concern_state_cleaner.rb
166
+ - lib/evilution/integration/loading/constant_pinner.rb
167
+ - lib/evilution/integration/loading/mutation_applier.rb
168
+ - lib/evilution/integration/loading/redefinition_recovery.rb
169
+ - lib/evilution/integration/loading/source_evaluator.rb
170
+ - lib/evilution/integration/loading/syntax_validator.rb
150
171
  - lib/evilution/integration/minitest.rb
151
172
  - lib/evilution/integration/minitest_crash_detector.rb
152
173
  - lib/evilution/integration/rspec.rb
153
174
  - lib/evilution/isolation.rb
154
175
  - lib/evilution/isolation/fork.rb
155
176
  - lib/evilution/isolation/in_process.rb
177
+ - lib/evilution/load_path.rb
178
+ - lib/evilution/load_path/subpath_resolver.rb
156
179
  - lib/evilution/mcp.rb
157
180
  - lib/evilution/mcp/info_tool.rb
158
181
  - lib/evilution/mcp/mutate_tool.rb
182
+ - lib/evilution/mcp/mutate_tool/config_builder.rb
183
+ - lib/evilution/mcp/mutate_tool/error_payload.rb
184
+ - lib/evilution/mcp/mutate_tool/option_parser.rb
185
+ - lib/evilution/mcp/mutate_tool/progress_streamer.rb
186
+ - lib/evilution/mcp/mutate_tool/report_trimmer.rb
187
+ - lib/evilution/mcp/mutate_tool/survived_enricher.rb
159
188
  - lib/evilution/mcp/server.rb
160
- - lib/evilution/mcp/session_diff_tool.rb
161
- - lib/evilution/mcp/session_list_tool.rb
162
- - lib/evilution/mcp/session_show_tool.rb
163
189
  - lib/evilution/mcp/session_tool.rb
164
190
  - lib/evilution/memory.rb
165
191
  - lib/evilution/memory/leak_check.rb
@@ -243,6 +269,7 @@ files:
243
269
  - lib/evilution/parallel.rb
244
270
  - lib/evilution/parallel/pool.rb
245
271
  - lib/evilution/parallel/work_queue.rb
272
+ - lib/evilution/parallel_db_warning.rb
246
273
  - lib/evilution/rails_detector.rb
247
274
  - lib/evilution/related_spec_heuristic.rb
248
275
  - lib/evilution/reporter.rb
@@ -262,10 +289,13 @@ files:
262
289
  - lib/evilution/reporter/html/sections/file_section.rb
263
290
  - lib/evilution/reporter/html/sections/header.rb
264
291
  - lib/evilution/reporter/html/sections/mutation_map.rb
292
+ - lib/evilution/reporter/html/sections/neutral_details.rb
265
293
  - lib/evilution/reporter/html/sections/summary_cards.rb
266
294
  - lib/evilution/reporter/html/sections/survived_details.rb
267
295
  - lib/evilution/reporter/html/sections/survived_entry.rb
268
296
  - lib/evilution/reporter/html/sections/truncation_notice.rb
297
+ - lib/evilution/reporter/html/sections/unparseable_details.rb
298
+ - lib/evilution/reporter/html/sections/unresolved_details.rb
269
299
  - lib/evilution/reporter/html/stylesheet.rb
270
300
  - lib/evilution/reporter/html/templates/baseline_comparison.html.erb
271
301
  - lib/evilution/reporter/html/templates/error_details.html.erb
@@ -273,14 +303,22 @@ files:
273
303
  - lib/evilution/reporter/html/templates/file_section.html.erb
274
304
  - lib/evilution/reporter/html/templates/header.html.erb
275
305
  - lib/evilution/reporter/html/templates/mutation_map.html.erb
306
+ - lib/evilution/reporter/html/templates/neutral_details.html.erb
276
307
  - lib/evilution/reporter/html/templates/report.html.erb
277
308
  - lib/evilution/reporter/html/templates/summary_cards.html.erb
278
309
  - lib/evilution/reporter/html/templates/survived_details.html.erb
279
310
  - lib/evilution/reporter/html/templates/survived_entry.html.erb
280
311
  - lib/evilution/reporter/html/templates/truncation_notice.html.erb
312
+ - lib/evilution/reporter/html/templates/unparseable_details.html.erb
313
+ - lib/evilution/reporter/html/templates/unresolved_details.html.erb
281
314
  - lib/evilution/reporter/json.rb
282
315
  - lib/evilution/reporter/progress_bar.rb
283
316
  - lib/evilution/reporter/suggestion.rb
317
+ - lib/evilution/reporter/suggestion/diff_helpers.rb
318
+ - lib/evilution/reporter/suggestion/registry.rb
319
+ - lib/evilution/reporter/suggestion/templates/generic.rb
320
+ - lib/evilution/reporter/suggestion/templates/minitest.rb
321
+ - lib/evilution/reporter/suggestion/templates/rspec.rb
284
322
  - lib/evilution/result.rb
285
323
  - lib/evilution/result/coverage_gap.rb
286
324
  - lib/evilution/result/coverage_gap_grouper.rb
@@ -297,7 +335,10 @@ files:
297
335
  - lib/evilution/session.rb
298
336
  - lib/evilution/session/diff.rb
299
337
  - lib/evilution/session/store.rb
338
+ - lib/evilution/source_ast_cache.rb
339
+ - lib/evilution/spec_ast_cache.rb
300
340
  - lib/evilution/spec_resolver.rb
341
+ - lib/evilution/spec_selector.rb
301
342
  - lib/evilution/subject.rb
302
343
  - lib/evilution/temp_dir_tracker.rb
303
344
  - lib/evilution/version.rb
@@ -1,63 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "json"
4
- require "mcp"
5
- require_relative "../session/store"
6
- require_relative "../session/diff"
7
-
8
- require_relative "../mcp"
9
-
10
- # @deprecated Superseded by {Evilution::MCP::SessionTool} (action: "diff") as of 0.22.8.
11
- # No longer registered with the MCP server; retained only for direct Ruby callers.
12
- # Will be removed entirely — tracked by EV-h8pw / GH #686.
13
- class Evilution::MCP::SessionDiffTool < MCP::Tool
14
- tool_name "evilution-session-diff"
15
- description "DEPRECATED: use evilution-session with action: 'diff'. " \
16
- "Compare two mutation testing sessions and return the diff. " \
17
- "Shows new regressions, fixed mutations, and persistent survivors."
18
- input_schema(
19
- properties: {
20
- base: {
21
- type: "string",
22
- description: "Path to the base (older) session JSON file"
23
- },
24
- head: {
25
- type: "string",
26
- description: "Path to the head (newer) session JSON file"
27
- }
28
- }
29
- )
30
-
31
- class << self
32
- # rubocop:disable Lint/UnusedMethodArgument
33
- def call(server_context:, base: nil, head: nil)
34
- return error_response("config_error", "base is required") unless base
35
- return error_response("config_error", "head is required") unless head
36
-
37
- store = Evilution::Session::Store.new
38
- base_data = store.load(base)
39
- head_data = store.load(head)
40
-
41
- diff = Evilution::Session::Diff.new
42
- result = diff.call(base_data, head_data)
43
-
44
- ::MCP::Tool::Response.new([{ type: "text", text: ::JSON.generate(result.to_h) }])
45
- rescue Evilution::Error => e
46
- error_response("not_found", e.message)
47
- rescue ::JSON::ParserError => e
48
- error_response("parse_error", e.message)
49
- rescue SystemCallError => e
50
- error_response("runtime_error", e.message)
51
- end
52
- # rubocop:enable Lint/UnusedMethodArgument
53
-
54
- private
55
-
56
- def error_response(type, message)
57
- ::MCP::Tool::Response.new(
58
- [{ type: "text", text: ::JSON.generate({ error: { type: type, message: message } }) }],
59
- error: true
60
- )
61
- end
62
- end
63
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "json"
4
- require "mcp"
5
- require_relative "../session/store"
6
-
7
- require_relative "../mcp"
8
-
9
- # @deprecated Superseded by {Evilution::MCP::SessionTool} (action: "list") as of 0.22.8.
10
- # No longer registered with the MCP server; retained only for direct Ruby callers.
11
- # Will be removed entirely — tracked by EV-h8pw / GH #686.
12
- class Evilution::MCP::SessionListTool < MCP::Tool
13
- tool_name "evilution-session-list"
14
- description "DEPRECATED: use evilution-session with action: 'list'. " \
15
- "List past mutation testing sessions with summary statistics. " \
16
- "Returns sessions in reverse chronological order."
17
- input_schema(
18
- properties: {
19
- results_dir: {
20
- type: "string",
21
- description: "Session results directory (default: .evilution/results)"
22
- },
23
- limit: {
24
- type: "integer",
25
- description: "Return only the N most recent sessions"
26
- }
27
- }
28
- )
29
-
30
- class << self
31
- # rubocop:disable Lint/UnusedMethodArgument
32
- def call(server_context:, results_dir: nil, limit: nil)
33
- store_opts = {}
34
- store_opts[:results_dir] = results_dir if results_dir
35
- store = Evilution::Session::Store.new(**store_opts)
36
- entries = store.list
37
- entries = entries.first(limit) if limit
38
-
39
- payload = entries.map { |e| stringify_keys(e) }
40
- ::MCP::Tool::Response.new([{ type: "text", text: ::JSON.generate(payload) }])
41
- end
42
- # rubocop:enable Lint/UnusedMethodArgument
43
-
44
- private
45
-
46
- def stringify_keys(hash)
47
- hash.transform_keys(&:to_s)
48
- end
49
- end
50
- end
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "json"
4
- require "mcp"
5
- require_relative "../session/store"
6
-
7
- require_relative "../mcp"
8
-
9
- # @deprecated Superseded by {Evilution::MCP::SessionTool} (action: "show") as of 0.22.8.
10
- # No longer registered with the MCP server; retained only for direct Ruby callers.
11
- # Will be removed entirely — tracked by EV-h8pw / GH #686.
12
- class Evilution::MCP::SessionShowTool < MCP::Tool
13
- tool_name "evilution-session-show"
14
- description "DEPRECATED: use evilution-session with action: 'show'. " \
15
- "Show full details of a past mutation testing session, " \
16
- "including survived mutations with diffs."
17
- input_schema(
18
- properties: {
19
- path: {
20
- type: "string",
21
- description: "Path to the session JSON file (as returned by evilution-session-list)"
22
- }
23
- }
24
- )
25
-
26
- class << self
27
- # rubocop:disable Lint/UnusedMethodArgument
28
- def call(server_context:, path: nil)
29
- unless path
30
- return ::MCP::Tool::Response.new(
31
- [{ type: "text", text: ::JSON.generate({ error: { type: "config_error", message: "path is required" } }) }],
32
- error: true
33
- )
34
- end
35
-
36
- store = Evilution::Session::Store.new
37
- data = store.load(path)
38
- ::MCP::Tool::Response.new([{ type: "text", text: ::JSON.generate(data) }])
39
- rescue Evilution::Error => e
40
- ::MCP::Tool::Response.new(
41
- [{ type: "text", text: ::JSON.generate({ error: { type: "not_found", message: e.message } }) }],
42
- error: true
43
- )
44
- rescue ::JSON::ParserError => e
45
- ::MCP::Tool::Response.new(
46
- [{ type: "text", text: ::JSON.generate({ error: { type: "parse_error", message: e.message } }) }],
47
- error: true
48
- )
49
- rescue SystemCallError => e
50
- ::MCP::Tool::Response.new(
51
- [{ type: "text", text: ::JSON.generate({ error: { type: "runtime_error", message: e.message } }) }],
52
- error: true
53
- )
54
- end
55
- # rubocop:enable Lint/UnusedMethodArgument
56
- end
57
- end