legionio 1.4.68 → 1.4.70

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1521235e6e865bd6d74f41b93c57e159de23d9dee0f044486c8059a6af780ab0
4
- data.tar.gz: 7bdb02eb37ca2f03b9c0b6f7f494ebbb394d69f53ff83dd4bb5c1e27cb37e85e
3
+ metadata.gz: 6f924192ddb882e9dd08fa0ada9088c4f9f468ef8d782df7a06888774ebe5b9d
4
+ data.tar.gz: f7eab58ceaf4f9119dad859f9b5d47945b57854a2e65e7f68484660da9c2f3a9
5
5
  SHA512:
6
- metadata.gz: 78a7171285d6a6eebda11d87d2782a68b6edb0dbc1f0be6d6034c1b4c7e535a5ae2dc1feb34ecbcc5af64d767759c7af7c78c1c50ed8672a1f08dea738c6b23b
7
- data.tar.gz: 0b7c0e4d1298e13815383df8667d2b14debf66675c981002847fa19a01d0be6afa4314d811ba7cde7285ee46ddf37e9985e8a86745aef2b4b122d2a7dc1e16ac
6
+ metadata.gz: 5b120a8843d8626da5508cfef24fa2791539c135d090a83a1519e9db99d3fb78aac7b52fde48b2c844435687114dada9b9622d73f6ab204607e73f422f2a9b90
7
+ data.tar.gz: 6965a20d9389f2b8e16be7e83fdac2835c6b18d47269bb9bf9f102588d03ed7df72b62cc119afeb1f7b0c29695efd0654e60af093bff4e00d3e2eefc76f76723
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Legion Changelog
2
2
 
3
+ ## [1.4.70] - 2026-03-19
4
+
5
+ ### Added
6
+ - GAIA cognitive layer as a core boot phase: `setup_gaia` runs between LLM and telemetry in the startup sequence
7
+ - Two-phase extension loading: all extensions are fully loaded (require + autobuild) before any actors are hooked (AMQP subscriptions, timers, etc.), preventing race conditions during boot
8
+ - `gaia: true` parameter on `Service.new` to control GAIA initialization
9
+ - GAIA graceful shutdown and reload support (shuts down before extensions, restarts after data)
10
+
11
+ ### Changed
12
+ - Boot order is now deterministic: Logging -> Settings -> Crypt -> Transport -> Cache -> Data -> RBAC -> LLM -> GAIA -> Telemetry -> Extensions -> API
13
+ - Extension actors are collected into `@pending_actors` during `load_extensions`, then started all at once via `hook_all_actors`
14
+
15
+ ## [1.4.69] - 2026-03-19
16
+
17
+ ### Fixed
18
+ - Constant resolution bug in transport/subscription layers: `const_defined?` and `const_get` now pass `inherit: false` to prevent Ruby from finding top-level gem constants (`::Redis`, `::Vault`, `::Data`) through `Object` when checking dynamically created `Module.new` namespaces (`Transport::Exchanges`, `Transport::Queues`)
19
+ - `Subscription#queue` now uses `queues.const_get(actor_const, false)` instead of `Kernel.const_get(queue_string)` to search only the Queues module's own constants
20
+ - Added `llm-gateway` to `core_extension_names` so it is included under `:core` role profile
21
+ - `build_extension_entry` now forces nesting for multi-segment gem names (e.g. `lex-llm-gateway`) to produce correct require paths regardless of call-site `nesting:` argument
22
+
3
23
  ## [1.4.68] - 2026-03-19
4
24
 
5
25
  ### Added
@@ -30,8 +30,8 @@ module Legion
30
30
  end
31
31
 
32
32
  def queue
33
- create_queue unless queues.const_defined?(actor_const)
34
- Kernel.const_get queue_string
33
+ create_queue unless queues.const_defined?(actor_const, false)
34
+ queues.const_get(actor_const, false)
35
35
  end
36
36
 
37
37
  def queue_string
@@ -33,13 +33,13 @@ module Legion
33
33
  end
34
34
 
35
35
  def build_default_exchange
36
- return transport_class::Exchanges.const_get(lex_const) if transport_class::Exchanges.const_defined? lex_const
36
+ return transport_class::Exchanges.const_get(lex_const, false) if transport_class::Exchanges.const_defined?(lex_const, false)
37
37
 
38
38
  amqp = amqp_prefix
