AGENTS.md - rbs-merge Development Guide
π― Project Overview
rbs-merge is a format-specific implementation of the *-merge gem family for RBS (Ruby Signature) files. It provides intelligent RBS file merging using AST analysis with the RBS parser.
Core Philosophy: Intelligent RBS type signature merging that preserves structure, comments, and formatting while applying updates from templates.
Repository: https://github.com/kettle-rb/rbs-merge
Current Version: 2.0.0
Required Ruby: >= 3.2.0 (currently developed against Ruby 4.0.1)
ποΈ Architecture: Format-Specific Implementation
What rbs-merge Provides
-
Rbs::Merge::SmartMergerβ RBS-specific SmartMerger implementation -
Rbs::Merge::FileAnalysisβ RBS file analysis with class/module/method extraction -
Rbs::Merge::NodeWrapperβ Wrapper for RBS AST nodes -
Rbs::Merge::MergeResultβ RBS-specific merge result -
Rbs::Merge::ConflictResolverβ RBS conflict resolution -
Rbs::Merge::FreezeNodeβ RBS freeze block support -
Rbs::Merge::DebugLoggerβ RBS-specific debug logging
Key Dependencies
| Gem | Role |
|---|---|
ast-merge (~> 4.0) |
Base classes and shared infrastructure |
tree_haver (~> 5.0) |
Unified parser adapter (wraps RBS) |
rbs (>= 3.10) |
Ruby type signature parser |
version_gem (~> 1.1) |
Version management |
Parser Backend
rbs-merge uses the RBS parser exclusively via TreeHaverβs :rbs_backend:
| Backend | Parser | Platform | Notes |
|---|---|---|---|
:rbs_backend |
RBS gem | MRI only | Official Ruby type signature parser |
π Project Structure
lib/rbs/merge/
βββ smart_merger.rb # Main SmartMerger implementation
βββ file_analysis.rb # RBS file analysis
βββ node_wrapper.rb # AST node wrapper
βββ merge_result.rb # Merge result object
βββ conflict_resolver.rb # Conflict resolution
βββ freeze_node.rb # Freeze block support
βββ debug_logger.rb # Debug logging
βββ version.rb
spec/rbs/merge/
βββ smart_merger_spec.rb
βββ file_analysis_spec.rb
βββ node_wrapper_spec.rb
βββ integration/
π§ Development Workflows
Running Tests
# Full suite
bundle exec rspec
# Single file (disable coverage threshold check)
K_SOUP_COV_MIN_HARD=false bundle exec rspec spec/rbs/merge/smart_merger_spec.rb
# RBS backend tests
bundle exec rspec --tag rbs_backend
bundle exec rspec --tag rbs_grammar
Coverage Reports
cd /home/pboling/src/kettle-rb/ast-merge/vendor/rbs-merge
bin/rake coverage && bin/kettle-soup-cover -d
π Project Conventions
API Conventions
SmartMerger API
-
mergeβ Returns a String (the merged RBS content) -
merge_resultβ Returns a MergeResult object -
to_son MergeResult returns the merged content as a string
RBS-Specific Features
Class/Module Merging:
# Template
class MyClass
def foo: () -> String
end
# Destination
class MyClass
def bar: () -> Integer
def foo: () -> String # Custom implementation
end
Freeze Blocks:
# rbs-merge:freeze
class CustomType
attr_reader custom: String
end
# rbs-merge:unfreeze
class StandardType
attr_reader name: String
end
π§ͺ Testing Patterns
TreeHaver Dependency Tags
Available tags:
-
:rbs_grammarβ Requires RBS grammar -
:rbs_backendβ Requires RBS backend -
:rbs_parsingβ Requires RBS parser
β CORRECT:
RSpec.describe Rbs::Merge::SmartMerger, :rbs_backend do
# Skipped if RBS parser not available
end
β WRONG:
before do
skip "Requires RBS" unless defined?(RBS) # DO NOT DO THIS
end
π‘ Key Insights
- Type signature merging: RBS definitions matched by name (class/module/method)
- Method overloads: Multiple method signatures can coexist
- Generic types: Type parameters preserved during merge
- Interface definitions: Interfaces matched by name
-
Freeze blocks use
# rbs-merge:freeze: Standard comment syntax - MRI only: RBS parser only works on MRI Ruby
π« Common Pitfalls
- RBS requires MRI: Does not work on JRuby or TruffleRuby
-
NEVER use manual skip checks β Use dependency tags (
:rbs_backend,:rbs_grammar) - Do NOT load vendor gems β They are not part of this project; they do not exist in CI
-
Use
tmp/for temporary files β Never use/tmpor other system directories
π§ RBS-Specific Notes
Declaration Types
class MyClass # Class declaration
attr_reader name: String # Attribute
def foo: () -> String # Method signature
end
module MyModule # Module declaration
def self.bar: () -> Integer # Module method
end
interface _Enumerable[T] # Interface with type parameter
def each: () { (T) -> void } -> void
end
type status = :pending | :done # Type alias
Merge Behavior
- Classes: Matched by class name; methods merged within
- Methods: Matched by method name; signatures can be overloaded
- Interfaces: Matched by interface name
- Type aliases: Matched by type name
- Comments: Preserved when attached to declarations
- Freeze blocks: Protect customizations from template updates
- Generic types: Type parameters preserved and matched