lex-mind-growth 0.1.0

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.
Files changed (33) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +19 -0
  3. data/LICENSE +21 -0
  4. data/lex-mind-growth.gemspec +29 -0
  5. data/lib/legion/extensions/mind_growth/actors/growth_cycle.rb +55 -0
  6. data/lib/legion/extensions/mind_growth/client.rb +36 -0
  7. data/lib/legion/extensions/mind_growth/helpers/build_pipeline.rb +63 -0
  8. data/lib/legion/extensions/mind_growth/helpers/cognitive_models.rb +60 -0
  9. data/lib/legion/extensions/mind_growth/helpers/concept_proposal.rb +69 -0
  10. data/lib/legion/extensions/mind_growth/helpers/constants.rb +55 -0
  11. data/lib/legion/extensions/mind_growth/helpers/fitness_evaluator.rb +58 -0
  12. data/lib/legion/extensions/mind_growth/helpers/proposal_store.rb +71 -0
  13. data/lib/legion/extensions/mind_growth/runners/analyzer.rb +52 -0
  14. data/lib/legion/extensions/mind_growth/runners/builder.rb +254 -0
  15. data/lib/legion/extensions/mind_growth/runners/orchestrator.rb +178 -0
  16. data/lib/legion/extensions/mind_growth/runners/proposer.rb +269 -0
  17. data/lib/legion/extensions/mind_growth/runners/validator.rb +57 -0
  18. data/lib/legion/extensions/mind_growth/version.rb +9 -0
  19. data/lib/legion/extensions/mind_growth.rb +25 -0
  20. data/spec/legion/extensions/mind_growth/actors/growth_cycle_spec.rb +81 -0
  21. data/spec/legion/extensions/mind_growth/client_spec.rb +111 -0
  22. data/spec/legion/extensions/mind_growth/helpers/build_pipeline_spec.rb +190 -0
  23. data/spec/legion/extensions/mind_growth/helpers/cognitive_models_spec.rb +106 -0
  24. data/spec/legion/extensions/mind_growth/helpers/concept_proposal_spec.rb +209 -0
  25. data/spec/legion/extensions/mind_growth/helpers/fitness_evaluator_spec.rb +123 -0
  26. data/spec/legion/extensions/mind_growth/helpers/proposal_store_spec.rb +203 -0
  27. data/spec/legion/extensions/mind_growth/runners/analyzer_spec.rb +108 -0
  28. data/spec/legion/extensions/mind_growth/runners/builder_spec.rb +347 -0
  29. data/spec/legion/extensions/mind_growth/runners/orchestrator_spec.rb +185 -0
  30. data/spec/legion/extensions/mind_growth/runners/proposer_spec.rb +493 -0
  31. data/spec/legion/extensions/mind_growth/runners/validator_spec.rb +120 -0
  32. data/spec/spec_helper.rb +22 -0
  33. metadata +91 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 811a3676ca7c7fd4168c30606006adeca6020be69398bb94128130ab6da89c34