39
39
  transport_class::Exchanges.const_set(lex_const, Class.new(Legion::Transport::Exchange) do
40
40
  define_method(:exchange_name) { amqp }
41
41
  end)
42
- @default_exchange = transport_class::Exchanges.const_get(lex_const)
42
+ @default_exchange = transport_class::Exchanges.const_get(lex_const, false)
43
43
  end
44
44
  end
45
45
  end
@@ -27,9 +27,9 @@ module Legion
27
27
  end
28
28
 
29
29
  def generate_base_modules
30
- lex_class.const_set('Transport', Module.new) unless lex_class.const_defined?('Transport')
30
+ lex_class.const_set('Transport', Module.new) unless lex_class.const_defined?('Transport', false)
31
31
  %w[Queues Exchanges Messages Consumers].each do |thing|
32
- next if transport_class.const_defined? thing
32
+ next if transport_class.const_defined?(thing, false)
33
33
 
34
34
  transport_class.const_set(thing, Module.new)
35
35
  end
@@ -68,7 +68,7 @@ module Legion
68
68
  end
69
69
 
70
70
  def auto_create_dlx_exchange
71
- dlx = if transport_class::Exchanges.const_defined? 'Dlx'
71
+ dlx = if transport_class::Exchanges.const_defined?('Dlx', false)
72
72
  transport_class::Exchanges::Dlx
73
73
  else
