evilution 0.15.0 → 0.16.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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/.beads/.migration-hint-ts +1 -1
  3. data/.beads/issues.jsonl +32 -32
  4. data/CHANGELOG.md +21 -0
  5. data/lib/evilution/cli.rb +1 -0
  6. data/lib/evilution/config.rb +7 -1
  7. data/lib/evilution/mutator/operator/bang_method.rb +48 -0
  8. data/lib/evilution/mutator/operator/bitwise_complement.rb +31 -0
  9. data/lib/evilution/mutator/operator/bitwise_replacement.rb +30 -0
  10. data/lib/evilution/mutator/operator/break_statement.rb +50 -0
  11. data/lib/evilution/mutator/operator/class_variable_write.rb +25 -0
  12. data/lib/evilution/mutator/operator/collection_replacement.rb +25 -1
  13. data/lib/evilution/mutator/operator/ensure_removal.rb +27 -0
  14. data/lib/evilution/mutator/operator/explicit_super_mutation.rb +47 -0
  15. data/lib/evilution/mutator/operator/global_variable_write.rb +25 -0
  16. data/lib/evilution/mutator/operator/inline_rescue.rb +39 -0
  17. data/lib/evilution/mutator/operator/instance_variable_write.rb +25 -0
  18. data/lib/evilution/mutator/operator/local_variable_assignment.rb +16 -0
  19. data/lib/evilution/mutator/operator/next_statement.rb +50 -0
  20. data/lib/evilution/mutator/operator/redo_statement.rb +18 -0
  21. data/lib/evilution/mutator/operator/rescue_body_replacement.rb +94 -0
  22. data/lib/evilution/mutator/operator/rescue_removal.rb +37 -0
  23. data/lib/evilution/mutator/operator/send_mutation.rb +11 -2
  24. data/lib/evilution/mutator/operator/zsuper_removal.rb +16 -0
  25. data/lib/evilution/mutator/registry.rb +17 -1
  26. data/lib/evilution/reporter/progress_bar.rb +84 -0
  27. data/lib/evilution/reporter/suggestion.rb +225 -1
  28. data/lib/evilution/runner.rb +23 -11
  29. data/lib/evilution/version.rb +1 -1
  30. data/lib/evilution.rb +17 -0
  31. metadata +19 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5cc9d6a79710b327b773b23138b767bb377260031a679d9d3ed1e8fc1308654
4
- data.tar.gz: 7389805db21c5a7e406d388f517616b6df4b1bc0e139ebd83e907ec1093f9e9f
3
+ metadata.gz: 46fcb52d0561a5756a54aec57feea73d0879dfee69a0da9b191b4204cefc3a09
4
+ data.tar.gz: d827547c959475ca82836ad80bfa0c02bdcf129a112fd0919dbf99a7fca3542e
5
5
  SHA512:
