Module: Rigor::FlowContribution::Merger

Defined in:
lib/rigor/flow_contribution/merger.rb

Overview

Composes any number of Rigor::FlowContribution bundles into a single MergeResult per ADR-2 § “Plugin Contribution Merging”. The merger is the **single point of integration** the analyzer uses to combine contributions from built-in narrowing rules, ‘RBS::Extended` annotations, and plugins; slice 4 routes the existing internal narrowing through it and slice 6 wires plugin-side cache producers around it.

## Authority tiers

  • Tier 0: ‘:builtin` — Core Ruby semantics and accepted RBS contracts. Authoritative; lower tiers may not contradict.

  • Tier 1: ‘:rbs_extended` (`RBS::Extended` directive bundles, v0.0.9 group D reference impl) and `:generated` (generated signatures / metadata).

  • Tier 2: ‘:plugin` and `plugin.<id>` source families.

  • Tier 3: anything else — treated as the lowest tier.

Within a tier, contributions are merged in deterministic order: provenance-supplied ‘plugin_id` alphabetical (nil plugin ids sort first to keep `:rbs_extended` / `:generated` pre-plugin contributions stable), then by their original input position as the final tie-break.

## Composition rules (ADR-2)

  • ‘:return_type` — Intersect via `Type::Combinator.intersection`; collapse to bot raises `:return_type_collapse`.

  • ‘:truthy_fact` / `:falsey_fact` / `:post_return_fact` —Edge-local; accumulate while deduping by payload equality.

  • ‘:mutation` / `:invalidation` / `:role` — Union; dedupe by equality.

  • ‘:exception` — Single-valued. Two non-`nil` non-equal exceptional effects raise `:exceptional_disagreement`.

## Cross-tier contradictions

Lower tiers may refine higher tiers but must not weaken them. Slice 3 surfaces contradictions through ‘:lower_tier_contradiction` when:

  • the higher tier already pinned a ‘return_type` and a lower tier’s intersection collapses to ‘bot`;

  • the higher tier set ‘exceptional` to a non-`nil` value and a lower tier disagrees.

In every conflict case the result keeps the higher-tier value for that slot, records a Conflict with both provenances, and continues processing the remaining slots / contributions.

Constant Summary collapse

AUTHORITY_TIERS =

rubocop:disable Metrics/ModuleLength

{
  builtin: 0,
  rbs_extended: 1,
  generated: 1
}.freeze

Class Method Summary collapse

Class Method Details

.merge(contributions) ⇒ MergeResult

Parameters:

Returns:



65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/rigor/flow_contribution/merger.rb', line 65

def merge(contributions)
  contributions = Array(contributions)
  return MergeResult.new if contributions.empty?

  ordered = order_contributions(contributions)
  state = MergeState.new
  ordered.each do |contribution|
    tier = tier_for(contribution.provenance)
    fold_into(state, contribution, tier)
  end
  state.to_result
end

.tier_for(provenance) ⇒ Object



78
79
80
81
82
83
84
# File 'lib/rigor/flow_contribution/merger.rb', line 78

def tier_for(provenance)
  family = provenance.respond_to?(:source_family) ? provenance.source_family : nil
  return AUTHORITY_TIERS[family] if AUTHORITY_TIERS.key?(family)
  return 2 if family == :plugin || family.to_s.start_with?("plugin.")

  3
end