From 837e9f2f22e8fa3b4bc1ed6f69c9c529a78afcf3 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Sun, 8 Nov 2026 07:55:50 +0000 Subject: [PATCH 1/2] Fix Rake description matching across formatting --- gems/prism-merge/lib/prism/merge.rb | 7 +++- .../spec/fixtures_integration_spec.rb | 34 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/gems/prism-merge/lib/prism/merge.rb b/gems/prism-merge/lib/prism/merge.rb index b7c7e27c3..768c7ebb4 100644 --- a/gems/prism-merge/lib/prism/merge.rb +++ b/gems/prism-merge/lib/prism/merge.rb @@ -742,7 +742,12 @@ def ruby_dsl_signature_for(node, require_aliases: nil) when :git_source, :gem, :eval_gemfile, :platform, :group, :task [:ruby_dsl_call, call.name, ruby_dsl_first_argument(call)] when :desc - [:ruby_dsl_desc, call.slice] + argument = call.arguments&.arguments + if call.receiver.nil? && call.block.nil? && argument&.one? && argument.first.is_a?(::Prism::StringNode) + [:ruby_dsl_desc, argument.first.unescaped] + else + [:ruby_dsl_desc, call.slice] + end end end diff --git a/gems/prism-merge/spec/fixtures_integration_spec.rb b/gems/prism-merge/spec/fixtures_integration_spec.rb index 1e1cb5629..9f9fd4bce 100644 --- a/gems/prism-merge/spec/fixtures_integration_spec.rb +++ b/gems/prism-merge/spec/fixtures_integration_spec.rb @@ -69,6 +69,40 @@ def json_ready(value) expect(result[:output]).to include('gem "rake"') end + it 'keeps Rake descriptions stable across formatting differences and repeated merges' do + template = <<~RUBY + desc "Default tasks aggregator" + task :default do + puts :template + end + + desc "Generate YARD documentation" + task :yard do + puts :yard + end + RUBY + destination = <<~RUBY + desc("Default tasks aggregator") + task :default do + puts :destination + end + + # Alef-generated content + task :alef do + puts :alef + end + RUBY + options = { preference: :destination, add_template_only_nodes: true, + signature_generator: PRISM_MERGE.ruby_dsl_signature_generator } + once = PRISM_MERGE.merge_ruby(template, destination, 'ruby', **options).fetch(:output) + twice = PRISM_MERGE.merge_ruby(template, once, 'ruby', **options).fetch(:output) + + expect(twice).to eq(once) + expect(once.scan('Default tasks aggregator').size).to eq(1) + expect(once.scan('Generate YARD documentation').size).to eq(1) + expect(once).to include('puts :destination', '# Alef-generated content', 'puts :alef', 'task :yard') + end + it 'adds template-only top-level nodes at their template anchor' do result = PRISM_MERGE.merge_ruby( <<~RUBY, From 06cffe33a89f11d1555f048eb83c189b60c96892 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Sun, 8 Nov 2026 07:57:39 +0000 Subject: [PATCH 2/2] Avoid emitting destination comments twice --- .../lib/prism/merge/node_emission_support.rb | 2 ++ .../spec/reproducible_merge_spec.rb | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/gems/prism-merge/lib/prism/merge/node_emission_support.rb b/gems/prism-merge/lib/prism/merge/node_emission_support.rb index e536bfda4..eb9472bb0 100644 --- a/gems/prism-merge/lib/prism/merge/node_emission_support.rb +++ b/gems/prism-merge/lib/prism/merge/node_emission_support.rb @@ -400,6 +400,8 @@ def emit_node(result:, node:, analysis:, source:, matched_template_node: nil) last_filtered_leading_line = nil if source == :destination + emitted_dest_lines = result.line_metadata.filter_map { |metadata| metadata[:dest_line] }.to_set + leading_comments = leading_comments.reject { |comment| emitted_dest_lines.include?(comment.location.start_line) } leading_comments, = collapse_matched_template_leading_prefix( leading_comments, matched_template_node diff --git a/gems/prism-merge/spec/reproducible_merge_spec.rb b/gems/prism-merge/spec/reproducible_merge_spec.rb index 8c1e0ee0d..162415c67 100644 --- a/gems/prism-merge/spec/reproducible_merge_spec.rb +++ b/gems/prism-merge/spec/reproducible_merge_spec.rb @@ -106,6 +106,31 @@ end end + it 'emits destination comments attached to adjacent nodes only once on repeated merges' do + template = <<~RUBY + begin + require "stone_checksums" + rescue LoadError + puts :missing + end + RUBY + destination = template + <<~RUBY + # This file is auto-generated by alef — DO NOT EDIT. + # alef:hash:123 + # To regenerate: alef generate + # To verify freshness: alef verify + + require "bundler" + RUBY + options = { preference: :destination, add_template_only_nodes: true, + signature_generator: Prism::Merge.ruby_dsl_signature_generator } + once = Prism::Merge.merge_ruby(template, destination, 'ruby', **options).fetch(:output) + twice = Prism::Merge.merge_ruby(template, once, 'ruby', **options).fetch(:output) + + expect(once).to eq(destination) + expect(twice).to eq(once) + end + describe 'Ruby merge with comment-only destination (regression)' do # A scaffold reduced to just its magic comment merges into a full template. # The destination pragma arrives as an unmatched statement node because the