6
- metadata.gz: 376eb8895c863791d581d2565c4551af7f8868a543cf17aecc88444101d11f6c7fffb2c80e1b1110526a2e6d0ac57750c3a170c3e73cd45cb7833df24c1fdd72
7
- data.tar.gz: 8e244c54f10d5871a4d82f1be0aefc3ab276b850cf53800d28d4606ea401ca92b5e21aa88d6ece2e6e1b1d779550c4e7251c3c11f895ec0e8c13264aa9c5b80f
6
+ metadata.gz: c2169e9aec3f90c27bb7de779d7f66cc261c73a58234e356020684527918487522fb13e2ab39a1c50bf0378602ac2849fe280bd9774b934e440d0c2b525ee89f
7
+ data.tar.gz: 39da178f17461bfd29557994914e487cfd86abcc7a009cc05af0a944426b971cf39824f8a188414ce6af647f1e87989a1c313237c693c885ce75e68b61f38e77
@@ -1 +1 @@
1
- 1774634346
1
+ 1774720771
data/.beads/issues.jsonl CHANGED
@@ -8,53 +8,53 @@
8
8
  {"id":"EV-10","title":"Publish v0.1.0 gem release","description":"Gemspec is ready. Tag v0.1.0, build the gem, and publish to RubyGems. Steps: verify gemspec metadata, run rake release (or manual gem build + gem push), create GitHub release with CHANGELOG notes.","status":"closed","priority":3,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-02T16:21:53.571182801+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-06T11:44:35.748868691+07:00","closed_at":"2026-03-06T11:44:35.748868691+07:00","close_reason":"Published to RubyGems"}
9
9
  {"id":"EV-100","title":"Epic: Expand send/method dispatch mutations","description":"Add more method dispatch mutation variants to close the gap with Mutant. Specifically called out in feedback: []/fetch, bang/non-bang, enumerable reductions.","status":"open","priority":2,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:17.980288778+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:16:17.980288778+07:00"}
10
10
  {"id":"EV-101","title":"Add RSpec suggestion templates for compound assignment mutations","description":"Add concrete RSpec it-block suggestion templates for survived compound assignment mutations to the SuggestionReporter. Follow patterns from existing suggestion templates.","status":"closed","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:24.805269164+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T17:07:01.363637634+07:00","closed_at":"2026-03-23T17:07:01.363637634+07:00","close_reason":"PR merged. Added static and concrete RSpec suggestion templates for compound assignment mutations.","dependencies":[{"issue_id":"EV-101","depends_on_id":"EV-86","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
11
- {"id":"EV-102","title":"Implement redo statement mutator","description":"Create a mutator for redo statements. Mutations: remove redo statement entirely. Prism redo_node.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:25.879559251+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:16:25.879559251+07:00","dependencies":[{"issue_id":"EV-102","depends_on_id":"EV-91","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
12
- {"id":"EV-103","title":"Add pop/shift and push/unshift method swap pairs","description":"Add orthogonal method swap pairs: pop↔shift (remove from end vs start), push↔unshift (add to end vs start). Register in the collection method swap operator.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:26.957515204+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:16:26.957515204+07:00","dependencies":[{"issue_id":"EV-103","depends_on_id":"EV-96","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
11
+ {"id":"EV-102","title":"Implement redo statement mutator","description":"Create a mutator for redo statements. Mutations: remove redo statement entirely. Prism redo_node.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:25.879559251+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T11:57:54.07790533+07:00","dependencies":[{"issue_id":"EV-102","depends_on_id":"EV-91","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
12
+ {"id":"EV-103","title":"Add pop/shift and push/unshift method swap pairs","description":"Add orthogonal method swap pairs: pop↔shift (remove from end vs start), push↔unshift (add to end vs start). Register in the collection method swap operator.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:26.957515204+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T12:07:42.123537403+07:00","dependencies":[{"issue_id":"EV-103","depends_on_id":"EV-96","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
13
13
  {"id":"EV-104","title":"Implement global variable mutation","description":"Create a mutator for global variable writes ($gvar = val). Mutations: remove assignment, replace value with nil. Prism global_variable_write_node.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:27.098660867+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:16:27.098660867+07:00","dependencies":[{"issue_id":"EV-104","depends_on_id":"EV-87","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
14
14
  {"id":"EV-105","title":"Epic: Bitwise operator mutations","description":"Add mutation operators for bitwise expressions (&, |, ^, ~). Mutant supports full bitwise swaps; Evilution has none. Important for low-level Ruby code and flag/bitmask operations.","status":"open","priority":2,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:35.702212876+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T17:08:39.7050919+07:00"}
15
- {"id":"EV-106","title":"Add each_key/each_value and assoc/rassoc method swap pairs","description":"Add orthogonal method swap pairs: each_key↔each_value (iterate keys vs values), assoc↔rassoc (search by key vs value). Register in the collection method swap operator.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:38.318168048+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:16:38.318168048+07:00","dependencies":[{"issue_id":"EV-106","depends_on_id":"EV-96","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
16
- {"id":"EV-107","title":"Add RSpec suggestion templates for control flow mutations","description":"Add concrete RSpec suggestion templates for survived break/next/redo mutations to the SuggestionReporter.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:39.204347976+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:16:39.204347976+07:00","dependencies":[{"issue_id":"EV-107","depends_on_id":"EV-91","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
17
- {"id":"EV-108","title":"Add RSpec suggestion templates for variable mutations","description":"Add concrete RSpec suggestion templates for survived variable mutations to the SuggestionReporter.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:42.648616139+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:16:42.648616139+07:00","dependencies":[{"issue_id":"EV-108","depends_on_id":"EV-87","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
18
- {"id":"EV-109","title":"Implement bitwise binary operator mutator (&, |, ^)","description":"Create a new mutator that swaps bitwise binary operators (&, |, ^) with each other. Register in Mutator::Registry. These are Prism call_node operations with bitwise method names.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:44.961048186+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:16:44.961048186+07:00","dependencies":[{"issue_id":"EV-109","depends_on_id":"EV-105","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
15
+ {"id":"EV-106","title":"Add each_key/each_value and assoc/rassoc method swap pairs","description":"Add orthogonal method swap pairs: each_key↔each_value (iterate keys vs values), assoc↔rassoc (search by key vs value). Register in the collection method swap operator.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:38.318168048+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T12:52:13.426110699+07:00","dependencies":[{"issue_id":"EV-106","depends_on_id":"EV-96","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
16
+ {"id":"EV-107","title":"Add RSpec suggestion templates for control flow mutations","description":"Add concrete RSpec suggestion templates for survived break/next/redo mutations to the SuggestionReporter.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:39.204347976+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T12:02:40.995082091+07:00","dependencies":[{"issue_id":"EV-107","depends_on_id":"EV-91","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
17
+ {"id":"EV-108","title":"Add RSpec suggestion templates for variable mutations","description":"Add concrete RSpec suggestion templates for survived variable mutations to the SuggestionReporter.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:42.648616139+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T01:53:09.086144843+07:00","dependencies":[{"issue_id":"EV-108","depends_on_id":"EV-87","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
18
+ {"id":"EV-109","title":"Implement bitwise binary operator mutator (&, |, ^)","description":"Create a new mutator that swaps bitwise binary operators (&, |, ^) with each other. Register in Mutator::Registry. These are Prism call_node operations with bitwise method names.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:44.961048186+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T13:57:57.624854414+07:00","dependencies":[{"issue_id":"EV-109","depends_on_id":"EV-105","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
19
19
  {"id":"EV-11","title":"Wire coverage-based filtering into Runner","description":"Collector and TestMap exist and pass specs, but Runner never calls them. When config.coverage is true, Runner collects aggregate line-level coverage via Coverage::Collector, builds a Coverage::TestMap, and skips mutations on lines that no test exercises (marking them as survived with zero duration). Note: Ruby Coverage provides aggregate per-file line hit counts, not per-test-file tracking, so we filter out uncovered mutations rather than selecting specific test files.","status":"closed","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-04T11:52:42.607616728+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-06T11:02:25.4848637+07:00","closed_at":"2026-03-05T14:53:24.894588088+07:00","close_reason":"Coverage-based test selection wired into Runner"}
20
20
  {"id":"EV-110","title":"Epic: Hooks system for mutation lifecycle","description":"Implement a hooks system allowing users to register custom Ruby code at critical execution points. Mutant provides 8 hook points (env_infection_pre/post, setup_integration_pre/post, mutation_insert_pre/post, mutation_worker_process_start, test_worker_process_start). Essential for Rails database isolation in parallel workers, custom instrumentation, and specialized test environment setup.","status":"open","priority":2,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:46.117603755+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:16:46.117603755+07:00"}
21
- {"id":"EV-111","title":"Add grep/grep_v and take/drop method swap pairs","description":"Add orthogonal method swap pairs: grep↔grep_v (match vs non-match), take↔drop (keep first N vs remove first N). Register in the collection method swap operator.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:47.432188126+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:16:47.432188126+07:00","dependencies":[{"issue_id":"EV-111","depends_on_id":"EV-96","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
21
+ {"id":"EV-111","title":"Add grep/grep_v and take/drop method swap pairs","description":"Add orthogonal method swap pairs: grep↔grep_v (match vs non-match), take↔drop (keep first N vs remove first N). Register in the collection method swap operator.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:47.432188126+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T12:57:53.868853745+07:00","dependencies":[{"issue_id":"EV-111","depends_on_id":"EV-96","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
22
22
  {"id":"EV-112","title":"Epic: Super call mutations","description":"Add mutations for super and zsuper (implicit super) calls. Mutant distinguishes zsuper (forwards all args) from explicit super (specific args) and mutates between them. Evilution has none.","status":"open","priority":2,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:51.249178891+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:16:51.249178891+07:00"}
23
- {"id":"EV-113","title":"Implement rescue clause removal mutator","description":"Create a mutator that removes individual rescue clauses from begin/rescue blocks. When multiple rescue clauses exist, remove each one individually. Prism rescue_node.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:53.280678046+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:16:53.280678046+07:00","dependencies":[{"issue_id":"EV-113","depends_on_id":"EV-89","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
24
- {"id":"EV-114","title":"Implement bitwise complement (~) mutator","description":"Create a mutator for the bitwise NOT/complement operator (~). Mutations: remove ~ (unwrap to operand), swap with unary minus. This is a Prism call_node with ~ as the method name and no arguments.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:55.061833915+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:16:55.061833915+07:00","dependencies":[{"issue_id":"EV-114","depends_on_id":"EV-105","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
25
- {"id":"EV-115","title":"Add min/max and min_by/max_by method swap pairs","description":"Add orthogonal method swap pairs: min↔max, min_by↔max_by. Register in the collection method swap operator.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:57.324659361+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:16:57.324659361+07:00","dependencies":[{"issue_id":"EV-115","depends_on_id":"EV-96","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
26
- {"id":"EV-116","title":"Implement zsuper removal mutator","description":"Create a mutator for implicit super calls (zsuper — super with no parens). Mutations: remove the super call entirely. Prism forwarding_super_node.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:00.030638552+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:00.030638552+07:00","dependencies":[{"issue_id":"EV-116","depends_on_id":"EV-112","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
27
- {"id":"EV-117","title":"Implement rescue body replacement mutator","description":"Create a mutator that replaces rescue clause bodies with nil or re-raise. Tests whether the rescue handler logic is actually tested.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:07.741756241+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:07.741756241+07:00","dependencies":[{"issue_id":"EV-117","depends_on_id":"EV-89","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
28
- {"id":"EV-118","title":"Add compact/flatten and zip/product method swap pairs","description":"Add method swap pairs: compact↔flatten (remove nils vs flatten nesting), zip↔product (pairwise vs cartesian). Register in the collection method swap operator.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:09.488783588+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:09.488783588+07:00","dependencies":[{"issue_id":"EV-118","depends_on_id":"EV-96","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
23
+ {"id":"EV-113","title":"Implement rescue clause removal mutator","description":"Create a mutator that removes individual rescue clauses from begin/rescue blocks. When multiple rescue clauses exist, remove each one individually. Prism rescue_node.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:53.280678046+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T01:58:05.000652348+07:00","dependencies":[{"issue_id":"EV-113","depends_on_id":"EV-89","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
24
+ {"id":"EV-114","title":"Implement bitwise complement (~) mutator","description":"Create a mutator for the bitwise NOT/complement operator (~). Mutations: remove ~ (unwrap to operand), swap with unary minus. This is a Prism call_node with ~ as the method name and no arguments.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:55.061833915+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T14:10:45.714387484+07:00","dependencies":[{"issue_id":"EV-114","depends_on_id":"EV-105","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
25
+ {"id":"EV-115","title":"Add min/max and min_by/max_by method swap pairs","description":"Add orthogonal method swap pairs: min↔max, min_by↔max_by. Register in the collection method swap operator.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:57.324659361+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T13:03:36.13864378+07:00","dependencies":[{"issue_id":"EV-115","depends_on_id":"EV-96","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
26
+ {"id":"EV-116","title":"Implement zsuper removal mutator","description":"Create a mutator for implicit super calls (zsuper — super with no parens). Mutations: remove the super call entirely. Prism forwarding_super_node.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:00.030638552+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T14:22:27.928183609+07:00","dependencies":[{"issue_id":"EV-116","depends_on_id":"EV-112","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
27
+ {"id":"EV-117","title":"Implement rescue body replacement mutator","description":"Create a mutator that replaces rescue clause bodies with nil or re-raise. Tests whether the rescue handler logic is actually tested.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:07.741756241+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T09:46:31.420660224+07:00","dependencies":[{"issue_id":"EV-117","depends_on_id":"EV-89","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
28
+ {"id":"EV-118","title":"Add compact/flatten and zip/product method swap pairs","description":"Add method swap pairs: compact↔flatten (remove nils vs flatten nesting), zip↔product (pairwise vs cartesian). Register in the collection method swap operator.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:09.488783588+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T13:07:37.800200558+07:00","dependencies":[{"issue_id":"EV-118","depends_on_id":"EV-96","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
29
29
  {"id":"EV-119","title":"Design hook registry and configuration API","description":"Design the hook system API: how hooks are registered (config file, Ruby API), what data they receive, and how errors are handled. Support at minimum: worker_process_start, mutation_insert_pre/post, and setup_integration_pre/post hook points.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:09.630400111+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:09.630400111+07:00","dependencies":[{"issue_id":"EV-119","depends_on_id":"EV-110","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
30
30
  {"id":"EV-12","title":"Resolve suggestion gap","description":"The workflow section instructs agents to read a suggestion field from survived[], but the JSON reporter output does not include suggestion (lib/evilution/reporter/json.rb only emits operator/file/line/status/duration/diff). Either add suggestion to the JSON output/schema or update the workflow steps to match the actual report fields. See GitHub issue #12.","status":"closed","priority":2,"issue_type":"bug","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-05T12:33:23.674094791+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-05T13:05:48.785616141+07:00","closed_at":"2026-03-05T13:05:48.785616141+07:00","close_reason":"Wired Suggestion into JSON reporter for survived mutations, updated README schema, added specs"}
31
- {"id":"EV-120","title":"Implement explicit super argument mutations","description":"Create a mutator for explicit super(args) calls. Mutations: remove arguments (super() with no args), remove individual arguments, replace super with zsuper. Prism super_node.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:10.245605293+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:10.245605293+07:00","dependencies":[{"issue_id":"EV-120","depends_on_id":"EV-112","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
32
- {"id":"EV-121","title":"Add RSpec suggestion templates for bitwise operator mutations","description":"Add concrete RSpec suggestion templates for survived bitwise operator mutations to the SuggestionReporter.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:12.673234672+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:12.673234672+07:00","dependencies":[{"issue_id":"EV-121","depends_on_id":"EV-105","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
33
- {"id":"EV-122","title":"Implement inline rescue mutation","description":"Create a mutator for inline rescue expressions (expr rescue fallback). Mutations: remove rescue (keep only expr), replace fallback with nil. Prism rescue_modifier_node.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:18.843414731+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:18.843414731+07:00","dependencies":[{"issue_id":"EV-122","depends_on_id":"EV-89","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
31
+ {"id":"EV-120","title":"Implement explicit super argument mutations","description":"Create a mutator for explicit super(args) calls. Mutations: remove arguments (super() with no args), remove individual arguments, replace super with zsuper. Prism super_node.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:10.245605293+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T14:27:56.442898048+07:00","dependencies":[{"issue_id":"EV-120","depends_on_id":"EV-112","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
32
+ {"id":"EV-121","title":"Add RSpec suggestion templates for bitwise operator mutations","description":"Add concrete RSpec suggestion templates for survived bitwise operator mutations to the SuggestionReporter.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:12.673234672+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T14:16:14.791646944+07:00","dependencies":[{"issue_id":"EV-121","depends_on_id":"EV-105","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
33
+ {"id":"EV-122","title":"Implement inline rescue mutation","description":"Create a mutator for inline rescue expressions (expr rescue fallback). Mutations: remove rescue (keep only expr), replace fallback with nil. Prism rescue_modifier_node.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:18.843414731+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T10:05:16.865721015+07:00","dependencies":[{"issue_id":"EV-122","depends_on_id":"EV-89","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
34
34
  {"id":"EV-123","title":"Fix version mismatch between CLI and MCP JSON output","description":"CLI reports 0.12.0 but MCP JSON version field says 0.11.2. The version string in the MCP server response is hardcoded or reading from a stale source. Should read from Evilution::VERSION consistently. Identified in real-world comparison testing feedback.","status":"closed","priority":1,"issue_type":"bug","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:19.056996262+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:34:16.40564094+07:00","closed_at":"2026-03-23T11:34:16.40564094+07:00","close_reason":"Already fixed — all version references (MCP server, JSON reporter, HTML reporter) use Evilution::VERSION consistently. The 0.11.2 seen in feedback was likely a stale gem installation."}
35
- {"id":"EV-124","title":"Add first/last and keys/values method swap pairs","description":"Add orthogonal method swap pairs: first↔last (Array endpoints), keys↔values (Hash extraction). Register in the collection method swap operator.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:19.366299705+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:19.366299705+07:00","dependencies":[{"issue_id":"EV-124","depends_on_id":"EV-96","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
36
- {"id":"EV-125","title":"Add RSpec suggestion templates for super mutations","description":"Add concrete RSpec suggestion templates for survived super mutations to the SuggestionReporter.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:20.933060824+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:20.933060824+07:00","dependencies":[{"issue_id":"EV-125","depends_on_id":"EV-112","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
35
+ {"id":"EV-124","title":"Add first/last and keys/values method swap pairs","description":"Add orthogonal method swap pairs: first↔last (Array endpoints), keys↔values (Hash extraction). Register in the collection method swap operator.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:19.366299705+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T13:11:43.391612732+07:00","dependencies":[{"issue_id":"EV-124","depends_on_id":"EV-96","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
36
+ {"id":"EV-125","title":"Add RSpec suggestion templates for super mutations","description":"Add concrete RSpec suggestion templates for survived super mutations to the SuggestionReporter.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:20.933060824+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T14:36:21.649380826+07:00","dependencies":[{"issue_id":"EV-125","depends_on_id":"EV-112","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
37
37
  {"id":"EV-126","title":"Implement hook registry module","description":"Create Evilution::Hooks::Registry that stores and dispatches hook callbacks. Support registering hooks by name, running hooks with context data, and error isolation (one failing hook shouldn't crash the run).","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:21.393514581+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:21.393514581+07:00","dependencies":[{"issue_id":"EV-126","depends_on_id":"EV-110","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
38
- {"id":"EV-127","title":"Implement ensure clause removal mutator","description":"Create a mutator that removes ensure blocks from begin/ensure expressions. Tests whether cleanup code is actually necessary. Prism ensure_node.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:27.334061542+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:27.334061542+07:00","dependencies":[{"issue_id":"EV-127","depends_on_id":"EV-89","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
38
+ {"id":"EV-127","title":"Implement ensure clause removal mutator","description":"Create a mutator that removes ensure blocks from begin/ensure expressions. Tests whether cleanup code is actually necessary. Prism ensure_node.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:27.334061542+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T10:37:25.904594009+07:00","dependencies":[{"issue_id":"EV-127","depends_on_id":"EV-89","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
39
39
  {"id":"EV-128","title":"Visual progress bar for TTY execution","description":"Add a visual TTY progress bar during mutation testing showing mutation count, kills, elapsed time, and ETA. Mutant has had this since v0.14.2. Important for user experience during long-running mutation testing sessions.","status":"open","priority":2,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:30.526136549+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:30.526136549+07:00"}
40
- {"id":"EV-129","title":"Add bang vs non-bang method mutations","description":"Create mutations that swap bang methods with their non-bang equivalents and vice versa (e.g., save! ↔ save, sort! ↔ sort, map! ↔ map, uniq! ↔ uniq). Tests whether the in-place vs copy semantics matter.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:30.959721048+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:30.959721048+07:00","dependencies":[{"issue_id":"EV-129","depends_on_id":"EV-100","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
40
+ {"id":"EV-129","title":"Add bang vs non-bang method mutations","description":"Create mutations that swap bang methods with their non-bang equivalents and vice versa (e.g., save! ↔ save, sort! ↔ sort, map! ↔ map, uniq! ↔ uniq). Tests whether the in-place vs copy semantics matter.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:30.959721048+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T13:16:36.661424217+07:00","dependencies":[{"issue_id":"EV-129","depends_on_id":"EV-100","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
41
41
  {"id":"EV-13","title":"Enable true per-mutation isolation with temp file copies","description":"Replace direct file writes with temp-dir + $LOAD_PATH isolation so multiple workers can mutate the same file in parallel. Remove per-file grouping from Pool#partition.","status":"closed","priority":2,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-05T13:42:04.711580397+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-05T13:44:34.779951959+07:00","closed_at":"2026-03-05T13:44:34.779951959+07:00","close_reason":"Implemented temp file isolation via $LOAD_PATH and round-robin partition"}
42
42
  {"id":"EV-130","title":"Add worker_process_start hook point","description":"Fire the worker_process_start hook when a parallel worker process starts (after fork). This is the most important hook for Rails database isolation — users need to re-establish database connections after fork.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:31.360903708+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:31.360903708+07:00","dependencies":[{"issue_id":"EV-130","depends_on_id":"EV-110","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
43
43
  {"id":"EV-131","title":"Epic: Index access operator mutations ([] / []=)","description":"Add mutations for index access operators. Mutant mutates [] to fetch/dig and []=. These surface real semantic differences (e.g., KeyError vs nil on missing key). Specifically called out in feedback as important.","status":"open","priority":2,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:33.13561849+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:33.13561849+07:00"}
44
- {"id":"EV-132","title":"Add RSpec suggestion templates for exception handling mutations","description":"Add concrete RSpec suggestion templates for survived exception handling mutations to the SuggestionReporter.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:38.53663204+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:38.53663204+07:00","dependencies":[{"issue_id":"EV-132","depends_on_id":"EV-89","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
45
- {"id":"EV-133","title":"Implement TTY progress bar renderer","description":"Create a progress bar component that renders to TTY showing: [=====> ] 45/100 mutations | 38 killed | 2 survived | 00:23 elapsed | ~00:28 remaining. Detect TTY vs piped output and only show in TTY mode.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:40.177839247+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:40.177839247+07:00","dependencies":[{"issue_id":"EV-133","depends_on_id":"EV-128","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
44
+ {"id":"EV-132","title":"Add RSpec suggestion templates for exception handling mutations","description":"Add concrete RSpec suggestion templates for survived exception handling mutations to the SuggestionReporter.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:38.53663204+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T10:48:10.93836753+07:00","dependencies":[{"issue_id":"EV-132","depends_on_id":"EV-89","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
45
+ {"id":"EV-133","title":"Implement TTY progress bar renderer","description":"Create a progress bar component that renders to TTY showing: [=====> ] 45/100 mutations | 38 killed | 2 survived | 00:23 elapsed | ~00:28 remaining. Detect TTY vs piped output and only show in TTY mode.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:40.177839247+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T15:05:19.015910008+07:00","dependencies":[{"issue_id":"EV-133","depends_on_id":"EV-128","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
46
46
  {"id":"EV-134","title":"Implement [] to fetch mutation","description":"Create a mutator that replaces hash/array [] access with .fetch(). This surfaces whether code handles missing keys properly. The [] vs fetch distinction was specifically called out in comparison feedback.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:42.107226515+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:42.107226515+07:00","dependencies":[{"issue_id":"EV-134","depends_on_id":"EV-131","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
47
- {"id":"EV-135","title":"Add enumerable reduction method swaps","description":"Add method swap mutations for enumerable reductions: sum↔inject, count↔length↔size, detect↔find, select↔filter, collect↔map. Some may already exist; add the missing ones.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:42.397830812+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:42.397830812+07:00","dependencies":[{"issue_id":"EV-135","depends_on_id":"EV-100","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
47
+ {"id":"EV-135","title":"Add enumerable reduction method swaps","description":"Add method swap mutations for enumerable reductions: sum↔inject, count↔length↔size, detect↔find, select↔filter, collect↔map. Some may already exist; add the missing ones.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:42.397830812+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T13:31:17.723196272+07:00","dependencies":[{"issue_id":"EV-135","depends_on_id":"EV-100","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
48
48
  {"id":"EV-136","title":"Add mutation_insert_pre/post hook points","description":"Fire hooks before and after each mutation is inserted into source. Provides the mutation details (operator, location, original/mutated code) to the hook callback.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:44.656504163+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:44.656504163+07:00","dependencies":[{"issue_id":"EV-136","depends_on_id":"EV-110","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
49
- {"id":"EV-137","title":"Integrate progress bar with Runner","description":"Wire the progress bar into Runner so it updates after each mutation result. Support both sequential and parallel execution modes. Ensure it doesn't interfere with verbose output.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:50.780537596+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:50.780537596+07:00","dependencies":[{"issue_id":"EV-137","depends_on_id":"EV-128","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
49
+ {"id":"EV-137","title":"Integrate progress bar with Runner","description":"Wire the progress bar into Runner so it updates after each mutation result. Support both sequential and parallel execution modes. Ensure it doesn't interfere with verbose output.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:50.780537596+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T19:24:43.716640415+07:00","dependencies":[{"issue_id":"EV-137","depends_on_id":"EV-128","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
50
50
  {"id":"EV-138","title":"Implement [] to dig mutation","description":"Create a mutator that replaces nested [] access with .dig(). Tests whether nil propagation through nested access is handled.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:50.859337664+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:50.859337664+07:00","dependencies":[{"issue_id":"EV-138","depends_on_id":"EV-131","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
51
- {"id":"EV-139","title":"Add to_s/to_i/to_f/to_a/to_h conversion method swaps","description":"Add mutations that swap type conversion methods with each other (e.g., to_s↔to_i, to_a↔to_h). Tests whether the correct type conversion is used.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:52.403463105+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:52.403463105+07:00","dependencies":[{"issue_id":"EV-139","depends_on_id":"EV-100","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
51
+ {"id":"EV-139","title":"Add to_s/to_i/to_f/to_a/to_h conversion method swaps","description":"Add mutations that swap type conversion methods with each other (e.g., to_s↔to_i, to_a↔to_h). Tests whether the correct type conversion is used.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:52.403463105+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T13:44:39.03275591+07:00","dependencies":[{"issue_id":"EV-139","depends_on_id":"EV-100","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
52
52
  {"id":"EV-14","title":"Add changelog_uri to gemspec metadata","description":"The published gem on RubyGems is missing the Changelog link. Add changelog_uri to spec.metadata in evilution.gemspec pointing to CHANGELOG.md on master.","status":"closed","priority":2,"issue_type":"bug","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-06T11:46:12.648668594+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-06T11:47:41.23691915+07:00","closed_at":"2026-03-06T11:47:41.23691915+07:00","close_reason":"Added changelog_uri to gemspec"}
53
53
  {"id":"EV-140","title":"Add setup_integration_pre/post hook points","description":"Fire hooks before and after the test integration is set up. Allows custom environment configuration before tests run.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:17:53.595178841+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:17:53.595178841+07:00","dependencies":[{"issue_id":"EV-140","depends_on_id":"EV-110","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
54
54
  {"id":"EV-141","title":"Epic: Pattern matching mutations (case/in)","description":"Add mutation operators for Ruby 3.x+ pattern matching expressions (case/in). Mutant mutates guards, alternatives, and predicates in pattern matching. Important as pattern matching adoption grows.","status":"open","priority":2,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:18:00.138078298+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:18:00.138078298+07:00"}
55
55
  {"id":"EV-142","title":"Implement []= removal mutation","description":"Create a mutator that removes []= (index assignment) statements. Tests whether the assignment is actually necessary.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:18:01.34824668+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:18:01.34824668+07:00","dependencies":[{"issue_id":"EV-142","depends_on_id":"EV-131","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
56
- {"id":"EV-143","title":"Add --no-progress flag to disable progress bar","description":"Add CLI flag and config option to disable the progress bar for CI/piped environments where TTY detection might not work correctly.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:18:01.773000995+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:18:01.773000995+07:00","dependencies":[{"issue_id":"EV-143","depends_on_id":"EV-128","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
57
- {"id":"EV-144","title":"Add send vs public_send method swap","description":"Add a mutation that swaps send with public_send and vice versa. Tests whether the method visibility bypass is intentional.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:18:03.825429179+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:18:03.825429179+07:00","dependencies":[{"issue_id":"EV-144","depends_on_id":"EV-100","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
56
+ {"id":"EV-143","title":"Add --no-progress flag to disable progress bar","description":"Add CLI flag and config option to disable the progress bar for CI/piped environments where TTY detection might not work correctly.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:18:01.773000995+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T19:30:09.471623389+07:00","dependencies":[{"issue_id":"EV-143","depends_on_id":"EV-128","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
57
+ {"id":"EV-144","title":"Add send vs public_send method swap","description":"Add a mutation that swaps send with public_send and vice versa. Tests whether the method visibility bypass is intentional.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:18:03.825429179+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T13:54:46.379099536+07:00","dependencies":[{"issue_id":"EV-144","depends_on_id":"EV-100","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
58
58
  {"id":"EV-145","title":"Add hooks configuration to .evilution.yml","description":"Allow hooks to be specified in .evilution.yml config file. Support both inline Ruby blocks and file paths to Ruby scripts. Example: hooks: { worker_process_start: 'config/evilution_hooks.rb' }.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:18:04.207200619+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:18:04.207200619+07:00","dependencies":[{"issue_id":"EV-145","depends_on_id":"EV-110","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
59
59
  {"id":"EV-146","title":"Add RSpec suggestion templates for index access mutations","description":"Add concrete RSpec suggestion templates for survived index access mutations to the SuggestionReporter.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:18:11.229320728+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:18:11.229320728+07:00","dependencies":[{"issue_id":"EV-146","depends_on_id":"EV-131","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
60
60
  {"id":"EV-147","title":"Implement pattern matching guard mutation","description":"Mutate guard clauses in pattern matching (in pattern if guard). Mutations: remove guard (always match), negate guard. Prism in_node with guard.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:18:12.368560689+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:18:12.368560689+07:00","dependencies":[{"issue_id":"EV-147","depends_on_id":"EV-141","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
@@ -278,11 +278,11 @@
278
278
  {"id":"EV-9","title":"Add multi-Ruby CI test matrix (3.2, 3.3, 4.0)","description":"CI currently only tests Ruby 4.0.1. Add a matrix strategy testing Ruby 3.2, 3.3, and 4.0 to ensure compatibility across supported Ruby versions. Prism ships with Ruby 3.3+ so 3.2 may need the prism gem as a dependency.","status":"closed","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-02T16:21:51.239774764+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-05T12:31:51.83181612+07:00","closed_at":"2026-03-05T12:31:51.83181612+07:00","close_reason":"Closed"}
279
279
  {"id":"EV-90","title":"Implement bitwise compound assignment mutator (&=, |=, ^=, <<=, >>=)","description":"Create mutations that swap bitwise compound assignment operators with each other. These are Prism op_asgn nodes with bitwise operator tokens.","status":"closed","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:15:49.296076429+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T16:41:58.956765738+07:00","closed_at":"2026-03-23T16:41:58.956765738+07:00","close_reason":"PR merged. Added bitwise compound assignment mutations (&=, |=, ^=, <<=, >>=) to CompoundAssignment mutator.","dependencies":[{"issue_id":"EV-90","depends_on_id":"EV-86","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
280
280
  {"id":"EV-91","title":"Epic: Break/next/redo control flow mutations","description":"Add mutations for break, next, and redo statements. Mutant mutates these control flow interrupts (swap break/next, remove redo, mutate break/next values). Important for testing loop behavior. Evilution has none.","status":"open","priority":2,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:15:52.890173894+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:15:52.890173894+07:00"}
281
- {"id":"EV-92","title":"Implement local variable assignment removal mutator","description":"Create a mutator that removes local variable assignments (lvasgn nodes), keeping only the value expression or removing the entire statement. Follow existing statement deletion patterns.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:15:54.68007817+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:15:54.68007817+07:00","dependencies":[{"issue_id":"EV-92","depends_on_id":"EV-87","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
281
+ {"id":"EV-92","title":"Implement local variable assignment removal mutator","description":"Create a mutator that removes local variable assignments (lvasgn nodes), keeping only the value expression or removing the entire statement. Follow existing statement deletion patterns.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:15:54.68007817+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T01:05:19.57588682+07:00","dependencies":[{"issue_id":"EV-92","depends_on_id":"EV-87","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
282
282
  {"id":"EV-93","title":"Implement logical compound assignment mutator (&&=, ||=)","description":"Create mutations that swap &&= with ||= and vice versa. These are Prism and_asgn/or_asgn nodes, distinct from op_asgn.","status":"closed","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:15:59.501149639+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T16:57:19.169563553+07:00","closed_at":"2026-03-23T16:57:19.169563553+07:00","close_reason":"PR #363 merged. Added &&= and ||= swap mutations for all variable types.","dependencies":[{"issue_id":"EV-93","depends_on_id":"EV-86","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
283
- {"id":"EV-94","title":"Implement break statement mutator","description":"Create a mutator for break statements. Mutations: remove break (let loop continue), replace break value with nil, swap break with next. Prism break_node.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:05.462729749+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:16:05.462729749+07:00","dependencies":[{"issue_id":"EV-94","depends_on_id":"EV-91","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
284
- {"id":"EV-95","title":"Implement instance variable mutation","description":"Create a mutator for instance variable writes (@ivar = val). Mutations: remove assignment, replace value with nil. Prism instance_variable_write_node.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:07.10678859+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:16:07.10678859+07:00","dependencies":[{"issue_id":"EV-95","depends_on_id":"EV-87","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
283
+ {"id":"EV-94","title":"Implement break statement mutator","description":"Create a mutator for break statements. Mutations: remove break (let loop continue), replace break value with nil, swap break with next. Prism break_node.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:05.462729749+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T10:58:26.095710774+07:00","dependencies":[{"issue_id":"EV-94","depends_on_id":"EV-91","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
284
+ {"id":"EV-95","title":"Implement instance variable mutation","description":"Create a mutator for instance variable writes (@ivar = val). Mutations: remove assignment, replace value with nil. Prism instance_variable_write_node.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:07.10678859+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T01:10:37.845543866+07:00","dependencies":[{"issue_id":"EV-95","depends_on_id":"EV-87","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
285
285
  {"id":"EV-96","title":"Epic: Expand collection method swap pairs","description":"Add more orthogonal method swap pairs to close the gap with Mutant's 20+ pairs. Evilution has 14 pairs; needs 6+ more covering common Ruby collection methods.","status":"open","priority":2,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:07.686812036+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:16:07.686812036+07:00"}
286
286
  {"id":"EV-97","title":"Add compound assignment to removal mutator","description":"Extend the compound assignment mutators to also generate a removal mutation (remove the entire compound assignment statement). Aligns with Mutant's statement deletion behavior.","status":"closed","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:15.098552795+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T17:02:39.203119113+07:00","closed_at":"2026-03-23T17:02:39.203119113+07:00","close_reason":"PR merged. Added removal mutations for all compound assignment types.","dependencies":[{"issue_id":"EV-97","depends_on_id":"EV-86","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
287
- {"id":"EV-98","title":"Implement next statement mutator","description":"Create a mutator for next statements. Mutations: remove next (let block continue), replace next value with nil, swap next with break. Prism next_node.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:16.638335477+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:16:16.638335477+07:00","dependencies":[{"issue_id":"EV-98","depends_on_id":"EV-91","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
288
- {"id":"EV-99","title":"Implement class variable mutation","description":"Create a mutator for class variable writes (@@cvar = val). Mutations: remove assignment, replace value with nil. Prism class_variable_write_node.","status":"open","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:15.967143759+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-23T11:16:15.967143759+07:00","dependencies":[{"issue_id":"EV-99","depends_on_id":"EV-87","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
287
+ {"id":"EV-98","title":"Implement next statement mutator","description":"Create a mutator for next statements. Mutations: remove next (let block continue), replace next value with nil, swap next with break. Prism next_node.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:16.638335477+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T11:48:21.34281786+07:00","dependencies":[{"issue_id":"EV-98","depends_on_id":"EV-91","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
288
+ {"id":"EV-99","title":"Implement class variable mutation","description":"Create a mutator for class variable writes (@@cvar = val). Mutations: remove assignment, replace value with nil. Prism class_variable_write_node.","status":"in_progress","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-23T11:16:15.967143759+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-29T01:20:18.161043033+07:00","dependencies":[{"issue_id":"EV-99","depends_on_id":"EV-87","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
data/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.16.0] - 2026-03-29
4
+
5
+ ### Added
6
+
7
+ - **Variable mutation operators** — `LocalVariableAssignment`, `InstanceVariableWrite`, `ClassVariableWrite`, `GlobalVariableWrite` replace variable assignments with `nil` to test whether stored values are actually used (#394, #395, #396, #397)
8
+ - **Rescue/ensure mutation operators** — `RescueRemoval` removes rescue clauses, `RescueBodyReplacement` replaces rescue bodies with `nil`, `InlineRescue` removes inline `rescue` fallback values, `EnsureRemoval` removes ensure blocks (#399, #400, #401, #402)
9
+ - **Loop control mutation operators** — `BreakStatement`, `NextStatement`, `RedoStatement` remove loop control flow statements to test whether early exits and restarts are necessary (#404, #405, #406)
10
+ - **BangMethod operator** — swaps bang methods with their non-bang counterparts (`sort!` → `sort`, `map!` → `map`, etc.) to test whether in-place mutation semantics matter (#413)
11
+ - **Bitwise mutation operators** — `BitwiseReplacement` swaps `&`, `|`, `^` with each other; `BitwiseComplement` removes `~` or swaps it with unary minus (#416, #417)
12
+ - **Super call mutation operators** — `ZsuperRemoval` replaces implicit `super` with `nil`; `ExplicitSuperMutation` removes arguments, removes individual arguments, or replaces `super(args)` with implicit `super` (#419, #420)
13
+ - **CollectionReplacement expansions** — added method swap pairs: `pop`/`shift`, `push`/`unshift`, `each_key`/`each_value`, `assoc`/`rassoc`, `grep`/`grep_v`, `take`/`drop`, `min`/`max`, `min_by`/`max_by`, `compact`/`flatten`, `zip`/`product`, `first`/`last`, `keys`/`values` (#407, #408, #409, #410, #411, #412)
14
+ - **SendMutation expansions** — enumerable reduction method swaps (`reduce`/`inject`, `sum`/`count`, `tally`/`group_by`, etc.) and conversion method swaps (`to_s`/`to_i`/`to_f`/`to_a`/`to_h`) (#414, #415)
15
+ - **Suggestion templates** — concrete RSpec suggestion templates for variable mutations, rescue/ensure mutations, bitwise mutations, and super call mutations (#398, #403, #418, #421)
16
+ - **TTY progress bar** — real-time progress display showing `[=====> ] 45/100 mutations | 38 killed | 2 survived | 00:23 elapsed | ~00:28 remaining`; TTY-aware rendering (carriage return for TTY, newlines for piped output); integrated into Runner for both sequential and parallel execution (#422, #423)
17
+ - **`--no-progress` flag** — CLI flag and config option to disable the progress bar for CI/piped environments (#424)
18
+
19
+ ### Changed
20
+
21
+ - **Operator count** — 46 operators (up from 30), covering variables, rescue/ensure, loop control, bang methods, bitwise operators, and super calls
22
+ - **Runner refactoring** — extracted `notify_result` method for unified result callbacks, progress bar updates, and diagnostic logging across sequential and parallel execution modes
23
+
3
24
  ## [0.15.0] - 2026-03-29
4
25
 
5
26
  ### Added
data/lib/evilution/cli.rb CHANGED
@@ -161,6 +161,7 @@ class Evilution::CLI
161
161
  opts.on("--isolation STRATEGY", "Isolation: auto, fork, in_process (default: auto)") { |s| @options[:isolation] = s }
162
162
  opts.on("--stdin", "Read target file paths from stdin (one per line)") { @options[:stdin] = true }
163
163
  opts.on("--suggest-tests", "Generate concrete RSpec test code in suggestions") { @options[:suggest_tests] = true }
164
+ opts.on("--no-progress", "Disable progress bar") { @options[:progress] = false }
164
165
  opts.on("--save-session", "Save session results to .evilution/results/") { @options[:save_session] = true }
165
166
  opts.on("-v", "--verbose", "Verbose output") { @options[:verbose] = true }
166
167
  opts.on("-q", "--quiet", "Suppress output") { @options[:quiet] = true }
@@ -19,6 +19,7 @@ class Evilution::Config
19
19
  isolation: :auto,
20
20
  incremental: false,
21
21
  suggest_tests: false,
22
+ progress: true,
22
23
  save_session: false,
23
24
  line_ranges: {},
24
25
  spec_files: []
@@ -27,7 +28,7 @@ class Evilution::Config
27
28
  attr_reader :target_files, :timeout, :format,
28
29
  :target, :min_score, :integration, :verbose, :quiet,
29
30
  :jobs, :fail_fast, :baseline, :isolation, :incremental, :suggest_tests,
30
- :save_session, :line_ranges, :spec_files
31
+ :progress, :save_session, :line_ranges, :spec_files
31
32
 
32
33
  def initialize(**options)
33
34
  file_options = options.delete(:skip_config_file) ? {} : load_config_file
@@ -72,6 +73,10 @@ class Evilution::Config
72
73
  suggest_tests
73
74
  end
74
75
 
76
+ def progress?
77
+ progress
78
+ end
79
+
75
80
  def save_session?
76
81
  save_session
77
82
  end
@@ -148,6 +153,7 @@ class Evilution::Config
148
153
  @isolation = validate_isolation(merged[:isolation])
149
154
  @incremental = merged[:incremental]
150
155
  @suggest_tests = merged[:suggest_tests]
156
+ @progress = merged[:progress]
151
157
  @save_session = merged[:save_session]
152
158
  @line_ranges = merged[:line_ranges] || {}
153
159
  @spec_files = Array(merged[:spec_files])
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../operator"
4
+
5
+ class Evilution::Mutator::Operator::BangMethod < Evilution::Mutator::Base
6
+ KNOWN_BANG_PAIRS = %i[
7
+ sort map collect select reject uniq compact flatten
8
+ shuffle reverse slice gsub sub strip chomp chop squeeze
9
+ delete encode merge update save
10
+ ].to_set.freeze
11
+
12
+ def visit_call_node(node)
13
+ return super unless node.receiver
14
+
15
+ loc = node.message_loc
16
+ return super unless loc
17
+
18
+ name = node.name.to_s
19
+
20
+ if name.end_with?("!")
21
+ generate_non_bang(node, loc, name)
22
+ elsif KNOWN_BANG_PAIRS.include?(node.name)
23
+ generate_bang(node, loc, name)
24
+ end
25
+
26
+ super
27
+ end
28
+
29
+ private
30
+
31
+ def generate_non_bang(node, loc, name)
32
+ add_mutation(
33
+ offset: loc.start_offset,
34
+ length: loc.length,
35
+ replacement: name.chomp("!"),
36
+ node: node
37
+ )
38
+ end
39
+
40
+ def generate_bang(node, loc, name)
41
+ add_mutation(
42
+ offset: loc.start_offset,
43
+ length: loc.length,
44
+ replacement: "#{name}!",
45
+ node: node
46
+ )
47
+ end
48
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../operator"
4
+
5
+ class Evilution::Mutator::Operator::BitwiseComplement < Evilution::Mutator::Base
6
+ def visit_call_node(node)
7
+ if node.name == :~ && node.receiver && node.arguments.nil?
8
+ loc = node.message_loc
9
+ receiver_loc = node.receiver.location
10
+
11
+ # Remove ~: replace entire ~expr with just the receiver expression
12
+ receiver_source = @file_source[receiver_loc.start_offset, receiver_loc.length]
13
+ add_mutation(
14
+ offset: node.location.start_offset,
15
+ length: node.location.length,
16
+ replacement: receiver_source,
17
+ node: node
18
+ )
19
+
20
+ # Swap ~ with unary minus
21
+ add_mutation(
22
+ offset: loc.start_offset,
23
+ length: loc.length,
24
+ replacement: "-",
25
+ node: node
26
+ )
27
+ end
28
+
29
+ super
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../operator"
4
+
5
+ class Evilution::Mutator::Operator::BitwiseReplacement < Evilution::Mutator::Base
6
+ REPLACEMENTS = {
7
+ :& => %i[| ^],
8
+ :| => %i[& ^],
9
+ :^ => %i[& |]
10
+ }.freeze
11
+
12
+ def visit_call_node(node)
13
+ replacements = REPLACEMENTS[node.name]
14
+ return super unless replacements
15
+
16
+ loc = node.message_loc
17
+ return super unless loc
18
+
19
+ replacements.each do |replacement|
20
+ add_mutation(
21
+ offset: loc.start_offset,
22
+ length: loc.length,
23
+ replacement: replacement.to_s,
24
+ node: node
25
+ )
26
+ end
27
+
28
+ super
29
+ end
30
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../operator"
4
+
5
+ class Evilution::Mutator::Operator::BreakStatement < Evilution::Mutator::Base
6
+ def visit_break_node(node)
7
+ generate_removal(node)
8
+ generate_nil_value(node)
9
+ generate_next_swap(node)
10
+
11
+ super
12
+ end
13
+
14
+ private
15
+
16
+ def generate_removal(node)
17
+ loc = node.location
18
+
19
+ add_mutation(
20
+ offset: loc.start_offset,
21
+ length: loc.length,
22
+ replacement: "nil",
23
+ node: node
24
+ )
25
+ end
26
+
27
+ def generate_nil_value(node)
28
+ return if node.arguments.nil?
29
+
30
+ args_loc = node.arguments.location
31
+
32
+ add_mutation(
33
+ offset: args_loc.start_offset,
34
+ length: args_loc.length,
35
+ replacement: "nil",
36
+ node: node
37
+ )
38
+ end
39
+
40
+ def generate_next_swap(node)
41
+ keyword_loc = node.keyword_loc
42
+
43
+ add_mutation(
44
+ offset: keyword_loc.start_offset,
45
+ length: keyword_loc.length,
46
+ replacement: "next",
47
+ node: node
48
+ )
49
+ end
50
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../operator"
4
+
5
+ class Evilution::Mutator::Operator::ClassVariableWrite < Evilution::Mutator::Base
6
+ def visit_class_variable_write_node(node)
7
+ # Mutation 1: remove assignment, keep only the value expression
8
+ add_mutation(
9
+ offset: node.location.start_offset,
10
+ length: node.location.length,
11
+ replacement: node.value.slice,
12
+ node: node
13
+ )
14
+
15
+ # Mutation 2: replace value with nil
16
+ add_mutation(
17
+ offset: node.value.location.start_offset,
18
+ length: node.value.location.length,
19
+ replacement: "nil",
20
+ node: node
21
+ )
22
+
23
+ super
24
+ end
25
+ end
@@ -17,7 +17,31 @@ class Evilution::Mutator::Operator::CollectionReplacement < Evilution::Mutator::
17
17
  any?: [:all?],
18
18
  all?: [:any?],
19
19
  count: [:length],
20
- length: [:count]
20
+ length: [:count],
21
+ pop: [:shift],
22
+ shift: [:pop],
23
+ push: [:unshift],
24
+ unshift: [:push],
25
+ each_key: [:each_value],
26
+ each_value: [:each_key],
27
+ assoc: [:rassoc],
28
+ rassoc: [:assoc],
29
+ grep: [:grep_v],
30
+ grep_v: [:grep],
31
+ take: [:drop],
32
+ drop: [:take],
33
+ min: [:max],
34
+ max: [:min],
35
+ min_by: [:max_by],
36
+ max_by: [:min_by],
37
+ compact: [:flatten],
38
+ flatten: [:compact],
39
+ zip: [:product],
40
+ product: [:zip],
41
+ first: [:last],
42
+ last: [:first],
43
+ keys: [:values],
44
+ values: [:keys]
21
45
  }.freeze
22
46
 
23
47
  def visit_call_node(node)
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../operator"
4
+
5
+ class Evilution::Mutator::Operator::EnsureRemoval < Evilution::Mutator::Base
6
+ def visit_ensure_node(node)
7
+ remove_start = line_start_after_newline(node.ensure_keyword_loc.start_offset)
8
+ remove_end = line_start_after_newline(node.end_keyword_loc.start_offset)
9
+
10
+ add_mutation(
11
+ offset: remove_start,
12
+ length: remove_end - remove_start,
13
+ replacement: "",
14
+ node: node
15
+ )
16
+
17
+ super
18
+ end
19
+
20
+ private
21
+
22
+ def line_start_after_newline(offset)
23
+ pos = offset
24
+ pos -= 1 while pos.positive? && @file_source[pos - 1] != "\n"
25
+ pos
26
+ end
27
+ end