lex-agentic-integration 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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile +5 -0
- data/LICENSE +21 -0
- data/README.md +13 -0
- data/lex-agentic-integration.gemspec +30 -0
- data/lib/legion/extensions/agentic/integration/boundary/client.rb +15 -0
- data/lib/legion/extensions/agentic/integration/boundary/helpers/boundary.rb +90 -0
- data/lib/legion/extensions/agentic/integration/boundary/helpers/boundary_engine.rb +123 -0
- data/lib/legion/extensions/agentic/integration/boundary/helpers/constants.rb +44 -0
- data/lib/legion/extensions/agentic/integration/boundary/runners/cognitive_boundary.rb +100 -0
- data/lib/legion/extensions/agentic/integration/boundary/version.rb +13 -0
- data/lib/legion/extensions/agentic/integration/boundary.rb +19 -0
- data/lib/legion/extensions/agentic/integration/context/client.rb +26 -0
- data/lib/legion/extensions/agentic/integration/context/helpers/constants.rb +34 -0
- data/lib/legion/extensions/agentic/integration/context/helpers/context_manager.rb +150 -0
- data/lib/legion/extensions/agentic/integration/context/helpers/frame.rb +99 -0
- data/lib/legion/extensions/agentic/integration/context/runners/context.rb +94 -0
- data/lib/legion/extensions/agentic/integration/context/version.rb +13 -0
- data/lib/legion/extensions/agentic/integration/context.rb +19 -0
- data/lib/legion/extensions/agentic/integration/distributed_cognition/client.rb +19 -0
- data/lib/legion/extensions/agentic/integration/distributed_cognition/helpers/constants.rb +51 -0
- data/lib/legion/extensions/agentic/integration/distributed_cognition/helpers/distribution_engine.rb +159 -0
- data/lib/legion/extensions/agentic/integration/distributed_cognition/helpers/participant.rb +100 -0
- data/lib/legion/extensions/agentic/integration/distributed_cognition/runners/distributed_cognition.rb +107 -0
- data/lib/legion/extensions/agentic/integration/distributed_cognition/version.rb +13 -0
- data/lib/legion/extensions/agentic/integration/distributed_cognition.rb +19 -0
- data/lib/legion/extensions/agentic/integration/gestalt/client.rb +21 -0
- data/lib/legion/extensions/agentic/integration/gestalt/helpers/constants.rb +51 -0
- data/lib/legion/extensions/agentic/integration/gestalt/helpers/pattern.rb +90 -0
- data/lib/legion/extensions/agentic/integration/gestalt/helpers/pattern_store.rb +123 -0
- data/lib/legion/extensions/agentic/integration/gestalt/runners/gestalt.rb +82 -0
- data/lib/legion/extensions/agentic/integration/gestalt/version.rb +13 -0
- data/lib/legion/extensions/agentic/integration/gestalt.rb +19 -0
- data/lib/legion/extensions/agentic/integration/global_workspace/actors/competition.rb +45 -0
- data/lib/legion/extensions/agentic/integration/global_workspace/client.rb +29 -0
- data/lib/legion/extensions/agentic/integration/global_workspace/helpers/broadcast.rb +62 -0
- data/lib/legion/extensions/agentic/integration/global_workspace/helpers/competitor.rb +59 -0
- data/lib/legion/extensions/agentic/integration/global_workspace/helpers/constants.rb +65 -0
- data/lib/legion/extensions/agentic/integration/global_workspace/helpers/workspace.rb +188 -0
- data/lib/legion/extensions/agentic/integration/global_workspace/runners/global_workspace.rb +104 -0
- data/lib/legion/extensions/agentic/integration/global_workspace/version.rb +13 -0
- data/lib/legion/extensions/agentic/integration/global_workspace.rb +20 -0
- data/lib/legion/extensions/agentic/integration/integration/client.rb +19 -0
- data/lib/legion/extensions/agentic/integration/integration/helpers/constants.rb +62 -0
- data/lib/legion/extensions/agentic/integration/integration/helpers/integrated_representation.rb +106 -0
- data/lib/legion/extensions/agentic/integration/integration/helpers/integration_engine.rb +163 -0
- data/lib/legion/extensions/agentic/integration/integration/helpers/modal_signal.rb +62 -0
- data/lib/legion/extensions/agentic/integration/integration/runners/cognitive_integration.rb +105 -0
- data/lib/legion/extensions/agentic/integration/integration/version.rb +13 -0
- data/lib/legion/extensions/agentic/integration/integration.rb +20 -0
- data/lib/legion/extensions/agentic/integration/labyrinth/actors/thread_walker.rb +45 -0
- data/lib/legion/extensions/agentic/integration/labyrinth/client.rb +23 -0
- data/lib/legion/extensions/agentic/integration/labyrinth/helpers/constants.rb +39 -0
- data/lib/legion/extensions/agentic/integration/labyrinth/helpers/labyrinth.rb +138 -0
- data/lib/legion/extensions/agentic/integration/labyrinth/helpers/labyrinth_engine.rb +177 -0
- data/lib/legion/extensions/agentic/integration/labyrinth/helpers/node.rb +64 -0
- data/lib/legion/extensions/agentic/integration/labyrinth/runners/cognitive_labyrinth.rb +128 -0
- data/lib/legion/extensions/agentic/integration/labyrinth/version.rb +13 -0
- data/lib/legion/extensions/agentic/integration/labyrinth.rb +22 -0
- data/lib/legion/extensions/agentic/integration/map/actors/decay.rb +45 -0
- data/lib/legion/extensions/agentic/integration/map/client.rb +29 -0
- data/lib/legion/extensions/agentic/integration/map/helpers/cognitive_map_store.rb +179 -0
- data/lib/legion/extensions/agentic/integration/map/helpers/constants.rb +71 -0
- data/lib/legion/extensions/agentic/integration/map/helpers/graph_traversal.rb +124 -0
- data/lib/legion/extensions/agentic/integration/map/helpers/location.rb +71 -0
- data/lib/legion/extensions/agentic/integration/map/runners/cognitive_map.rb +95 -0
- data/lib/legion/extensions/agentic/integration/map/version.rb +13 -0
- data/lib/legion/extensions/agentic/integration/map.rb +20 -0
- data/lib/legion/extensions/agentic/integration/mosaic/client.rb +15 -0
- data/lib/legion/extensions/agentic/integration/mosaic/helpers/constants.rb +55 -0
- data/lib/legion/extensions/agentic/integration/mosaic/helpers/mosaic.rb +111 -0
- data/lib/legion/extensions/agentic/integration/mosaic/helpers/mosaic_engine.rb +124 -0
- data/lib/legion/extensions/agentic/integration/mosaic/helpers/tessera.rb +86 -0
- data/lib/legion/extensions/agentic/integration/mosaic/runners/cognitive_mosaic.rb +76 -0
- data/lib/legion/extensions/agentic/integration/mosaic/version.rb +13 -0
- data/lib/legion/extensions/agentic/integration/mosaic.rb +22 -0
- data/lib/legion/extensions/agentic/integration/mycelium/client.rb +19 -0
- data/lib/legion/extensions/agentic/integration/mycelium/helpers/constants.rb +52 -0
- data/lib/legion/extensions/agentic/integration/mycelium/helpers/fruiting_body.rb +50 -0
- data/lib/legion/extensions/agentic/integration/mycelium/helpers/hypha.rb +76 -0
- data/lib/legion/extensions/agentic/integration/mycelium/helpers/mycelial_node.rb +72 -0
- data/lib/legion/extensions/agentic/integration/mycelium/helpers/mycelium_engine.rb +151 -0
- data/lib/legion/extensions/agentic/integration/mycelium/runners/cognitive_mycelium.rb +73 -0
- data/lib/legion/extensions/agentic/integration/mycelium/version.rb +13 -0
- data/lib/legion/extensions/agentic/integration/mycelium.rb +23 -0
- data/lib/legion/extensions/agentic/integration/phenomenal_binding/client.rb +25 -0
- data/lib/legion/extensions/agentic/integration/phenomenal_binding/helpers/binding_engine.rb +156 -0
- data/lib/legion/extensions/agentic/integration/phenomenal_binding/helpers/binding_unit.rb +72 -0
- data/lib/legion/extensions/agentic/integration/phenomenal_binding/helpers/constants.rb +35 -0
- data/lib/legion/extensions/agentic/integration/phenomenal_binding/helpers/stream.rb +51 -0
- data/lib/legion/extensions/agentic/integration/phenomenal_binding/runners/phenomenal_binding.rb +110 -0
- data/lib/legion/extensions/agentic/integration/phenomenal_binding/version.rb +13 -0
- data/lib/legion/extensions/agentic/integration/phenomenal_binding.rb +20 -0
- data/lib/legion/extensions/agentic/integration/qualia/client.rb +19 -0
- data/lib/legion/extensions/agentic/integration/qualia/helpers/constants.rb +74 -0
- data/lib/legion/extensions/agentic/integration/qualia/helpers/quale.rb +103 -0
- data/lib/legion/extensions/agentic/integration/qualia/helpers/qualia_engine.rb +142 -0
- data/lib/legion/extensions/agentic/integration/qualia/runners/qualia.rb +67 -0
- data/lib/legion/extensions/agentic/integration/qualia/version.rb +13 -0
- data/lib/legion/extensions/agentic/integration/qualia.rb +19 -0
- data/lib/legion/extensions/agentic/integration/situation_model/client.rb +28 -0
- data/lib/legion/extensions/agentic/integration/situation_model/helpers/client.rb +23 -0
- data/lib/legion/extensions/agentic/integration/situation_model/helpers/constants.rb +40 -0
- data/lib/legion/extensions/agentic/integration/situation_model/helpers/situation_engine.rb +73 -0
- data/lib/legion/extensions/agentic/integration/situation_model/helpers/situation_event.rb +56 -0
- data/lib/legion/extensions/agentic/integration/situation_model/helpers/situation_model.rb +90 -0
- data/lib/legion/extensions/agentic/integration/situation_model/runners/situation_model.rb +99 -0
- data/lib/legion/extensions/agentic/integration/situation_model/version.rb +13 -0
- data/lib/legion/extensions/agentic/integration/situation_model.rb +21 -0
- data/lib/legion/extensions/agentic/integration/synthesis/client.rb +29 -0
- data/lib/legion/extensions/agentic/integration/synthesis/helpers/constants.rb +41 -0
- data/lib/legion/extensions/agentic/integration/synthesis/helpers/synthesis.rb +63 -0
- data/lib/legion/extensions/agentic/integration/synthesis/helpers/synthesis_engine.rb +213 -0
- data/lib/legion/extensions/agentic/integration/synthesis/helpers/synthesis_stream.rb +67 -0
- data/lib/legion/extensions/agentic/integration/synthesis/runners/cognitive_synthesis.rb +82 -0
- data/lib/legion/extensions/agentic/integration/synthesis/version.rb +13 -0
- data/lib/legion/extensions/agentic/integration/synthesis.rb +20 -0
- data/lib/legion/extensions/agentic/integration/tapestry/client.rb +15 -0
- data/lib/legion/extensions/agentic/integration/tapestry/helpers/constants.rb +46 -0
- data/lib/legion/extensions/agentic/integration/tapestry/helpers/loom_engine.rb +125 -0
- data/lib/legion/extensions/agentic/integration/tapestry/helpers/tapestry.rb +144 -0
- data/lib/legion/extensions/agentic/integration/tapestry/helpers/thread.rb +112 -0
- data/lib/legion/extensions/agentic/integration/tapestry/runners/cognitive_tapestry.rb +81 -0
- data/lib/legion/extensions/agentic/integration/tapestry/version.rb +13 -0
- data/lib/legion/extensions/agentic/integration/tapestry.rb +22 -0
- data/lib/legion/extensions/agentic/integration/tessellation/client.rb +19 -0
- data/lib/legion/extensions/agentic/integration/tessellation/helpers/constants.rb +72 -0
- data/lib/legion/extensions/agentic/integration/tessellation/helpers/mosaic.rb +99 -0
- data/lib/legion/extensions/agentic/integration/tessellation/helpers/tessellation_engine.rb +141 -0
- data/lib/legion/extensions/agentic/integration/tessellation/helpers/tile.rb +76 -0
- data/lib/legion/extensions/agentic/integration/tessellation/runners/cognitive_tessellation.rb +58 -0
- data/lib/legion/extensions/agentic/integration/tessellation/version.rb +13 -0
- data/lib/legion/extensions/agentic/integration/tessellation.rb +22 -0
- data/lib/legion/extensions/agentic/integration/version.rb +11 -0
- data/lib/legion/extensions/agentic/integration/zeitgeist/helpers/client.rb +17 -0
- data/lib/legion/extensions/agentic/integration/zeitgeist/helpers/cognitive_signal.rb +38 -0
- data/lib/legion/extensions/agentic/integration/zeitgeist/helpers/constants.rb +64 -0
- data/lib/legion/extensions/agentic/integration/zeitgeist/helpers/trend_window.rb +80 -0
- data/lib/legion/extensions/agentic/integration/zeitgeist/helpers/zeitgeist_engine.rb +157 -0
- data/lib/legion/extensions/agentic/integration/zeitgeist/runners/cognitive_zeitgeist.rb +99 -0
- data/lib/legion/extensions/agentic/integration/zeitgeist/version.rb +13 -0
- data/lib/legion/extensions/agentic/integration/zeitgeist.rb +22 -0
- data/lib/legion/extensions/agentic/integration.rb +34 -0
- data/spec/legion/extensions/agentic/integration/boundary/helpers/boundary_engine_spec.rb +165 -0
- data/spec/legion/extensions/agentic/integration/boundary/helpers/boundary_spec.rb +168 -0
- data/spec/legion/extensions/agentic/integration/boundary/helpers/constants_spec.rb +65 -0
- data/spec/legion/extensions/agentic/integration/boundary/runners/cognitive_boundary_spec.rb +111 -0
- data/spec/legion/extensions/agentic/integration/context/client_spec.rb +58 -0
- data/spec/legion/extensions/agentic/integration/context/helpers/constants_spec.rb +32 -0
- data/spec/legion/extensions/agentic/integration/context/helpers/context_manager_spec.rb +190 -0
- data/spec/legion/extensions/agentic/integration/context/helpers/frame_spec.rb +164 -0
- data/spec/legion/extensions/agentic/integration/context/runners/context_spec.rb +142 -0
- data/spec/legion/extensions/agentic/integration/distributed_cognition/client_spec.rb +21 -0
- data/spec/legion/extensions/agentic/integration/distributed_cognition/helpers/distribution_engine_spec.rb +149 -0
- data/spec/legion/extensions/agentic/integration/distributed_cognition/helpers/participant_spec.rb +116 -0
- data/spec/legion/extensions/agentic/integration/distributed_cognition/runners/distributed_cognition_spec.rb +102 -0
- data/spec/legion/extensions/agentic/integration/gestalt/client_spec.rb +53 -0
- data/spec/legion/extensions/agentic/integration/gestalt/helpers/constants_spec.rb +27 -0
- data/spec/legion/extensions/agentic/integration/gestalt/helpers/pattern_spec.rb +128 -0
- data/spec/legion/extensions/agentic/integration/gestalt/helpers/pattern_store_spec.rb +155 -0
- data/spec/legion/extensions/agentic/integration/gestalt/runners/gestalt_spec.rb +114 -0
- data/spec/legion/extensions/agentic/integration/global_workspace/client_spec.rb +49 -0
- data/spec/legion/extensions/agentic/integration/global_workspace/helpers/broadcast_spec.rb +83 -0
- data/spec/legion/extensions/agentic/integration/global_workspace/helpers/competitor_spec.rb +84 -0
- data/spec/legion/extensions/agentic/integration/global_workspace/helpers/workspace_spec.rb +174 -0
- data/spec/legion/extensions/agentic/integration/global_workspace/runners/global_workspace_spec.rb +118 -0
- data/spec/legion/extensions/agentic/integration/integration/cognitive_integration_spec.rb +7 -0
- data/spec/legion/extensions/agentic/integration/integration/helpers/integrated_representation_spec.rb +149 -0
- data/spec/legion/extensions/agentic/integration/integration/helpers/integration_engine_spec.rb +191 -0
- data/spec/legion/extensions/agentic/integration/integration/helpers/modal_signal_spec.rb +81 -0
- data/spec/legion/extensions/agentic/integration/labyrinth/client_spec.rb +122 -0
- data/spec/legion/extensions/agentic/integration/labyrinth/cognitive_labyrinth_spec.rb +19 -0
- data/spec/legion/extensions/agentic/integration/labyrinth/helpers/constants_spec.rb +72 -0
- data/spec/legion/extensions/agentic/integration/labyrinth/helpers/labyrinth_engine_spec.rb +222 -0
- data/spec/legion/extensions/agentic/integration/labyrinth/helpers/labyrinth_spec.rb +235 -0
- data/spec/legion/extensions/agentic/integration/labyrinth/helpers/node_spec.rb +157 -0
- data/spec/legion/extensions/agentic/integration/labyrinth/runners/cognitive_labyrinth_spec.rb +209 -0
- data/spec/legion/extensions/agentic/integration/map/client_spec.rb +89 -0
- data/spec/legion/extensions/agentic/integration/map/helpers/cognitive_map_store_spec.rb +321 -0
- data/spec/legion/extensions/agentic/integration/map/helpers/location_spec.rb +165 -0
- data/spec/legion/extensions/agentic/integration/map/runners/cognitive_map_spec.rb +190 -0
- data/spec/legion/extensions/agentic/integration/mosaic/client_spec.rb +16 -0
- data/spec/legion/extensions/agentic/integration/mosaic/helpers/constants_spec.rb +37 -0
- data/spec/legion/extensions/agentic/integration/mosaic/helpers/mosaic_engine_spec.rb +108 -0
- data/spec/legion/extensions/agentic/integration/mosaic/helpers/mosaic_spec.rb +120 -0
- data/spec/legion/extensions/agentic/integration/mosaic/helpers/tessera_spec.rb +92 -0
- data/spec/legion/extensions/agentic/integration/mosaic/runners/cognitive_mosaic_spec.rb +88 -0
- data/spec/legion/extensions/agentic/integration/mycelium/client_spec.rb +25 -0
- data/spec/legion/extensions/agentic/integration/mycelium/cognitive_mycelium_spec.rb +7 -0
- data/spec/legion/extensions/agentic/integration/mycelium/helpers/constants_spec.rb +28 -0
- data/spec/legion/extensions/agentic/integration/mycelium/helpers/fruiting_body_spec.rb +33 -0
- data/spec/legion/extensions/agentic/integration/mycelium/helpers/hypha_spec.rb +69 -0
- data/spec/legion/extensions/agentic/integration/mycelium/helpers/mycelial_node_spec.rb +79 -0
- data/spec/legion/extensions/agentic/integration/mycelium/helpers/mycelium_engine_spec.rb +136 -0
- data/spec/legion/extensions/agentic/integration/mycelium/runners/cognitive_mycelium_spec.rb +61 -0
- data/spec/legion/extensions/agentic/integration/phenomenal_binding/client_spec.rb +52 -0
- data/spec/legion/extensions/agentic/integration/phenomenal_binding/helpers/binding_engine_spec.rb +290 -0
- data/spec/legion/extensions/agentic/integration/phenomenal_binding/helpers/binding_unit_spec.rb +195 -0
- data/spec/legion/extensions/agentic/integration/phenomenal_binding/helpers/constants_spec.rb +92 -0
- data/spec/legion/extensions/agentic/integration/phenomenal_binding/helpers/stream_spec.rb +128 -0
- data/spec/legion/extensions/agentic/integration/phenomenal_binding/runners/phenomenal_binding_spec.rb +174 -0
- data/spec/legion/extensions/agentic/integration/qualia/client_spec.rb +21 -0
- data/spec/legion/extensions/agentic/integration/qualia/helpers/quale_spec.rb +168 -0
- data/spec/legion/extensions/agentic/integration/qualia/helpers/qualia_engine_spec.rb +174 -0
- data/spec/legion/extensions/agentic/integration/qualia/qualia_spec.rb +7 -0
- data/spec/legion/extensions/agentic/integration/qualia/runners_spec.rb +70 -0
- data/spec/legion/extensions/agentic/integration/situation_model/client_spec.rb +51 -0
- data/spec/legion/extensions/agentic/integration/situation_model/helpers/constants_spec.rb +56 -0
- data/spec/legion/extensions/agentic/integration/situation_model/helpers/situation_engine_spec.rb +203 -0
- data/spec/legion/extensions/agentic/integration/situation_model/helpers/situation_event_spec.rb +94 -0
- data/spec/legion/extensions/agentic/integration/situation_model/helpers/situation_model_spec.rb +235 -0
- data/spec/legion/extensions/agentic/integration/situation_model/runners/situation_model_spec.rb +204 -0
- data/spec/legion/extensions/agentic/integration/synthesis/client_spec.rb +26 -0
- data/spec/legion/extensions/agentic/integration/synthesis/helpers/constants_spec.rb +87 -0
- data/spec/legion/extensions/agentic/integration/synthesis/helpers/synthesis_engine_spec.rb +233 -0
- data/spec/legion/extensions/agentic/integration/synthesis/helpers/synthesis_spec.rb +101 -0
- data/spec/legion/extensions/agentic/integration/synthesis/helpers/synthesis_stream_spec.rb +116 -0
- data/spec/legion/extensions/agentic/integration/synthesis/runners/cognitive_synthesis_spec.rb +181 -0
- data/spec/legion/extensions/agentic/integration/tapestry/client_spec.rb +55 -0
- data/spec/legion/extensions/agentic/integration/tapestry/helpers/constants_spec.rb +88 -0
- data/spec/legion/extensions/agentic/integration/tapestry/helpers/loom_engine_spec.rb +225 -0
- data/spec/legion/extensions/agentic/integration/tapestry/helpers/tapestry_spec.rb +229 -0
- data/spec/legion/extensions/agentic/integration/tapestry/helpers/thread_spec.rb +211 -0
- data/spec/legion/extensions/agentic/integration/tapestry/runners/cognitive_tapestry_spec.rb +171 -0
- data/spec/legion/extensions/agentic/integration/tessellation/client_spec.rb +40 -0
- data/spec/legion/extensions/agentic/integration/tessellation/helpers/mosaic_spec.rb +99 -0
- data/spec/legion/extensions/agentic/integration/tessellation/helpers/tessellation_engine_spec.rb +145 -0
- data/spec/legion/extensions/agentic/integration/tessellation/helpers/tile_spec.rb +138 -0
- data/spec/legion/extensions/agentic/integration/zeitgeist/helpers/cognitive_signal_spec.rb +62 -0
- data/spec/legion/extensions/agentic/integration/zeitgeist/helpers/constants_spec.rb +73 -0
- data/spec/legion/extensions/agentic/integration/zeitgeist/helpers/trend_window_spec.rb +100 -0
- data/spec/legion/extensions/agentic/integration/zeitgeist/helpers/zeitgeist_engine_spec.rb +163 -0
- data/spec/legion/extensions/agentic/integration/zeitgeist/runners/cognitive_zeitgeist_spec.rb +165 -0
- data/spec/spec_helper.rb +46 -0
- metadata +320 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Integration::Labyrinth::Helpers::Constants do
|
|
4
|
+
describe 'NODE_TYPES' do
|
|
5
|
+
it 'includes all expected node types' do
|
|
6
|
+
expected = %i[corridor junction dead_end entrance exit minotaur_lair]
|
|
7
|
+
expect(described_class::NODE_TYPES).to eq(expected)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'is frozen' do
|
|
11
|
+
expect(described_class::NODE_TYPES).to be_frozen
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe 'MAX_LABYRINTHS' do
|
|
16
|
+
it 'is a positive integer' do
|
|
17
|
+
expect(described_class::MAX_LABYRINTHS).to be_a(Integer)
|
|
18
|
+
expect(described_class::MAX_LABYRINTHS).to be_positive
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe 'MAX_NODES' do
|
|
23
|
+
it 'is a positive integer' do
|
|
24
|
+
expect(described_class::MAX_NODES).to be_a(Integer)
|
|
25
|
+
expect(described_class::MAX_NODES).to be_positive
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe 'COMPLEXITY_LABELS' do
|
|
30
|
+
it 'covers the full 0..1 range' do
|
|
31
|
+
[0.0, 0.1, 0.3, 0.5, 0.7, 0.9, 1.0].each do |score|
|
|
32
|
+
match = described_class::COMPLEXITY_LABELS.any? { |range, _| range.cover?(score) }
|
|
33
|
+
expect(match).to be(true), "score #{score} not covered by COMPLEXITY_LABELS"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'maps low scores to :trivial' do
|
|
38
|
+
label = described_class::COMPLEXITY_LABELS.find { |r, _| r.cover?(0.05) }&.last
|
|
39
|
+
expect(label).to eq(:trivial)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'maps high scores to :labyrinthine' do
|
|
43
|
+
label = described_class::COMPLEXITY_LABELS.find { |r, _| r.cover?(0.95) }&.last
|
|
44
|
+
expect(label).to eq(:labyrinthine)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
describe 'DANGER_LABELS' do
|
|
49
|
+
it 'covers the full 0..1 range' do
|
|
50
|
+
[0.0, 0.24, 0.3, 0.6, 0.8, 1.0].each do |level|
|
|
51
|
+
match = described_class::DANGER_LABELS.any? { |range, _| range.cover?(level) }
|
|
52
|
+
expect(match).to be(true), "level #{level} not covered by DANGER_LABELS"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'maps low danger to :safe' do
|
|
57
|
+
label = described_class::DANGER_LABELS.find { |r, _| r.cover?(0.1) }&.last
|
|
58
|
+
expect(label).to eq(:safe)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'maps high danger to :lethal' do
|
|
62
|
+
label = described_class::DANGER_LABELS.find { |r, _| r.cover?(0.9) }&.last
|
|
63
|
+
expect(label).to eq(:lethal)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe 'MINOTAUR_DANGER_LEVEL' do
|
|
68
|
+
it 'is >= 0.5' do
|
|
69
|
+
expect(described_class::MINOTAUR_DANGER_LEVEL).to be >= 0.5
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Integration::Labyrinth::Helpers::LabyrinthEngine do
|
|
4
|
+
subject(:engine) { described_class.new }
|
|
5
|
+
|
|
6
|
+
let(:lab_id) do
|
|
7
|
+
result = engine.create_labyrinth(name: 'Test Maze')
|
|
8
|
+
result.labyrinth_id
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
describe '#create_labyrinth' do
|
|
12
|
+
it 'creates a new labyrinth and returns it' do
|
|
13
|
+
lab = engine.create_labyrinth(name: 'Puzzle', domain: :logic)
|
|
14
|
+
expect(lab).to be_a(Legion::Extensions::Agentic::Integration::Labyrinth::Helpers::Labyrinth)
|
|
15
|
+
expect(lab.name).to eq('Puzzle')
|
|
16
|
+
expect(lab.domain).to eq(:logic)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'uses provided labyrinth_id' do
|
|
20
|
+
lab = engine.create_labyrinth(name: 'Custom', labyrinth_id: 'my-id')
|
|
21
|
+
expect(lab.labyrinth_id).to eq('my-id')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'generates a UUID when labyrinth_id is not provided' do
|
|
25
|
+
lab = engine.create_labyrinth(name: 'Auto')
|
|
26
|
+
expect(lab.labyrinth_id).to match(/\A[0-9a-f-]{36}\z/)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'raises ArgumentError at MAX_LABYRINTHS' do
|
|
30
|
+
max = Legion::Extensions::Agentic::Integration::Labyrinth::Helpers::Constants::MAX_LABYRINTHS
|
|
31
|
+
max.times { |i| engine.create_labyrinth(name: "lab #{i}") }
|
|
32
|
+
expect { engine.create_labyrinth(name: 'overflow') }.to raise_error(ArgumentError, /max labyrinths/)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'stores the labyrinth in @labyrinths' do
|
|
36
|
+
lab = engine.create_labyrinth(name: 'Stored')
|
|
37
|
+
expect(engine.labyrinths[lab.labyrinth_id]).to eq(lab)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe '#add_node_to' do
|
|
42
|
+
it 'adds a corridor node' do
|
|
43
|
+
node = engine.add_node_to(labyrinth_id: lab_id, node_type: :corridor, content: 'a dark hallway')
|
|
44
|
+
expect(node.node_type).to eq(:corridor)
|
|
45
|
+
expect(node.content).to eq('a dark hallway')
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'auto-assigns danger level for minotaur_lair' do
|
|
49
|
+
node = engine.add_node_to(labyrinth_id: lab_id, node_type: :minotaur_lair)
|
|
50
|
+
expect(node.danger_level).to eq(Legion::Extensions::Agentic::Integration::Labyrinth::Helpers::Constants::MINOTAUR_DANGER_LEVEL)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'uses provided node_id' do
|
|
54
|
+
node = engine.add_node_to(labyrinth_id: lab_id, node_type: :corridor, node_id: 'fixed-id')
|
|
55
|
+
expect(node.node_id).to eq('fixed-id')
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'raises ArgumentError for unknown labyrinth' do
|
|
59
|
+
expect do
|
|
60
|
+
engine.add_node_to(labyrinth_id: 'nonexistent', node_type: :corridor)
|
|
61
|
+
end.to raise_error(ArgumentError, /not found/)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
describe '#connect_nodes' do
|
|
66
|
+
let(:from_id) { engine.add_node_to(labyrinth_id: lab_id, node_type: :entrance).node_id }
|
|
67
|
+
let(:to_id) { engine.add_node_to(labyrinth_id: lab_id, node_type: :corridor).node_id }
|
|
68
|
+
|
|
69
|
+
it 'connects two nodes bidirectionally by default' do
|
|
70
|
+
engine.connect_nodes(labyrinth_id: lab_id, from_id: from_id, to_id: to_id)
|
|
71
|
+
lab = engine.labyrinths[lab_id]
|
|
72
|
+
expect(lab.nodes[from_id].connections).to include(to_id)
|
|
73
|
+
expect(lab.nodes[to_id].connections).to include(from_id)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'connects one-way when bidirectional: false' do
|
|
77
|
+
engine.connect_nodes(labyrinth_id: lab_id, from_id: from_id, to_id: to_id, bidirectional: false)
|
|
78
|
+
lab = engine.labyrinths[lab_id]
|
|
79
|
+
expect(lab.nodes[from_id].connections).to include(to_id)
|
|
80
|
+
expect(lab.nodes[to_id].connections).not_to include(from_id)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'returns a result hash' do
|
|
84
|
+
result = engine.connect_nodes(labyrinth_id: lab_id, from_id: from_id, to_id: to_id)
|
|
85
|
+
expect(result[:connected]).to be(true)
|
|
86
|
+
expect(result[:from]).to eq(from_id)
|
|
87
|
+
expect(result[:to]).to eq(to_id)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe '#move' do
|
|
92
|
+
before do
|
|
93
|
+
engine.add_node_to(labyrinth_id: lab_id, node_type: :entrance, node_id: 'entrance')
|
|
94
|
+
engine.add_node_to(labyrinth_id: lab_id, node_type: :corridor, node_id: 'corridor')
|
|
95
|
+
engine.connect_nodes(labyrinth_id: lab_id, from_id: 'entrance', to_id: 'corridor')
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'moves to a connected node and returns result hash' do
|
|
99
|
+
result = engine.move(labyrinth_id: lab_id, node_id: 'corridor')
|
|
100
|
+
expect(result[:success]).to be(true)
|
|
101
|
+
expect(result[:node_id]).to eq('corridor')
|
|
102
|
+
expect(result[:node_type]).to eq(:corridor)
|
|
103
|
+
expect(result[:path_length]).to eq(1)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it 'includes minotaur check result' do
|
|
107
|
+
result = engine.move(labyrinth_id: lab_id, node_id: 'corridor')
|
|
108
|
+
expect(result[:minotaur]).to be_a(Hash)
|
|
109
|
+
expect(result[:minotaur][:encountered]).to be(false)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
describe '#backtrack' do
|
|
114
|
+
before do
|
|
115
|
+
engine.add_node_to(labyrinth_id: lab_id, node_type: :entrance, node_id: 'entrance')
|
|
116
|
+
engine.add_node_to(labyrinth_id: lab_id, node_type: :corridor, node_id: 'corridor')
|
|
117
|
+
engine.connect_nodes(labyrinth_id: lab_id, from_id: 'entrance', to_id: 'corridor')
|
|
118
|
+
engine.move(labyrinth_id: lab_id, node_id: 'corridor')
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it 'backtracks to previous node' do
|
|
122
|
+
result = engine.backtrack(labyrinth_id: lab_id)
|
|
123
|
+
expect(result[:success]).to be(true)
|
|
124
|
+
expect(result[:node_id]).to eq('entrance')
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it 'returns failure when no breadcrumbs' do
|
|
128
|
+
engine.backtrack(labyrinth_id: lab_id)
|
|
129
|
+
result = engine.backtrack(labyrinth_id: lab_id)
|
|
130
|
+
expect(result[:success]).to be(false)
|
|
131
|
+
expect(result[:reason]).to eq(:no_breadcrumbs)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
describe '#follow_thread' do
|
|
136
|
+
before do
|
|
137
|
+
engine.add_node_to(labyrinth_id: lab_id, node_type: :entrance, node_id: 'entrance')
|
|
138
|
+
engine.add_node_to(labyrinth_id: lab_id, node_type: :corridor, node_id: 'corridor')
|
|
139
|
+
engine.connect_nodes(labyrinth_id: lab_id, from_id: 'entrance', to_id: 'corridor')
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it 'follows to the first unvisited node' do
|
|
143
|
+
result = engine.follow_thread(labyrinth_id: lab_id)
|
|
144
|
+
expect(result[:success]).to be(true)
|
|
145
|
+
expect(result[:node_id]).to eq('corridor')
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it 'returns thread_exhausted when nothing unvisited' do
|
|
149
|
+
engine.labyrinths[lab_id].nodes['corridor'].visited = true
|
|
150
|
+
result = engine.follow_thread(labyrinth_id: lab_id)
|
|
151
|
+
expect(result[:success]).to be(false)
|
|
152
|
+
expect(result[:reason]).to eq(:thread_exhausted)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
describe '#check_minotaur' do
|
|
157
|
+
before do
|
|
158
|
+
engine.add_node_to(labyrinth_id: lab_id, node_type: :minotaur_lair, node_id: 'lair', content: 'sunk cost fallacy')
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it 'returns encountered: true when on minotaur_lair' do
|
|
162
|
+
engine.labyrinths[lab_id].instance_variable_set(:@current_node_id, 'lair')
|
|
163
|
+
result = engine.check_minotaur(labyrinth_id: lab_id)
|
|
164
|
+
expect(result[:encountered]).to be(true)
|
|
165
|
+
expect(result[:misconception]).to eq('sunk cost fallacy')
|
|
166
|
+
expect(result[:danger_label]).to be_a(Symbol)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
it 'returns encountered: false for safe node' do
|
|
170
|
+
engine.add_node_to(labyrinth_id: lab_id, node_type: :corridor, node_id: 'safe')
|
|
171
|
+
engine.labyrinths[lab_id].instance_variable_set(:@current_node_id, 'safe')
|
|
172
|
+
result = engine.check_minotaur(labyrinth_id: lab_id)
|
|
173
|
+
expect(result[:encountered]).to be(false)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
describe '#labyrinth_report' do
|
|
178
|
+
before do
|
|
179
|
+
engine.add_node_to(labyrinth_id: lab_id, node_type: :entrance, node_id: 'entrance')
|
|
180
|
+
engine.add_node_to(labyrinth_id: lab_id, node_type: :dead_end, node_id: 'dead')
|
|
181
|
+
engine.add_node_to(labyrinth_id: lab_id, node_type: :exit, node_id: 'exit')
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it 'returns a comprehensive report hash' do
|
|
185
|
+
report = engine.labyrinth_report(labyrinth_id: lab_id)
|
|
186
|
+
expect(report).to include(:labyrinth_id, :name, :node_count, :nodes_by_type, :visited_count, :breadcrumb_trail, :lost)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it 'includes nodes_by_type breakdown' do
|
|
190
|
+
report = engine.labyrinth_report(labyrinth_id: lab_id)
|
|
191
|
+
expect(report[:nodes_by_type]).to include(entrance: 1, dead_end: 1, exit: 1)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
describe '#list_labyrinths' do
|
|
196
|
+
it 'returns empty array initially' do
|
|
197
|
+
fresh = described_class.new
|
|
198
|
+
expect(fresh.list_labyrinths).to eq([])
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
it 'lists all created labyrinths' do
|
|
202
|
+
fresh = described_class.new
|
|
203
|
+
fresh.create_labyrinth(name: 'A')
|
|
204
|
+
fresh.create_labyrinth(name: 'B')
|
|
205
|
+
list = fresh.list_labyrinths
|
|
206
|
+
expect(list.size).to eq(2)
|
|
207
|
+
expect(list.map { |l| l[:name] }).to include('A', 'B')
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
describe '#delete_labyrinth' do
|
|
212
|
+
it 'removes the labyrinth' do
|
|
213
|
+
result = engine.delete_labyrinth(labyrinth_id: lab_id)
|
|
214
|
+
expect(result[:deleted]).to be(true)
|
|
215
|
+
expect(engine.labyrinths).not_to have_key(lab_id)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
it 'raises ArgumentError for unknown labyrinth' do
|
|
219
|
+
expect { engine.delete_labyrinth(labyrinth_id: 'nope') }.to raise_error(ArgumentError, /not found/)
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Integration::Labyrinth::Helpers::Labyrinth do
|
|
4
|
+
let(:labyrinth_id) { 'lab-001' }
|
|
5
|
+
let(:labyrinth) { described_class.new(labyrinth_id: labyrinth_id, name: 'Test Maze', domain: :reasoning) }
|
|
6
|
+
|
|
7
|
+
let(:node_class) { Legion::Extensions::Agentic::Integration::Labyrinth::Helpers::Node }
|
|
8
|
+
|
|
9
|
+
def make_node(id, type, **)
|
|
10
|
+
node_class.new(node_id: id, node_type: type, **)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe '#initialize' do
|
|
14
|
+
it 'creates a labyrinth with correct attributes' do
|
|
15
|
+
expect(labyrinth.labyrinth_id).to eq(labyrinth_id)
|
|
16
|
+
expect(labyrinth.name).to eq('Test Maze')
|
|
17
|
+
expect(labyrinth.domain).to eq(:reasoning)
|
|
18
|
+
expect(labyrinth.nodes).to eq({})
|
|
19
|
+
expect(labyrinth.breadcrumbs).to eq([])
|
|
20
|
+
expect(labyrinth.current_node_id).to be_nil
|
|
21
|
+
expect(labyrinth.solved).to be(false)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe '#add_node' do
|
|
26
|
+
it 'adds a node to the labyrinth' do
|
|
27
|
+
node = make_node('n1', :corridor)
|
|
28
|
+
labyrinth.add_node(node)
|
|
29
|
+
expect(labyrinth.nodes['n1']).to eq(node)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'sets current_node_id to entrance node' do
|
|
33
|
+
entrance = make_node('entrance-1', :entrance)
|
|
34
|
+
labyrinth.add_node(entrance)
|
|
35
|
+
expect(labyrinth.current_node_id).to eq('entrance-1')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'does not override current_node_id once set' do
|
|
39
|
+
entrance = make_node('entrance-1', :entrance)
|
|
40
|
+
entrance2 = make_node('entrance-2', :entrance)
|
|
41
|
+
labyrinth.add_node(entrance)
|
|
42
|
+
labyrinth.add_node(entrance2)
|
|
43
|
+
expect(labyrinth.current_node_id).to eq('entrance-1')
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'raises ArgumentError for non-Node argument' do
|
|
47
|
+
expect { labyrinth.add_node('not a node') }.to raise_error(ArgumentError, /must be a/)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'raises ArgumentError when MAX_NODES exceeded' do
|
|
51
|
+
max = Legion::Extensions::Agentic::Integration::Labyrinth::Helpers::Constants::MAX_NODES
|
|
52
|
+
max.times { |i| labyrinth.add_node(make_node("n#{i}", :corridor)) }
|
|
53
|
+
expect { labyrinth.add_node(make_node('overflow', :corridor)) }.to raise_error(ArgumentError, /max nodes/)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe '#move_to!' do
|
|
58
|
+
before do
|
|
59
|
+
entrance = make_node('entrance', :entrance)
|
|
60
|
+
corridor = make_node('corridor', :corridor)
|
|
61
|
+
exit_node = make_node('exit', :exit)
|
|
62
|
+
entrance.connect!('corridor')
|
|
63
|
+
corridor.connect!('entrance')
|
|
64
|
+
corridor.connect!('exit')
|
|
65
|
+
exit_node.connect!('corridor')
|
|
66
|
+
labyrinth.add_node(entrance)
|
|
67
|
+
labyrinth.add_node(corridor)
|
|
68
|
+
labyrinth.add_node(exit_node)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'moves to a connected node' do
|
|
72
|
+
labyrinth.move_to!('corridor')
|
|
73
|
+
expect(labyrinth.current_node_id).to eq('corridor')
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'marks the target node as visited' do
|
|
77
|
+
labyrinth.move_to!('corridor')
|
|
78
|
+
expect(labyrinth.nodes['corridor'].visited).to be(true)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'drops a breadcrumb on the previous node' do
|
|
82
|
+
labyrinth.move_to!('corridor')
|
|
83
|
+
expect(labyrinth.breadcrumbs).to include('entrance')
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it 'sets solved when moving to exit' do
|
|
87
|
+
labyrinth.move_to!('corridor')
|
|
88
|
+
labyrinth.move_to!('exit')
|
|
89
|
+
expect(labyrinth.solved?).to be(true)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it 'raises ArgumentError for unknown node_id' do
|
|
93
|
+
expect { labyrinth.move_to!('unknown') }.to raise_error(ArgumentError, /not found/)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it 'raises ArgumentError when not connected' do
|
|
97
|
+
expect { labyrinth.move_to!('exit') }.to raise_error(ArgumentError, /not connected/)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
describe '#backtrack!' do
|
|
102
|
+
before do
|
|
103
|
+
entrance = make_node('entrance', :entrance)
|
|
104
|
+
corridor = make_node('corridor', :corridor)
|
|
105
|
+
entrance.connect!('corridor')
|
|
106
|
+
corridor.connect!('entrance')
|
|
107
|
+
labyrinth.add_node(entrance)
|
|
108
|
+
labyrinth.add_node(corridor)
|
|
109
|
+
labyrinth.move_to!('corridor')
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it 'returns nil when no breadcrumbs' do
|
|
113
|
+
empty_lab = described_class.new(labyrinth_id: 'x', name: 'x')
|
|
114
|
+
expect(empty_lab.backtrack!).to be_nil
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'moves back to the previous node' do
|
|
118
|
+
labyrinth.backtrack!
|
|
119
|
+
expect(labyrinth.current_node_id).to eq('entrance')
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
describe '#follow_thread' do
|
|
124
|
+
before do
|
|
125
|
+
entrance = make_node('entrance', :entrance)
|
|
126
|
+
corridor = make_node('corridor', :corridor)
|
|
127
|
+
entrance.connect!('corridor')
|
|
128
|
+
corridor.connect!('entrance')
|
|
129
|
+
labyrinth.add_node(entrance)
|
|
130
|
+
labyrinth.add_node(corridor)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it 'moves to the first unvisited connected node' do
|
|
134
|
+
result = labyrinth.follow_thread
|
|
135
|
+
expect(result).not_to be_nil
|
|
136
|
+
expect(labyrinth.current_node_id).to eq('corridor')
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it 'returns nil when all connections are visited' do
|
|
140
|
+
labyrinth.nodes['corridor'].visited = true
|
|
141
|
+
result = labyrinth.follow_thread
|
|
142
|
+
expect(result).to be_nil
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it 'returns nil when current node is nil' do
|
|
146
|
+
empty_lab = described_class.new(labyrinth_id: 'x', name: 'x')
|
|
147
|
+
expect(empty_lab.follow_thread).to be_nil
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
describe '#path_length' do
|
|
152
|
+
it 'returns 0 when no moves made' do
|
|
153
|
+
expect(labyrinth.path_length).to eq(0)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it 'returns breadcrumb count' do
|
|
157
|
+
entrance = make_node('entrance', :entrance)
|
|
158
|
+
corridor = make_node('corridor', :corridor)
|
|
159
|
+
entrance.connect!('corridor')
|
|
160
|
+
corridor.connect!('entrance')
|
|
161
|
+
labyrinth.add_node(entrance)
|
|
162
|
+
labyrinth.add_node(corridor)
|
|
163
|
+
labyrinth.move_to!('corridor')
|
|
164
|
+
expect(labyrinth.path_length).to eq(1)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
describe '#complexity' do
|
|
169
|
+
it 'returns 0.0 for empty labyrinth' do
|
|
170
|
+
expect(labyrinth.complexity).to eq(0.0)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it 'is higher with more dead ends and minotaur lairs' do
|
|
174
|
+
labyrinth.add_node(make_node('d1', :dead_end))
|
|
175
|
+
labyrinth.add_node(make_node('d2', :dead_end))
|
|
176
|
+
labyrinth.add_node(make_node('m1', :minotaur_lair))
|
|
177
|
+
expect(labyrinth.complexity).to be > 0.0
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it 'is clamped between 0.0 and 1.0' do
|
|
181
|
+
labyrinth.add_node(make_node('m1', :minotaur_lair))
|
|
182
|
+
expect(labyrinth.complexity).to be_between(0.0, 1.0)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
it 'rounds to 10 decimal places' do
|
|
186
|
+
labyrinth.add_node(make_node('n1', :corridor))
|
|
187
|
+
expect(labyrinth.complexity.to_s.split('.').last.to_s.length).to be <= 10
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
describe '#complexity_label' do
|
|
192
|
+
it 'returns a symbol' do
|
|
193
|
+
labyrinth.add_node(make_node('n1', :corridor))
|
|
194
|
+
expect(labyrinth.complexity_label).to be_a(Symbol)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
it 'returns :trivial for a labyrinth with only well-connected corridors' do
|
|
198
|
+
# A junction connected to several corridors — no dead ends, no minotaurs
|
|
199
|
+
j = make_node('j', :junction)
|
|
200
|
+
c1 = make_node('c1', :corridor)
|
|
201
|
+
c2 = make_node('c2', :corridor)
|
|
202
|
+
c3 = make_node('c3', :corridor)
|
|
203
|
+
j.connect!('c1')
|
|
204
|
+
j.connect!('c2')
|
|
205
|
+
j.connect!('c3')
|
|
206
|
+
c1.connect!('j')
|
|
207
|
+
c2.connect!('j')
|
|
208
|
+
c3.connect!('j')
|
|
209
|
+
labyrinth.add_node(j)
|
|
210
|
+
labyrinth.add_node(c1)
|
|
211
|
+
labyrinth.add_node(c2)
|
|
212
|
+
labyrinth.add_node(c3)
|
|
213
|
+
expect(labyrinth.complexity_label).to eq(:trivial)
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
describe '#solved?' do
|
|
218
|
+
it 'returns false by default' do
|
|
219
|
+
expect(labyrinth.solved?).to be(false)
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
describe '#lost?' do
|
|
224
|
+
it 'returns false with no current node' do
|
|
225
|
+
expect(labyrinth.lost?).to be(false)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
describe '#to_h' do
|
|
230
|
+
it 'includes expected keys' do
|
|
231
|
+
h = labyrinth.to_h
|
|
232
|
+
expect(h).to include(:labyrinth_id, :name, :domain, :node_count, :current_node_id, :path_length, :solved, :complexity, :complexity_label)
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Integration::Labyrinth::Helpers::Node do
|
|
4
|
+
let(:node_id) { 'node-001' }
|
|
5
|
+
|
|
6
|
+
describe '#initialize' do
|
|
7
|
+
it 'creates a corridor node' do
|
|
8
|
+
node = described_class.new(node_id: node_id, node_type: :corridor)
|
|
9
|
+
expect(node.node_id).to eq(node_id)
|
|
10
|
+
expect(node.node_type).to eq(:corridor)
|
|
11
|
+
expect(node.visited).to be(false)
|
|
12
|
+
expect(node.connections).to eq([])
|
|
13
|
+
expect(node.danger_level).to eq(0.0)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'creates a minotaur_lair node' do
|
|
17
|
+
node = described_class.new(node_id: node_id, node_type: :minotaur_lair, content: 'false belief')
|
|
18
|
+
expect(node.node_type).to eq(:minotaur_lair)
|
|
19
|
+
expect(node.content).to eq('false belief')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'clamps danger_level to 0..1' do
|
|
23
|
+
node = described_class.new(node_id: node_id, node_type: :corridor, danger_level: 2.5)
|
|
24
|
+
expect(node.danger_level).to eq(1.0)
|
|
25
|
+
|
|
26
|
+
node2 = described_class.new(node_id: node_id, node_type: :corridor, danger_level: -1.0)
|
|
27
|
+
expect(node2.danger_level).to eq(0.0)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'raises ArgumentError for unknown node_type' do
|
|
31
|
+
expect do
|
|
32
|
+
described_class.new(node_id: node_id, node_type: :dungeon)
|
|
33
|
+
end.to raise_error(ArgumentError, /unknown node_type/)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'accepts all valid node types' do
|
|
37
|
+
Legion::Extensions::Agentic::Integration::Labyrinth::Helpers::Constants::NODE_TYPES.each do |type|
|
|
38
|
+
expect { described_class.new(node_id: "n-#{type}", node_type: type) }.not_to raise_error
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe '#connect!' do
|
|
44
|
+
it 'adds a connection' do
|
|
45
|
+
node = described_class.new(node_id: node_id, node_type: :corridor)
|
|
46
|
+
node.connect!('other-node')
|
|
47
|
+
expect(node.connections).to include('other-node')
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'does not duplicate connections' do
|
|
51
|
+
node = described_class.new(node_id: node_id, node_type: :corridor)
|
|
52
|
+
node.connect!('other-node')
|
|
53
|
+
node.connect!('other-node')
|
|
54
|
+
expect(node.connections.count('other-node')).to eq(1)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'returns self for chaining' do
|
|
58
|
+
node = described_class.new(node_id: node_id, node_type: :corridor)
|
|
59
|
+
expect(node.connect!('other')).to eq(node)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
describe '#disconnect!' do
|
|
64
|
+
it 'removes a connection' do
|
|
65
|
+
node = described_class.new(node_id: node_id, node_type: :corridor)
|
|
66
|
+
node.connect!('other-node')
|
|
67
|
+
node.disconnect!('other-node')
|
|
68
|
+
expect(node.connections).not_to include('other-node')
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'returns self for chaining' do
|
|
72
|
+
node = described_class.new(node_id: node_id, node_type: :corridor)
|
|
73
|
+
expect(node.disconnect!('nonexistent')).to eq(node)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
describe '#dead_end?' do
|
|
78
|
+
it 'returns true for :dead_end type' do
|
|
79
|
+
node = described_class.new(node_id: node_id, node_type: :dead_end)
|
|
80
|
+
expect(node.dead_end?).to be(true)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'returns true for corridor with no connections' do
|
|
84
|
+
node = described_class.new(node_id: node_id, node_type: :corridor)
|
|
85
|
+
expect(node.dead_end?).to be(true)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'returns false for corridor with connections' do
|
|
89
|
+
node = described_class.new(node_id: node_id, node_type: :corridor)
|
|
90
|
+
node.connect!('somewhere')
|
|
91
|
+
expect(node.dead_end?).to be(false)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it 'returns false for entrance with no connections' do
|
|
95
|
+
node = described_class.new(node_id: node_id, node_type: :entrance)
|
|
96
|
+
expect(node.dead_end?).to be(false)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it 'returns false for exit with no connections' do
|
|
100
|
+
node = described_class.new(node_id: node_id, node_type: :exit)
|
|
101
|
+
expect(node.dead_end?).to be(false)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe '#junction?' do
|
|
106
|
+
it 'returns true for :junction type' do
|
|
107
|
+
node = described_class.new(node_id: node_id, node_type: :junction)
|
|
108
|
+
expect(node.junction?).to be(true)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it 'returns true for corridor with 3+ connections' do
|
|
112
|
+
node = described_class.new(node_id: node_id, node_type: :corridor)
|
|
113
|
+
node.connect!('a')
|
|
114
|
+
node.connect!('b')
|
|
115
|
+
node.connect!('c')
|
|
116
|
+
expect(node.junction?).to be(true)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it 'returns false for corridor with 2 connections' do
|
|
120
|
+
node = described_class.new(node_id: node_id, node_type: :corridor)
|
|
121
|
+
node.connect!('a')
|
|
122
|
+
node.connect!('b')
|
|
123
|
+
expect(node.junction?).to be(false)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
describe '#dangerous?' do
|
|
128
|
+
it 'returns true for :minotaur_lair type' do
|
|
129
|
+
node = described_class.new(node_id: node_id, node_type: :minotaur_lair)
|
|
130
|
+
expect(node.dangerous?).to be(true)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it 'returns true for danger_level >= 0.5' do
|
|
134
|
+
node = described_class.new(node_id: node_id, node_type: :corridor, danger_level: 0.5)
|
|
135
|
+
expect(node.dangerous?).to be(true)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'returns false for safe corridor' do
|
|
139
|
+
node = described_class.new(node_id: node_id, node_type: :corridor, danger_level: 0.2)
|
|
140
|
+
expect(node.dangerous?).to be(false)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
describe '#to_h' do
|
|
145
|
+
it 'returns a hash with expected keys' do
|
|
146
|
+
node = described_class.new(node_id: node_id, node_type: :junction, content: 'crossroads', danger_level: 0.3)
|
|
147
|
+
node.connect!('n2')
|
|
148
|
+
h = node.to_h
|
|
149
|
+
expect(h[:node_id]).to eq(node_id)
|
|
150
|
+
expect(h[:node_type]).to eq(:junction)
|
|
151
|
+
expect(h[:content]).to eq('crossroads')
|
|
152
|
+
expect(h[:connections]).to eq(['n2'])
|
|
153
|
+
expect(h[:visited]).to be(false)
|
|
154
|
+
expect(h[:danger_level]).to eq(0.3)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|