4
+ data.tar.gz: c40ec71e46d799b65100fa378c8840ca55579147159bd39eee9a8b6858b6ab97
5
+ SHA512:
6
+ metadata.gz: 997b9b014e37450333f475b4dd2ab8c70cc4bf7cd355d5cbc73a9873aa73dae4b7394b925f61cd97b9a8102ca038e0b6902bf32bee1a86b119b93f745380d8a7
7
+ data.tar.gz: 4765d190fee92ce1c98e20a40955fa85521adc98dfe2b4013817fd4a01b7b4e18348323e665cafc605967196c367180f157cf28894ca70b089a955fb2cb470ef
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rake'
8
+ gem 'rspec', '~> 3.13'
9
+ gem 'rspec_junit_formatter'
10
+ gem 'rubocop', '~> 1.75'
11
+ gem 'rubocop-rspec'
12
+ gem 'simplecov'
13
+ end
14
+
15
+ gem 'legion-llm', path: '../../legion-llm', require: false
16
+ gem 'lex-codegen', path: '../../extensions-core/lex-codegen', require: false
17
+ gem 'lex-exec', path: '../../extensions-core/lex-exec', require: false
18
+
19
+ gem 'legion-gaia', path: '../../legion-gaia'
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 LegionIO
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/legion/extensions/mind_growth/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'lex-mind-growth'
7
+ spec.version = Legion::Extensions::MindGrowth::VERSION
8
+ spec.authors = ['Esity']
9
+ spec.email = ['matthewdiverson@gmail.com']
10
+
11
+ spec.summary = 'LEX Mind Growth'
12
+ spec.description = 'Autonomous cognitive architecture expansion for LegionIO'
13
+ spec.homepage = 'https://github.com/LegionIO/lex-mind-growth'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 3.4'
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-mind-growth'
19
+ spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-mind-growth'
20
+ spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-mind-growth'
21
+ spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-mind-growth/issues'
22
+ spec.metadata['rubygems_mfa_required'] = 'true'
23
+
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ Dir.glob('{lib,spec}/**/*') + %w[lex-mind-growth.gemspec Gemfile LICENSE]
26
+ end
27
+ spec.require_paths = ['lib']
28
+ spec.add_development_dependency 'legion-gaia'
29
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/actors/every'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module MindGrowth
8
+ module Actor
9
+ class GrowthCycle < Legion::Extensions::Actors::Every
10
+ def runner_class
11
+ Legion::Extensions::MindGrowth::Runners::Orchestrator
12
+ end
13
+
14
+ def runner_function
15
+ 'run_growth_cycle'
16
+ end
17
+
18
+ def time
19
+ 3600
20
+ end
21
+
22
+ def enabled?
23
+ codegen_loaded? || exec_loaded?
24
+ end
25
+
26
+ def run_now?
27
+ false
28
+ end
29
+
30
+ def use_runner?
31
+ false
32
+ end
33
+
34
+ def check_subtask?
35
+ false
36
+ end
37
+
38
+ def generate_task?
39
+ false
40
+ end
41
+
42
+ private
43
+
44
+ def codegen_loaded?
45
+ defined?(Legion::Extensions::Codegen)
46
+ end
47
+
48
+ def exec_loaded?
49
+ defined?(Legion::Extensions::Exec)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module MindGrowth
6
+ class Client
7
+ def initialize; end
8
+
9
+ # Proposer delegation
10
+ def analyze_gaps(**) = Runners::Proposer.analyze_gaps(**)
11
+ def propose_concept(**) = Runners::Proposer.propose_concept(**)
12
+ def evaluate_proposal(**) = Runners::Proposer.evaluate_proposal(**)
13
+ def list_proposals(**) = Runners::Proposer.list_proposals(**)
14
+ def proposal_stats(**) = Runners::Proposer.proposal_stats(**)
15
+
16
+ # Analyzer delegation
17
+ def cognitive_profile(**) = Runners::Analyzer.cognitive_profile(**)
18
+ def identify_weak_links(**) = Runners::Analyzer.identify_weak_links(**)
19
+ def recommend_priorities(**) = Runners::Analyzer.recommend_priorities(**)
20
+
21
+ # Builder delegation
22
+ def build_extension(**) = Runners::Builder.build_extension(**)
23
+ def build_status(**) = Runners::Builder.build_status(**)
24
+
25
+ # Validator delegation
26
+ def validate_proposal(**) = Runners::Validator.validate_proposal(**)
27
+ def validate_scores(**) = Runners::Validator.validate_scores(**)
28
+ def validate_fitness(**) = Runners::Validator.validate_fitness(**)
29
+
30
+ # Orchestrator delegation
31
+ def run_growth_cycle(**) = Runners::Orchestrator.run_growth_cycle(**)
32
+ def growth_status(**) = Runners::Orchestrator.growth_status(**)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module MindGrowth
6
+ module Helpers
7
+ class BuildPipeline
8
+ STAGES = %i[scaffold implement test validate register complete failed].freeze
9
+
10
+ attr_reader :proposal, :stage, :errors, :artifacts, :started_at, :completed_at
11
+
12
+ def initialize(proposal)
13
+ @proposal = proposal
14
+ @stage = :scaffold
15
+ @errors = []
16
+ @artifacts = {}
17
+ @started_at = Time.now.utc
18
+ @completed_at = nil
19
+ end
20
+
21
+ def advance!(result)
22
+ return if complete? || failed?
23
+
24
+ if result[:success]
25
+ @artifacts[@stage] = result
26
+ next_idx = STAGES.index(@stage) + 1
27
+ @stage = STAGES[next_idx] || :complete
28
+ @completed_at = Time.now.utc if @stage == :complete
29
+ else
30
+ @errors << { stage: @stage, error: result[:error], at: Time.now.utc }
31
+ @stage = :failed if @errors.size >= Helpers::Constants::MAX_FIX_ATTEMPTS
32
+ end
33
+ end
34
+
35
+ def complete?
36
+ @stage == :complete
37
+ end
38
+
39
+ def failed?
40
+ @stage == :failed
41
+ end
42
+
43
+ def to_h
44
+ {
45
+ proposal_id: @proposal.id,
46
+ stage: @stage,
47
+ errors: @errors,
48
+ artifacts: @artifacts,
49
+ started_at: @started_at,
50
+ completed_at: @completed_at,
51
+ duration_ms: duration_ms
52
+ }
53
+ end
54
+
55
+ def duration_ms
56
+ end_time = @completed_at || Time.now.utc
57
+ ((end_time - @started_at) * 1000).round
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module MindGrowth
6
+ module Helpers
7
+ module CognitiveModels
8
+ module_function
9
+
10
+ MODELS = {
11
+ global_workspace: {
12
+ name: 'Global Workspace Theory (Baars)',
13
+ required: %i[attention global_workspace broadcasting working_memory consciousness],
14
+ description: 'Conscious access via global broadcasting to specialized processors'
15
+ },
16
+ free_energy: {
17
+ name: 'Free Energy Principle (Friston)',
18
+ required: %i[prediction free_energy predictive_coding belief_revision active_inference error_monitoring],
19
+ description: 'Minimize surprise through predictive models and active inference'
20
+ },
21
+ dual_process: {
22
+ name: 'Dual Process Theory (Kahneman)',
23
+ required: %i[intuition dual_process inhibition executive_function cognitive_control],
24
+ description: 'System 1 (fast/automatic) vs System 2 (slow/deliberate) processing'
25
+ },
26
+ somatic_marker: {
27
+ name: 'Somatic Marker Hypothesis (Damasio)',
28
+ required: %i[emotion somatic_marker interoception appraisal embodied_simulation],
29
+ description: 'Emotion-cognition integration for decision making'
30
+ },
31
+ working_memory: {
32
+ name: 'Working Memory Model (Baddeley)',
33
+ required: %i[working_memory episodic_buffer attention executive_function cognitive_load],
34
+ description: 'Multi-component model with central executive and slave systems'
35
+ }
36
+ }.freeze
37
+
38
+ def gap_analysis(existing_extensions)
39
+ existing_names = existing_extensions.map { |e| e.to_s.downcase.to_sym }
40
+ MODELS.map do |key, model|
41
+ missing = model[:required] - existing_names
42
+ coverage = 1.0 - (missing.size.to_f / model[:required].size)
43
+ {
44
+ model: key,
45
+ name: model[:name],
46
+ coverage: coverage.round(2),
47
+ missing: missing,
48
+ total_required: model[:required].size
49
+ }
50
+ end
51
+ end
52
+
53
+ def recommend_from_gaps(gap_results)
54
+ gap_results.flat_map { |g| g[:missing] }.tally.sort_by { |_, count| -count }.map(&:first)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module MindGrowth
6
+ module Helpers
7
+ class ConceptProposal
8
+ FIELDS = %i[
9
+ id name module_name category description metaphor helpers runner_methods
10
+ rationale scores status origin created_at evaluated_at built_at
11
+ ].freeze
12
+
13
+ attr_reader(*FIELDS)
14
+
15
+ def initialize(name:, module_name:, category:, description:, metaphor: nil, helpers: [],
16
+ runner_methods: [], rationale: nil, origin: :proposer)
17
+ @id = SecureRandom.uuid
18
+ @name = name
19
+ @module_name = module_name
20
+ @category = category.to_sym
21
+ @description = description
22
+ @metaphor = metaphor
23
+ @helpers = helpers # array of { name:, methods: [] }
24
+ @runner_methods = runner_methods # array of { name:, params: [], returns: '' }
25
+ @rationale = rationale
26
+ @scores = {} # { novelty: 0.0..1.0, fit: 0.0..1.0, ... }
27
+ @status = :proposed
28
+ @origin = origin
29
+ @created_at = Time.now.utc
30
+ @evaluated_at = nil
31
+ @built_at = nil
32
+ end
33
+
34
+ def evaluate!(scores)
35
+ @scores = scores
36
+ @evaluated_at = Time.now.utc
37
+ @status = passing_evaluation? ? :approved : :rejected
38
+ end
39
+
40
+ def passing_evaluation?
41
+ return false if @scores.empty?
42
+
43
+ Helpers::Constants::EVALUATION_DIMENSIONS.all? { |dim| (@scores[dim] || 0) >= Helpers::Constants::MIN_DIMENSION_SCORE }
44
+ end
45
+
46
+ def auto_approvable?
47
+ return false if @scores.empty?
48
+
49
+ Helpers::Constants::EVALUATION_DIMENSIONS.all? { |dim| (@scores[dim] || 0) >= Helpers::Constants::AUTO_APPROVE_THRESHOLD }
50
+ end
51
+
52
+ def transition!(new_status)
53
+ new_status = new_status.to_sym
54
+ unless Helpers::Constants::PROPOSAL_STATUSES.include?(new_status)
55
+ raise ArgumentError, "invalid status: #{new_status} (valid: #{Helpers::Constants::PROPOSAL_STATUSES.join(', ')})"
56
+ end
57
+
58
+ @status = new_status
59
+ @built_at = Time.now.utc if new_status == :passing
60
+ end
61
+
62
+ def to_h
63
+ FIELDS.to_h { |f| [f, send(f)] }
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module MindGrowth
6
+ module Helpers
7
+ module Constants
8
+ # Proposal status lifecycle
9
+ PROPOSAL_STATUSES = %i[
10
+ proposed evaluating approved rejected building testing passing wired active degraded pruned build_failed
11
+ ].freeze
12
+
13
+ # Cognitive categories and target distribution
14
+ CATEGORIES = %i[cognition perception introspection safety communication memory motivation coordination].freeze
15
+
16
+ TARGET_DISTRIBUTION = {
17
+ cognition: 0.30,
18
+ perception: 0.15,
19
+ introspection: 0.12,
20
+ safety: 0.12,
21
+ communication: 0.10,
22
+ memory: 0.10,
23
+ motivation: 0.06,
24
+ coordination: 0.05
25
+ }.freeze
26
+
27
+ # Evaluation dimensions
28
+ EVALUATION_DIMENSIONS = %i[novelty fit cognitive_value implementability composability].freeze
29
+ MIN_DIMENSION_SCORE = 0.6
30
+ AUTO_APPROVE_THRESHOLD = 0.9
31
+ REDUNDANCY_THRESHOLD = 0.8
32
+
33
+ # Build pipeline
34
+ MAX_FIX_ATTEMPTS = 3
35
+ BUILD_TIMEOUT_MS = 600_000 # 10 minutes
36
+
37
+ # Fitness function weights
38
+ FITNESS_WEIGHTS = {
39
+ invocation_rate: 0.25,
40
+ impact_score: 0.30,
41
+ health: 0.25,
42
+ error_penalty: -0.15,
43
+ latency_penalty: -0.05
44
+ }.freeze
45
+
46
+ PRUNE_THRESHOLD = 0.2
47
+ IMPROVEMENT_THRESHOLD = 0.4
48
+
49
+ # Reference cognitive models
50
+ COGNITIVE_MODELS = %i[global_workspace free_energy dual_process somatic_marker working_memory].freeze
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module MindGrowth
6
+ module Helpers
7
+ module FitnessEvaluator
8
+ extend self
9
+
10
+ def fitness(extension)
11
+ w = Helpers::Constants::FITNESS_WEIGHTS
12
+ invocation = normalize_invocations(extension[:invocation_count] || 0)
13
+ impact = extension[:impact_score] || 0.5
14
+ health = extension[:health_score] || 1.0
15
+ error_rate = extension[:error_rate] || 0.0
16
+ latency = normalize_latency(extension[:avg_latency_ms] || 0)
17
+
18
+ score = (w[:invocation_rate] * invocation) +
19
+ (w[:impact_score] * impact) +
20
+ (w[:health] * health) +
21
+ (w[:error_penalty] * error_rate) +
22
+ (w[:latency_penalty] * latency)
23
+ score.clamp(0.0, 1.0).round(3)
24
+ end
25
+
26
+ def rank(extensions)
27
+ extensions.map { |e| e.merge(fitness: fitness(e)) }.sort_by { |e| -e[:fitness] }
28
+ end
29
+
30
+ def prune_candidates(extensions)
31
+ extensions.select { |e| fitness(e) < Helpers::Constants::PRUNE_THRESHOLD }
32
+ end
33
+
34
+ def improvement_candidates(extensions)
35
+ extensions.select do |e|
36
+ f = fitness(e)
37
+ f >= Helpers::Constants::PRUNE_THRESHOLD && f < Helpers::Constants::IMPROVEMENT_THRESHOLD
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def normalize_invocations(count)
44
+ # Log scale: 0 invocations = 0.0, 1000+ = 1.0
45
+ return 0.0 if count.zero?
46
+
47
+ (Math.log10(count + 1) / 3.0).clamp(0.0, 1.0)
48
+ end
49
+
50
+ def normalize_latency(ms_val)
51
+ # Higher latency = higher penalty (0-1 scale, 5000ms = 1.0)
52
+ (ms_val / 5000.0).clamp(0.0, 1.0)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module MindGrowth
6
+ module Helpers
7
+ class ProposalStore
8
+ MAX_PROPOSALS = 500
9
+
10
+ def initialize
11
+ @proposals = {}
12
+ @mutex = Mutex.new
13
+ end
14
+
15
+ def store(proposal)
16
+ @mutex.synchronize do
17
+ evict_oldest if @proposals.size >= MAX_PROPOSALS
18
+ @proposals[proposal.id] = proposal
19
+ end
20
+ end
21
+
22
+ def get(id)
23
+ @mutex.synchronize { @proposals[id] }
24
+ end
25
+
26
+ def all
27
+ @mutex.synchronize { @proposals.values.dup }
28
+ end
29
+
30
+ def by_status(status)
31
+ @mutex.synchronize { @proposals.values.select { |p| p.status == status.to_sym } }
32
+ end
33
+
34
+ def by_category(category)
35
+ @mutex.synchronize { @proposals.values.select { |p| p.category == category.to_sym } }
36
+ end
37
+
38
+ def approved
39
+ by_status(:approved)
40
+ end
41
+
42
+ def build_queue
43
+ by_status(:approved).sort_by { |p| -(p.scores.values.sum / p.scores.size.to_f) }
44
+ end
45
+
46
+ def recent(limit: 20)
47
+ @mutex.synchronize { @proposals.values.sort_by { |p| -p.created_at.to_f }.first(limit) }
48
+ end
49
+
50
+ def stats
51
+ @mutex.synchronize do
52
+ statuses = @proposals.values.group_by(&:status).transform_values(&:count)
53
+ { total: @proposals.size, by_status: statuses }
54
+ end
55
+ end
56
+
57
+ def clear
58
+ @mutex.synchronize { @proposals.clear }
59
+ end
60
+
61
+ private
62
+
63
+ def evict_oldest
64
+ oldest = @proposals.values.min_by { |p| p.created_at.to_f }
65
+ @proposals.delete(oldest.id) if oldest
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module MindGrowth
6
+ module Runners
7
+ module Analyzer
8
+ extend self
9
+
10
+ def cognitive_profile(existing_extensions: nil, **)
11
+ extensions = existing_extensions || current_extensions
12
+ gap_data = Helpers::CognitiveModels.gap_analysis(extensions)
13
+ {
14
+ success: true,
15
+ total_extensions: extensions.size,
16
+ model_coverage: gap_data,
17
+ overall_coverage: (gap_data.sum { |g| g[:coverage] } / gap_data.size.to_f).round(2)
18
+ }
19
+ rescue ArgumentError => e
20
+ { success: false, error: e.message }
21
+ end
22
+
23
+ def identify_weak_links(extensions: [], **)
24
+ weak = extensions.select { |e| Helpers::FitnessEvaluator.fitness(e) < Helpers::Constants::IMPROVEMENT_THRESHOLD }
25
+ { success: true, weak_links: Helpers::FitnessEvaluator.rank(weak), count: weak.size }
26
+ rescue ArgumentError => e
27
+ { success: false, error: e.message }
28
+ end
29
+
30
+ def recommend_priorities(existing_extensions: nil, **)
31
+ gaps = Helpers::CognitiveModels.recommend_from_gaps(
32
+ Helpers::CognitiveModels.gap_analysis(existing_extensions || current_extensions)
33
+ )
34
+ { success: true, priorities: gaps.first(10), rationale: 'Based on cognitive model gap analysis' }
35
+ rescue ArgumentError => e
36
+ { success: false, error: e.message }
37
+ end
38
+
39
+ private
40
+
41
+ def current_extensions
42
+ if defined?(Legion::Extensions::Metacognition::Helpers::Constants::SUBSYSTEMS)
43
+ Legion::Extensions::Metacognition::Helpers::Constants::SUBSYSTEMS
44
+ else
45
+ []
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end