Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog,
and this project adheres to Semantic Versioning,
and yes, platform and engine support are part of the public API.
Please file a bug if you notice a violation of semantic versioning.
Unreleased
Added
Changed
Deprecated
Removed
Fixed
Security
4.0.6 - 2026-02-19
- TAG: v4.0.6
- COVERAGE: 96.37% – 2552/2648 lines in 50 files
- BRANCH COVERAGE: 87.22% – 812/931 branches in 50 files
- 98.81% documented
Added
- AGENTS.md
Changed
- appraisal2 v3.0.6
- kettle-test v1.0.10
- stone_checksums v1.0.3
- tree_haver v5.0.5
- tree_stump v0.2.0
- fork no longer required, updates all applied upstream
- Updated documentation on hostile takeover of RubyGems
- https://dev.to/galtzo/hostile-takeover-of-rubygems-my-thoughts-5hlo
4.0.5 - 2026-02-01
- TAG: v4.0.5
- COVERAGE: 96.37% – 2552/2648 lines in 50 files
- BRANCH COVERAGE: 87.22% – 812/931 branches in 50 files
- 98.81% documented
Added
- More shared examples for ConflictResolverBase
Changed
- Upgrade to tree_haver v5.0.3
- improve robustness of tests
Fixed
- Documentation fixes related to gem family section
4.0.4 - 2026-01-20
- TAG: v4.0.4
- COVERAGE: 96.37% – 2552/2648 lines in 50 files
- BRANCH COVERAGE: 87.22% – 812/931 branches in 50 files
- 98.81% documented
Added
-
RSpec Split Loading Pattern: New files for granular RSpec dependency tag loading
-
lib/ast/merge/rspec/setup.rb- Loads only registry and helper classes (no RSpec configuration) -
lib/ast/merge/rspec/dependency_tags_helpers.rb- DependencyTags helper module -
lib/ast/merge/rspec/dependency_tags_config.rb- RSpec.configure block with exclusion filters - Enables registering known gems before RSpec.configure runs (solving catch-22 problem)
- Required for ast-merge test suite to preserve SimpleCov coverage
- Required for merge gems that register other merge gems as dependencies
-
-
Ast::Merge::RSpec::MergeGemRegistry.force_check_availability!: Deferred availability checking for accurate test coverage- Called automatically in
before(:suite)hook AFTER SimpleCov is loaded - Prevents premature gem loading that would bypass coverage instrumentation
- Ensures accurate coverage reporting in merge gem test suites
- Called automatically in
Changed
-
RSpec Dependency Tags: Fixed exclusion filter setup to properly skip tests when optional gems unavailable
- Exclusion filters now set during
RSpec.configure(not inbefore(:suite)which runs too late) - Fixed RSpec API usage:
config.filter_run_excluding tag => true(not[tag] = true) -
ast-mergeuses split loading pattern inspec/spec_helper.rbandspec/config/tree_haver.rb -
markdown-mergeuses split pattern (registers:commonmarker_merge,:markly_merge) -
markly-mergeuses split pattern (registers:prism_merge) -
commonmarker-mergeuses simple pattern (no registrations needed)
- Exclusion filters now set during
Fixed
-
Test coverage accuracy: Fixed premature gem loading that bypassed SimpleCov instrumentation
-
MergeGemRegistry.registered_gemsnow returns ONLY explicitly registered gems, not all KNOWN_GEMS - RSpec exclusion filters are configured in
before(:suite)hook afterforce_check_availability!runs - This ensures gems are loaded AFTER SimpleCov sets up coverage instrumentation
- Previously, commonmarker-merge reported only 11 lines covered when it should have been far more
-
-
RSpec Dependency Tags: Tests with tags like
:markdown_merge,:markly_mergenow properly skip when those gems aren’t available- Fixed 141 test failures caused by tests running without required gems loaded
- Removed
requirestatements from integration specs - dependency tags handle gem loading - Fixed tag usage: Tests using
Markdown::Merge::PartialTemplateMergerwith:marklybackend now correctly tagged with both:markdown_mergeand:markly_merge
-
Thread-Safety Spec: Fixed JRuby concurrency test failure in
NodeTyping::Normalizer#canonical_type- Changed from unsynchronized
Arrayto thread-safeQueuefor collecting results from concurrent threads - Eliminates
ConcurrencyError: Detected invalid array contents due to unsynchronized modifications
- Changed from unsynchronized
4.0.3 - 2026-01-19
- TAG: v4.0.3
- COVERAGE: 97.30% – 2739/2815 lines in 53 files
- BRANCH COVERAGE: 89.84% – 893/994 branches in 53 files
- 98.81% documented
Added
-
Ast::Merge::RSpec::MergeGemRegistry.register_known_gems: Selective registration of known merge gems for RSpec dependency tags- Allows test suites to explicitly register only the merge gems they need, avoiding overhead of registering all known gems
- Usage in
spec/config/tree_haver.rb:MergeGemRegistry.register_known_gems(:prism_merge, :commonmarker_merge) - Enables proper RSpec tag-based test skipping for optional merge gem dependencies
- Example: Tests tagged with
:prism_mergeare automatically skipped when prism-merge isn’t available
Changed
- Upgrade to tree_haver v5.0.2
-
RSpec dependency tag load order pattern: Merge gems now load tree_haver and dependency tags early via
spec/config/tree_haver.rb- Ensures
TreeHaver::RSpec::DependencyTagsis loaded before gems register themselves - Pattern: Load tree_haver/rspec → Load ast/merge/rspec → Register known gems → Load library
- Applied to markdown-merge and markly-merge; other merge gems should follow this pattern
- Ensures
4.0.2 - 2026-01-12
- TAG: v4.0.2
- COVERAGE: 97.30% – 2739/2815 lines in 53 files
- BRANCH COVERAGE: 89.84% – 893/994 branches in 53 files
- 98.81% documented
Added
-
Recipe::Runnertarget file override: Accepttarget_filesparameter to override recipe targets-
Runner.new(recipe, target_files: ["file1.md", "file2.md"])- Process only specified files - Paths are expanded relative to
base_dir - When not specified, falls back to recipe’s configured targets
-
-
exe/ast-merge-recipefile arguments: Accept target files on command line-
ast-merge-recipe recipe.yml file1.md file2.md- Override recipe targets - Updated help text and banner to document new usage
-
-
bin/update_gem_family_sectionfile arguments: Accept target files on command line-
bin/update_gem_family_section vendor/my-gem/README.md- Process specific file(s) - If no files specified, defaults to
README.md+vendor/*/README.md - Added
--skip-fixoption to skip the formatting fix step
-
Changed
-
bin/update_gem_family_section: Refactored to useOptionParserfor clean option handling- Consistent with
bin/fix_readme_formattingstyle - Properly separates options from file arguments
- Consistent with
4.0.1 - 2026-01-11
- TAG: v4.0.1
- COVERAGE: 96.45% – 2553/2647 lines in 51 files
- BRANCH COVERAGE: 87.41% – 812/929 branches in 51 files
- 98.80% documented
Added
-
Ast::Merge::RSpec::MergeGemRegistry- Fully dynamic merge gem registration for RSpec dependency tags-
register(tag_name, require_path:, merger_class:, test_source:, category:)- Register a merge gem -
available?(tag_name)- Check if a merge gem is available and functional -
registered_gems- Get all registered gem tag names -
gems_by_category(category)- Filter gems by category (:markdown, :data, :code, :config, :other) -
summary- Get availability status of all registered gems - Automatically defines
*_available?methods onDependencyTagsat registration time - External merge gems can now get full RSpec tag support without modifying ast-merge
-
Changed
- Upgrade to tree_haver v5.0.1
-
Ast::Merge::AstNodenow inherits fromTreeHaver::Base::Node- Ensures synthetic nodes stay in sync with the canonical Node API
- Inherits
Comparable,Enumerablefrom base class - Retains all existing methods and behavior (Point, Location, signature, etc.)
- Constructor calls
super(self, source: source)to properly initialize base class
-
RSpec Dependency Tags refactored to use MergeGemRegistry
- Removed hardcoded merge gem availability checks
- Removed
MERGE_GEM_TEST_SOURCESconstant -
*_available?methods are now defined dynamically when gems register -
any_markdown_merge_available?now queries registry by category - RSpec exclusion filters are configured dynamically from registry
-
Ast::Merge::Testing::TestableNodenow delegates toTreeHaver::RSpec::TestableNode- The TestableNode implementation has been moved to tree_haver for sharing across all merge gems
-
spec/support/testable_node.rbnow requires and re-exports the tree_haver version - Backward compatible: existing tests continue to work unchanged
-
spec/ast/merge/node_wrapper_base_spec.rbrefactored to useTestableNodeinstead of mocks- Real TreeHaver::Node behavior for most tests
- Mocks only retained for edge case testing (e.g., invalid end_line before start_line)
4.0.0 - 2026-01-11
- TAG: v4.0.0
- COVERAGE: 96.52% – 2555/2647 lines in 51 files
- BRANCH COVERAGE: 87.62% – 814/929 branches in 51 files
- 98.80% documented
Added
-
Recipe::Preset#normalize_whitespace- option to collapse excessive blank lines in merged output -
Recipe::Preset#rehydrate_link_references- option to convert inline links to reference style -
Recipe::Runner::Result#problems- access document problems found during merge -
exe/ast-merge-recipe --show-problems- flag to display document problems in CLI output -
Ast::Merge::DiffMapperBase- Abstract base class for mapping unified git diffs to AST node paths-
DiffHunkstruct for representing diff hunks with line numbers and content -
DiffLinestruct for individual diff lines with type (:context,:addition,:removal) -
DiffMappingstruct for mapping changes to AST paths with operation type -
DiffParseResultstruct for parsed diff with file paths and hunks -
#parse_diff(diff_text)- Parse unified git diff format into structured hunks -
#determine_operation(hunk)- Detect:add,:remove, or:modifyfrom hunk content - Abstract
#map_hunk_to_pathsfor format-specific implementations - Abstract
#create_analysisfor format-specific file analysis
-
-
Ast::Merge::ConflictResolverBase- New options for advanced merge control:-
recursive: true | false | Integer- Control recursive merging of nested structures-
true(default): Unlimited depth recursive merging -
false: Disabled, replace entire matched nodes -
Integer > 0: Maximum recursion depth -
0: Invalid, raisesArgumentError
-
-
remove_template_missing_nodes: false- Whentrue, removes destination nodes not present in template -
#should_recurse?(depth)- Helper to check if recursion should continue at given depth -
#validate_recursive!- Validation for recursive parameter
-
-
exe/ast-merge-diff- CLI executable for applying git diffs via AST-aware merging- Auto-detects format from file extension (
.yml,.yaml,.json,.rb, etc.) -
--diff FILE- Path to unified diff file (use-for stdin, default: stdin) -
--original FILE- Original file for AST path mapping (required) -
--destination FILE- Destination file to merge into (required) -
--format FORMAT- Override format auto-detection -
--dry-run- Preview changes without writing -
--verbose- Detailed output -
--add-only- Only apply additions from diff -
--remove-only- Only apply removals from diff - Uses bundler/inline with dynamic gem loading based on detected format
- Auto-detects format from file extension (
Changed
- BREAKING: Upgrade to tree_haver v5.0.0
-
BREAKING: Refactored navigation classes into
Ast::Merge::Navigablenamespace-
Ast::Merge::NavigableStatement→Ast::Merge::Navigable::Statement -
Ast::Merge::InjectionPoint→Ast::Merge::Navigable::InjectionPoint -
Ast::Merge::InjectionPointFinder→Ast::Merge::Navigable::InjectionPointFinder - Each class is now in its own file under
lib/ast/merge/navigable/ - Uses autoload for lazy loading
-
-
bin/fix_readme_formatting- Rewritten to use SmartMerger API- Now uses
Markdown::Merge::SmartMergerwithnormalize_whitespace: :link_refsandrehydrate_link_references: true - The
:link_refsmode collapses excessive blank lines AND removes blank lines between consecutive link reference definitions - Merges empty template with destination to apply cleanup transformations
- Reports duplicate link definitions, link ref spacing fixes, and other problems from
MergeResult#problems - Removed custom regex-based link rehydration and whitespace normalization
- Now uses
Fixed
-
Ast::Merge::PartialTemplateMergerBase#normalize_matchernow preservessame_or_shallowerkey from boundary config -
Ast::Merge::PartialTemplateMergerBase#mergenow passesboundary_same_or_shallowertoInjectionPointFinder#find
3.1.0 - 2026-01-08
- TAG: v3.1.0
- COVERAGE: 96.89% – 2465/2544 lines in 47 files
- BRANCH COVERAGE: 89.62% – 794/886 branches in 47 files
- 98.75% documented
Added
-
Ast::Merge::EmitterBase- Abstract base class for format-specific emitters- Provides common infrastructure for converting AST structures back to text
- Tracks indentation level with configurable
indent_size(default: 2 spaces) - Manages output lines collection with
#linesaccessor -
#emit_blank_line- Emit an empty line -
#emit_leading_comments- Emit comments from CommentTracker -
#emit_raw_lines- Emit lines without modification (preserves exact formatting) -
#to_s- Get output as a single string with trailing newline -
#clear- Reset emitter state -
#indent/#dedent- Increase/decrease indentation level - Subclass hooks:
#initialize_subclass_state,#clear_subclass_state,#emit_tracked_comment - Used by jsonc-merge, json-merge, bash-merge, toml-merge, and psych-merge emitters
Changed
- tree_haver v4.0.0
3.0.0 - 2026-01-05
- TAG: v3.0.0
- COVERAGE: 96.93% – 2462/2540 lines in 47 files
- BRANCH COVERAGE: 89.62% – 794/886 branches in 47 files
- 98.72% documented
Added
-
TestableNodespec helper class that wraps a mock in a realTreeHaver::Node, providing consistent API testing without relying on fragile mocks -
Recipe::Preset#match_refineraccessor method (was missing, causing errors in Recipe::Runner) - Minimal reproduction specs for
to_commonmarknormalization behavior:-
spec/integration/link_reference_preservation_spec.rb- tests link ref preservation -
spec/integration/table_formatting_preservation_spec.rb- tests table padding preservation
-
-
Ast::Merge::PartialTemplateMergerBase- Abstract base class for parser-agnostic partial template merging-
#build_position_based_signature_generator- Creates signature generators that match elements by position - Position counters reset per document key, enabling tables at same position to match regardless of structure
-
Changed
-
BREAKING:
NavigableStatement#textnow requires nodes to conform to TreeHaver Node API (must have#textmethod)- Removed conditional fallbacks for
to_plaintext,to_commonmark,slice - Nodes must now implement
#textdirectly (all TreeHaver backends already do)
- Removed conditional fallbacks for
-
BREAKING:
ContentMatchRefiner#extract_contentnow requires nodes to conform to TreeHaver Node API- Removed conditional fallbacks for
text_content,string_content,content,to_s - Custom
content_extractorproc still supported for non-standard nodes
- Removed conditional fallbacks for
- Signature generators and typing scripts now receive TreeHaver nodes directly (no NavigableStatement wrapping)
- Removed NavigableStatement wrapping from
FileAnalyzable#generate_signatureandNodeTyping.process
Removed
-
BREAKING:
Ast::Merge::PartialTemplateMergerremoved. UseMarkdown::Merge::PartialTemplateMergerdirectly.- The base class
Ast::Merge::PartialTemplateMergerBaseremains for other parsers to extend - Migration: change
Ast::Merge::PartialTemplateMerger.new(parser: :markly, ...)to
Markdown::Merge::PartialTemplateMerger.new(backend: :markly, ...)
- The base class
Fixed
-
Source-based rendering:
Markdown::Merge::PartialTemplateMerger#node_to_textnow prefers extracting
original source text usinganalysis.source_rangeinstead ofto_commonmark. This preserves:- Link reference definitions (no conversion to inline links)
- Table column padding/alignment
- Original formatting exactly as written
2.0.10 - 2026-01-04
- TAG: v2.0.10
- COVERAGE: 97.10% – 2642/2721 lines in 48 files
- BRANCH COVERAGE: 89.57% – 893/997 branches in 48 files
- 98.72% documented
Added
- Dependency tags for
rbs_mergeandnot_rbs_merge
Changed
- Upgraded to
tree_haverv3.2.4 (major new features, and bug fixes, see release notes)
Fixed
-
PartialTemplateMerger#build_merged_contentpreviously always injected an extra newline between parts, now join is context-aware
2.0.9 - 2026-01-02
- TAG: v2.0.9
- COVERAGE: 97.09% – 2637/2716 lines in 48 files
- BRANCH COVERAGE: 89.64% – 883/985 branches in 48 files
- 98.71% documented
Fixed
-
NavigableStatement.find_matchingnow returns empty array when no criteria specified -
Previously, when bothtype: nilandtext: niland no block was given, the method would
match ALL statements (since no conditions filtered anything out). This caused
PartialTemplateMergerto incorrectly reporthas_section: truewhenanchor: nilwas passed.
Now returns an empty array when no criteria are specified.
2.0.8 - 2026-01-01
- TAG: v2.0.8
- COVERAGE: 97.09% – 2636/2715 lines in 48 files
- BRANCH COVERAGE: 89.73% – 882/983 branches in 48 files
- 98.71% documented
Added
-
Ast::Merge::NodeWrapperBaseabstract base class for format-specific node wrappers- Provides common functionality shared by
*::Merge::NodeWrapperclasses across gems - Handles source context (lines, source string), line info, comments, content extraction
- Defines abstract
#compute_signaturethat subclasses must implement - Includes
#node_wrapper?to distinguish fromNodeTyping::Wrapper - Includes
#underlying_nodeto access the raw TreeHaver node (NOT#unwrapto avoid
conflict withNodeTyping::Wrapper#unwrapsemantics inFileAnalyzable) - Documents relationship between
NodeWrapperBaseandNodeTyping::Wrapper:-
NodeWrapperBase: Format-specific functionality (line info, signatures, type predicates) -
NodeTyping::Wrapper: Custom merge classification (merge_typeattribute)
-
- Nodes can be double-wrapped:
NodeTyping::Wrapper(Format::Merge::NodeWrapper(tree_sitter_node)) - Accepts
**optionsin initialize for subclass-specific parameters (e.g.,backend,document_root)
- Provides common functionality shared by
2.0.7 - 2026-01-01
- TAG: v2.0.7
- COVERAGE: 97.31% – 2569/2640 lines in 47 files
- BRANCH COVERAGE: 89.87% – 869/967 branches in 47 files
- 98.84% documented
Added
-
Ast::Merge::NodeTyping::Normalizermodule for thread-safe backend type normalization- Provides shared infrastructure for format-specific normalizers (toml-merge, markdown-merge)
- Thread-safe backend registration via mutex-protected operations
-
configure_normalizerfor initial backend mappings configuration -
register_backendfor runtime registration of new backends -
canonical_typefor mapping backend-specific types to canonical types -
wrapfor wrapping nodes with canonical merge_type -
registered_backends,backend_registered?,mappings_for,canonical_typesquery methods
-
Ast::Merge::NodeTyping::FrozenWrapperclass for frozen AST nodes- Includes
Freezablebehavior for freeze marker support -
frozen_node?,slice,signaturemethods
- Includes
- Split
NodeTypingmodule into separate files following autoload pattern:ast/merge/node_typing/wrapper.rbast/merge/node_typing/frozen_wrapper.rbast/merge/node_typing/normalizer.rb
- Comprehensive specs for
NodeTyping::Normalizerincluding thread-safety tests - RBS type signatures for
NodeTyping::NormalizerandNodeTyping::FrozenWrapper
2.0.6 - 2026-01-01
- TAG: v2.0.6
- COVERAGE: 97.19% – 2522/2595 lines in 44 files
- BRANCH COVERAGE: 89.91% – 864/961 branches in 44 files
- 98.82% documented
Added
- Comprehensive mocked tests for
Ast::Merge::Recipe::Runner(47 new tests):- Tests for
#runmethod with section found, changed, and unchanged scenarios - Tests for
#runwith section not found (skipped vs appended) - Tests for actual file writes in non-dry_run mode
- Tests for exception handling during merge
- Tests for
#summarywith all status counts (updated, would_update, unchanged, skipped, errors) - Tests for
#results_by_statusgrouping - Tests for
#results_tableformatting (file, status, changed, message) - Tests for
#summary_tablein both dry_run and non-dry_run modes - Tests for
#make_relativeedge cases (base_dir, recipe base, unknown paths) - Tests for
#make_relativewithout recipe_path
- Tests for
2.0.5 - 2025-12-31
- TAG: v2.0.5
- COVERAGE: 91.68% – 2379/2595 lines in 44 files
- BRANCH COVERAGE: 81.37% – 782/961 branches in 44 files
- 98.82% documented
Added
- Comprehensive tests for
Ast::Merge::AstNodeand nested structs (Point, Location) - Tests for
Ast::Merge::Comment::Styleclass methods and instance methods - Tests for
Ast::Merge::Comment::Lineincluding freeze marker detection - Tests for
Ast::Merge::Comment::Blockincluding raw_content and children modes - Tests for
Ast::Merge::Comment::Parseredge cases (unclosed blocks, mixed content, auto-detection) - Tests for
Ast::Merge::NavigableStatementtree navigation methods - Tests for
Ast::Merge::InjectionPoint(start_line, end_line, inspect) - Tests for
Ast::Merge::InjectionPointFinderboundary options (boundary_type, boundary_text, boundary_matcher, boundary_same_or_shallower) - Tests for
Ast::Merge::PartialTemplateMerger::Resultincluding injection_point and default values - Tests for
Ast::Merge::PartialTemplateMergertext pattern normalization (regex strings, plain strings) - Tests for
Ast::Merge::PartialTemplateMergeranchor normalization with level options - Tests for
Ast::Merge::PartialTemplateMergerunknown when_missing fallback behavior - Tests for
Ast::Merge::PartialTemplateMergersection boundary detection and replace_mode behavior - Tests for
Ast::Merge::PartialTemplateMergerunknown parser error handling - Tests for
Ast::Merge::Recipe::Runner::Resultstats and error attributes - Tests for
Ast::Merge::Recipe::Runneractual file writes (non-dry-run mode) - Tests for
Ast::Merge::Recipe::Runnererror handling (unreadable files, missing template) - Tests for
Ast::Merge::Recipe::Runnerwhen_missing with append behavior - Tests for
Ast::Merge::Recipe::Configsame_or_shallower boundary, replace_mode, level options - Tests for
Ast::Merge::Recipe::Configinjection parsing with empty/nil/Regexp patterns - Tests for
Ast::Merge::Recipe::Configexpand_targets with absolute patterns - Tests for
Ast::Merge::Recipe::Presetcallable add_missing and node_typing - Tests for
Ast::Merge::Recipe::Presetscript_loader caching - Tests for
Ast::Merge::Recipe::ScriptLoadersyntax error handling - Tests for
Ast::Merge::Recipe::ScriptLoaderabsolute path resolution - Tests for
Ast::Merge::ContentMatchRefinerextract_node_type with typed nodes - Tests for
Ast::Merge::ContentMatchRefinerfilter_nodes with node_types
Changed
- tree_haver v3.2.1
- Internal files now use autoload instead of
require_relativefor consistency
2.0.4 - 2025-12-31
- TAG: v2.0.4
- COVERAGE: 88.61% – 2903/3276 lines in 53 files
- BRANCH COVERAGE: 67.90% – 700/1031 branches in 53 files
- 98.82% documented
Added
- Many more tests
Fixed
- RSpec shared examples for
Ast::Merge::DebugLoggernow handle Ruby 4.0+ where benchmark is a bundled gem- The
#time logs start and completion with timingtest now checksBENCHMARK_AVAILABLEconstant - When benchmark is available: expects full timing output with “Starting:”, “Completed:”, and
real_ms - When benchmark is unavailable: expects warning message about benchmark gem not being available
- Fixes CI failures on Ruby 4.0.0 for downstream gems (e.g., bash-merge) using the shared examples
- The
2.0.3 - 2025-12-30
- TAG: v2.0.3
- COVERAGE: 88.45% – 2894/3272 lines in 53 files
- BRANCH COVERAGE: 67.83% – 698/1029 branches in 53 files
- 98.82% documented
Fixed
-
Ast::Merge::DebugLogger::BENCHMARK_AVAILABLEnow correctly detects when benchmark gem is unavailable- Previous implementation used
autoloadwhich never raisesLoadError(it only registers for lazy loading) - Now uses
require "benchmark"which properly catchesLoadErroron Ruby 4.0+ where benchmark is a bundled gem - The
#timemethod now correctly falls back to non-timed execution when benchmark is unavailable
- Previous implementation used
2.0.2 - 2025-12-30
- TAG: v2.0.2
- COVERAGE: 88.47% – 2894/3271 lines in 53 files
- BRANCH COVERAGE: 67.83% – 698/1029 branches in 53 files
- 98.82% documented
Added
- Backend Platform Compatibility section to README and GEM_FAMILY_SECTION.md
- Documents which tree_haver backends work on MRI, JRuby, and TruffleRuby
- Explains why JRuby and TruffleRuby have limited tree-sitter backend support
Changed
- Updated RSpec README documentation for tree_haver dependency tag changes
- All tags now follow consistent naming conventions with suffixes
- Backend tags use
*_backendsuffix - Engine tags use
*_enginesuffix - Grammar tags use
*_grammarsuffix - Parsing capability tags use
*_parsingsuffix - See lib/ast/merge/rspec/README.md for complete tag reference
2.0.1 - 2025-12-29
- TAG: v2.0.1
- COVERAGE: 88.47% – 2894/3271 lines in 53 files
- BRANCH COVERAGE: 67.83% – 698/1029 branches in 53 files
- 98.82% documented
Changed
- Upgraded dependencies
- yard-fence v0.8.1
- tree_haver v3.1.2
- kettle-test v1.0.7
Fixed
- Documentation cleanup (via yard-fence)
2.0.0 - 2025-12-28
- TAG: v2.0.0
- COVERAGE: 88.47% – 2894/3271 lines in 53 files
- BRANCH COVERAGE: 67.83% – 698/1029 branches in 53 files
- 98.82% documented
Added
-
RSpec Dependency Tags: Conditional test execution based on available merge gems
- New
lib/ast/merge/rspec/dependency_tags.rbprovides automatic test filtering - Tags for all merge gems:
:markly_merge,:prism_merge,:json_merge,:toml_merge, etc. - Composite tag
:any_markdown_mergefor tests that work with any markdown merger - Negated tags (e.g.,
:not_prism_merge) for testing fallback behavior -
AST_MERGE_DEBUG=1environment variable prints dependency summary - Eliminates need for
requirestatements inside spec files - See lib/ast/merge/rspec/README.md for full documentation
- New
-
Recipe::Preset: Base class for merge configuration presets
- Provides merge configuration (signature generators, node typing, preferences) without requiring template files
-
Recipe::Confignow inherits fromPreset, adding template/target handling -
to_hmethod converts preset to SmartMerger-compatible options hash - Enables kettle-jem and other libraries to define reusable merge presets
- Supports script loading for signature generators and node typing via
ScriptLoader
-
exe/ast-merge-recipe: Shipped executable for running merge recipes
- Uses
bundler/inlinefor dependency management - Supports
--dry-run,--verbose,--parser,--base-diroptions - Uses
optparsefor proper option parsing - Loads merge gems from recipe YAML
merge_gemssection - Development mode via
KETTLE_RB_DEV=trueor--dev-rootoption - All gems sourced from gem.coop
- Uses
-
PartialTemplateMerger: Section-based merging for partial templates
- Find and merge specific sections in destination documents
- Support for both
replace_mode(full replacement) and merge mode (intelligent merging) - Configurable anchor and boundary matchers for section detection
- Custom
signature_generatorandnode_typingsupport for advanced matching -
when_missingbehavior::skip,:append,:prepend
-
Recipe: YAML-based recipe system for defining merge operations
- Load recipes from YAML files with
Recipe.load(path) - Define template, targets, injection point, merge preferences
- Support for
when_missing: skip|add|errorbehavior - Support for
replace_modeoption - Automatic path resolution relative to recipe file location
- Load recipes from YAML files with
-
RecipeRunner: Execute recipes against multiple target files
- Uses PartialTemplateMerger for section-based merging
- Dry-run mode with
--dry-runflag - Results tracking with status, stats, and error reporting
- TableTennis integration for formatted output
- Support for different parsers (markly, commonmarker, prism, psych)
-
RecipeScriptLoader: Load Ruby scripts referenced by recipes
- Convention: scripts in folder matching recipe basename (e.g.,
my_recipe/formy_recipe.yml) - Support for inline lambda expressions in YAML
- Script caching for performance
- Validation that scripts return callable objects
- Convention: scripts in folder matching recipe basename (e.g.,
-
bin/ast-merge-recipe: CLI for running merge recipes
bin/ast-merge-recipe RECIPE_FILE [--dry-run] [--verbose]- Color-coded output with status symbols
- Summary table with counts
-
NavigableStatement: New wrapper class for uniform node navigation (language-agnostic)
- Provides flat list navigation (
next,previous,index) for all nodes - Tree depth calculation with
tree_depthmethod -
same_or_shallower_than?for level-based boundary detection - Language-agnostic section boundaries using tree hierarchy
- Provides tree navigation (
tree_parent,tree_next,tree_previous) for parser-backed nodes -
synthetic?method to detect nodes without tree navigation (GapLineNode, LinkDefinitionNode, etc.) -
type?andtext_matches?helpers for node matching -
node_attributefor accessing parser-specific attributes with fallbacks -
each_followingandtake_untilfor sequential traversal -
find_matchingandfind_firstclass methods for querying statement lists -
build_listclass method to create linked statement lists from raw statements
- Provides flat list navigation (
-
InjectionPoint: New class for defining where content can be injected (language-agnostic)
- Supports positions:
:before,:after,:first_child,:last_child,:replace - Optional boundary for replacement ranges
-
replacement?,child_injection?,sibling_injection?predicates -
replaced_statementsto get all statements in a replacement range
- Supports positions:
-
InjectionPointFinder: New class for finding injection points by matching criteria
-
findto locate a single injection point by type/text pattern -
find_allto locate all matching injection points - Works with any
*-mergegem (prism-merge, markly-merge, psych-merge, etc.)
-
-
SmartMergerBase:
add_template_only_nodesnow accepts a callable filter- Boolean
true/falsestill works as before (add all or none) - Callable (Proc/Lambda) receives
(node, entry)and returns truthy to add the node - Enables selective addition of template-only nodes based on signature, type, or content
- Example use case: Add missing link reference definitions while skipping other template content
- Entry hash includes
:template_node,:signature,:template_indexfor filtering decisions
- Boolean
-
ContentMatchRefiner: New match refiner for fuzzy text content matching
- Uses Levenshtein distance to pair nodes with similar (but not identical) text
- Configurable scoring weights for content similarity, length, and position
- Custom content extractor support for parser-specific text extraction
- Node type filtering to limit which types are processed
- Can be combined with other refiners (e.g., TableMatchRefiner)
- Useful for matching paragraphs, headings, comments with minor edits
-
SmartMergerBase: Added validity check after FileAnalysis creation
- Checks
valid?after creating FileAnalysis and raises appropriate parse error if invalid - Catches silent failures like grammar not available or parse errors
- Documented the FileAnalysis error handling pattern for all *-merge gems
- Checks
-
SmartMergerBase: Added explicit
node_typingparameter- All child SmartMergers were already using
node_typingvia**format_options - Now explicitly documented and accessible via
attr_reader :node_typing - Validates node_typing configuration via
NodeTyping.validate!if provided - Enables per-node-type merge preferences across all
*-mergegems
- All child SmartMergers were already using
-
ConflictResolverBase: Added
match_refinerparameter and**optionsfor forward compatibility- All batch-strategy resolvers were storing
match_refinerlocally - Now explicitly accepted in base class with
attr_reader :match_refiner - Added
**optionscatch-all for future parameters without breaking child classes
- All batch-strategy resolvers were storing
-
MergeResultBase: Added
**optionsfor forward compatibility- Allows subclasses to accept new parameters without modification
- Maintains backward compatibility with existing no-arg and keyword-arg constructors
-
RBS Signatures: Added comprehensive type signatures for base classes
-
SmartMergerBasewith all standard options and abstract method declarations -
ConflictResolverBasewith strategy-based resolution methods -
MergeResultBasewith unified constructor and decision tracking -
MatchRefinerBasewith similarity computation interface -
RegionMergeablemodule for nested content merging -
NodeTypingmodule withWrapperclass for typed nodes - Type aliases:
node_typing_callable,node_typing_hash,preference_type
-
-
Documentation: Updated README with comprehensive base class documentation
- Standard options table with all
SmartMergerBaseparameters - Forward compatibility section explaining the
**optionspattern - Complete “Creating a New Merge Gem” example with all base classes
- Base Classes Reference table
- Standard options table with all
Changed
-
BREAKING - Namespace Reorganization: Major restructuring for better organization
-
Ast::Merge::Region→Ast::Merge::Detector::Region(Struct moved into Detector namespace) -
Ast::Merge::RegionDetectorBase→Ast::Merge::Detector::Base -
Ast::Merge::RegionMergeable→Ast::Merge::Detector::Mergeable -
Ast::Merge::FencedCodeBlockDetector→Ast::Merge::Detector::FencedCodeBlock -
Ast::Merge::YamlFrontmatterDetector→Ast::Merge::Detector::YamlFrontmatter -
Ast::Merge::TomlFrontmatterDetector→Ast::Merge::Detector::TomlFrontmatter -
Ast::Merge::Recipeclass →Ast::Merge::Recipe::Config -
Ast::Merge::RecipeRunner→Ast::Merge::Recipe::Runner -
Ast::Merge::RecipeScriptLoader→Ast::Merge::Recipe::ScriptLoader -
Ast::Merge::RegionMergeable::RegionConfig→Ast::Merge::Detector::Mergeable::Config -
Ast::Merge::RegionMergeable::ExtractedRegion→Ast::Merge::Detector::Mergeable::ExtractedRegion
-
1.1.0 - 2025-12-18
- TAG: v1.1.0
- COVERAGE: 95.16% – 2338/2457 lines in 44 files
- BRANCH COVERAGE: 82.59% – 517/626 branches in 44 files
- 98.45% documented
Added
-
tree_haver Integration: Major architectural enhancement
- Added
tree_haver(~> 3.1) as a runtime dependency -
Ast::Merge::AstNodenow implements the TreeHaver::Node protocol for compatibility with tree_haver-based merge operations- Adds:
type,kind,text,start_byte,end_byte,start_point,end_point,children,child_count,child(index),each,named?,structural?,has_error?,missing?,inner_node - Adds
Pointstruct compatible withTreeHaver::Point - Adds
SyntheticNodealias for clarity (synthetic = not backed by a real parser)
- Adds:
-
Comment::Line,Comment::Block,Comment::Emptynow have explicittypemethods -
Text::LineNodeandText::WordNodenow inherit fromAstNode, gaining TreeHaver::Node protocol compliance - This enables
*-mergegems to leverage tree_haver’s cross-Ruby parsing capabilities (MRI, JRuby, TruffleRuby)
- Added
-
Documentation: Comprehensive updates across the gem family
- Updated all vendor gem READMEs with standardized gem family tables
- Added
tree_haveras the foundation layer in architecture documentation - Clarified the two-layer architecture: tree_haver (parsing) → ast-merge (merge infrastructure)
- Added detailed documentation to
FencedCodeBlockDetectorexplaining when to use native AST nodes vs text-based detection - Updated markdown-merge documentation to highlight inner code block merging capabilities
-
Example Scripts: Added comprehensive examples demonstrating inner-merge capabilities
-
examples/markdown_code_merge.rb- Shows how markdown-merge delegates to language-specific parsers for semantic merging - Documentation proving that language-specific parsers create full ASTs of embedded code blocks
-
Changed
-
Architecture: Refactored to use tree_haver as the parsing foundation
- All tree-sitter-based gems (bash-merge, json-merge, jsonc-merge, toml-merge) now use tree_haver
- Parser-specific gems (prism-merge, psych-merge, markdown-merge, markly-merge, commonmarker-merge) use tree_haver backends
- Provides unified API across different Ruby implementations and parsing backends
-
Documentation Structure: Standardized gem family tables across all 12 vendor gems
-
Changed from 3-column to 4-column format: Gem Format Parser Backend(s) Description - All parser backends now annotated with “(via tree_haver)” where applicable
- ast-merge description updated from “Shared infrastructure” to “Infrastructure: Shared base classes and merge logic”
- markdown-merge description updated to “Foundation: Shared base for Markdown mergers with inner code block merging”
-
- Configuration Documentation: Enhanced backend selection documentation
Fixed
- Fixed gemspec and Appraisals alignment with tree_haver requirements
- Fixed CI workflow conditions and retry logic
- Fixed badge rendering in documentation
- Fixed README structure issues (removed H3 duplicates, standardized gem family tables)
1.0.0 - 2025-12-12
- TAG: v1.0.0
- COVERAGE: 96.60% – 2301/2382 lines in 44 files
- BRANCH COVERAGE: 86.00% – 522/607 branches in 44 files
- 99.29% documented
Added
- Initial release