evilution 1.3.0 → 1.5.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.
- checksums.yaml +4 -4
- data/.beads/interactions.jsonl +132 -0
- data/.rubocop_todo.yml +5 -0
- data/CHANGELOG.md +127 -0
- data/README.md +136 -38
- data/docs/architecture.md +135 -11
- data/docs/ast_pattern_syntax.md +50 -5
- data/docs/isolation.md +121 -2
- data/docs/migration-from-mutant.md +1 -1
- data/lib/evilution/ast/aasm_declaration.rb +133 -0
- data/lib/evilution/ast/callback_declaration.rb +91 -0
- data/lib/evilution/ast/class_body.rb +101 -0
- data/lib/evilution/ast/included_block.rb +41 -0
- data/lib/evilution/ast/literal_callable.rb +21 -0
- data/lib/evilution/ast/never_nil.rb +36 -0
- data/lib/evilution/ast/parser.rb +187 -21
- data/lib/evilution/ast/pattern/method_name.rb +63 -0
- data/lib/evilution/ast/pattern/parser.rb +14 -15
- data/lib/evilution/ast/regexp_pattern.rb +104 -0
- data/lib/evilution/ast/round_half_mode.rb +38 -0
- data/lib/evilution/ast/scope_declaration.rb +45 -0
- data/lib/evilution/ast/source_surgeon.rb +2 -3
- data/lib/evilution/ast/syntax_check.rb +56 -0
- data/lib/evilution/ast/uncovered_code.rb +88 -0
- data/lib/evilution/ast/value_object_definition.rb +25 -0
- data/lib/evilution/baseline/failure_formatter.rb +27 -0
- data/lib/evilution/baseline/report.rb +66 -0
- data/lib/evilution/baseline/spec_failure.rb +38 -0
- data/lib/evilution/baseline.rb +67 -30
- data/lib/evilution/cli/exit_guard.rb +26 -4
- data/lib/evilution/cli/parser/file_args.rb +2 -1
- data/lib/evilution/cli/parser/options_builder.rb +1 -1
- data/lib/evilution/cli.rb +3 -2
- data/lib/evilution/config/validators/spec_mappings.rb +2 -1
- data/lib/evilution/config.rb +3 -2
- data/lib/evilution/equivalent/detector.rb +3 -1
- data/lib/evilution/equivalent/heuristic/guarded_index_fetch/condition.rb +66 -0
- data/lib/evilution/equivalent/heuristic/guarded_index_fetch/disturbance.rb +54 -0
- data/lib/evilution/equivalent/heuristic/guarded_index_fetch/early_exit.rb +73 -0
- data/lib/evilution/equivalent/heuristic/guarded_index_fetch/guard.rb +36 -0
- data/lib/evilution/equivalent/heuristic/guarded_index_fetch/index_read.rb +82 -0
- data/lib/evilution/equivalent/heuristic/guarded_index_fetch/node_path.rb +43 -0
- data/lib/evilution/equivalent/heuristic/guarded_index_fetch.rb +65 -0
- data/lib/evilution/example_filter.rb +45 -0
- data/lib/evilution/hooks/registry.rb +2 -1
- data/lib/evilution/integration/base.rb +11 -1
- data/lib/evilution/integration/known_failures.rb +26 -0
- data/lib/evilution/integration/loading/body_call_neutralizer.rb +177 -13
- data/lib/evilution/integration/loading/callback_redeclaration.rb +186 -0
- data/lib/evilution/integration/loading/concern_redeclaration.rb +80 -0
- data/lib/evilution/integration/loading/concern_state_cleaner.rb +22 -15
- data/lib/evilution/integration/loading/mutation_applier.rb +44 -1
- data/lib/evilution/integration/loading/own_methods.rb +87 -0
- data/lib/evilution/integration/loading/redefinition_recovery.rb +2 -3
- data/lib/evilution/integration/loading/source_evaluator.rb +9 -2
- data/lib/evilution/integration/loading/superseded_modules.rb +103 -0
- data/lib/evilution/integration/loading/syntax_validator.rb +2 -2
- data/lib/evilution/integration/loading/test_class_cache.rb +46 -0
- data/lib/evilution/integration/minitest/test_ids.rb +21 -0
- data/lib/evilution/integration/minitest.rb +45 -6
- data/lib/evilution/integration/rspec/baseline_runner.rb +47 -3
- data/lib/evilution/integration/rspec/example_ids.rb +35 -0
- data/lib/evilution/integration/rspec/result_builder.rb +6 -1
- data/lib/evilution/integration/rspec/state_guard/anonymous_example_group_examples.rb +36 -0
- data/lib/evilution/integration/rspec/state_guard.rb +4 -1
- data/lib/evilution/integration/rspec.rb +22 -4
- data/lib/evilution/integration/test_unit/result_builder.rb +6 -0
- data/lib/evilution/integration/test_unit/test_ids.rb +18 -0
- data/lib/evilution/integration/test_unit.rb +37 -9
- data/lib/evilution/isolation/fork.rb +84 -11
- data/lib/evilution/isolation/in_process.rb +100 -8
- data/lib/evilution/mcp/info_tool/status_glossary.rb +15 -4
- data/lib/evilution/mcp/info_tool.rb +3 -2
- data/lib/evilution/mcp/mutate_tool/progress_streamer.rb +2 -1
- data/lib/evilution/memory/leak_check.rb +27 -3
- data/lib/evilution/mutation.rb +21 -9
- data/lib/evilution/mutator/base.rb +69 -5
- data/lib/evilution/mutator/constant_value_subjects.rb +13 -0
- data/lib/evilution/mutator/operator/alias_removal.rb +56 -0
- data/lib/evilution/mutator/operator/argument_nil_substitution.rb +2 -2
- data/lib/evilution/mutator/operator/argument_order_permutation.rb +101 -0
- data/lib/evilution/mutator/operator/argument_removal.rb +10 -2
- data/lib/evilution/mutator/operator/array_literal.rb +29 -0
- data/lib/evilution/mutator/operator/attr_accessor_swap.rb +68 -0
- data/lib/evilution/mutator/operator/boolean_literal_replacement.rb +2 -0
- data/lib/evilution/mutator/operator/call_to_nil.rb +12 -3
- data/lib/evilution/mutator/operator/comparison_operand_swap.rb +54 -0
- data/lib/evilution/mutator/operator/complex_literal.rb +39 -0
- data/lib/evilution/mutator/operator/compound_assignment.rb +47 -0
- data/lib/evilution/mutator/operator/constant_namespace_strip.rb +57 -0
- data/lib/evilution/mutator/operator/constant_read_to_nil.rb +58 -0
- data/lib/evilution/mutator/operator/constant_write_to_nil.rb +32 -0
- data/lib/evilution/mutator/operator/data_struct_member.rb +82 -0
- data/lib/evilution/mutator/operator/exception_swallow.rb +84 -0
- data/lib/evilution/mutator/operator/explicit_super_mutation.rb +2 -2
- data/lib/evilution/mutator/operator/float_literal.rb +54 -19
- data/lib/evilution/mutator/operator/format_specifier_swap.rb +100 -0
- data/lib/evilution/mutator/operator/forwarded_argument_drop.rb +142 -0
- data/lib/evilution/mutator/operator/freeze_removal.rb +86 -0
- data/lib/evilution/mutator/operator/hash_literal.rb +51 -0
- data/lib/evilution/mutator/operator/index_range_to_drop.rb +67 -0
- data/lib/evilution/mutator/operator/index_receiver_to_self.rb +20 -0
- data/lib/evilution/mutator/operator/index_to_key_predicate.rb +58 -0
- data/lib/evilution/mutator/operator/index_write_to_value.rb +37 -0
- data/lib/evilution/mutator/operator/integer_division_to_fdiv.rb +62 -0
- data/lib/evilution/mutator/operator/integer_literal.rb +39 -9
- data/lib/evilution/mutator/operator/keyword_argument.rb +25 -2
- data/lib/evilution/mutator/operator/keyword_value_swap.rb +75 -0
- data/lib/evilution/mutator/operator/memoization_break.rb +18 -0
- data/lib/evilution/mutator/operator/mixin_removal.rb +18 -55
- data/lib/evilution/mutator/operator/nil_replacement.rb +2 -0
- data/lib/evilution/mutator/operator/no_matching_pattern_else.rb +47 -0
- data/lib/evilution/mutator/operator/numbered_parameter_swap.rb +78 -0
- data/lib/evilution/mutator/operator/off_by_one_boundary.rb +68 -0
- data/lib/evilution/mutator/operator/operator_write_receiver_promotion.rb +38 -0
- data/lib/evilution/mutator/operator/optional_parameter_to_required.rb +14 -3
- data/lib/evilution/mutator/operator/pattern_matching_array.rb +12 -1
- data/lib/evilution/mutator/operator/pattern_wildcard_widening.rb +117 -0
- data/lib/evilution/mutator/operator/pin_operator_removal.rb +48 -0
- data/lib/evilution/mutator/operator/range_replacement.rb +2 -0
- data/lib/evilution/mutator/operator/rational_literal.rb +40 -0
- data/lib/evilution/mutator/operator/reduce_to_sum.rb +1 -1
- data/lib/evilution/mutator/operator/regex_simplification.rb +53 -132
- data/lib/evilution/mutator/operator/regexp_alternation_branch_deletion.rb +54 -0
- data/lib/evilution/mutator/operator/regexp_anchor_promotion.rb +43 -0
- data/lib/evilution/mutator/operator/regexp_capture_to_passive.rb +61 -0
- data/lib/evilution/mutator/operator/regexp_character_type_complement.rb +54 -0
- data/lib/evilution/mutator/operator/regexp_named_group_rename.rb +106 -0
- data/lib/evilution/mutator/operator/regexp_option_removal.rb +51 -0
- data/lib/evilution/mutator/operator/regexp_quantifier_minimum_swap.rb +45 -0
- data/lib/evilution/mutator/operator/rescue_class_widening.rb +54 -0
- data/lib/evilution/mutator/operator/rescue_else_concatenation.rb +62 -0
- data/lib/evilution/mutator/operator/rescue_handler_concatenation.rb +69 -0
- data/lib/evilution/mutator/operator/rescue_handler_promotion.rb +65 -0
- data/lib/evilution/mutator/operator/return_keyword_removal.rb +79 -0
- data/lib/evilution/mutator/operator/rightward_assignment.rb +46 -0
- data/lib/evilution/mutator/operator/round_half_mode_swap.rb +36 -0
- data/lib/evilution/mutator/operator/safe_navigation_insertion.rb +57 -0
- data/lib/evilution/mutator/operator/safe_navigation_removal.rb +2 -24
- data/lib/evilution/mutator/operator/send_mutation.rb +2 -0
- data/lib/evilution/mutator/operator/short_circuit_to_eager.rb +34 -0
- data/lib/evilution/mutator/operator/sleep_to_zero.rb +43 -0
- data/lib/evilution/mutator/operator/splat_operator.rb +41 -24
- data/lib/evilution/mutator/operator/statement_reorder.rb +153 -0
- data/lib/evilution/mutator/operator/string_literal.rb +29 -0
- data/lib/evilution/mutator/operator/superclass_removal.rb +14 -40
- data/lib/evilution/mutator/operator/symbol_literal.rb +47 -2
- data/lib/evilution/mutator/operator/to_i_to_integer.rb +1 -1
- data/lib/evilution/mutator/operator/variable_read_to_nil.rb +113 -0
- data/lib/evilution/mutator/operator/visibility_removal.rb +74 -0
- data/lib/evilution/mutator/primitives.rb +24 -2
- data/lib/evilution/mutator/receiver_positions.rb +79 -0
- data/lib/evilution/mutator/registry.rb +58 -2
- data/lib/evilution/mutator/rescue_handlers.rb +81 -0
- data/lib/evilution/parallel/work_queue/channel/frame.rb +6 -0
- data/lib/evilution/parallel/work_queue/channel.rb +58 -5
- data/lib/evilution/parallel/work_queue/dispatcher/reply.rb +41 -0
- data/lib/evilution/parallel/work_queue/dispatcher.rb +14 -1
- data/lib/evilution/parallel/work_queue/worker/loop.rb +5 -0
- data/lib/evilution/parallel/work_queue/worker.rb +13 -4
- data/lib/evilution/parallel/work_queue.rb +15 -9
- data/lib/evilution/process_supervisor.rb +3 -2
- data/lib/evilution/reporter/cli/line_formatters/baseline_neutralized_notice.rb +55 -0
- data/lib/evilution/reporter/cli/metrics_block.rb +2 -0
- data/lib/evilution/reporter/json/baseline.rb +15 -0
- data/lib/evilution/reporter/json.rb +7 -2
- data/lib/evilution/result/baseline_neutralization.rb +10 -0
- data/lib/evilution/result/mutation_result.rb +9 -1
- data/lib/evilution/result/summary.rb +42 -2
- data/lib/evilution/runner/baseline_runner.rb +9 -4
- data/lib/evilution/runner/canary.rb +2 -57
- data/lib/evilution/runner/canary_failure_message.rb +101 -0
- data/lib/evilution/runner/mutation_executor/neutralizer/baseline_failed.rb +22 -8
- data/lib/evilution/runner/mutation_executor/result_cache.rb +3 -0
- data/lib/evilution/runner/mutation_executor/result_packer.rb +4 -2
- data/lib/evilution/runner/mutation_executor/strategy/parallel.rb +10 -0
- data/lib/evilution/runner/mutation_executor.rb +1 -1
- data/lib/evilution/runner/mutation_planner.rb +11 -2
- data/lib/evilution/runner/report_publisher.rb +3 -2
- data/lib/evilution/runner/subject_pipeline/target.rb +86 -0
- data/lib/evilution/runner/subject_pipeline.rb +57 -43
- data/lib/evilution/runner.rb +5 -2
- data/lib/evilution/subject.rb +25 -2
- data/lib/evilution/version.rb +1 -1
- data/lib/evilution.rb +90 -0
- data/script/memory_check +62 -16
- metadata +113 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5f47321127e4fb0090ec208b430b7c14da3ca3b9b16e883f076b9707c787744a
|
|
4
|
+
data.tar.gz: 78951f4619c9f8911e94272734b74606df0633131741201c7056c3a3dd1977dd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 57ec220fad323e96e3bb3a4cb75b58dac381018d1b22334b4eb27b58c43db6219f732df58387eb41328332c90fb82d023829ac7df5632748223ada9aac048e26
|
|
7
|
+
data.tar.gz: 5960714fac3df7b1beb118043e899961a4069dad090887e36727cda63d0b81eda52661ec9cb8745df7def51481e39aad9340673c9ec255fa59c1d32f506be3a9
|
data/.beads/interactions.jsonl
CHANGED
|
@@ -527,3 +527,135 @@
|
|
|
527
527
|
{"id":"int-356e6b7a","kind":"field_change","created_at":"2026-09-28T02:59:40.442681333Z","actor":"Denis Kiselev","issue_id":"EV-ae7u","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
528
528
|
{"id":"int-a15bfc29","kind":"field_change","created_at":"2026-09-28T03:35:22.865923943Z","actor":"Denis Kiselev","issue_id":"EV-ziy0","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
529
529
|
{"id":"int-c4c3b0ee","kind":"field_change","created_at":"2026-09-28T05:00:30.703915081Z","actor":"Denis Kiselev","issue_id":"EV-qj40","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
530
|
+
{"id":"int-6b262792","kind":"field_change","created_at":"2026-10-02T07:16:11.010581665Z","actor":"Denis Kiselev","issue_id":"EV-in17.1","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Endless defs already covered by MethodBodyReplacement + MethodBodyToRaise; regression specs added instead of a new operator"}}
|
|
531
|
+
{"id":"int-20d9d25f","kind":"field_change","created_at":"2026-10-02T08:01:02.739469298Z","actor":"Denis Kiselev","issue_id":"EV-in17.2","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: DataStructMember operator (drop + adjacent swap), ArgumentRemoval skips member lists"}}
|
|
532
|
+
{"id":"int-b844f06a","kind":"field_change","created_at":"2026-10-02T08:55:07.467612422Z","actor":"Denis Kiselev","issue_id":"EV-in17.3","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: PinOperatorRemoval operator"}}
|
|
533
|
+
{"id":"int-813d5c0f","kind":"field_change","created_at":"2026-10-02T11:26:04.587491311Z","actor":"Denis Kiselev","issue_id":"EV-in17.4","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: RightwardAssignment operator"}}
|
|
534
|
+
{"id":"int-d795672b","kind":"field_change","created_at":"2026-10-02T11:53:57.423520962Z","actor":"Denis Kiselev","issue_id":"EV-in17.5","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: NumberedParameterSwap operator (re-scoped from ImplicitBlockParam)"}}
|
|
535
|
+
{"id":"int-3548d60e","kind":"field_change","created_at":"2026-10-02T12:26:03.455515136Z","actor":"Denis Kiselev","issue_id":"EV-in17.6","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: ForwardedArgumentDrop operator (re-scoped from ArgumentForwarding)"}}
|
|
536
|
+
{"id":"int-97a35172","kind":"field_change","created_at":"2026-10-02T15:11:30.435617553Z","actor":"Denis Kiselev","issue_id":"EV-in17.7","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: PatternWildcardWidening operator"}}
|
|
537
|
+
{"id":"int-80bc2182","kind":"field_change","created_at":"2026-10-02T15:36:15.078727262Z","actor":"Denis Kiselev","issue_id":"EV-in17.8","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: NoMatchingPatternElse operator (else insertion; removal already in case_in)"}}
|
|
538
|
+
{"id":"int-074659be","kind":"field_change","created_at":"2026-10-02T15:51:58.478518221Z","actor":"Denis Kiselev","issue_id":"EV-gw8w","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Merged via PR #1693: constant and delimiters preserved in array/find pattern mutations"}}
|
|
539
|
+
{"id":"int-cf36f2ea","kind":"field_change","created_at":"2026-10-03T02:50:23.790936748Z","actor":"Denis Kiselev","issue_id":"EV-p7l7","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: keyword_argument keeps an anonymous ** the body uses"}}
|
|
540
|
+
{"id":"int-c78705e8","kind":"field_change","created_at":"2026-10-03T03:34:56.023536028Z","actor":"Denis Kiselev","issue_id":"EV-wuwz","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: delimiters helper replaces safe navigation in PatternMatchingArray"}}
|
|
541
|
+
{"id":"int-d742aa8a","kind":"field_change","created_at":"2026-10-03T04:11:14.742547553Z","actor":"Denis Kiselev","issue_id":"EV-a508","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: diff-lcs capped < 2, canary names gem activation conflicts, README defaults MCP to bundle exec"}}
|
|
542
|
+
{"id":"int-5015cfd3","kind":"field_change","created_at":"2026-10-03T05:16:17.707344911Z","actor":"Denis Kiselev","issue_id":"EV-70lx.1","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: ArgumentOrderPermutation operator with order-free skip list"}}
|
|
543
|
+
{"id":"int-2b6321e3","kind":"field_change","created_at":"2026-10-03T05:46:41.353299041Z","actor":"Denis Kiselev","issue_id":"EV-70lx.2","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: KeywordValueSwap operator"}}
|
|
544
|
+
{"id":"int-ac550ceb","kind":"field_change","created_at":"2026-10-03T06:08:06.145495467Z","actor":"Denis Kiselev","issue_id":"EV-70lx.3","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: covered by block_body_promotion; regression specs added"}}
|
|
545
|
+
{"id":"int-cb212c6d","kind":"field_change","created_at":"2026-10-03T11:26:40.545576039Z","actor":"Denis Kiselev","issue_id":"EV-70lx.4","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: covered by method_call_removal; regression specs added"}}
|
|
546
|
+
{"id":"int-db265ed7","kind":"field_change","created_at":"2026-10-03T12:19:57.106972968Z","actor":"Denis Kiselev","issue_id":"EV-70lx.5","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: ComparisonOperandSwap for <=>; between?/clamp covered by argument_order_permutation"}}
|
|
547
|
+
{"id":"int-a75daaf2","kind":"field_change","created_at":"2026-10-03T12:54:36.679091996Z","actor":"Denis Kiselev","issue_id":"EV-70lx.6","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: round/truncate swaps in send_mutation, new integer_division_to_fdiv; half: mode split to EV-qt7f"}}
|
|
548
|
+
{"id":"int-7f131376","kind":"field_change","created_at":"2026-10-03T15:01:33.349965452Z","actor":"Denis Kiselev","issue_id":"EV-70lx.7","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: OffByOneBoundary operator"}}
|
|
549
|
+
{"id":"int-ba54b5af","kind":"field_change","created_at":"2026-10-03T15:46:31.592677693Z","actor":"Denis Kiselev","issue_id":"EV-70lx.8","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: FormatSpecifierSwap operator; argument_order_permutation skips format methods"}}
|
|
550
|
+
{"id":"int-d2496545","kind":"field_change","created_at":"2026-10-03T16:55:37.885963941Z","actor":"Denis Kiselev","issue_id":"EV-70lx.9","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: ExceptionSwallow in strict profile; ignore_patterns grammar split to EV-a6ey"}}
|
|
551
|
+
{"id":"int-dc691f35","kind":"field_change","created_at":"2026-10-04T02:07:48.713969725Z","actor":"Denis Kiselev","issue_id":"EV-70lx.10","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: StatementReorder in strict profile"}}
|
|
552
|
+
{"id":"int-e47209dc","kind":"field_change","created_at":"2026-10-04T02:22:56.861285181Z","actor":"Denis Kiselev","issue_id":"EV-70lx.11","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Won't do: measured on evilution's lib/ (409 files, true -> false, whole spec file per mutant): 384 survived, 18 killed, of which 17 were self-run artefacts (a no-op control mutant was killed on the same 17 files) and 1 real (status_glossary_spec asserts frozen strings). One real signal in 409 does not justify a file-level emission path."}}
|
|
553
|
+
{"id":"int-6259aee3","kind":"field_change","created_at":"2026-10-04T03:01:50.164062208Z","actor":"Denis Kiselev","issue_id":"EV-70lx.12","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: AliasRemoval operator; shared attribution refactor split to EV-u9rq"}}
|
|
554
|
+
{"id":"int-55084cba","kind":"field_change","created_at":"2026-10-04T04:36:27.295973647Z","actor":"Denis Kiselev","issue_id":"EV-ib1c.1","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Decision: regexp_parser as runtime dependency, edit by offsets, compile-check every mutant. Evidence posted on #1524. Foundation work split to EV-ib1c.9."}}
|
|
555
|
+
{"id":"int-fc063d48","kind":"field_change","created_at":"2026-10-04T11:06:20.183354321Z","actor":"Denis Kiselev","issue_id":"EV-ib1c.9","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: regexp_parser runtime dependency and Evilution::AST::RegexpPattern helper"}}
|
|
556
|
+
{"id":"int-135695f4","kind":"field_change","created_at":"2026-10-04T11:37:04.708217274Z","actor":"Denis Kiselev","issue_id":"EV-spob","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: regex_simplification moved onto RegexpPattern"}}
|
|
557
|
+
{"id":"int-474f5d9c","kind":"field_change","created_at":"2026-10-04T12:01:54.857943644Z","actor":"Denis Kiselev","issue_id":"EV-ib1c.2","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: RegexpCharacterTypeComplement operator"}}
|
|
558
|
+
{"id":"int-8f7efc39","kind":"field_change","created_at":"2026-10-04T12:20:59.73456011Z","actor":"Denis Kiselev","issue_id":"EV-ib1c.3","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: RegexpAnchorPromotion operator"}}
|
|
559
|
+
{"id":"int-e092039b","kind":"field_change","created_at":"2026-10-04T12:44:08.732168047Z","actor":"Denis Kiselev","issue_id":"EV-ib1c.4","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: RegexpAlternationBranchDeletion operator, capped at 10 branches"}}
|
|
560
|
+
{"id":"int-89a03141","kind":"field_change","created_at":"2026-10-04T13:55:56.95130631Z","actor":"Denis Kiselev","issue_id":"EV-ib1c.5","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: RegexpQuantifierMinimumSwap operator"}}
|
|
561
|
+
{"id":"int-90e07229","kind":"field_change","created_at":"2026-10-04T14:26:18.913357911Z","actor":"Denis Kiselev","issue_id":"EV-ib1c.6","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: RegexpCaptureToPassive operator"}}
|
|
562
|
+
{"id":"int-c6376ed4","kind":"field_change","created_at":"2026-10-04T15:23:59.584161061Z","actor":"Denis Kiselev","issue_id":"EV-ib1c.7","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: RegexpNamedGroupRename operator"}}
|
|
563
|
+
{"id":"int-f45f11c3","kind":"field_change","created_at":"2026-10-04T15:51:06.656516074Z","actor":"Denis Kiselev","issue_id":"EV-ib1c.8","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: RegexpOptionRemoval operator"}}
|
|
564
|
+
{"id":"int-f74a65f3","kind":"field_change","created_at":"2026-10-05T01:59:49.116216501Z","actor":"Denis Kiselev","issue_id":"EV-waxv.1","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: RescueHandlerPromotion operator"}}
|
|
565
|
+
{"id":"int-086b8b8f","kind":"field_change","created_at":"2026-10-05T02:25:10.675826812Z","actor":"Denis Kiselev","issue_id":"EV-waxv.2","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: RescueHandlerConcatenation operator (keeps the rescue)"}}
|
|
566
|
+
{"id":"int-8504c32b","kind":"field_change","created_at":"2026-10-05T03:09:46.042410063Z","actor":"Denis Kiselev","issue_id":"EV-waxv.3","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: RescueElseConcatenation operator (broad rescues only)"}}
|
|
567
|
+
{"id":"int-1a680abd","kind":"field_change","created_at":"2026-10-05T03:45:51.492632284Z","actor":"Denis Kiselev","issue_id":"EV-waxv.4","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: ReturnKeywordRemoval operator"}}
|
|
568
|
+
{"id":"int-6be631a1","kind":"field_change","created_at":"2026-10-05T03:53:41.798959331Z","actor":"Denis Kiselev","issue_id":"EV-waxv.5","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged: covered by existing operators; cross-operator regression spec added"}}
|
|
569
|
+
{"id":"int-e7ea4f25","kind":"field_change","created_at":"2026-10-05T04:35:10.212440677Z","actor":"Denis Kiselev","issue_id":"EV-a6ey","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
570
|
+
{"id":"int-a9f620b2","kind":"field_change","created_at":"2026-10-05T05:20:43.639066857Z","actor":"Denis Kiselev","issue_id":"EV-8lcr","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
571
|
+
{"id":"int-3b99b201","kind":"field_change","created_at":"2026-10-05T06:16:47.160706383Z","actor":"Denis Kiselev","issue_id":"EV-8gzw","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
572
|
+
{"id":"int-72b4a08c","kind":"field_change","created_at":"2026-10-05T09:41:56.709809764Z","actor":"Denis Kiselev","issue_id":"EV-h3ir","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
573
|
+
{"id":"int-49f7ebbb","kind":"field_change","created_at":"2026-10-05T11:53:56.598210615Z","actor":"Denis Kiselev","issue_id":"EV-hoki","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
574
|
+
{"id":"int-34d1379a","kind":"field_change","created_at":"2026-10-05T14:22:47.087299042Z","actor":"Denis Kiselev","issue_id":"EV-56hj","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
575
|
+
{"id":"int-6e4bcbc4","kind":"field_change","created_at":"2026-10-06T05:17:58.928359175Z","actor":"Denis Kiselev","issue_id":"EV-mrfr","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
576
|
+
{"id":"int-f8e7bd2e","kind":"field_change","created_at":"2026-10-06T06:34:59.884610002Z","actor":"Denis Kiselev","issue_id":"EV-8ngq","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
577
|
+
{"id":"int-b1698bc2","kind":"field_change","created_at":"2026-10-06T07:20:23.824889017Z","actor":"Denis Kiselev","issue_id":"EV-vqw1","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
578
|
+
{"id":"int-a6a57a8d","kind":"field_change","created_at":"2026-10-06T08:34:34.810437348Z","actor":"Denis Kiselev","issue_id":"EV-z6y1","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
579
|
+
{"id":"int-a7b8436b","kind":"field_change","created_at":"2026-10-06T11:37:21.379002004Z","actor":"Denis Kiselev","issue_id":"EV-pvau","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
580
|
+
{"id":"int-316ed97b","kind":"field_change","created_at":"2026-10-06T11:37:21.692481188Z","actor":"Denis Kiselev","issue_id":"EV-e380","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
581
|
+
{"id":"int-deca8540","kind":"field_change","created_at":"2026-10-06T15:23:09.145528907Z","actor":"Denis Kiselev","issue_id":"EV-z4m2","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
582
|
+
{"id":"int-36097126","kind":"field_change","created_at":"2026-10-06T14:41:05.739141414Z","actor":"Denis Kiselev","issue_id":"EV-84gq","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
583
|
+
{"id":"int-61e3037a","kind":"field_change","created_at":"2026-10-06T17:43:49.128023228Z","actor":"Denis Kiselev","issue_id":"EV-feu9","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
584
|
+
{"id":"int-6c0d6f1f","kind":"field_change","created_at":"2026-10-07T02:23:54.251384914Z","actor":"Denis Kiselev","issue_id":"EV-lgk8","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
585
|
+
{"id":"int-08fd2997","kind":"field_change","created_at":"2026-10-07T10:43:53.874903719Z","actor":"Denis Kiselev","issue_id":"EV-rm8v","extra":{"field":"status","new_value":"in_progress","old_value":"open"}}
|
|
586
|
+
{"id":"int-35f8d0ca","kind":"field_change","created_at":"2026-10-07T12:55:20.009979823Z","actor":"Denis Kiselev","issue_id":"EV-rm8v","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
587
|
+
{"id":"int-fa03cb81","kind":"field_change","created_at":"2026-10-07T13:07:12.57941145Z","actor":"Denis Kiselev","issue_id":"EV-wk0w","extra":{"field":"status","new_value":"in_progress","old_value":"open"}}
|
|
588
|
+
{"id":"int-e6956eaa","kind":"field_change","created_at":"2026-10-07T16:15:46.858274377Z","actor":"Denis Kiselev","issue_id":"EV-wk0w","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
589
|
+
{"id":"int-995cf63f","kind":"field_change","created_at":"2026-10-07T16:18:39.396678188Z","actor":"Denis Kiselev","issue_id":"EV-s06e","extra":{"field":"status","new_value":"in_progress","old_value":"open"}}
|
|
590
|
+
{"id":"int-f1fa1ab6","kind":"field_change","created_at":"2026-10-07T17:32:04.485879419Z","actor":"Denis Kiselev","issue_id":"EV-s06e","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
591
|
+
{"id":"int-5c22155e","kind":"field_change","created_at":"2026-10-07T17:32:39.183171753Z","actor":"Denis Kiselev","issue_id":"EV-0s4b","extra":{"field":"status","new_value":"in_progress","old_value":"open"}}
|
|
592
|
+
{"id":"int-a14e860c","kind":"field_change","created_at":"2026-10-08T08:20:24.543215213Z","actor":"Denis Kiselev","issue_id":"EV-u9rq","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged in PR #1784"}}
|
|
593
|
+
{"id":"int-bf8b9522","kind":"field_change","created_at":"2026-10-08T08:52:17.811825439Z","actor":"Denis Kiselev","issue_id":"EV-qt7f","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged"}}
|
|
594
|
+
{"id":"int-14d78256","kind":"field_change","created_at":"2026-10-08T10:27:35.588313336Z","actor":"Denis Kiselev","issue_id":"EV-5pr3","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged in PR #1786"}}
|
|
595
|
+
{"id":"int-d497edf6","kind":"field_change","created_at":"2026-10-08T10:49:38.951908776Z","actor":"Denis Kiselev","issue_id":"EV-d6nh","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged in PR #1782"}}
|
|
596
|
+
{"id":"int-a89400aa","kind":"field_change","created_at":"2026-10-08T10:49:39.784886076Z","actor":"Denis Kiselev","issue_id":"EV-16yn","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged in PR #1787"}}
|
|
597
|
+
{"id":"int-063a91c3","kind":"field_change","created_at":"2026-10-08T14:30:58.962802382Z","actor":"Denis Kiselev","issue_id":"EV-44o7.1","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged in PR #1788"}}
|
|
598
|
+
{"id":"int-ce1db8e8","kind":"field_change","created_at":"2026-10-08T15:22:23.92716218Z","actor":"Denis Kiselev","issue_id":"EV-44o7.2","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged in PR #1789"}}
|
|
599
|
+
{"id":"int-f2edc8c0","kind":"field_change","created_at":"2026-10-08T15:50:38.509740051Z","actor":"Denis Kiselev","issue_id":"EV-44o7.3","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged"}}
|
|
600
|
+
{"id":"int-4532acdd","kind":"field_change","created_at":"2026-10-08T16:05:32.697825805Z","actor":"Denis Kiselev","issue_id":"EV-44o7.4","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged"}}
|
|
601
|
+
{"id":"int-3faeb9f1","kind":"field_change","created_at":"2026-10-08T16:24:22.591450952Z","actor":"Denis Kiselev","issue_id":"EV-44o7.5","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged"}}
|
|
602
|
+
{"id":"int-19e1698e","kind":"field_change","created_at":"2026-10-09T01:14:41.828480992Z","actor":"Denis Kiselev","issue_id":"EV-44o7.6","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged in PR #1793"}}
|
|
603
|
+
{"id":"int-039ad18d","kind":"field_change","created_at":"2026-10-09T01:36:15.256754769Z","actor":"Denis Kiselev","issue_id":"EV-44o7.7","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged"}}
|
|
604
|
+
{"id":"int-51ef3062","kind":"field_change","created_at":"2026-10-09T04:48:31.278559024Z","actor":"Denis Kiselev","issue_id":"EV-44o7.8","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged"}}
|
|
605
|
+
{"id":"int-ed3b7e74","kind":"field_change","created_at":"2026-10-09T05:54:31.526895048Z","actor":"Denis Kiselev","issue_id":"EV-44o7.9","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged"}}
|
|
606
|
+
{"id":"int-9874144e","kind":"field_change","created_at":"2026-10-09T06:26:03.337252742Z","actor":"Denis Kiselev","issue_id":"EV-44o7.10","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged in PR #1797"}}
|
|
607
|
+
{"id":"int-aeb0703e","kind":"field_change","created_at":"2026-10-09T15:13:18.21752595Z","actor":"Denis Kiselev","issue_id":"EV-44o7.11","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged in PR #1798"}}
|
|
608
|
+
{"id":"int-332bf695","kind":"field_change","created_at":"2026-10-09T15:32:32.76074471Z","actor":"Denis Kiselev","issue_id":"EV-2mcz.1","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged"}}
|
|
609
|
+
{"id":"int-8335751c","kind":"field_change","created_at":"2026-10-09T16:48:20.829398546Z","actor":"Denis Kiselev","issue_id":"EV-2mcz.2","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged"}}
|
|
610
|
+
{"id":"int-077fb2db","kind":"field_change","created_at":"2026-10-09T17:25:15.331671484Z","actor":"Denis Kiselev","issue_id":"EV-2mcz.3","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged in PR #1801"}}
|
|
611
|
+
{"id":"int-6e37d720","kind":"field_change","created_at":"2026-10-10T02:39:49.114281637Z","actor":"Denis Kiselev","issue_id":"EV-2mcz.4","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged in PR #1802"}}
|
|
612
|
+
{"id":"int-05630371","kind":"field_change","created_at":"2026-10-10T02:42:37.400418751Z","actor":"Denis Kiselev","issue_id":"EV-2mcz.5","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Not built: subsumed by conditional_negation (see notes)"}}
|
|
613
|
+
{"id":"int-ec1c86a5","kind":"field_change","created_at":"2026-10-10T03:11:41.589463361Z","actor":"Denis Kiselev","issue_id":"EV-2mcz.6","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Not built: covered by argument_removal and argument_list_removal (see notes)"}}
|
|
614
|
+
{"id":"int-8e02e146","kind":"field_change","created_at":"2026-10-10T03:43:33.223836416Z","actor":"Denis Kiselev","issue_id":"EV-2mcz.7","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged in PR #1803"}}
|
|
615
|
+
{"id":"int-0e6a6164","kind":"field_change","created_at":"2026-10-10T04:15:43.08219523Z","actor":"Denis Kiselev","issue_id":"EV-2mcz.8","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged in PR #1804"}}
|
|
616
|
+
{"id":"int-65f057dd","kind":"field_change","created_at":"2026-10-10T04:39:55.8764372Z","actor":"Denis Kiselev","issue_id":"EV-2mcz.9","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged"}}
|
|
617
|
+
{"id":"int-03c6c7d4","kind":"field_change","created_at":"2026-10-10T05:29:16.915905317Z","actor":"Denis Kiselev","issue_id":"EV-2mcz.10","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged"}}
|
|
618
|
+
{"id":"int-2ae5b260","kind":"field_change","created_at":"2026-10-10T06:06:58.238154954Z","actor":"Denis Kiselev","issue_id":"EV-09tf","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged in PR #1807"}}
|
|
619
|
+
{"id":"int-4242cd56","kind":"field_change","created_at":"2026-10-10T06:07:20.049330881Z","actor":"Denis Kiselev","issue_id":"EV-qbli","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Already resolved by PR #1752 (see notes)"}}
|
|
620
|
+
{"id":"int-1fe4ff62","kind":"field_change","created_at":"2026-10-10T06:39:22.070064099Z","actor":"Denis Kiselev","issue_id":"EV-pdf2","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged"}}
|
|
621
|
+
{"id":"int-f5478aa6","kind":"field_change","created_at":"2026-10-10T08:43:08.630764823Z","actor":"Denis Kiselev","issue_id":"EV-pb28","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged"}}
|
|
622
|
+
{"id":"int-3bb8846f","kind":"field_change","created_at":"2026-10-10T17:02:23.92423546Z","actor":"Denis Kiselev","issue_id":"EV-mmnr","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged"}}
|
|
623
|
+
{"id":"int-0a46b82d","kind":"field_change","created_at":"2026-10-10T17:19:28.950843549Z","actor":"Denis Kiselev","issue_id":"EV-z62t","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged"}}
|
|
624
|
+
{"id":"int-5ffd7f55","kind":"field_change","created_at":"2026-10-10T18:13:32.786243533Z","actor":"Denis Kiselev","issue_id":"EV-r671","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Merged in PR #1813"}}
|
|
625
|
+
{"id":"int-05b35017","kind":"field_change","created_at":"2026-10-11T02:08:31.809173892Z","actor":"Denis Kiselev","issue_id":"EV-du3x.3","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
626
|
+
{"id":"int-0bdb6c53","kind":"field_change","created_at":"2026-10-11T02:30:52.265602529Z","actor":"Denis Kiselev","issue_id":"EV-du3x.4","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
627
|
+
{"id":"int-ebf31a7d","kind":"field_change","created_at":"2026-10-11T02:54:50.995980383Z","actor":"Denis Kiselev","issue_id":"EV-du3x.5","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
628
|
+
{"id":"int-9201eec7","kind":"field_change","created_at":"2026-10-11T03:20:24.150429118Z","actor":"Denis Kiselev","issue_id":"EV-du3x.8","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
629
|
+
{"id":"int-1615cdd7","kind":"field_change","created_at":"2026-10-11T03:35:40.33005889Z","actor":"Denis Kiselev","issue_id":"EV-8v7d.3","extra":{"field":"status","new_value":"open","old_value":"in_progress"}}
|
|
630
|
+
{"id":"int-dc67c789","kind":"field_change","created_at":"2026-10-11T03:35:40.330186872Z","actor":"Denis Kiselev","issue_id":"EV-8v7d.3","extra":{"field":"assignee","new_value":"","old_value":"Denis Kiselev"}}
|
|
631
|
+
{"id":"int-a2eb413a","kind":"field_change","created_at":"2026-10-11T03:37:08.729446267Z","actor":"Denis Kiselev","issue_id":"EV-8v7d.1","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
632
|
+
{"id":"int-575b9a13","kind":"field_change","created_at":"2026-10-11T04:05:34.244398732Z","actor":"Denis Kiselev","issue_id":"EV-8v7d.2","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
633
|
+
{"id":"int-8b79c268","kind":"field_change","created_at":"2026-10-11T05:05:14.944704585Z","actor":"Denis Kiselev","issue_id":"EV-8v7d.13","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
634
|
+
{"id":"int-362d232a","kind":"field_change","created_at":"2026-10-11T06:06:19.598640958Z","actor":"Denis Kiselev","issue_id":"EV-8v7d.3","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
635
|
+
{"id":"int-6131204f","kind":"field_change","created_at":"2026-10-11T06:40:39.507468072Z","actor":"Denis Kiselev","issue_id":"EV-8v7d.4","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Dropped: same signal as constant_write_to_nil, see notes"}}
|
|
636
|
+
{"id":"int-31b475dc","kind":"field_change","created_at":"2026-10-11T06:40:41.935593905Z","actor":"Denis Kiselev","issue_id":"EV-8v7d.5","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Dropped: same signal as constant_write_to_nil, see notes"}}
|
|
637
|
+
{"id":"int-68909b2c","kind":"field_change","created_at":"2026-10-11T07:21:23.395993815Z","actor":"Denis Kiselev","issue_id":"EV-8v7d.6","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
638
|
+
{"id":"int-16097dd9","kind":"field_change","created_at":"2026-10-11T07:34:24.726032091Z","actor":"Denis Kiselev","issue_id":"EV-8v7d.7","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
639
|
+
{"id":"int-0be9025f","kind":"field_change","created_at":"2026-10-11T09:03:21.790906842Z","actor":"Denis Kiselev","issue_id":"EV-8v7d.8","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
640
|
+
{"id":"int-3f1237f4","kind":"field_change","created_at":"2026-10-11T14:51:19.252166741Z","actor":"Denis Kiselev","issue_id":"EV-8v7d.9","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
641
|
+
{"id":"int-3de7b591","kind":"field_change","created_at":"2026-10-11T15:04:24.700626826Z","actor":"Denis Kiselev","issue_id":"EV-8v7d.10","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
642
|
+
{"id":"int-8be80121","kind":"field_change","created_at":"2026-10-11T15:25:09.306475739Z","actor":"Denis Kiselev","issue_id":"EV-8v7d.11","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
643
|
+
{"id":"int-ba0e15a2","kind":"field_change","created_at":"2026-10-11T16:00:28.519385107Z","actor":"Denis Kiselev","issue_id":"EV-8v7d.12","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
644
|
+
{"id":"int-6d7bd577","kind":"field_change","created_at":"2026-10-11T16:59:42.035608579Z","actor":"Denis Kiselev","issue_id":"EV-ygg9","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
645
|
+
{"id":"int-547fecfe","kind":"field_change","created_at":"2026-10-11T17:22:19.47687736Z","actor":"Denis Kiselev","issue_id":"EV-ye3m","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
646
|
+
{"id":"int-bcd11e26","kind":"field_change","created_at":"2026-10-12T02:41:08.259992159Z","actor":"Denis Kiselev","issue_id":"EV-g6hd","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
647
|
+
{"id":"int-f8ca392d","kind":"field_change","created_at":"2026-10-12T03:01:03.096649962Z","actor":"Denis Kiselev","issue_id":"EV-xl1j","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
648
|
+
{"id":"int-de6c4d1e","kind":"field_change","created_at":"2026-10-12T06:59:29.699566039Z","actor":"Denis Kiselev","issue_id":"EV-m78s","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
649
|
+
{"id":"int-ed6de844","kind":"field_change","created_at":"2026-10-12T08:28:48.148716841Z","actor":"Denis Kiselev","issue_id":"EV-yub9","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
650
|
+
{"id":"int-1f781f0a","kind":"field_change","created_at":"2026-10-12T10:31:54.974975403Z","actor":"Denis Kiselev","issue_id":"EV-ubcq","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
651
|
+
{"id":"int-0dfc1b96","kind":"field_change","created_at":"2026-10-12T10:54:10.977752113Z","actor":"Denis Kiselev","issue_id":"EV-ri8j","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
652
|
+
{"id":"int-b79f62f6","kind":"field_change","created_at":"2026-10-12T11:14:05.558635903Z","actor":"Denis Kiselev","issue_id":"EV-wf52","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
653
|
+
{"id":"int-5c475ad8","kind":"field_change","created_at":"2026-10-12T12:55:34.339718031Z","actor":"Denis Kiselev","issue_id":"EV-j1jw","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
654
|
+
{"id":"int-3b9be5e5","kind":"field_change","created_at":"2026-10-13T03:07:40.88128569Z","actor":"Denis Kiselev","issue_id":"EV-2gf0","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
655
|
+
{"id":"int-c15e7ffc","kind":"field_change","created_at":"2026-10-13T05:08:40.172189792Z","actor":"Denis Kiselev","issue_id":"EV-04b1","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
656
|
+
{"id":"int-3e12caeb","kind":"field_change","created_at":"2026-10-13T05:39:14.357741483Z","actor":"Denis Kiselev","issue_id":"EV-2nj3","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
657
|
+
{"id":"int-ec3489ad","kind":"field_change","created_at":"2026-10-13T07:48:03.231041662Z","actor":"Denis Kiselev","issue_id":"EV-i2qf","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
658
|
+
{"id":"int-4ebb6cc6","kind":"field_change","created_at":"2026-10-13T10:22:13.338137009Z","actor":"Denis Kiselev","issue_id":"EV-70w5","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
659
|
+
{"id":"int-e24299f1","kind":"field_change","created_at":"2026-10-13T17:20:30.564416401Z","actor":"Denis Kiselev","issue_id":"EV-x38e","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
660
|
+
{"id":"int-2efcd224","kind":"field_change","created_at":"2026-10-14T01:56:27.118884805Z","actor":"Denis Kiselev","issue_id":"EV-9zfe","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
|
|
661
|
+
{"id":"int-5a397b18","kind":"field_change","created_at":"2026-10-14T06:28:38.598622816Z","actor":"Denis Kiselev","issue_id":"EV-23dn","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"README update merged in PR #1867 (GH #1864)"}}
|
data/.rubocop_todo.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,133 @@
|
|
|
2
2
|
|
|
3
3
|
Versioning policy: see [docs/versioning.md](docs/versioning.md).
|
|
4
4
|
|
|
5
|
+
## [1.5.0] - 2026-10-14
|
|
6
|
+
|
|
7
|
+
The `default` profile grows from 136 to 155 operators, `strict` gains one more, and several existing operators emit more mutants than before: literals, collections, constants, indexes, variable reads and compound writes are all probed more closely. Constant assignments outside a method are subjects now, as class-body code was in 1.4.0. Mutation scores will move: every new mutant is one your suite has never been measured against. Pin the gem version and the operator profile if you need a stable score across runs.
|
|
8
|
+
|
|
9
|
+
The other half of the release is about trusting a verdict. Several mutants were counted as kills they had not earned, or survived whatever the tests did; several ways a test process can die now give a verdict instead of an error or a lost run. See "Fixed". Most of these came from the stability runs on real-world repositories before this release.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Constant assignments are subjects** — `LIMIT = 10`, `Config::LIMIT = 10` and `TOTAL += 1` outside any method are subjects of their own, named after the constant (`Config::LIMIT`); a value-object definition (`Data.define` / `Struct.new`) stays the subject it was. The literal operators mutate the assigned value, `constant_write_to_nil` the assignment, `compound_assignment` an operator write. Code that read the constant while the file loaded keeps the old value (PRs #1822, #1823, #1827; GH #1820, #1509, #1515)
|
|
14
|
+
- **A concern's `prepended do ... end` block is treated like `included do ... end`** — scopes, AASM guards and callbacks, and callback and validation declarations written in it are subjects, named after the concern (PR #1851, GH #1846)
|
|
15
|
+
- **Nineteen operators in the `default` profile**:
|
|
16
|
+
- **Literals** — `complex_literal` (`5i` -> `0i`, `1i`, `6i`, `4i`, `nil`) and `rational_literal` (`1.5r` -> `0r`, `1r`, `2.5r`, `0.5r`, `nil`) (PRs #1791, #1792; GH #1499, #1500)
|
|
17
|
+
- **Constants** (epic GH #1424) — `constant_read_to_nil` (`Config::MAX` -> `nil`), `constant_namespace_strip` (`A::B::C` -> `C`, `B::C`), `constant_write_to_nil` (`LIMIT = 10` -> `LIMIT = nil`) (PRs #1818, #1819, #1823; GH #1507, #1508, #1509)
|
|
18
|
+
- **Variable reads** — `variable_read_to_nil`: a read of a local, instance, class or global variable, or `self`, becomes `nil` (`build(name: @name)` -> `build(name: nil)`). It skips call receivers, `if` / `unless` / `while` / `until` conditions and the left operand of `&&` / `||` (PR #1824, GH #1512)
|
|
19
|
+
- **Indexes** (epic GH #1422) — `index_receiver_to_self` (`h[k]` -> `self[k]`), `index_to_key_predicate` (`h[k]` -> `h.key?(k)`), `index_range_to_drop` (`a[n..-1]` -> `a.drop(n)`), `index_write_to_value` (`h[k] = v` -> `v`) (PRs #1814–#1817; GH #1490, #1491, #1492, #1495)
|
|
20
|
+
- **`operator_write_receiver_promotion`** — `@account.balance += x` -> `@account += x` (PR #1830, GH #1518)
|
|
21
|
+
- **Ruby idioms** (epic GH #1427) — `memoization_break` (`@x ||= expr` -> `@x = expr`), `short_circuit_to_eager` (`a && b` -> `a & b`), `freeze_removal` (`LIST = %w[a b].freeze` -> `LIST = %w[a b]`), `rescue_class_widening` (`rescue KeyError` -> `rescue StandardError`, `rescue Exception`), `visibility_removal` (drops `private` / `protected` / `module_function`), `attr_accessor_swap` (`attr_reader :name` -> `attr_accessor :name`, or removed), `sleep_to_zero` (`sleep delay` -> `sleep 0`) (PRs #1799, #1800, #1802–#1806; GH #1532, #1533, #1535, #1538–#1541)
|
|
22
|
+
- **`round_half_mode_swap`** — `amount.round(2, half: :even)` -> `half: :up`, `half: :down`: a survivor means no example rounds a value that sits on a tie. `symbol_literal` leaves these modes alone (PR #1785, GH #1711)
|
|
23
|
+
- **`safe_navigation_insertion` in the `strict` profile** — `user.name` -> `user&.name`, to surface calls no test reaches with a nil receiver. It touches nearly every call with a receiver, hence `strict` (PR #1801, GH #1534)
|
|
24
|
+
- **More mutants from existing operators**:
|
|
25
|
+
- **`integer_literal`** — `n - 1` beside `n + 1` (and `-1` for `0`), plus one sentinel past the next integer width above the literal (`42` -> `167`, int8 to int64) to surface missing overflow checks (PRs #1788, #1789; GH #1496, #1497)
|
|
26
|
+
- **`float_literal`** — `Float::NAN`, `Float::INFINITY` and `-Float::INFINITY` (PR #1790, GH #1498)
|
|
27
|
+
- **`array_literal`** — deletes each element in turn, and replaces a single-element array with its element (`[x]` -> `x`) (PRs #1793, #1794; GH #1501, #1502)
|
|
28
|
+
- **`hash_literal`** — deletes each pair in turn, and renames each label key in turn (PRs #1795, #1796; GH #1503, #1504)
|
|
29
|
+
- **`string_literal` / `symbol_literal`** — an interpolated string or symbol is replaced as a whole (`"a #{x}"` -> `""`, `nil`); a backtick or `%x` command is replaced with `nil` (PRs #1797, #1798; GH #1505, #1506)
|
|
30
|
+
- **`compound_assignment`** — operator and logical writes on attributes (`obj.count += 1`, `obj.name ||= x`), on indexes (`counts[key] += 1`, `cache[key] ||= x`) and operator writes on constants, where only plain variables were mutated before (PRs #1825–#1829; GH #1513–#1517)
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- **A mutant that makes its own file fail to load is `killed`** — `OPERATORS = %i[+ -].to_set` with the array turned into `nil` raises while the file loads, before any test runs. When the original file loads, that is a kill (`error_message` starts with `load failed:`); when the original fails the same way, it stays an `error` (PR #1845, GH #1834)
|
|
35
|
+
- **A test process that dies on an unhandled exception gives a verdict** — under fork isolation a child that died on an exception no handler saw was an `error` reading `child exited 1 without writing a result`. While the tests ran it is now `killed`, as a crashing test would be (`error_message` starts with `child died` and names the exception); before they ran it is an `error` carrying the exception's own class (PR #1855, GH #1852)
|
|
36
|
+
- **Under `in_process`, an exception outside `StandardError` is contained** — a `SystemStackError` from a mutant that recurses without end is reported for that mutation (`killed`, `error_message` starting with `test run ended by`, or `error` when raised before the tests) and the run goes on (PR #1857, GH #1853)
|
|
37
|
+
- **Exit status 2 for an `in_process` run that a mutation took down** — when a mutation ends the process in a way that cannot be contained (a stack overflow through `const_missing` under the usual stack limit), stderr names the mutation and suggests `--isolation fork`, and the run ends with 2, the status of a tool error. It ended with Ruby's 1, which reads as a score below `--min-score`, although no report was written. A VM abort (`[BUG]`) still ends as an abort does. See [docs/isolation.md](docs/isolation.md#what-in_process-cannot-contain) (PRs #1857, #1866; GH #1853, #1863)
|
|
38
|
+
- **Class-body operators share one rule for which method a mutant belongs to** — `mixin_removal`, `superclass_removal`, `data_struct_member` and `alias_removal` each had their own copy and disagreed. Now the first method of the scope in source order, searching body blocks such as `included do ... end`; a class whose methods all sit in `class << self` is reached through them, where it got no class-body mutants before (PR #1784, GH #1718)
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- **In-process isolation no longer kills mutants that fork isolation reports as survivors** — a file that installs an anonymous module on its host (`Object.extend(Module.new { def const_missing ... })`) left one more module behind each time it was evaluated, so a mutation reached every earlier mutation's code through `super` and was reported killed for a predecessor's bug. The methods of those modules are now removed when the file is evaluated again (PR #1860, GH #1856)
|
|
43
|
+
- **A constant mutation no longer outlives its file under `in_process`** — the original source is put back after every mutation, so `LIMIT = nil` from a file's last mutation cannot fail the tests of the next file's mutants and count as their kill (PR #1841, GH #1832)
|
|
44
|
+
- **Mutations of a concern with an `included` block are no longer falsely killed under fork** — re-evaluating the mutated file blanked the `included do ... end` block, so a class including the concern during the example got none of its declarations and crashed whatever the mutant did. The block is kept; the same for `prepended` (PRs #1842, #1847; GH #1833, #1843)
|
|
45
|
+
- **Mutations of a method defined in a concern's `included` or `prepended` block reach classes that took the concern before the run** — they survived every time, since those classes kept the original method (PR #1850, GH #1848)
|
|
46
|
+
- **A result that cannot be read no longer aborts the run** — under fork isolation, bytes on the result pipe that were not a result raised in the parent and the run ended without a report; the mutation is now an `error` reading `child exited 0; its result was unreadable (...)`. Under `--jobs`, a frame a worker did not write, or one cut short (ten seconds are allowed for the rest), replaces that worker and records `worker's result could not be read; the worker was replaced`, where it raised, hung or was taken for a result (PRs #1859, #1861; GH #1854, #1858)
|
|
47
|
+
- **No more mutants that only Prism accepts** — on an interpreter that compiles with parse.y (Ruby 3.3 by default), a mutant such as `def f(v); LIMIT ||= v; end` was marked parseable and then raised `SyntaxError` when evaluated, landing in `errors`. It is now `unparseable` there (PR #1839, GH #1821)
|
|
48
|
+
- **The to-nil operators leave the receiver of a definition or a compound write alone** — `def self.build` -> `def nil.build` survived whatever the tests did, one false survivor per `def self.x`; `counter.total += 1` -> `nil.total += 1` was killed by any test reaching the line (PRs #1837, #1840; GH #1831, #1838)
|
|
49
|
+
- **A rooted value-object definition inside a scope is named after the top level** — `module Outer; ::Point = Data.define(...)` gave `Outer::Point`, so `--target Point` missed it (PR #1836, GH #1835)
|
|
50
|
+
|
|
51
|
+
### Documentation
|
|
52
|
+
|
|
53
|
+
- **README: how to read a verdict when a test process died, and the `error` messages that are no verdict at all** — the statuses table, a new table under "Diagnosing errored mutations", and what `--isolation in_process` cannot contain (PR #1867, GH #1864)
|
|
54
|
+
|
|
55
|
+
## [1.4.0] - 2026-10-07
|
|
56
|
+
|
|
57
|
+
Two things in this release. Code that lives in a class body rather than in a method — scopes, AASM guards, callback conditions, value-object definitions — is now mutated, where a run aimed at it used to report `0 mutations`. And the `default` profile grows from 111 to 136 operators, with two more in `strict`. Mutation scores will move on both counts — every new subject and every new operator produces mutants your suite has never been measured against. Pin the gem version and the operator profile if you need a stable score across runs.
|
|
58
|
+
|
|
59
|
+
A red baseline also means something different now: it no longer hides survivors, and it no longer lets an already-failing example count as a kill. See "Changed".
|
|
60
|
+
|
|
61
|
+
Most of the subject and baseline work comes from a field report on a Rails application running 1.3.0 (GH #1740–#1744).
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
|
|
65
|
+
- **Subjects outside `def`** — subjects were methods only, so a file or a line range holding nothing but class-body code got no mutations at all:
|
|
66
|
+
- **Value-object definitions** — `Point = Data.define(:x, :y)` and `class Coord < Struct.new(:lat, :lng)` outside any method are subjects of their own, named after the constant and mutated by the operators that apply to them. `--target Foo` matches a value-object constant named `Foo` (PR #1738, GH #1683)
|
|
67
|
+
- **ActiveRecord scopes** — `scope :recent, -> { where(recent: true) }` with a literal body is a subject named after the class method it defines (`Order.recent`) and mutated like a method body (PR #1747, GH #1742)
|
|
68
|
+
- **AASM guards and callbacks** — lambdas and blocks written out inside an `event` or `state` declaration, named after the method the declaration defines (`Order#ship`, `Order#paid?`) (PR #1755, GH #1750)
|
|
69
|
+
- **Callback and validation declarations** — the conditions and bodies of `validate :credit_limit, if: -> { paid? }`, `before_save { ... }`, `before_action ..., unless: -> { ... }`, named the way the declaration reads (`Order.validate(:credit_limit)`, `Order.before_save`). The mutated declaration takes the place of the original in its callback chain, so the two do not sit side by side and mask each other (PR #1759, GH #1749)
|
|
70
|
+
- **The same three inside a concern's `included do ... end` block**, named after the concern (`Publishable.published`, `Shippable#ship`, `Publishable.validates(:title)`) and re-declared on every class that already includes it (PRs #1757, #1764, #1766; GH #1748, #1756, #1758)
|
|
71
|
+
- **A warning when targeted lines hold code no subject covers** — a line range, or a whole file with no subject at all, that contains class-body code outside every subject (DSL calls, constant lists) now says so on stderr and in JSON as `summary.uncovered_code` (`[{ file, lines: ["2-4", "9"] }]`), instead of a bare `0 mutations` that reads as "nothing to test here" (PR #1746, GH #1741)
|
|
72
|
+
- **Seven operators for modern syntax (`default` profile)** — pattern matching, anonymous arguments and `Data` / `Struct` (epic GH #1428):
|
|
73
|
+
- **`data_struct_member`** — drops a member, or swaps adjacent members, of a `Data.define` / `Struct.new` definition (PR #1685, GH #1543)
|
|
74
|
+
- **`pin_operator_removal`** — `in ^expected` to `in expected`: the pattern captures instead of comparing (PR #1686, GH #1544)
|
|
75
|
+
- **`rightward_assignment`** — `value => [a, b]` to `value in [a, b]`: a mismatch returns `false` instead of raising (PR #1687, GH #1545)
|
|
76
|
+
- **`numbered_parameter_swap`** — `pairs.map { _1 - _2 }` to `pairs.map { _2 - _1 }` (PR #1688, GH #1546)
|
|
77
|
+
- **`forwarded_argument_drop`** — `g(*, **, &)` to `g(**, &)`; a `...` signature is spelled out so one part can be left out (PR #1690, GH #1547)
|
|
78
|
+
- **`pattern_wildcard_widening`** — `in [a, b]` to `in [a, b, *]`, `in { age: Integer }` to `in { age: _ }`: the pattern accepts shapes it rejected (PR #1692, GH #1548)
|
|
79
|
+
- **`no_matching_pattern_else`** — adds an empty `else` to a `case/in` that has none, so an unmatched value yields `nil` instead of raising (PR #1694, GH #1549)
|
|
80
|
+
- **Seven operators for arguments, numbers and declarations (`default` profile)** (epic GH #1429):
|
|
81
|
+
- **`argument_order_permutation`** — `compute(a, b)` to `compute(b, a)` for calls, `super` and `yield`. Calls that take arguments in any order, such as `OptionParser#on`, can be silenced with `ignore_patterns` (PR #1706, GH #1550)
|
|
82
|
+
- **`keyword_value_swap`** — `compute(x: a, y: b)` to `compute(x: b, y: a)`, keeping the keys (PR #1707, GH #1551)
|
|
83
|
+
- **`comparison_operand_swap`** — `x.age <=> y.age` to `y.age <=> x.age`: reverses a sort block or a custom ordering (PR #1710, GH #1554)
|
|
84
|
+
- **`integer_division_to_fdiv`** — `a / b` to `a.fdiv(b)`: integer division keeps its remainder (PR #1712, GH #1555)
|
|
85
|
+
- **`off_by_one_boundary`** — `n.times` to `(n - 1).times`, `items.first(n)` to `items.first(n - 1)`, for counts held in a variable (PR #1713, GH #1556)
|
|
86
|
+
- **`format_specifier_swap`** — `format("%05d", n)` to `format("%d", n)`, `"%.2f"` to `"%f"` / `"%s"` (PR #1714, GH #1557)
|
|
87
|
+
- **`alias_removal`** — drops an `alias` or `alias_method` declaration (PR #1719, GH #1561)
|
|
88
|
+
- **Seven structural regexp operators (`default` profile)** — built on `regexp_parser`, so each edits one token of the pattern rather than one byte (epic GH #1426; PR #1722, GH #1721):
|
|
89
|
+
- **`regexp_character_type_complement`** — `/\d+/` to `/\D+/`, `/\bword/` to `/\Bword/` (PR #1725, GH #1525)
|
|
90
|
+
- **`regexp_anchor_promotion`** — `^` to `\A`, `$` and `\Z` to `\z`: the pattern stops matching around newlines (PR #1726, GH #1526)
|
|
91
|
+
- **`regexp_alternation_branch_deletion`** — `/cat|dog/` to `/dog/` and `/cat/` (PR #1727, GH #1527)
|
|
92
|
+
- **`regexp_quantifier_minimum_swap`** — `*` to `+` and back, keeping lazy and possessive markers, so only the empty case changes (PR #1728, GH #1528)
|
|
93
|
+
- **`regexp_capture_to_passive`** — `/id=(\d+)/` to `/id=(?:\d+)/`: `$1` and `m[1]` lose their value (PR #1729, GH #1529)
|
|
94
|
+
- **`regexp_named_group_rename`** — `/(?<user>\w+)@/` to `/(?<_user>\w+)@/`: `m[:user]` no longer finds it (PR #1730, GH #1530)
|
|
95
|
+
- **`regexp_option_removal`** — drops `i` or `m` where it changes what the pattern matches (PR #1731, GH #1531)
|
|
96
|
+
- **Four operators for `rescue` and `return` (`default` profile)** (epic GH #1425):
|
|
97
|
+
- **`rescue_handler_promotion`** — runs a rescue handler instead of the code it protects, so the happy path never runs (PR #1732, GH #1519)
|
|
98
|
+
- **`rescue_handler_concatenation`** — runs the handler after the protected code as well, so a successful run also does what the handler does (PR #1733, GH #1520)
|
|
99
|
+
- **`rescue_else_concatenation`** — moves the `else` body into the protected code, so an error it raises is now rescued (PR #1734, GH #1521)
|
|
100
|
+
- **`return_keyword_removal`** — `return :neg if x.negative?` to `:neg if x.negative?`: control flow continues past a guard clause (PR #1735, GH #1522)
|
|
101
|
+
- **Two operators in the `strict` profile**:
|
|
102
|
+
- **`exception_swallow`** — `record.save!` to `record.save! rescue nil`, for statements that raise by convention: a survivor means no test makes it fail and checks the error comes out (PR #1716, GH #1558)
|
|
103
|
+
- **`statement_reorder`** — swaps two adjacent statements that both act, to surface side effects whose order is never asserted (PR #1717, GH #1559)
|
|
104
|
+
- **`ignore_patterns` accepts every method name** — names ending in `?`, `!` or `=` and operator methods are written as they are (`call{name=valid?|save!}`, `call{name=<=>}`); names made of reserved characters go in quotes (`call{name='|'}`). They used to raise `ConfigError`, which left the new operators' noisiest call sites impossible to silence. See `docs/ast_pattern_syntax.md` (PR #1737, GH #1715)
|
|
105
|
+
- **`index_to_fetch` under a guard on the same key is equivalent** — `config.fetch(:size)` cannot raise inside `if config[:size]`, nor after `return unless config[:size]`; both forms are now reported `equivalent` instead of surviving on every run (PRs #1754, #1763; GH #1744, #1753)
|
|
106
|
+
- **A red baseline says why it was red** — the failing examples with their first error line, or the error that stopped the file before any example ran (load error, timeout, a baseline process that died), on stderr once the baseline finishes, under the score line, and in JSON as `summary.baseline_failures`. The baseline used to report pass or fail and nothing else (PR #1752, GH #1743)
|
|
107
|
+
|
|
108
|
+
### Changed
|
|
109
|
+
|
|
110
|
+
- **A red baseline no longer hides survivors, and no longer counts as a kill** — every survivor covered by a spec file that was red in the baseline used to be recorded `neutral`, although its tests had passed: real gaps disappeared behind full marks. And where the red example failed again in the mutation run, every mutation it ran against was reported `killed`. Now a survivor stays a survivor whatever the baseline did, and a mutation is `neutral` only when its tests failed on nothing but examples that were already failing. `summary.baseline_neutralized` counts those. Expect a lower score on a project with a red or flaky spec file — the earlier one was wrong in both directions (PR #1762, GH #1751)
|
|
111
|
+
- **The same under Minitest and Test::Unit** — tests are named `Class#test` and `test(Class)` in the baseline and in a mutation run alike (PR #1768, GH #1761)
|
|
112
|
+
- **`send_mutation` swaps rounding modes** — `round` to `floor` and `ceil` (PR #1712, GH #1555)
|
|
113
|
+
- **Dependencies** — `regexp_parser` (`>= 2.9, < 3`, the range RuboCop uses) is a new runtime dependency. `diff-lcs` is capped below 2 while `rspec-expectations` requires `< 2.0` (PRs #1722, #1704)
|
|
114
|
+
- **Run the MCP server through `bundle exec`** — the README setup now shows `"command": "bundle", "args": ["exec", "evilution", "mcp"]`, so the project's `Gemfile.lock` decides which gem versions are loaded (PR #1704, GH #1684)
|
|
115
|
+
|
|
116
|
+
### Fixed
|
|
117
|
+
|
|
118
|
+
- **Methods inside `class << self` are named as class methods** — they were listed as `Gateway#create_group`, so `--target Gateway.create_group` matched nothing and `Gateway.` / `Gateway#` selected the wrong set (PR #1745, GH #1740)
|
|
119
|
+
- **`regex_simplification` no longer mutates group syntax as if it were a quantifier or an anchor** — `/(?:ab)+/` became `/(:ab)+/`, a lookahead became a capture of `=a`, and anchors inside a `(?#comment)` were "removed", producing mutants that could never be killed (PR #1723, GH #1720)
|
|
120
|
+
- **`pattern_matching_array` keeps the constant of a deconstruct pattern** — mutating `Point[x, y]` also removed the `Point` check, and parenthesized, qualified and find patterns lost their shape (PR #1693, GH #1691; fix by [@mikamikasuki](https://github.com/mikamikasuki))
|
|
121
|
+
- **`keyword_argument` no longer removes an anonymous `**` the body forwards** — `def call(*, **, &)` forwarding `target.call(*, **, &)` produced a mutant Ruby rejects (PR #1695, GH #1689)
|
|
122
|
+
- **A gem activation conflict is reported as one** — with the MCP server started outside Bundler, RubyGems activated `diff-lcs` 2.0 and RSpec then refused to load; the run stopped at the proof-of-life canary with a message about the canary. It now names the conflict and the fix (PR #1704, GH #1684)
|
|
123
|
+
- **Eight more advisory messages can no longer be turned into errors** — a project that raises on warnings promoted evilution's own notices ("HTML report written to …", "coverage targeting unavailable", …) to exceptions; they now go through `Evilution::Diagnostic` like the rest (PR #1739, GH #1591)
|
|
124
|
+
- **In-process RSpec runs no longer keep about 1 MB per mutation** — each run with a suite hook left its examples reachable through `RSpec::Core::AnonymousExampleGroup` for the life of the process (65 MB to 220 MB over 150 runs, flat after the fix) (PR #1765; fix by [@gipcompany](https://github.com/gipcompany))
|
|
125
|
+
- **`in_process` isolation runs every mutation's tests under Minitest and Test::Unit** — only the first mutation got its tests run; each later one was an error (`no Minitest tests executed (0 test methods ran)`), and since errors are left out of the score the report read as 100% on one mutation. A test file is now loaded once per process and its classes are dispatched again for every mutation. A suite that builds tests at load time from the code under test still needs `--isolation fork`; see [docs/isolation.md](docs/isolation.md) (PR #1775, GH #1767)
|
|
126
|
+
- **A looping mutant no longer hangs an `in_process` run** — the per-mutation timeout raised `Timeout::Error` once; the test framework recorded one failed test and moved on to the next, which reached the same loop with no timer left. The timeout now raises a `SignalException` that ends the whole test run, and the mutation is reported `timed_out` (PR #1777, GH #1776)
|
|
127
|
+
- **The uncovered-code warning no longer names lines that were mutated, or top-level `require`s** — it listed an `alias_method` line the same run had mutated, and `require` / `require_relative` lines that hold nothing to mutate (PR #1773, GH #1770)
|
|
128
|
+
- **`optional_parameter_to_required` no longer emits mutants Ruby rejects** — optional parameters must be contiguous, so dropping the default of one that sits between two others (`def f(a = nil, b = nil, c = nil)` to `def f(a = nil, b, c = nil)`) is a syntax error. Of several optional parameters only the first is mutated, and the last unless a `*rest` parameter follows (PR #1779, GH #1771)
|
|
129
|
+
- **`splat_operator` leaves the rest of a pattern alone** — `x => { key:, **opts }` became `x => { key:, opts }`, which does not parse. The rest of an array or find pattern (`in [a, *rest]`) is no longer mutated either, although that mutant parsed: expect fewer `splat_operator` mutants in code that uses them (PR #1778, GH #1772; fix by [@gipcompany](https://github.com/gipcompany))
|
|
130
|
+
- **`splat_operator` no longer demotes a `**` that follows another `**`** — `bar(**a, **b)` became `bar(**a, b)`, a positional argument after a keyword splat (PR #1782, GH #1780)
|
|
131
|
+
|
|
5
132
|
## [1.3.0] - 2026-09-28
|
|
6
133
|
|
|
7
134
|
Operator expansion release: the `default` profile grows from 88 to 111 operators, mostly around call sites and blocks, and several existing operators gain new replacements. Mutation scores will move — every new operator produces mutants your suite has never been measured against. Pin the gem version and the operator profile if you need a stable score across runs.
|