Class: RosettAi::Composition::Composer

Inherits:
Object
  • Object
show all
Defined in:
lib/rosett_ai/composition/composer.rb

Overview

Orchestrates behaviour composition: discovers behaviour files, resolves scopes, sorts by priority, detects conflicts and circular dependencies, and produces a deterministic result.

Supports configurable merge strategies for rule ID collisions:

  • first_wins: highest-priority rule wins (default)
  • deep_merge: recursively merge hash fields from all scopes
  • array_union: combine array fields from all scopes

Author:

  • hugo

  • claude

Instance Method Summary collapse

Constructor Details

#initialize(scope_resolver: ScopeResolver.new, priority_sorter: PrioritySorter.new, conflict_detector: ConflictDetector.new, cycle_detector: CircularDependencyDetector.new) ⇒ Composer

Returns a new instance of Composer.

Parameters:

  • scope_resolver (ScopeResolver) (defaults to: ScopeResolver.new)

    scope resolution strategy

  • priority_sorter (PrioritySorter) (defaults to: PrioritySorter.new)

    priority sorting strategy

  • conflict_detector (ConflictDetector) (defaults to: ConflictDetector.new)

    conflict detection

  • cycle_detector (CircularDependencyDetector) (defaults to: CircularDependencyDetector.new)

    cycle detection



24
25
26
27
28
29
30
31
32
33
34
# File 'lib/rosett_ai/composition/composer.rb', line 24

def initialize(
  scope_resolver: ScopeResolver.new,
  priority_sorter: PrioritySorter.new,
  conflict_detector: ConflictDetector.new,
  cycle_detector: CircularDependencyDetector.new
)
  @scope_resolver = scope_resolver
  @priority_sorter = priority_sorter
  @conflict_detector = conflict_detector
  @cycle_detector = cycle_detector
end

Instance Method Details

#compose(behaviour_files, project_root: nil, strict: false, target: nil, merge_strategy: 'first_wins') ⇒ CompositionResult

Composes all behaviour files into a single result.

Parameters:

  • behaviour_files (Array<String>)

    paths to behaviour YAML files

  • project_root (Pathname, nil) (defaults to: nil)

    project root for scope resolution

  • strict (Boolean) (defaults to: false)

    treat warnings as errors

  • target (String, nil) (defaults to: nil)

    compilation target (for sensitive filtering)

  • merge_strategy (String) (defaults to: 'first_wins')

    merge strategy name (first_wins, deep_merge, array_union)

Returns:

Raises:



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/rosett_ai/composition/composer.rb', line 45

def compose(behaviour_files, project_root: nil, strict: false, target: nil,
            merge_strategy: 'first_wins')
  behaviours = load_behaviours(behaviour_files, project_root: project_root)
  check_circular_dependencies!(behaviours)

  annotated_rules = annotate_rules(behaviours)
  annotated_rules = filter_sensitive(annotated_rules, target) if target

  sorted_rules = @priority_sorter.sort_rules(annotated_rules)
  conflicts = @conflict_detector.detect(sorted_rules)

  if strict && !conflicts.empty?
    raise RosettAi::CompositionError,
          "Composition conflicts in strict mode:\n#{conflicts.join("\n")}"
  end

  strategy = MergeStrategy.resolve(merge_strategy)
  merged_rules = strategy.new.apply(sorted_rules)

  trace = build_trace(merged_rules)
  warnings = build_warnings(conflicts, behaviours)
  sources = behaviours.to_h { |behaviour| [behaviour[:name], behaviour[:scope]] }

  CompositionResult.new(
    rules: merged_rules,
    trace: trace,
    warnings: warnings,
    conflicts: conflicts,
    sources: sources,
    merge_strategy: merge_strategy
  )
end