Module: Legion::MCP::SelfGenerate

Extended by:
Logging::Helper
Defined in:
lib/legion/mcp/self_generate.rb

Constant Summary collapse

MAX_GAPS_PER_CYCLE =
5
COOLDOWN_SECONDS =
300

Class Method Summary collapse

Class Method Details

.cooldown_remainingObject



113
114
115
116
117
118
# File 'lib/legion/mcp/self_generate.rb', line 113

def cooldown_remaining
  return 0 unless last_cycle_at

  remaining = cooldown_seconds - (Time.now - last_cycle_at)
  [remaining, 0].max.round(1)
end

.cooldown_secondsObject



130
131
132
# File 'lib/legion/mcp/self_generate.rb', line 130

def cooldown_seconds
  Legion::Settings.dig(:codegen, :self_generate, :cooldown_seconds) || COOLDOWN_SECONDS
end

.cycle_countObject



149
150
151
# File 'lib/legion/mcp/self_generate.rb', line 149

def cycle_count
  mutex.synchronize { @cycle_count || 0 }
end

.cycle_history(limit = 10) ⇒ Object



103
104
105
# File 'lib/legion/mcp/self_generate.rb', line 103

def cycle_history(limit = 10)
  mutex.synchronize { (@cycle_history || []).last(limit) }
end

.enabled?Boolean

Returns:

  • (Boolean)


16
17
18
# File 'lib/legion/mcp/self_generate.rb', line 16

def enabled?
  Legion::Settings.dig(:codegen, :self_generate, :enabled) == true
end

.in_cooldown?Boolean

Returns:

  • (Boolean)


107
108
109
110
111
# File 'lib/legion/mcp/self_generate.rb', line 107

def in_cooldown?
  return false unless last_cycle_at

  Time.now - last_cycle_at < cooldown_seconds
end

.last_cycle_atObject



145
146
147
# File 'lib/legion/mcp/self_generate.rb', line 145

def last_cycle_at
  mutex.synchronize { @last_cycle_at }
end

.max_gaps_per_cycleObject

private helpers



126
127
128
# File 'lib/legion/mcp/self_generate.rb', line 126

def max_gaps_per_cycle
  Legion::Settings.dig(:codegen, :self_generate, :max_gaps_per_cycle) || MAX_GAPS_PER_CYCLE
end

.mutexObject



153
154
155
# File 'lib/legion/mcp/self_generate.rb', line 153

def mutex
  @mutex ||= Mutex.new
end

.publish_gap(gap) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/legion/mcp/self_generate.rb', line 56

def publish_gap(gap)
  return false unless defined?(Legion::Transport::Messages::Dynamic)

  log.debug("[mcp][self_generate] action=publish_gap gap_id=#{gap[:id]} type=#{gap[:type]}")

  Legion::Transport::Messages::Dynamic.new(
    function: 'codegen.gap.detected',
    data:     {
      gap_id:           gap[:id],
      gap_type:         gap[:type],
      intent:           gap[:intent] || gap[:intent_text],
      occurrence_count: gap[:occurrences] || gap[:observation_count] || gap[:failure_count] || 1,
      priority:         gap[:priority],
      metadata:         gap[:metadata] || {},
      detected_at:      Time.now.iso8601
    }
  ).publish
  true
rescue StandardError => e
  handle_exception(e, level: :warn, operation: 'legion.mcp.self_generate.publish_gap')
  false
end

.record_cycle(published_count) ⇒ Object



134
135
136
137
138
139
140
141
142
143
# File 'lib/legion/mcp/self_generate.rb', line 134

def record_cycle(published_count)
  mutex.synchronize do
    @last_cycle_at   = Time.now
    @cycle_count     = (@cycle_count || 0) + 1
    @total_published = (@total_published || 0) + published_count
    @cycle_history ||= []
    @cycle_history << { at: Time.now, published: published_count }
    @cycle_history.shift if @cycle_history.size > 50
  end
end

.reset!Object



94
95
96
97
98
99
100
101
# File 'lib/legion/mcp/self_generate.rb', line 94

def reset!
  mutex.synchronize do
    @last_cycle_at   = nil
    @cycle_count     = 0
    @total_published = 0
    @cycle_history   = []
  end
end

.run_cycleObject



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/legion/mcp/self_generate.rb', line 20

def run_cycle
  log.debug("[mcp][self_generate] action=run_cycle enabled=#{enabled?} in_cooldown=#{in_cooldown?}")
  return { success: false, reason: :disabled } unless enabled?
  return { success: false, reason: :cooldown } if in_cooldown?

  gaps = GapDetector.detect_gaps
  return { success: true, gaps_found: 0, published: 0 } if gaps.empty?

  top_gaps = gaps.sort_by { |g| -g[:priority] }.first(max_gaps_per_cycle)

  published_count = 0
  top_gaps.each do |gap|
    published_count += 1 if publish_gap(gap)
  end

  if published_count.zero?
    reason = defined?(Legion::Transport::Messages::Dynamic) ? :publish_failed : :transport_unavailable
    return {
      success:    false,
      reason:     reason,
      gaps_found: gaps.size,
      processed:  top_gaps.size,
      published:  0
    }
  end

  record_cycle(published_count)

  {
    success:    true,
    gaps_found: gaps.size,
    processed:  top_gaps.size,
    published:  published_count
  }
end

.statusObject



79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/legion/mcp/self_generate.rb', line 79

def status
  log.debug("[mcp][self_generate] action=status enabled=#{enabled?}")
  {
    last_cycle_at:      last_cycle_at,
    total_cycles:       cycle_count,
    total_published:    total_published,
    cooldown_remaining: cooldown_remaining,
    pending_gaps:       GapDetector.detect_gaps.size,
    enabled:            enabled?
  }
rescue StandardError => e
  handle_exception(e, level: :error, operation: 'legion.mcp.self_generate.status')
  { error: e.message }
end

.total_publishedObject



120
121
122
# File 'lib/legion/mcp/self_generate.rb', line 120

def total_published
  mutex.synchronize { @total_published || 0 }
end