lex-mind-growth 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/legion/extensions/mind_growth/client.rb +5 -0
- data/lib/legion/extensions/mind_growth/helpers/phase_allocator.rb +106 -0
- data/lib/legion/extensions/mind_growth/runners/integration_tester.rb +116 -0
- data/lib/legion/extensions/mind_growth/runners/retrospective.rb +147 -0
- data/lib/legion/extensions/mind_growth/runners/wirer.rb +145 -0
- data/lib/legion/extensions/mind_growth/version.rb +1 -1
- data/lib/legion/extensions/mind_growth.rb +4 -0
- data/spec/legion/extensions/mind_growth/helpers/phase_allocator_spec.rb +323 -0
- data/spec/legion/extensions/mind_growth/runners/integration_tester_spec.rb +334 -0
- data/spec/legion/extensions/mind_growth/runners/retrospective_spec.rb +466 -0
- data/spec/legion/extensions/mind_growth/runners/wirer_spec.rb +526 -0
- metadata +9 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '0855bf68cbc39002e5c08851edd6f3ce71afddb38f0633078a68d878fe954dec'
|
|
4
|
+
data.tar.gz: dc106552129f3d41f91711c51fca3b2a90a0198aede48d14b4d589c0b319fea0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7d423da820af5d21f8b8f33f3c86850ae6cc6734d717386d31a862b800573694148626ade4587f5d2dbf10bafa14a61f0c973a2b1cefb1fe92817ef6c38527cd
|
|
7
|
+
data.tar.gz: 613aabc82f554a7de59965e8bb6a6543a72e346e74ea6618a6de65c15b4b40a2d2c4220230281dbf51e613aaa87d06b40738988627137244e9c7247229aef1be
|
|
@@ -30,6 +30,11 @@ module Legion
|
|
|
30
30
|
# Orchestrator delegation
|
|
31
31
|
def run_growth_cycle(**) = Runners::Orchestrator.run_growth_cycle(**)
|
|
32
32
|
def growth_status(**) = Runners::Orchestrator.growth_status(**)
|
|
33
|
+
|
|
34
|
+
# Retrospective delegation
|
|
35
|
+
def session_report(**) = Runners::Retrospective.session_report(**)
|
|
36
|
+
def trend_analysis(**) = Runners::Retrospective.trend_analysis(**)
|
|
37
|
+
def learning_extraction(**) = Runners::Retrospective.learning_extraction(**)
|
|
33
38
|
end
|
|
34
39
|
end
|
|
35
40
|
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module MindGrowth
|
|
6
|
+
module Helpers
|
|
7
|
+
module PhaseAllocator
|
|
8
|
+
# Maps cognitive categories to GAIA tick phases
|
|
9
|
+
CATEGORY_PHASE_MAP = {
|
|
10
|
+
perception: :sensory_processing,
|
|
11
|
+
attention: :sensory_processing,
|
|
12
|
+
emotion: :emotional_evaluation,
|
|
13
|
+
affect: :emotional_evaluation,
|
|
14
|
+
memory: :memory_retrieval,
|
|
15
|
+
knowledge: :knowledge_retrieval,
|
|
16
|
+
identity: :identity_entropy_check,
|
|
17
|
+
cognition: :working_memory_integration,
|
|
18
|
+
reasoning: :working_memory_integration,
|
|
19
|
+
safety: :procedural_check,
|
|
20
|
+
defense: :procedural_check,
|
|
21
|
+
prediction: :prediction_engine,
|
|
22
|
+
inference: :prediction_engine,
|
|
23
|
+
communication: :mesh_interface,
|
|
24
|
+
social: :mesh_interface,
|
|
25
|
+
coordination: :mesh_interface,
|
|
26
|
+
motivation: :action_selection,
|
|
27
|
+
executive: :action_selection,
|
|
28
|
+
learning: :memory_consolidation,
|
|
29
|
+
consolidation: :memory_consolidation,
|
|
30
|
+
introspection: :post_tick_reflection,
|
|
31
|
+
self: :post_tick_reflection,
|
|
32
|
+
reflection: :post_tick_reflection
|
|
33
|
+
}.freeze
|
|
34
|
+
|
|
35
|
+
# Dream cycle phase mappings
|
|
36
|
+
DREAM_PHASE_MAP = {
|
|
37
|
+
memory: :memory_audit,
|
|
38
|
+
association: :association_walk,
|
|
39
|
+
conflict: :contradiction_resolution,
|
|
40
|
+
curiosity: :agenda_formation,
|
|
41
|
+
consolidation: :consolidation_commit,
|
|
42
|
+
knowledge: :knowledge_promotion,
|
|
43
|
+
reflection: :dream_reflection,
|
|
44
|
+
narrative: :dream_narration
|
|
45
|
+
}.freeze
|
|
46
|
+
|
|
47
|
+
# Maps method name substrings to inferred GAIA phases for unknown categories
|
|
48
|
+
METHOD_INFERENCE_MAP = [
|
|
49
|
+
[%w[filter sense detect], :sensory_processing],
|
|
50
|
+
[%w[predict forecast estimate], :prediction_engine],
|
|
51
|
+
[%w[reflect evaluate assess], :post_tick_reflection],
|
|
52
|
+
[%w[store retrieve recall], :memory_retrieval],
|
|
53
|
+
[%w[decide select choose], :action_selection]
|
|
54
|
+
].freeze
|
|
55
|
+
|
|
56
|
+
module_function
|
|
57
|
+
|
|
58
|
+
def allocate_phase(category:, runner_methods: [])
|
|
59
|
+
category_sym = category.to_s.downcase.to_sym
|
|
60
|
+
|
|
61
|
+
# Check active phases first
|
|
62
|
+
phase = CATEGORY_PHASE_MAP[category_sym]
|
|
63
|
+
return { phase: phase, cycle: :active, confidence: :high } if phase
|
|
64
|
+
|
|
65
|
+
# Try to infer from runner method names
|
|
66
|
+
inferred = infer_from_methods(runner_methods)
|
|
67
|
+
return inferred if inferred
|
|
68
|
+
|
|
69
|
+
# Default to working_memory_integration (safest catch-all)
|
|
70
|
+
{ phase: :working_memory_integration, cycle: :active, confidence: :low }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def allocate_dream_phase(category:)
|
|
74
|
+
category_sym = category.to_s.downcase.to_sym
|
|
75
|
+
phase = DREAM_PHASE_MAP[category_sym]
|
|
76
|
+
return { phase: phase, cycle: :dream, confidence: :high } if phase
|
|
77
|
+
|
|
78
|
+
nil
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def infer_from_methods(methods)
|
|
82
|
+
method_names = methods.map(&:to_s)
|
|
83
|
+
match = METHOD_INFERENCE_MAP.find do |keywords, _phase|
|
|
84
|
+
method_names.any? { |m| keywords.any? { |kw| m.include?(kw) } }
|
|
85
|
+
end
|
|
86
|
+
return nil unless match
|
|
87
|
+
|
|
88
|
+
{ phase: match[1], cycle: :active, confidence: :medium }
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def valid_phase?(phase)
|
|
92
|
+
CATEGORY_PHASE_MAP.values.include?(phase) || DREAM_PHASE_MAP.values.include?(phase)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def phases_for_category(category)
|
|
96
|
+
category_sym = category.to_s.downcase.to_sym
|
|
97
|
+
results = []
|
|
98
|
+
results << CATEGORY_PHASE_MAP[category_sym] if CATEGORY_PHASE_MAP.key?(category_sym)
|
|
99
|
+
results << DREAM_PHASE_MAP[category_sym] if DREAM_PHASE_MAP.key?(category_sym)
|
|
100
|
+
results.compact
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module MindGrowth
|
|
6
|
+
module Runners
|
|
7
|
+
module IntegrationTester
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
9
|
+
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
10
|
+
|
|
11
|
+
extend self
|
|
12
|
+
|
|
13
|
+
TICK_BUDGET_MS = 5000
|
|
14
|
+
|
|
15
|
+
def test_extension_in_tick(ext_module:, runner_module:, fn:, phase:, test_args: {}, **) # rubocop:disable Naming/MethodParameterName
|
|
16
|
+
return { success: false, reason: :gaia_not_available } unless gaia_available?
|
|
17
|
+
|
|
18
|
+
runner_class = resolve_runner_class(ext_module, runner_module)
|
|
19
|
+
return { success: false, reason: :runner_not_found } unless runner_class
|
|
20
|
+
|
|
21
|
+
# Test 1: Method exists and is callable
|
|
22
|
+
method_check = test_method_callable(runner_class, fn)
|
|
23
|
+
return method_check unless method_check[:success]
|
|
24
|
+
|
|
25
|
+
# Test 2: Method returns valid response hash
|
|
26
|
+
response_check = test_valid_response(runner_class, fn, test_args)
|
|
27
|
+
return response_check unless response_check[:success]
|
|
28
|
+
|
|
29
|
+
# Test 3: Method completes within budget
|
|
30
|
+
perf_check = test_performance(runner_class, fn, test_args)
|
|
31
|
+
|
|
32
|
+
{
|
|
33
|
+
success: true,
|
|
34
|
+
method_callable: method_check[:success],
|
|
35
|
+
valid_response: response_check[:success],
|
|
36
|
+
performance: perf_check,
|
|
37
|
+
phase: phase
|
|
38
|
+
}
|
|
39
|
+
rescue StandardError => e
|
|
40
|
+
{ success: false, reason: :exception, error: e.message }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def benchmark_tick(with_extension: nil, iterations: 5, **)
|
|
44
|
+
return { success: false, reason: :gaia_not_available } unless gaia_available?
|
|
45
|
+
|
|
46
|
+
timings = iterations.times.map do
|
|
47
|
+
start = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
|
48
|
+
Legion::Gaia.heartbeat if Legion::Gaia.respond_to?(:heartbeat)
|
|
49
|
+
finish = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
|
50
|
+
((finish - start) * 1000).round(2)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
{
|
|
54
|
+
success: true,
|
|
55
|
+
with_extension: with_extension,
|
|
56
|
+
iterations: iterations,
|
|
57
|
+
avg_ms: (timings.sum / timings.size).round(2),
|
|
58
|
+
max_ms: timings.max,
|
|
59
|
+
min_ms: timings.min,
|
|
60
|
+
within_budget: timings.max <= TICK_BUDGET_MS
|
|
61
|
+
}
|
|
62
|
+
rescue StandardError => e
|
|
63
|
+
{ success: false, reason: :benchmark_failed, error: e.message }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def gaia_available?
|
|
69
|
+
defined?(Legion::Gaia)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def resolve_runner_class(ext_module, runner_module)
|
|
73
|
+
return nil unless defined?(Legion::Gaia::PhaseWiring)
|
|
74
|
+
|
|
75
|
+
Legion::Gaia::PhaseWiring.resolve_runner_class(ext_module.to_sym, runner_module.to_sym)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def test_method_callable(runner_class, fn) # rubocop:disable Naming/MethodParameterName
|
|
79
|
+
if runner_class.method_defined?(fn) || runner_class.respond_to?(fn)
|
|
80
|
+
{ success: true, method: fn }
|
|
81
|
+
else
|
|
82
|
+
{ success: false, reason: :method_not_defined, method: fn }
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def test_valid_response(runner_class, fn, args) # rubocop:disable Naming/MethodParameterName
|
|
87
|
+
host = Object.new.extend(runner_class)
|
|
88
|
+
result = host.send(fn, **args)
|
|
89
|
+
|
|
90
|
+
if result.is_a?(Hash)
|
|
91
|
+
{ success: true, response_type: :hash, keys: result.keys }
|
|
92
|
+
elsif result.nil?
|
|
93
|
+
{ success: true, response_type: :nil }
|
|
94
|
+
else
|
|
95
|
+
{ success: true, response_type: result.class.name }
|
|
96
|
+
end
|
|
97
|
+
rescue StandardError => e
|
|
98
|
+
{ success: false, reason: :invocation_error, error: e.message }
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def test_performance(runner_class, fn, args) # rubocop:disable Naming/MethodParameterName
|
|
102
|
+
host = Object.new.extend(runner_class)
|
|
103
|
+
start = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
|
104
|
+
host.send(fn, **args)
|
|
105
|
+
finish = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
|
106
|
+
duration_ms = ((finish - start) * 1000).round(2)
|
|
107
|
+
|
|
108
|
+
{ duration_ms: duration_ms, within_budget: duration_ms <= TICK_BUDGET_MS }
|
|
109
|
+
rescue StandardError => e
|
|
110
|
+
{ duration_ms: nil, error: e.message, within_budget: false }
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module MindGrowth
|
|
6
|
+
module Runners
|
|
7
|
+
module Retrospective
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
9
|
+
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
10
|
+
|
|
11
|
+
extend self
|
|
12
|
+
|
|
13
|
+
# Generates a summary of growth activity: proposals by status, recent builds, failures
|
|
14
|
+
def session_report(**)
|
|
15
|
+
proposals = Runners::Proposer.proposal_stats
|
|
16
|
+
recent = Runners::Proposer.list_proposals(limit: 10)
|
|
17
|
+
|
|
18
|
+
built = recent[:proposals].select { |p| %i[passing wired active].include?(p[:status]) }
|
|
19
|
+
failed = recent[:proposals].select { |p| %i[build_failed rejected pruned].include?(p[:status]) }
|
|
20
|
+
in_progress = recent[:proposals].select { |p| %i[proposed evaluating approved building testing].include?(p[:status]) }
|
|
21
|
+
|
|
22
|
+
{
|
|
23
|
+
success: true,
|
|
24
|
+
summary: {
|
|
25
|
+
total_proposals: proposals[:stats][:total],
|
|
26
|
+
by_status: proposals[:stats][:by_status],
|
|
27
|
+
recent_built: built.map { |p| { id: p[:id], name: p[:name], status: p[:status] } },
|
|
28
|
+
recent_failed: failed.map { |p| { id: p[:id], name: p[:name], status: p[:status] } },
|
|
29
|
+
in_progress: in_progress.map { |p| { id: p[:id], name: p[:name], status: p[:status] } }
|
|
30
|
+
},
|
|
31
|
+
generated_at: Time.now.utc
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Tracks extension count, quality, coverage over time
|
|
36
|
+
# Returns snapshot metrics suitable for time-series storage
|
|
37
|
+
def trend_analysis(extensions: [], **)
|
|
38
|
+
profile = Runners::Analyzer.cognitive_profile(existing_extensions: extensions.empty? ? nil : extensions)
|
|
39
|
+
ranked = extensions.empty? ? [] : Helpers::FitnessEvaluator.rank(extensions)
|
|
40
|
+
|
|
41
|
+
avg_fitness = ranked.empty? ? 0.0 : (ranked.sum { |e| e[:fitness] } / ranked.size).round(3)
|
|
42
|
+
prune_count = ranked.count { |e| e[:fitness] < Helpers::Constants::PRUNE_THRESHOLD }
|
|
43
|
+
healthy_count = ranked.count { |e| e[:fitness] >= Helpers::Constants::IMPROVEMENT_THRESHOLD }
|
|
44
|
+
|
|
45
|
+
{
|
|
46
|
+
success: true,
|
|
47
|
+
snapshot: {
|
|
48
|
+
extension_count: ranked.size,
|
|
49
|
+
overall_coverage: profile[:overall_coverage],
|
|
50
|
+
model_coverage: profile[:model_coverage]&.map { |m| { model: m[:model], coverage: m[:coverage] } },
|
|
51
|
+
avg_fitness: avg_fitness,
|
|
52
|
+
healthy_extensions: healthy_count,
|
|
53
|
+
prune_candidates: prune_count,
|
|
54
|
+
improvement_candidates: ranked.size - healthy_count - prune_count
|
|
55
|
+
},
|
|
56
|
+
generated_at: Time.now.utc
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Identifies patterns from build failures to improve future LLM prompts
|
|
61
|
+
def learning_extraction(**)
|
|
62
|
+
all_proposals = Runners::Proposer.list_proposals(limit: 100)
|
|
63
|
+
proposals = all_proposals[:proposals]
|
|
64
|
+
|
|
65
|
+
failed = proposals.select { |p| p[:status] == :build_failed }
|
|
66
|
+
rejected = proposals.select { |p| p[:status] == :rejected }
|
|
67
|
+
succeeded = proposals.select { |p| %i[passing wired active].include?(p[:status]) }
|
|
68
|
+
|
|
69
|
+
# Category success rates
|
|
70
|
+
category_stats = compute_category_stats(proposals)
|
|
71
|
+
|
|
72
|
+
# Extract patterns from failures
|
|
73
|
+
failure_patterns = extract_failure_patterns(failed)
|
|
74
|
+
|
|
75
|
+
{
|
|
76
|
+
success: true,
|
|
77
|
+
learnings: {
|
|
78
|
+
total_analyzed: proposals.size,
|
|
79
|
+
success_rate: proposals.empty? ? 0.0 : (succeeded.size.to_f / proposals.size).round(3),
|
|
80
|
+
rejection_rate: proposals.empty? ? 0.0 : (rejected.size.to_f / proposals.size).round(3),
|
|
81
|
+
build_failure_rate: proposals.empty? ? 0.0 : (failed.size.to_f / proposals.size).round(3),
|
|
82
|
+
category_stats: category_stats,
|
|
83
|
+
failure_patterns: failure_patterns,
|
|
84
|
+
recommendations: generate_recommendations(category_stats, failure_patterns)
|
|
85
|
+
},
|
|
86
|
+
generated_at: Time.now.utc
|
|
87
|
+
}
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def compute_category_stats(proposals)
|
|
93
|
+
by_category = proposals.group_by { |p| p[:category] }
|
|
94
|
+
by_category.transform_values do |cat_proposals|
|
|
95
|
+
succeeded = cat_proposals.count { |p| %i[passing wired active].include?(p[:status]) }
|
|
96
|
+
{
|
|
97
|
+
total: cat_proposals.size,
|
|
98
|
+
succeeded: succeeded,
|
|
99
|
+
success_rate: cat_proposals.empty? ? 0.0 : (succeeded.to_f / cat_proposals.size).round(3)
|
|
100
|
+
}
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def extract_failure_patterns(failed_proposals)
|
|
105
|
+
return [] if failed_proposals.empty?
|
|
106
|
+
|
|
107
|
+
# Group failures by category to identify problematic areas
|
|
108
|
+
by_category = failed_proposals.group_by { |p| p[:category] }
|
|
109
|
+
patterns = by_category.map do |category, proposals|
|
|
110
|
+
{ category: category, failure_count: proposals.size,
|
|
111
|
+
names: proposals.map { |p| p[:name] } }
|
|
112
|
+
end
|
|
113
|
+
patterns.sort_by { |p| -p[:failure_count] }
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def generate_recommendations(category_stats, failure_patterns)
|
|
117
|
+
recs = []
|
|
118
|
+
|
|
119
|
+
# Recommend avoiding categories with high failure rates
|
|
120
|
+
category_stats.each do |category, stats|
|
|
121
|
+
if stats[:total] >= 3 && stats[:success_rate] < 0.3
|
|
122
|
+
recs << { type: :avoid_category, category: category,
|
|
123
|
+
reason: "low success rate (#{(stats[:success_rate] * 100).round}%)" }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Recommend focus on categories with high success rates
|
|
127
|
+
if stats[:total] >= 3 && stats[:success_rate] > 0.8
|
|
128
|
+
recs << { type: :focus_category, category: category,
|
|
129
|
+
reason: "high success rate (#{(stats[:success_rate] * 100).round}%)" }
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Flag recurring failure patterns
|
|
134
|
+
failure_patterns.each do |pattern|
|
|
135
|
+
if pattern[:failure_count] >= 3
|
|
136
|
+
recs << { type: :investigate_failures, category: pattern[:category],
|
|
137
|
+
reason: "#{pattern[:failure_count]} build failures" }
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
recs
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module MindGrowth
|
|
6
|
+
module Runners
|
|
7
|
+
module Wirer
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
9
|
+
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
10
|
+
|
|
11
|
+
extend self
|
|
12
|
+
|
|
13
|
+
def analyze_fit(extension_name:, category:, runner_module: nil, runner_methods: [], **) # rubocop:disable Lint/UnusedMethodArgument
|
|
14
|
+
allocation = Helpers::PhaseAllocator.allocate_phase(
|
|
15
|
+
category: category,
|
|
16
|
+
runner_methods: runner_methods
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
dream_allocation = Helpers::PhaseAllocator.allocate_dream_phase(category: category)
|
|
20
|
+
|
|
21
|
+
{
|
|
22
|
+
success: true,
|
|
23
|
+
extension: extension_name,
|
|
24
|
+
active_phase: allocation,
|
|
25
|
+
dream_phase: dream_allocation,
|
|
26
|
+
recommendation: build_recommendation(allocation, dream_allocation)
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def wire_extension(extension_name:, ext_module:, runner_module:, fn:, phase:, **) # rubocop:disable Naming/MethodParameterName
|
|
31
|
+
return { success: false, reason: :gaia_not_available } unless gaia_available?
|
|
32
|
+
return { success: false, reason: :invalid_phase } unless Helpers::PhaseAllocator.valid_phase?(phase)
|
|
33
|
+
|
|
34
|
+
# Verify the runner class exists and has the method
|
|
35
|
+
runner_class = resolve_runner(ext_module: ext_module, runner_module: runner_module)
|
|
36
|
+
return { success: false, reason: :runner_not_found } unless runner_class
|
|
37
|
+
|
|
38
|
+
return { success: false, reason: :method_not_found, method: fn } unless runner_class.method_defined?(fn) || runner_class.respond_to?(fn)
|
|
39
|
+
|
|
40
|
+
# Record the wiring in our registry
|
|
41
|
+
wiring_registry[extension_name] = {
|
|
42
|
+
ext_module: ext_module,
|
|
43
|
+
runner_module: runner_module,
|
|
44
|
+
fn: fn,
|
|
45
|
+
phase: phase,
|
|
46
|
+
wired_at: Time.now,
|
|
47
|
+
enabled: true
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Trigger GAIA rediscovery to pick up the new wiring
|
|
51
|
+
rediscover_result = trigger_rediscovery
|
|
52
|
+
|
|
53
|
+
{
|
|
54
|
+
success: true,
|
|
55
|
+
extension: extension_name,
|
|
56
|
+
phase: phase,
|
|
57
|
+
fn: fn,
|
|
58
|
+
rediscovery: rediscover_result
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def unwire_extension(extension_name:, **)
|
|
63
|
+
entry = wiring_registry.delete(extension_name)
|
|
64
|
+
return { success: false, reason: :not_wired } unless entry
|
|
65
|
+
|
|
66
|
+
trigger_rediscovery
|
|
67
|
+
|
|
68
|
+
{ success: true, extension: extension_name, unwired_phase: entry[:phase] }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def disable_extension(extension_name:, **)
|
|
72
|
+
entry = wiring_registry[extension_name]
|
|
73
|
+
return { success: false, reason: :not_wired } unless entry
|
|
74
|
+
|
|
75
|
+
entry[:enabled] = false
|
|
76
|
+
{ success: true, extension: extension_name, status: :disabled }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def enable_extension(extension_name:, **)
|
|
80
|
+
entry = wiring_registry[extension_name]
|
|
81
|
+
return { success: false, reason: :not_wired } unless entry
|
|
82
|
+
|
|
83
|
+
entry[:enabled] = true
|
|
84
|
+
trigger_rediscovery
|
|
85
|
+
{ success: true, extension: extension_name, status: :enabled }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def wiring_status(**)
|
|
89
|
+
{
|
|
90
|
+
success: true,
|
|
91
|
+
wired_count: wiring_registry.size,
|
|
92
|
+
enabled_count: wiring_registry.count { |_, v| v[:enabled] },
|
|
93
|
+
extensions: wiring_registry.transform_values { |v| v.slice(:phase, :fn, :enabled, :wired_at) }
|
|
94
|
+
}
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def rewire_all(**)
|
|
98
|
+
return { success: false, reason: :gaia_not_available } unless gaia_available?
|
|
99
|
+
|
|
100
|
+
result = trigger_rediscovery
|
|
101
|
+
{ success: true, rediscovery: result }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
private
|
|
105
|
+
|
|
106
|
+
def gaia_available?
|
|
107
|
+
defined?(Legion::Gaia) && Legion::Gaia.respond_to?(:registry)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def resolve_runner(ext_module:, runner_module:)
|
|
111
|
+
return nil unless defined?(Legion::Gaia::PhaseWiring)
|
|
112
|
+
|
|
113
|
+
Legion::Gaia::PhaseWiring.resolve_runner_class(ext_module.to_sym, runner_module.to_sym)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def trigger_rediscovery
|
|
117
|
+
return { rediscovered: false, reason: :gaia_not_available } unless gaia_available?
|
|
118
|
+
|
|
119
|
+
Legion::Gaia.registry.rediscover
|
|
120
|
+
rescue StandardError => e
|
|
121
|
+
{ rediscovered: false, error: e.message }
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def wiring_registry
|
|
125
|
+
@wiring_registry ||= {}
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def build_recommendation(active_alloc, dream_alloc)
|
|
129
|
+
parts = []
|
|
130
|
+
parts << case active_alloc[:confidence]
|
|
131
|
+
when :high
|
|
132
|
+
"Wire to #{active_alloc[:phase]} (high confidence)"
|
|
133
|
+
when :medium
|
|
134
|
+
"Suggest #{active_alloc[:phase]} (medium confidence, verify manually)"
|
|
135
|
+
else
|
|
136
|
+
"Default to #{active_alloc[:phase]} (low confidence, manual review recommended)"
|
|
137
|
+
end
|
|
138
|
+
parts << "Also wire dream phase: #{dream_alloc[:phase]}" if dream_alloc
|
|
139
|
+
parts.join('. ')
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
@@ -9,11 +9,15 @@ require 'legion/extensions/mind_growth/helpers/proposal_store'
|
|
|
9
9
|
require 'legion/extensions/mind_growth/helpers/cognitive_models'
|
|
10
10
|
require 'legion/extensions/mind_growth/helpers/build_pipeline'
|
|
11
11
|
require 'legion/extensions/mind_growth/helpers/fitness_evaluator'
|
|
12
|
+
require 'legion/extensions/mind_growth/helpers/phase_allocator'
|
|
12
13
|
require 'legion/extensions/mind_growth/runners/proposer'
|
|
13
14
|
require 'legion/extensions/mind_growth/runners/analyzer'
|
|
14
15
|
require 'legion/extensions/mind_growth/runners/builder'
|
|
15
16
|
require 'legion/extensions/mind_growth/runners/validator'
|
|
16
17
|
require 'legion/extensions/mind_growth/runners/orchestrator'
|
|
18
|
+
require 'legion/extensions/mind_growth/runners/wirer'
|
|
19
|
+
require 'legion/extensions/mind_growth/runners/integration_tester'
|
|
20
|
+
require 'legion/extensions/mind_growth/runners/retrospective'
|
|
17
21
|
require 'legion/extensions/mind_growth/client'
|
|
18
22
|
|
|
19
23
|
module Legion
|