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.
Files changed (236) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +12 -0
  3. data/Gemfile +5 -0
  4. data/LICENSE +21 -0
  5. data/README.md +13 -0
  6. data/lex-agentic-integration.gemspec +30 -0
  7. data/lib/legion/extensions/agentic/integration/boundary/client.rb +15 -0
  8. data/lib/legion/extensions/agentic/integration/boundary/helpers/boundary.rb +90 -0
  9. data/lib/legion/extensions/agentic/integration/boundary/helpers/boundary_engine.rb +123 -0
  10. data/lib/legion/extensions/agentic/integration/boundary/helpers/constants.rb +44 -0
  11. data/lib/legion/extensions/agentic/integration/boundary/runners/cognitive_boundary.rb +100 -0
  12. data/lib/legion/extensions/agentic/integration/boundary/version.rb +13 -0
  13. data/lib/legion/extensions/agentic/integration/boundary.rb +19 -0
  14. data/lib/legion/extensions/agentic/integration/context/client.rb +26 -0
  15. data/lib/legion/extensions/agentic/integration/context/helpers/constants.rb +34 -0
  16. data/lib/legion/extensions/agentic/integration/context/helpers/context_manager.rb +150 -0
  17. data/lib/legion/extensions/agentic/integration/context/helpers/frame.rb +99 -0
  18. data/lib/legion/extensions/agentic/integration/context/runners/context.rb +94 -0
  19. data/lib/legion/extensions/agentic/integration/context/version.rb +13 -0
  20. data/lib/legion/extensions/agentic/integration/context.rb +19 -0
  21. data/lib/legion/extensions/agentic/integration/distributed_cognition/client.rb +19 -0
  22. data/lib/legion/extensions/agentic/integration/distributed_cognition/helpers/constants.rb +51 -0
  23. data/lib/legion/extensions/agentic/integration/distributed_cognition/helpers/distribution_engine.rb +159 -0
  24. data/lib/legion/extensions/agentic/integration/distributed_cognition/helpers/participant.rb +100 -0
  25. data/lib/legion/extensions/agentic/integration/distributed_cognition/runners/distributed_cognition.rb +107 -0
  26. data/lib/legion/extensions/agentic/integration/distributed_cognition/version.rb +13 -0
  27. data/lib/legion/extensions/agentic/integration/distributed_cognition.rb +19 -0
  28. data/lib/legion/extensions/agentic/integration/gestalt/client.rb +21 -0
  29. data/lib/legion/extensions/agentic/integration/gestalt/helpers/constants.rb +51 -0
  30. data/lib/legion/extensions/agentic/integration/gestalt/helpers/pattern.rb +90 -0
  31. data/lib/legion/extensions/agentic/integration/gestalt/helpers/pattern_store.rb +123 -0
  32. data/lib/legion/extensions/agentic/integration/gestalt/runners/gestalt.rb +82 -0
  33. data/lib/legion/extensions/agentic/integration/gestalt/version.rb +13 -0
  34. data/lib/legion/extensions/agentic/integration/gestalt.rb +19 -0
  35. data/lib/legion/extensions/agentic/integration/global_workspace/actors/competition.rb +45 -0
  36. data/lib/legion/extensions/agentic/integration/global_workspace/client.rb +29 -0
  37. data/lib/legion/extensions/agentic/integration/global_workspace/helpers/broadcast.rb +62 -0
  38. data/lib/legion/extensions/agentic/integration/global_workspace/helpers/competitor.rb +59 -0
  39. data/lib/legion/extensions/agentic/integration/global_workspace/helpers/constants.rb +65 -0
  40. data/lib/legion/extensions/agentic/integration/global_workspace/helpers/workspace.rb +188 -0
  41. data/lib/legion/extensions/agentic/integration/global_workspace/runners/global_workspace.rb +104 -0
  42. data/lib/legion/extensions/agentic/integration/global_workspace/version.rb +13 -0
  43. data/lib/legion/extensions/agentic/integration/global_workspace.rb +20 -0
  44. data/lib/legion/extensions/agentic/integration/integration/client.rb +19 -0
  45. data/lib/legion/extensions/agentic/integration/integration/helpers/constants.rb +62 -0
  46. data/lib/legion/extensions/agentic/integration/integration/helpers/integrated_representation.rb +106 -0
  47. data/lib/legion/extensions/agentic/integration/integration/helpers/integration_engine.rb +163 -0
  48. data/lib/legion/extensions/agentic/integration/integration/helpers/modal_signal.rb +62 -0
  49. data/lib/legion/extensions/agentic/integration/integration/runners/cognitive_integration.rb +105 -0
  50. data/lib/legion/extensions/agentic/integration/integration/version.rb +13 -0
  51. data/lib/legion/extensions/agentic/integration/integration.rb +20 -0
  52. data/lib/legion/extensions/agentic/integration/labyrinth/actors/thread_walker.rb +45 -0
  53. data/lib/legion/extensions/agentic/integration/labyrinth/client.rb +23 -0
  54. data/lib/legion/extensions/agentic/integration/labyrinth/helpers/constants.rb +39 -0
  55. data/lib/legion/extensions/agentic/integration/labyrinth/helpers/labyrinth.rb +138 -0
  56. data/lib/legion/extensions/agentic/integration/labyrinth/helpers/labyrinth_engine.rb +177 -0
  57. data/lib/legion/extensions/agentic/integration/labyrinth/helpers/node.rb +64 -0
  58. data/lib/legion/extensions/agentic/integration/labyrinth/runners/cognitive_labyrinth.rb +128 -0
  59. data/lib/legion/extensions/agentic/integration/labyrinth/version.rb +13 -0
  60. data/lib/legion/extensions/agentic/integration/labyrinth.rb +22 -0
  61. data/lib/legion/extensions/agentic/integration/map/actors/decay.rb +45 -0
  62. data/lib/legion/extensions/agentic/integration/map/client.rb +29 -0
  63. data/lib/legion/extensions/agentic/integration/map/helpers/cognitive_map_store.rb +179 -0
  64. data/lib/legion/extensions/agentic/integration/map/helpers/constants.rb +71 -0
  65. data/lib/legion/extensions/agentic/integration/map/helpers/graph_traversal.rb +124 -0
  66. data/lib/legion/extensions/agentic/integration/map/helpers/location.rb +71 -0
  67. data/lib/legion/extensions/agentic/integration/map/runners/cognitive_map.rb +95 -0
  68. data/lib/legion/extensions/agentic/integration/map/version.rb +13 -0
  69. data/lib/legion/extensions/agentic/integration/map.rb +20 -0
  70. data/lib/legion/extensions/agentic/integration/mosaic/client.rb +15 -0
  71. data/lib/legion/extensions/agentic/integration/mosaic/helpers/constants.rb +55 -0
  72. data/lib/legion/extensions/agentic/integration/mosaic/helpers/mosaic.rb +111 -0
  73. data/lib/legion/extensions/agentic/integration/mosaic/helpers/mosaic_engine.rb +124 -0
  74. data/lib/legion/extensions/agentic/integration/mosaic/helpers/tessera.rb +86 -0
  75. data/lib/legion/extensions/agentic/integration/mosaic/runners/cognitive_mosaic.rb +76 -0
  76. data/lib/legion/extensions/agentic/integration/mosaic/version.rb +13 -0
  77. data/lib/legion/extensions/agentic/integration/mosaic.rb +22 -0
  78. data/lib/legion/extensions/agentic/integration/mycelium/client.rb +19 -0
  79. data/lib/legion/extensions/agentic/integration/mycelium/helpers/constants.rb +52 -0
  80. data/lib/legion/extensions/agentic/integration/mycelium/helpers/fruiting_body.rb +50 -0
  81. data/lib/legion/extensions/agentic/integration/mycelium/helpers/hypha.rb +76 -0
  82. data/lib/legion/extensions/agentic/integration/mycelium/helpers/mycelial_node.rb +72 -0
  83. data/lib/legion/extensions/agentic/integration/mycelium/helpers/mycelium_engine.rb +151 -0
  84. data/lib/legion/extensions/agentic/integration/mycelium/runners/cognitive_mycelium.rb +73 -0
  85. data/lib/legion/extensions/agentic/integration/mycelium/version.rb +13 -0
  86. data/lib/legion/extensions/agentic/integration/mycelium.rb +23 -0
  87. data/lib/legion/extensions/agentic/integration/phenomenal_binding/client.rb +25 -0
  88. data/lib/legion/extensions/agentic/integration/phenomenal_binding/helpers/binding_engine.rb +156 -0
  89. data/lib/legion/extensions/agentic/integration/phenomenal_binding/helpers/binding_unit.rb +72 -0
  90. data/lib/legion/extensions/agentic/integration/phenomenal_binding/helpers/constants.rb +35 -0
  91. data/lib/legion/extensions/agentic/integration/phenomenal_binding/helpers/stream.rb +51 -0
  92. data/lib/legion/extensions/agentic/integration/phenomenal_binding/runners/phenomenal_binding.rb +110 -0
  93. data/lib/legion/extensions/agentic/integration/phenomenal_binding/version.rb +13 -0
  94. data/lib/legion/extensions/agentic/integration/phenomenal_binding.rb +20 -0
  95. data/lib/legion/extensions/agentic/integration/qualia/client.rb +19 -0
  96. data/lib/legion/extensions/agentic/integration/qualia/helpers/constants.rb +74 -0
  97. data/lib/legion/extensions/agentic/integration/qualia/helpers/quale.rb +103 -0
  98. data/lib/legion/extensions/agentic/integration/qualia/helpers/qualia_engine.rb +142 -0
  99. data/lib/legion/extensions/agentic/integration/qualia/runners/qualia.rb +67 -0
  100. data/lib/legion/extensions/agentic/integration/qualia/version.rb +13 -0
  101. data/lib/legion/extensions/agentic/integration/qualia.rb +19 -0
  102. data/lib/legion/extensions/agentic/integration/situation_model/client.rb +28 -0
  103. data/lib/legion/extensions/agentic/integration/situation_model/helpers/client.rb +23 -0
  104. data/lib/legion/extensions/agentic/integration/situation_model/helpers/constants.rb +40 -0
  105. data/lib/legion/extensions/agentic/integration/situation_model/helpers/situation_engine.rb +73 -0
  106. data/lib/legion/extensions/agentic/integration/situation_model/helpers/situation_event.rb +56 -0
  107. data/lib/legion/extensions/agentic/integration/situation_model/helpers/situation_model.rb +90 -0
  108. data/lib/legion/extensions/agentic/integration/situation_model/runners/situation_model.rb +99 -0
  109. data/lib/legion/extensions/agentic/integration/situation_model/version.rb +13 -0
  110. data/lib/legion/extensions/agentic/integration/situation_model.rb +21 -0
  111. data/lib/legion/extensions/agentic/integration/synthesis/client.rb +29 -0
  112. data/lib/legion/extensions/agentic/integration/synthesis/helpers/constants.rb +41 -0
  113. data/lib/legion/extensions/agentic/integration/synthesis/helpers/synthesis.rb +63 -0
  114. data/lib/legion/extensions/agentic/integration/synthesis/helpers/synthesis_engine.rb +213 -0
  115. data/lib/legion/extensions/agentic/integration/synthesis/helpers/synthesis_stream.rb +67 -0
  116. data/lib/legion/extensions/agentic/integration/synthesis/runners/cognitive_synthesis.rb +82 -0
  117. data/lib/legion/extensions/agentic/integration/synthesis/version.rb +13 -0
  118. data/lib/legion/extensions/agentic/integration/synthesis.rb +20 -0
  119. data/lib/legion/extensions/agentic/integration/tapestry/client.rb +15 -0
  120. data/lib/legion/extensions/agentic/integration/tapestry/helpers/constants.rb +46 -0
  121. data/lib/legion/extensions/agentic/integration/tapestry/helpers/loom_engine.rb +125 -0
  122. data/lib/legion/extensions/agentic/integration/tapestry/helpers/tapestry.rb +144 -0
  123. data/lib/legion/extensions/agentic/integration/tapestry/helpers/thread.rb +112 -0
  124. data/lib/legion/extensions/agentic/integration/tapestry/runners/cognitive_tapestry.rb +81 -0
  125. data/lib/legion/extensions/agentic/integration/tapestry/version.rb +13 -0
  126. data/lib/legion/extensions/agentic/integration/tapestry.rb +22 -0
  127. data/lib/legion/extensions/agentic/integration/tessellation/client.rb +19 -0
  128. data/lib/legion/extensions/agentic/integration/tessellation/helpers/constants.rb +72 -0
  129. data/lib/legion/extensions/agentic/integration/tessellation/helpers/mosaic.rb +99 -0
  130. data/lib/legion/extensions/agentic/integration/tessellation/helpers/tessellation_engine.rb +141 -0
  131. data/lib/legion/extensions/agentic/integration/tessellation/helpers/tile.rb +76 -0
  132. data/lib/legion/extensions/agentic/integration/tessellation/runners/cognitive_tessellation.rb +58 -0
  133. data/lib/legion/extensions/agentic/integration/tessellation/version.rb +13 -0
  134. data/lib/legion/extensions/agentic/integration/tessellation.rb +22 -0
  135. data/lib/legion/extensions/agentic/integration/version.rb +11 -0
  136. data/lib/legion/extensions/agentic/integration/zeitgeist/helpers/client.rb +17 -0
  137. data/lib/legion/extensions/agentic/integration/zeitgeist/helpers/cognitive_signal.rb +38 -0
  138. data/lib/legion/extensions/agentic/integration/zeitgeist/helpers/constants.rb +64 -0
  139. data/lib/legion/extensions/agentic/integration/zeitgeist/helpers/trend_window.rb +80 -0
  140. data/lib/legion/extensions/agentic/integration/zeitgeist/helpers/zeitgeist_engine.rb +157 -0
  141. data/lib/legion/extensions/agentic/integration/zeitgeist/runners/cognitive_zeitgeist.rb +99 -0
  142. data/lib/legion/extensions/agentic/integration/zeitgeist/version.rb +13 -0
  143. data/lib/legion/extensions/agentic/integration/zeitgeist.rb +22 -0
  144. data/lib/legion/extensions/agentic/integration.rb +34 -0
  145. data/spec/legion/extensions/agentic/integration/boundary/helpers/boundary_engine_spec.rb +165 -0
  146. data/spec/legion/extensions/agentic/integration/boundary/helpers/boundary_spec.rb +168 -0
  147. data/spec/legion/extensions/agentic/integration/boundary/helpers/constants_spec.rb +65 -0
  148. data/spec/legion/extensions/agentic/integration/boundary/runners/cognitive_boundary_spec.rb +111 -0
  149. data/spec/legion/extensions/agentic/integration/context/client_spec.rb +58 -0
  150. data/spec/legion/extensions/agentic/integration/context/helpers/constants_spec.rb +32 -0
  151. data/spec/legion/extensions/agentic/integration/context/helpers/context_manager_spec.rb +190 -0
  152. data/spec/legion/extensions/agentic/integration/context/helpers/frame_spec.rb +164 -0
  153. data/spec/legion/extensions/agentic/integration/context/runners/context_spec.rb +142 -0
  154. data/spec/legion/extensions/agentic/integration/distributed_cognition/client_spec.rb +21 -0
  155. data/spec/legion/extensions/agentic/integration/distributed_cognition/helpers/distribution_engine_spec.rb +149 -0
  156. data/spec/legion/extensions/agentic/integration/distributed_cognition/helpers/participant_spec.rb +116 -0
  157. data/spec/legion/extensions/agentic/integration/distributed_cognition/runners/distributed_cognition_spec.rb +102 -0
  158. data/spec/legion/extensions/agentic/integration/gestalt/client_spec.rb +53 -0
  159. data/spec/legion/extensions/agentic/integration/gestalt/helpers/constants_spec.rb +27 -0
  160. data/spec/legion/extensions/agentic/integration/gestalt/helpers/pattern_spec.rb +128 -0
  161. data/spec/legion/extensions/agentic/integration/gestalt/helpers/pattern_store_spec.rb +155 -0
  162. data/spec/legion/extensions/agentic/integration/gestalt/runners/gestalt_spec.rb +114 -0
  163. data/spec/legion/extensions/agentic/integration/global_workspace/client_spec.rb +49 -0
  164. data/spec/legion/extensions/agentic/integration/global_workspace/helpers/broadcast_spec.rb +83 -0
  165. data/spec/legion/extensions/agentic/integration/global_workspace/helpers/competitor_spec.rb +84 -0
  166. data/spec/legion/extensions/agentic/integration/global_workspace/helpers/workspace_spec.rb +174 -0
  167. data/spec/legion/extensions/agentic/integration/global_workspace/runners/global_workspace_spec.rb +118 -0
  168. data/spec/legion/extensions/agentic/integration/integration/cognitive_integration_spec.rb +7 -0
  169. data/spec/legion/extensions/agentic/integration/integration/helpers/integrated_representation_spec.rb +149 -0
  170. data/spec/legion/extensions/agentic/integration/integration/helpers/integration_engine_spec.rb +191 -0
  171. data/spec/legion/extensions/agentic/integration/integration/helpers/modal_signal_spec.rb +81 -0
  172. data/spec/legion/extensions/agentic/integration/labyrinth/client_spec.rb +122 -0
  173. data/spec/legion/extensions/agentic/integration/labyrinth/cognitive_labyrinth_spec.rb +19 -0
  174. data/spec/legion/extensions/agentic/integration/labyrinth/helpers/constants_spec.rb +72 -0
  175. data/spec/legion/extensions/agentic/integration/labyrinth/helpers/labyrinth_engine_spec.rb +222 -0
  176. data/spec/legion/extensions/agentic/integration/labyrinth/helpers/labyrinth_spec.rb +235 -0
  177. data/spec/legion/extensions/agentic/integration/labyrinth/helpers/node_spec.rb +157 -0
  178. data/spec/legion/extensions/agentic/integration/labyrinth/runners/cognitive_labyrinth_spec.rb +209 -0
  179. data/spec/legion/extensions/agentic/integration/map/client_spec.rb +89 -0
  180. data/spec/legion/extensions/agentic/integration/map/helpers/cognitive_map_store_spec.rb +321 -0
  181. data/spec/legion/extensions/agentic/integration/map/helpers/location_spec.rb +165 -0
  182. data/spec/legion/extensions/agentic/integration/map/runners/cognitive_map_spec.rb +190 -0
  183. data/spec/legion/extensions/agentic/integration/mosaic/client_spec.rb +16 -0
  184. data/spec/legion/extensions/agentic/integration/mosaic/helpers/constants_spec.rb +37 -0
  185. data/spec/legion/extensions/agentic/integration/mosaic/helpers/mosaic_engine_spec.rb +108 -0
  186. data/spec/legion/extensions/agentic/integration/mosaic/helpers/mosaic_spec.rb +120 -0
  187. data/spec/legion/extensions/agentic/integration/mosaic/helpers/tessera_spec.rb +92 -0
  188. data/spec/legion/extensions/agentic/integration/mosaic/runners/cognitive_mosaic_spec.rb +88 -0
  189. data/spec/legion/extensions/agentic/integration/mycelium/client_spec.rb +25 -0
  190. data/spec/legion/extensions/agentic/integration/mycelium/cognitive_mycelium_spec.rb +7 -0
  191. data/spec/legion/extensions/agentic/integration/mycelium/helpers/constants_spec.rb +28 -0
  192. data/spec/legion/extensions/agentic/integration/mycelium/helpers/fruiting_body_spec.rb +33 -0
  193. data/spec/legion/extensions/agentic/integration/mycelium/helpers/hypha_spec.rb +69 -0
  194. data/spec/legion/extensions/agentic/integration/mycelium/helpers/mycelial_node_spec.rb +79 -0
  195. data/spec/legion/extensions/agentic/integration/mycelium/helpers/mycelium_engine_spec.rb +136 -0
  196. data/spec/legion/extensions/agentic/integration/mycelium/runners/cognitive_mycelium_spec.rb +61 -0
  197. data/spec/legion/extensions/agentic/integration/phenomenal_binding/client_spec.rb +52 -0
  198. data/spec/legion/extensions/agentic/integration/phenomenal_binding/helpers/binding_engine_spec.rb +290 -0
  199. data/spec/legion/extensions/agentic/integration/phenomenal_binding/helpers/binding_unit_spec.rb +195 -0
  200. data/spec/legion/extensions/agentic/integration/phenomenal_binding/helpers/constants_spec.rb +92 -0
  201. data/spec/legion/extensions/agentic/integration/phenomenal_binding/helpers/stream_spec.rb +128 -0
  202. data/spec/legion/extensions/agentic/integration/phenomenal_binding/runners/phenomenal_binding_spec.rb +174 -0
  203. data/spec/legion/extensions/agentic/integration/qualia/client_spec.rb +21 -0
  204. data/spec/legion/extensions/agentic/integration/qualia/helpers/quale_spec.rb +168 -0
  205. data/spec/legion/extensions/agentic/integration/qualia/helpers/qualia_engine_spec.rb +174 -0
  206. data/spec/legion/extensions/agentic/integration/qualia/qualia_spec.rb +7 -0
  207. data/spec/legion/extensions/agentic/integration/qualia/runners_spec.rb +70 -0
  208. data/spec/legion/extensions/agentic/integration/situation_model/client_spec.rb +51 -0
  209. data/spec/legion/extensions/agentic/integration/situation_model/helpers/constants_spec.rb +56 -0
  210. data/spec/legion/extensions/agentic/integration/situation_model/helpers/situation_engine_spec.rb +203 -0
  211. data/spec/legion/extensions/agentic/integration/situation_model/helpers/situation_event_spec.rb +94 -0
  212. data/spec/legion/extensions/agentic/integration/situation_model/helpers/situation_model_spec.rb +235 -0
  213. data/spec/legion/extensions/agentic/integration/situation_model/runners/situation_model_spec.rb +204 -0
  214. data/spec/legion/extensions/agentic/integration/synthesis/client_spec.rb +26 -0
  215. data/spec/legion/extensions/agentic/integration/synthesis/helpers/constants_spec.rb +87 -0
  216. data/spec/legion/extensions/agentic/integration/synthesis/helpers/synthesis_engine_spec.rb +233 -0
  217. data/spec/legion/extensions/agentic/integration/synthesis/helpers/synthesis_spec.rb +101 -0
  218. data/spec/legion/extensions/agentic/integration/synthesis/helpers/synthesis_stream_spec.rb +116 -0
  219. data/spec/legion/extensions/agentic/integration/synthesis/runners/cognitive_synthesis_spec.rb +181 -0
  220. data/spec/legion/extensions/agentic/integration/tapestry/client_spec.rb +55 -0
  221. data/spec/legion/extensions/agentic/integration/tapestry/helpers/constants_spec.rb +88 -0
  222. data/spec/legion/extensions/agentic/integration/tapestry/helpers/loom_engine_spec.rb +225 -0
  223. data/spec/legion/extensions/agentic/integration/tapestry/helpers/tapestry_spec.rb +229 -0
  224. data/spec/legion/extensions/agentic/integration/tapestry/helpers/thread_spec.rb +211 -0
  225. data/spec/legion/extensions/agentic/integration/tapestry/runners/cognitive_tapestry_spec.rb +171 -0
  226. data/spec/legion/extensions/agentic/integration/tessellation/client_spec.rb +40 -0
  227. data/spec/legion/extensions/agentic/integration/tessellation/helpers/mosaic_spec.rb +99 -0
  228. data/spec/legion/extensions/agentic/integration/tessellation/helpers/tessellation_engine_spec.rb +145 -0
  229. data/spec/legion/extensions/agentic/integration/tessellation/helpers/tile_spec.rb +138 -0
  230. data/spec/legion/extensions/agentic/integration/zeitgeist/helpers/cognitive_signal_spec.rb +62 -0
  231. data/spec/legion/extensions/agentic/integration/zeitgeist/helpers/constants_spec.rb +73 -0
  232. data/spec/legion/extensions/agentic/integration/zeitgeist/helpers/trend_window_spec.rb +100 -0
  233. data/spec/legion/extensions/agentic/integration/zeitgeist/helpers/zeitgeist_engine_spec.rb +163 -0
  234. data/spec/legion/extensions/agentic/integration/zeitgeist/runners/cognitive_zeitgeist_spec.rb +165 -0
  235. data/spec/spec_helper.rb +46 -0
  236. metadata +320 -0
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Integration::Gestalt::Helpers::PatternStore do
6
+ subject(:store) { described_class.new }
7
+
8
+ describe '#initialize' do
9
+ it 'starts empty' do
10
+ expect(store.patterns).to eq([])
11
+ expect(store.size).to eq(0)
12
+ end
13
+ end
14
+
15
+ describe '#add' do
16
+ it 'adds a pattern' do
17
+ pattern = store.add(name: :test, elements: %i[a b c])
18
+ expect(pattern.name).to eq(:test)
19
+ expect(store.size).to eq(1)
20
+ end
21
+
22
+ it 'accepts domain' do
23
+ pattern = store.add(name: :test, elements: %i[a b c], domain: :math)
24
+ expect(pattern.domain).to eq(:math)
25
+ end
26
+ end
27
+
28
+ describe '#find' do
29
+ it 'finds by id' do
30
+ pattern = store.add(name: :test, elements: %i[a b c])
31
+ found = store.find(pattern.id)
32
+ expect(found).to eq(pattern)
33
+ end
34
+
35
+ it 'returns nil for unknown id' do
36
+ expect(store.find('unknown')).to be_nil
37
+ end
38
+ end
39
+
40
+ describe '#find_by_name' do
41
+ it 'returns patterns matching name' do
42
+ store.add(name: :test, elements: %i[a b c])
43
+ store.add(name: :test, elements: %i[d e f])
44
+ store.add(name: :other, elements: %i[g h i])
45
+ expect(store.find_by_name(:test).size).to eq(2)
46
+ end
47
+ end
48
+
49
+ describe '#in_domain' do
50
+ it 'filters by domain' do
51
+ store.add(name: :a, elements: %i[a b], domain: :math)
52
+ store.add(name: :b, elements: %i[c d], domain: :language)
53
+ expect(store.in_domain(:math).size).to eq(1)
54
+ end
55
+ end
56
+
57
+ describe '#complete' do
58
+ before do
59
+ store.add(name: :alphabet, elements: %i[a b c d e])
60
+ store.add(name: :numbers, elements: %i[one two three four five])
61
+ end
62
+
63
+ it 'returns completions ranked by confidence' do
64
+ completions = store.complete(%i[a b c])
65
+ expect(completions.size).to eq(1)
66
+ expect(completions.first[:pattern_name]).to eq(:alphabet)
67
+ end
68
+
69
+ it 'returns empty for no matches' do
70
+ expect(store.complete(%i[x y z])).to eq([])
71
+ end
72
+
73
+ it 'filters by domain' do
74
+ store.add(name: :math_abc, elements: %i[a b c d e], domain: :math)
75
+ completions = store.complete(%i[a b c], domain: :math)
76
+ expect(completions.size).to eq(1)
77
+ expect(completions.first[:pattern_name]).to eq(:math_abc)
78
+ end
79
+ end
80
+
81
+ describe '#group_by_proximity' do
82
+ it 'groups nearby items together' do
83
+ groups = store.group_by_proximity([1, 2, 3, 100, 101, 102])
84
+ expect(groups.size).to be >= 2
85
+ end
86
+
87
+ it 'returns single group for single item' do
88
+ groups = store.group_by_proximity([5])
89
+ expect(groups).to eq([[5]])
90
+ end
91
+
92
+ it 'returns empty for empty input' do
93
+ groups = store.group_by_proximity([])
94
+ expect(groups).to eq([[]])
95
+ end
96
+ end
97
+
98
+ describe '#group_by_similarity' do
99
+ it 'groups items by shared key' do
100
+ items = [
101
+ { type: :animal, name: :dog },
102
+ { type: :animal, name: :cat },
103
+ { type: :vehicle, name: :car }
104
+ ]
105
+ groups = store.group_by_similarity(items, key: :type)
106
+ expect(groups.size).to eq(2)
107
+ end
108
+ end
109
+
110
+ describe '#reinforce / #penalize' do
111
+ it 'reinforces a pattern' do
112
+ pattern = store.add(name: :test, elements: %i[a b c])
113
+ initial = pattern.strength
114
+ store.reinforce(pattern.id)
115
+ expect(pattern.strength).to be > initial
116
+ end
117
+
118
+ it 'penalizes a pattern' do
119
+ pattern = store.add(name: :test, elements: %i[a b c])
120
+ initial = pattern.strength
121
+ store.penalize(pattern.id)
122
+ expect(pattern.strength).to be < initial
123
+ end
124
+
125
+ it 'returns nil for unknown id' do
126
+ expect(store.reinforce('unknown')).to be_nil
127
+ expect(store.penalize('unknown')).to be_nil
128
+ end
129
+ end
130
+
131
+ describe '#decay_all' do
132
+ it 'decays all patterns' do
133
+ pattern = store.add(name: :test, elements: %i[a b c])
134
+ initial = pattern.strength
135
+ store.decay_all
136
+ expect(pattern.strength).to be < initial
137
+ end
138
+ end
139
+
140
+ describe '#remove' do
141
+ it 'removes a pattern' do
142
+ pattern = store.add(name: :test, elements: %i[a b c])
143
+ store.remove(pattern.id)
144
+ expect(store.size).to eq(0)
145
+ end
146
+ end
147
+
148
+ describe '#to_h' do
149
+ it 'returns stats' do
150
+ store.add(name: :test, elements: %i[a b c])
151
+ h = store.to_h
152
+ expect(h).to include(:pattern_count, :by_domain, :total_matches)
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Integration::Gestalt::Runners::Gestalt do
6
+ let(:runner) do
7
+ obj = Object.new
8
+ obj.extend(described_class)
9
+ obj
10
+ end
11
+
12
+ before do
13
+ runner.learn_pattern(name: :alphabet, elements: %i[a b c d e], domain: :language)
14
+ runner.learn_pattern(name: :counting, elements: %i[one two three four five], domain: :math)
15
+ end
16
+
17
+ describe '#learn_pattern' do
18
+ it 'stores a pattern' do
19
+ result = runner.learn_pattern(name: :test, elements: %i[x y z])
20
+ expect(result[:success]).to be true
21
+ expect(result[:pattern][:name]).to eq(:test)
22
+ end
23
+ end
24
+
25
+ describe '#complete_pattern' do
26
+ it 'completes a partial pattern' do
27
+ result = runner.complete_pattern(fragment: %i[a b c])
28
+ expect(result[:success]).to be true
29
+ expect(result[:count]).to eq(1)
30
+ expect(result[:best][:pattern_name]).to eq(:alphabet)
31
+ expect(result[:best][:missing]).to eq(%i[d e])
32
+ end
33
+
34
+ it 'returns empty for no match' do
35
+ result = runner.complete_pattern(fragment: %i[x y z])
36
+ expect(result[:count]).to eq(0)
37
+ end
38
+
39
+ it 'filters by domain' do
40
+ result = runner.complete_pattern(fragment: %i[a b c], domain: :math)
41
+ expect(result[:count]).to eq(0)
42
+ end
43
+ end
44
+
45
+ describe '#confirm_completion' do
46
+ it 'reinforces a correct completion' do
47
+ pattern_id = runner.store.patterns.first.id
48
+ result = runner.confirm_completion(pattern_id: pattern_id, correct: true)
49
+ expect(result[:success]).to be true
50
+ expect(result[:pattern][:strength]).to be > 1.0
51
+ end
52
+
53
+ it 'penalizes an incorrect completion' do
54
+ pattern_id = runner.store.patterns.first.id
55
+ result = runner.confirm_completion(pattern_id: pattern_id, correct: false)
56
+ expect(result[:success]).to be true
57
+ expect(result[:pattern][:strength]).to be < 1.0
58
+ end
59
+
60
+ it 'returns not_found for unknown pattern' do
61
+ result = runner.confirm_completion(pattern_id: 'unknown')
62
+ expect(result[:success]).to be false
63
+ end
64
+ end
65
+
66
+ describe '#group_items' do
67
+ it 'groups by proximity' do
68
+ result = runner.group_items(items: [1, 2, 3, 100, 101], principle: :proximity)
69
+ expect(result[:success]).to be true
70
+ expect(result[:group_count]).to be >= 2
71
+ end
72
+
73
+ it 'groups by similarity' do
74
+ items = [
75
+ { type: :a, v: 1 },
76
+ { type: :a, v: 2 },
77
+ { type: :b, v: 3 }
78
+ ]
79
+ result = runner.group_items(items: items, principle: :similarity)
80
+ expect(result[:group_count]).to eq(2)
81
+ end
82
+ end
83
+
84
+ describe '#update_gestalt' do
85
+ it 'decays patterns' do
86
+ initial = runner.store.patterns.first.strength
87
+ runner.update_gestalt
88
+ expect(runner.store.patterns.first.strength).to be < initial
89
+ end
90
+ end
91
+
92
+ describe '#patterns_in_domain' do
93
+ it 'returns patterns for domain' do
94
+ result = runner.patterns_in_domain(domain: :language)
95
+ expect(result[:count]).to eq(1)
96
+ end
97
+ end
98
+
99
+ describe '#remove_pattern' do
100
+ it 'removes a pattern' do
101
+ pattern_id = runner.store.patterns.first.id
102
+ runner.remove_pattern(id: pattern_id)
103
+ expect(runner.store.size).to eq(1)
104
+ end
105
+ end
106
+
107
+ describe '#gestalt_stats' do
108
+ it 'returns stats' do
109
+ result = runner.gestalt_stats
110
+ expect(result[:success]).to be true
111
+ expect(result[:stats]).to include(:pattern_count, :by_domain, :total_matches)
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Integration::GlobalWorkspace::Client do
4
+ subject(:client) { described_class.new }
5
+
6
+ it 'includes Runners::GlobalWorkspace' do
7
+ expect(described_class.ancestors).to include(Legion::Extensions::Agentic::Integration::GlobalWorkspace::Runners::GlobalWorkspace)
8
+ end
9
+
10
+ it 'supports full conscious access lifecycle' do
11
+ # Register subscribers (cognitive subsystems)
12
+ client.register_subscriber(id: :memory, name: 'lex-memory', domains: [:all])
13
+ client.register_subscriber(id: :emotion, name: 'lex-emotion', domains: [:safety])
14
+ client.register_subscriber(id: :planning, name: 'lex-planning', domains: [:cognition])
15
+
16
+ # Multiple subsystems submit content for conscious access
17
+ client.submit_for_broadcast(content: 'threat_nearby', source: :emotion, domain: :safety, salience: 0.85)
18
+ client.submit_for_broadcast(content: 'plan_step_3', source: :planning, domain: :cognition, salience: 0.5)
19
+ client.submit_for_broadcast(content: 'memory_trace', source: :memory, domain: :recall, salience: 0.3)
20
+
21
+ # Competition: emotion wins (highest salience, clear margin)
22
+ competition = client.run_competition
23
+ expect(competition[:success]).to be true
24
+ expect(competition[:broadcast][:content]).to eq('threat_nearby')
25
+
26
+ # Verify consciousness
27
+ conscious = client.query_consciousness(content: 'threat_nearby')
28
+ expect(conscious[:conscious]).to be true
29
+
30
+ # Subscribers acknowledge receipt
31
+ client.acknowledge_broadcast(subscriber_id: :memory)
32
+ client.acknowledge_broadcast(subscriber_id: :emotion)
33
+
34
+ # Check current state
35
+ current = client.current_broadcast
36
+ expect(current[:broadcast][:received_by]).to include(:memory, :emotion)
37
+
38
+ # Tick maintenance
39
+ client.update_global_workspace
40
+
41
+ # History preserved
42
+ history = client.broadcast_history(limit: 10)
43
+ expect(history[:total]).to eq(1)
44
+
45
+ # Stats
46
+ stats = client.workspace_stats
47
+ expect(stats[:stats][:subscribers]).to eq(3)
48
+ end
49
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Integration::GlobalWorkspace::Helpers::Broadcast do
4
+ subject(:broadcast) do
5
+ described_class.new(content: 'threat_detected', source: :emotion, domain: :safety, salience: 0.9,
6
+ coalition: %i[fear attention])
7
+ end
8
+
9
+ let(:constants) { Legion::Extensions::Agentic::Integration::GlobalWorkspace::Helpers::Constants }
10
+
11
+ describe '#initialize' do
12
+ it 'sets attributes' do
13
+ expect(broadcast.content).to eq('threat_detected')
14
+ expect(broadcast.source).to eq(:emotion)
15
+ expect(broadcast.domain).to eq(:safety)
16
+ expect(broadcast.salience).to eq(0.9)
17
+ expect(broadcast.coalition).to eq(%i[fear attention])
18
+ end
19
+
20
+ it 'clamps salience' do
21
+ high = described_class.new(content: :x, source: :s, domain: :d, salience: 1.5)
22
+ expect(high.salience).to eq(1.0)
23
+ end
24
+
25
+ it 'records broadcast_at' do
26
+ expect(broadcast.broadcast_at).to be_a(Time)
27
+ end
28
+
29
+ it 'starts with empty received_by' do
30
+ expect(broadcast.received_by).to be_empty
31
+ end
32
+
33
+ it 'limits coalition size' do
34
+ big = described_class.new(content: :x, source: :s, domain: :d, salience: 0.5,
35
+ coalition: (1..20).to_a)
36
+ expect(big.coalition.size).to eq(constants::MAX_COALITION_SIZE)
37
+ end
38
+ end
39
+
40
+ describe '#acknowledge' do
41
+ it 'adds subscriber to received_by' do
42
+ broadcast.acknowledge(:memory)
43
+ expect(broadcast.received_by).to include(:memory)
44
+ end
45
+
46
+ it 'does not duplicate' do
47
+ broadcast.acknowledge(:memory)
48
+ broadcast.acknowledge(:memory)
49
+ expect(broadcast.received_by.size).to eq(1)
50
+ end
51
+ end
52
+
53
+ describe '#expired?' do
54
+ it 'returns false when fresh' do
55
+ expect(broadcast.expired?).to be false
56
+ end
57
+ end
58
+
59
+ describe '#age' do
60
+ it 'returns elapsed time' do
61
+ expect(broadcast.age).to be >= 0.0
62
+ end
63
+ end
64
+
65
+ describe '#label' do
66
+ it 'returns :dominant for high salience' do
67
+ expect(broadcast.label).to eq(:dominant)
68
+ end
69
+
70
+ it 'returns :subliminal for low salience' do
71
+ low = described_class.new(content: :x, source: :s, domain: :d, salience: 0.1)
72
+ expect(low.label).to eq(:subliminal)
73
+ end
74
+ end
75
+
76
+ describe '#to_h' do
77
+ it 'returns hash with all fields' do
78
+ h = broadcast.to_h
79
+ expect(h).to include(:content, :source, :domain, :salience, :label, :coalition, :broadcast_at, :age,
80
+ :received_by, :expired)
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Integration::GlobalWorkspace::Helpers::Competitor do
4
+ subject(:competitor) do
5
+ described_class.new(content: 'idea', source: :cortex, domain: :cognition, salience: 0.6, coalition: [:attention])
6
+ end
7
+
8
+ let(:constants) { Legion::Extensions::Agentic::Integration::GlobalWorkspace::Helpers::Constants }
9
+
10
+ describe '#initialize' do
11
+ it 'sets attributes' do
12
+ expect(competitor.content).to eq('idea')
13
+ expect(competitor.source).to eq(:cortex)
14
+ expect(competitor.salience).to eq(0.6)
15
+ expect(competitor.urgency).to eq(0.0)
16
+ end
17
+
18
+ it 'clamps salience' do
19
+ high = described_class.new(content: :x, source: :s, domain: :d, salience: 2.0)
20
+ expect(high.salience).to eq(1.0)
21
+ end
22
+ end
23
+
24
+ describe '#effective_salience' do
25
+ it 'equals salience when no urgency' do
26
+ expect(competitor.effective_salience).to eq(0.6)
27
+ end
28
+
29
+ it 'includes urgency' do
30
+ competitor.urgency = 0.2
31
+ expect(competitor.effective_salience).to eq(0.8)
32
+ end
33
+
34
+ it 'clamps to 1.0' do
35
+ competitor.salience = 0.9
36
+ competitor.urgency = 0.3
37
+ expect(competitor.effective_salience).to eq(1.0)
38
+ end
39
+ end
40
+
41
+ describe '#decay' do
42
+ it 'reduces salience' do
43
+ before = competitor.salience
44
+ competitor.decay
45
+ expect(competitor.salience).to eq(before - constants::SALIENCE_DECAY)
46
+ end
47
+
48
+ it 'does not go below 0' do
49
+ competitor.salience = 0.01
50
+ competitor.decay
51
+ expect(competitor.salience).to eq(0.0)
52
+ end
53
+ end
54
+
55
+ describe '#boost_urgency' do
56
+ it 'increases urgency' do
57
+ competitor.boost_urgency
58
+ expect(competitor.urgency).to eq(constants::URGENCY_BOOST)
59
+ end
60
+
61
+ it 'caps at MAX_URGENCY' do
62
+ 50.times { competitor.boost_urgency }
63
+ expect(competitor.urgency).to eq(constants::MAX_URGENCY)
64
+ end
65
+ end
66
+
67
+ describe '#below_threshold?' do
68
+ it 'returns false when above threshold' do
69
+ expect(competitor.below_threshold?).to be false
70
+ end
71
+
72
+ it 'returns true when below threshold' do
73
+ competitor.salience = 0.1
74
+ expect(competitor.below_threshold?).to be true
75
+ end
76
+ end
77
+
78
+ describe '#to_h' do
79
+ it 'returns hash with all fields' do
80
+ h = competitor.to_h
81
+ expect(h).to include(:content, :source, :domain, :salience, :urgency, :effective_salience, :coalition)
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,174 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Integration::GlobalWorkspace::Helpers::Workspace do
4
+ subject(:ws) { described_class.new }
5
+
6
+ let(:constants) { Legion::Extensions::Agentic::Integration::GlobalWorkspace::Helpers::Constants }
7
+
8
+ describe '#register_subscriber / #unregister_subscriber' do
9
+ it 'registers a subscriber' do
10
+ expect(ws.register_subscriber(id: :memory, name: 'lex-memory', domains: [:all])).to be true
11
+ expect(ws.subscriber_count).to eq(1)
12
+ end
13
+
14
+ it 'unregisters a subscriber' do
15
+ ws.register_subscriber(id: :memory, name: 'lex-memory')
16
+ expect(ws.unregister_subscriber(id: :memory)).to be true
17
+ expect(ws.subscriber_count).to eq(0)
18
+ end
19
+
20
+ it 'returns false for unknown unregister' do
21
+ expect(ws.unregister_subscriber(id: :unknown)).to be false
22
+ end
23
+
24
+ it 'limits subscribers' do
25
+ constants::MAX_SUBSCRIBERS.times { |i| ws.register_subscriber(id: "sub_#{i}", name: "s#{i}") }
26
+ expect(ws.register_subscriber(id: :overflow, name: 'over')).to be false
27
+ end
28
+ end
29
+
30
+ describe '#submit' do
31
+ it 'adds a competitor' do
32
+ comp = ws.submit(content: 'idea', source: :cortex, domain: :cognition, salience: 0.6)
33
+ expect(comp).to be_a(Legion::Extensions::Agentic::Integration::GlobalWorkspace::Helpers::Competitor)
34
+ expect(ws.competitor_count).to eq(1)
35
+ end
36
+
37
+ it 'rejects below-threshold submissions' do
38
+ result = ws.submit(content: 'weak', source: :s, domain: :d, salience: 0.1)
39
+ expect(result).to be_nil
40
+ expect(ws.competitor_count).to eq(0)
41
+ end
42
+
43
+ it 'limits competitor queue' do
44
+ constants::MAX_COMPETITORS.times { |i| ws.submit(content: "c_#{i}", source: :s, domain: :d, salience: 0.5) }
45
+ ws.submit(content: :overflow, source: :s, domain: :d, salience: 0.5)
46
+ expect(ws.competitor_count).to eq(constants::MAX_COMPETITORS)
47
+ end
48
+ end
49
+
50
+ describe '#compete' do
51
+ it 'returns nil with no competitors' do
52
+ expect(ws.compete).to be_nil
53
+ end
54
+
55
+ it 'broadcasts the dominant competitor' do
56
+ ws.submit(content: 'strong', source: :emotion, domain: :safety, salience: 0.9)
57
+ ws.submit(content: 'weak', source: :cortex, domain: :cognition, salience: 0.3)
58
+ broadcast = ws.compete
59
+ expect(broadcast).to be_a(Legion::Extensions::Agentic::Integration::GlobalWorkspace::Helpers::Broadcast)
60
+ expect(broadcast.content).to eq('strong')
61
+ end
62
+
63
+ it 'returns nil when no clear winner (within margin)' do
64
+ ws.submit(content: 'a', source: :s, domain: :d, salience: 0.5)
65
+ ws.submit(content: 'b', source: :s, domain: :d, salience: 0.5)
66
+ result = ws.compete
67
+ expect(result).to be_nil
68
+ end
69
+
70
+ it 'boosts urgency of losers' do
71
+ ws.submit(content: 'a', source: :s, domain: :d, salience: 0.5)
72
+ ws.submit(content: 'b', source: :s, domain: :d, salience: 0.5)
73
+ ws.compete
74
+ expect(ws.competitors.first.urgency).to be > 0
75
+ end
76
+
77
+ it 'removes winner from competitors' do
78
+ ws.submit(content: 'winner', source: :s, domain: :d, salience: 0.9)
79
+ ws.compete
80
+ expect(ws.competitor_count).to eq(0)
81
+ end
82
+
83
+ it 'adds to broadcast history' do
84
+ ws.submit(content: 'winner', source: :s, domain: :d, salience: 0.9)
85
+ ws.compete
86
+ expect(ws.broadcast_history.size).to eq(1)
87
+ end
88
+ end
89
+
90
+ describe '#conscious?' do
91
+ it 'returns true for current broadcast content' do
92
+ ws.submit(content: 'idea', source: :s, domain: :d, salience: 0.9)
93
+ ws.compete
94
+ expect(ws.conscious?('idea')).to be true
95
+ end
96
+
97
+ it 'returns false when no broadcast' do
98
+ expect(ws.conscious?('anything')).to be false
99
+ end
100
+
101
+ it 'returns false for non-matching content' do
102
+ ws.submit(content: 'idea', source: :s, domain: :d, salience: 0.9)
103
+ ws.compete
104
+ expect(ws.conscious?('other')).to be false
105
+ end
106
+ end
107
+
108
+ describe '#current_content' do
109
+ it 'returns broadcast hash when active' do
110
+ ws.submit(content: 'idea', source: :cortex, domain: :cognition, salience: 0.9)
111
+ ws.compete
112
+ content = ws.current_content
113
+ expect(content[:content]).to eq('idea')
114
+ expect(content[:source]).to eq(:cortex)
115
+ end
116
+
117
+ it 'returns nil when no active broadcast' do
118
+ expect(ws.current_content).to be_nil
119
+ end
120
+ end
121
+
122
+ describe '#acknowledge' do
123
+ it 'acknowledges current broadcast' do
124
+ ws.submit(content: 'idea', source: :s, domain: :d, salience: 0.9)
125
+ ws.compete
126
+ expect(ws.acknowledge(subscriber_id: :memory)).to be true
127
+ end
128
+
129
+ it 'returns false when no broadcast' do
130
+ expect(ws.acknowledge(subscriber_id: :memory)).to be false
131
+ end
132
+ end
133
+
134
+ describe '#state' do
135
+ it 'returns :idle when empty' do
136
+ expect(ws.state).to eq(:idle)
137
+ end
138
+
139
+ it 'returns :broadcasting when broadcast active' do
140
+ ws.submit(content: 'idea', source: :s, domain: :d, salience: 0.9)
141
+ ws.compete
142
+ expect(ws.state).to eq(:broadcasting)
143
+ end
144
+
145
+ it 'returns :contention when multiple competitors' do
146
+ ws.submit(content: 'a', source: :s, domain: :d, salience: 0.5)
147
+ ws.submit(content: 'b', source: :s, domain: :d, salience: 0.5)
148
+ expect(ws.state).to eq(:contention)
149
+ end
150
+ end
151
+
152
+ describe '#tick' do
153
+ it 'decays competitors' do
154
+ ws.submit(content: 'idea', source: :s, domain: :d, salience: 0.5)
155
+ before = ws.competitors.first.salience
156
+ ws.tick
157
+ expect(ws.competitors.first&.salience || 0).to be <= before
158
+ end
159
+
160
+ it 'prunes weak competitors after decay' do
161
+ ws.submit(content: 'weak', source: :s, domain: :d, salience: constants::COMPETITION_THRESHOLD + 0.01)
162
+ ws.tick
163
+ expect(ws.competitor_count).to eq(0)
164
+ end
165
+ end
166
+
167
+ describe '#to_h' do
168
+ it 'returns stats hash' do
169
+ h = ws.to_h
170
+ expect(h).to include(:state, :state_label, :subscribers, :competitors, :broadcast_history, :utilization,
171
+ :competitions)
172
+ end
173
+ end
174
+ end