74
74
  transport_class::Exchanges.const_set('Dlx', Class.new(default_exchange) do
@@ -86,7 +86,7 @@ module Legion
86
86
  end
87
87
 
88
88
  def auto_create_dlx_queue
89
- return if transport_class::Queues.const_defined?('Dlx')
89
+ return if transport_class::Queues.const_defined?('Dlx', false)
90
90
 
91
91
  special_name = default_exchange.new.exchange_name
92
92
  dlx_queue = Legion::Transport::Queue.new "#{special_name}.dlx", auto_delete: false
@@ -18,9 +18,11 @@ module Legion
18
18
  @subscription_tasks = []
19
19
  @local_tasks = []
20
20
  @actors = []
21
+ @pending_actors = []
21
22
 
22
23
  find_extensions
23
24
  load_extensions
25
+ hook_all_actors
24
26
  end
25
27
 
26
28
  attr_reader :local_tasks
@@ -124,14 +126,14 @@ module Legion
124
126
 
125
127
  if extension.respond_to?(:meta_actors) && extension.meta_actors.is_a?(Hash)
126
128
  extension.meta_actors.each_value do |actor|
127
- extension.log.debug("hooking meta actor: #{actor}") if has_logger
128
- hook_actor(**actor)
129
+ extension.log.debug("deferring meta actor: #{actor}") if has_logger
130
+ @pending_actors << actor
129
131
  end
130
132
  end
131
133
 
132
134
  extension.actors.each_value do |actor|
133
- extension.log.debug("hooking literal actor: #{actor}") if has_logger
134
- hook_actor(**actor)
135
+ extension.log.debug("deferring literal actor: #{actor}") if has_logger
136
+ @pending_actors << actor
135
137
  end
136
138
  extension.log.info "Loaded v#{extension::VERSION}"
137
139
  Legion::Events.emit('extension.loaded', name: ext_name, version: entry[:gem_name])
@@ -161,6 +163,14 @@ module Legion
161
163
  false
162
164
  end
163
165
 
166
+ def hook_all_actors
167
+ return if @pending_actors.nil? || @pending_actors.empty?
168
+
169
+ Legion::Logging.info "Hooking #{@pending_actors.size} deferred actors"
170
+ @pending_actors.each { |actor| hook_actor(**actor) }
171
+ @pending_actors = []
172
+ end
173
+
164
174
  def hook_actor(extension:, extension_name:, actor_class:, size: 1, **opts)
165
175
  size = if Legion::Settings[:extensions].key?(extension_name.to_sym) && Legion::Settings[:extensions][extension_name.to_sym].key?(:workers)
166
176
  Legion::Settings[:extensions][extension_name.to_sym][:workers]
@@ -303,7 +313,7 @@ module Legion
303
313
  end
304
314
 
305
315
  def core_extension_names
306
- %w[codegen conditioner exec health lex log metering node ping scheduler tasker task_pruner telemetry
316
+ %w[codegen conditioner exec health lex llm-gateway log metering node ping scheduler tasker task_pruner telemetry
307
317
  transformer].freeze
308
318
  end
309
319
 
@@ -450,6 +460,9 @@ module Legion
450
460
  segments = Helpers::Segments.derive_segments(gem_name)
451
461
  tier = category == :default ? 5 : (categories.dig(category, :tier) || 5)
452
462
 
463
+ # Multi-segment gem names always need nesting for correct require paths
464
+ nesting = true if segments.length > 1
465
+
453
466
  if nesting
454
467
  const_path = Helpers::Segments.derive_const_path(gem_name)
455
468
  require_path = Helpers::Segments.derive_require_path(gem_name)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Legion
4
4
  module Readiness
5
- COMPONENTS = %i[settings crypt transport cache data extensions api].freeze
5
+ COMPONENTS = %i[settings crypt transport cache data gaia extensions api].freeze
6
6
  DRAIN_TIMEOUT = 5
7
7
 
8
8
  class << self
@@ -7,11 +7,12 @@ module Legion
7
7
  def modules
8
8
  base = [Legion::Crypt, Legion::Transport, Legion::Cache, Legion::Data, Legion::Supervision]
9
9
  base << Legion::LLM if defined?(Legion::LLM)
10
+ base << Legion::Gaia if defined?(Legion::Gaia)
10
11
  base.freeze
11
12
  end
12
13
 
13
14
  def initialize(transport: true, cache: true, data: true, supervision: true, extensions: true, # rubocop:disable Metrics/ParameterLists
14
- crypt: true, api: true, llm: true, log_level: 'info', http_port: nil)
15
+ crypt: true, api: true, llm: true, gaia: true, log_level: 'info', http_port: nil)
15
16
  setup_logging(log_level: log_level)
16
17
  Legion::Logging.debug('Starting Legion::Service')
17
18
  setup_settings
@@ -51,6 +52,11 @@ module Legion
51
52
  Legion::Readiness.mark_ready(:llm)
52
53
  end
53
54
 
55
+ if gaia
56
+ setup_gaia
57
+ Legion::Readiness.mark_ready(:gaia)
58
+ end
59
+
54
60
  setup_telemetry
55
61
  setup_supervision if supervision
56
62
 
@@ -210,6 +216,16 @@ module Legion
210
216
  Legion::Logging.warn "Legion::LLM failed to load: #{e.message}"
211
217
  end
212
218
 
219
+ def setup_gaia
220
+ require 'legion/gaia'
221
+ Legion::Settings.merge_settings('gaia', Legion::Gaia::Settings.default)
222
+ Legion::Gaia.boot
223
+ rescue LoadError
224
+ Legion::Logging.info 'Legion::Gaia gem is not installed, starting without cognitive layer'
225
+ rescue StandardError => e
226
+ Legion::Logging.warn "Legion::Gaia failed to load: #{e.message}"
227
+ end
228
+
213
229
  def setup_transport
214
230
  require 'legion/transport'
215
231
  Legion::Settings.merge_settings('transport', Legion::Transport::Settings.default)
@@ -295,6 +311,11 @@ module Legion
295
311
 
296
312
  Legion::Metrics.reset! if defined?(Legion::Metrics)
297
313
 
314
+ if defined?(Legion::Gaia) && Legion::Gaia.started?
315
+ Legion::Gaia.shutdown
316
+ Legion::Readiness.mark_not_ready(:gaia)
317
+ end
318
+
298
319
  Legion::Extensions.shutdown
299
320
  Legion::Readiness.mark_not_ready(:extensions)
300
321
 
@@ -330,6 +351,11 @@ module Legion
330
351
 
331
352
  shutdown_api
332
353
 
354
+ if defined?(Legion::Gaia) && Legion::Gaia.started?
355
+ Legion::Gaia.shutdown
356
+ Legion::Readiness.mark_not_ready(:gaia)
357
+ end
358
+
333
359
  Legion::Extensions.shutdown
334
360
  Legion::Readiness.mark_not_ready(:extensions)
335
361
 
@@ -357,6 +383,9 @@ module Legion
357
383
  setup_data
358
384
  Legion::Readiness.mark_ready(:data)
359
385
 
386
+ setup_gaia
387
+ Legion::Readiness.mark_ready(:gaia)
388
+
360
389
  setup_supervision
361
390
 
362
391
  load_extensions
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Legion
4
- VERSION = '1.4.68'
4
+ VERSION = '1.4.70'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legionio
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.68
4
+ version: 1.4.70
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity