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,290 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Integration::PhenomenalBinding::Helpers::BindingEngine do
4
+ subject(:engine) { described_class.new }
5
+
6
+ let(:stream_mod) { Legion::Extensions::Agentic::Integration::PhenomenalBinding::Helpers::Stream }
7
+
8
+ def register_n_streams(count, salience: 0.7)
9
+ count.times.map do |i|
10
+ engine.register_stream(stream_type: :perception, content: "content #{i}", salience: salience)
11
+ end
12
+ end
13
+
14
+ describe '#register_stream' do
15
+ it 'returns a Stream' do
16
+ stream = engine.register_stream(stream_type: :thought, content: 'hello')
17
+ expect(stream).to be_a(stream_mod)
18
+ end
19
+
20
+ it 'uses the given stream_type' do
21
+ stream = engine.register_stream(stream_type: :emotion, content: 'joy')
22
+ expect(stream.stream_type).to eq(:emotion)
23
+ end
24
+
25
+ it 'uses the given salience' do
26
+ stream = engine.register_stream(stream_type: :memory, content: 'old', salience: 0.9)
27
+ expect(stream.salience).to eq(0.9)
28
+ end
29
+
30
+ it 'stores stream internally' do
31
+ stream = engine.register_stream(stream_type: :prediction, content: 'rain')
32
+ stats = engine.to_h
33
+ expect(stats[:stream_count]).to eq(1)
34
+ expect(engine.unbound_streams.map(&:id)).to include(stream.id)
35
+ end
36
+
37
+ it 'prunes oldest stream when MAX_STREAMS exceeded' do
38
+ 101.times { |i| engine.register_stream(stream_type: :perception, content: "s#{i}") }
39
+ expect(engine.to_h[:stream_count]).to eq(100)
40
+ end
41
+ end
42
+
43
+ describe '#create_binding' do
44
+ it 'returns a BindingUnit' do
45
+ s = engine.register_stream(stream_type: :perception, content: 'x')
46
+ binding = engine.create_binding(stream_ids: [s.id], binding_type: :perceptual)
47
+ expect(binding).to be_a(Legion::Extensions::Agentic::Integration::PhenomenalBinding::Helpers::BindingUnit)
48
+ end
49
+
50
+ it 'computes initial coherence from stream saliences' do
51
+ s1 = engine.register_stream(stream_type: :perception, content: 'a', salience: 0.8)
52
+ s2 = engine.register_stream(stream_type: :thought, content: 'b', salience: 0.6)
53
+ binding = engine.create_binding(stream_ids: [s1.id, s2.id], binding_type: :conceptual)
54
+ expect(binding.coherence).to be_within(0.001).of(0.7)
55
+ end
56
+
57
+ it 'ignores stream_ids that are not registered' do
58
+ s = engine.register_stream(stream_type: :perception, content: 'real')
59
+ binding = engine.create_binding(stream_ids: [s.id, 'ghost-id'], binding_type: :temporal)
60
+ expect(binding.stream_ids).to eq([s.id])
61
+ end
62
+
63
+ it 'stores binding internally' do
64
+ s = engine.register_stream(stream_type: :memory, content: 'y')
65
+ engine.create_binding(stream_ids: [s.id], binding_type: :narrative)
66
+ expect(engine.to_h[:binding_count]).to eq(1)
67
+ end
68
+
69
+ it 'sets attention_weight' do
70
+ s = engine.register_stream(stream_type: :intention, content: 'z')
71
+ binding = engine.create_binding(stream_ids: [s.id], binding_type: :emotional, attention_weight: 0.9)
72
+ expect(binding.attention_weight).to eq(0.9)
73
+ end
74
+
75
+ it 'prunes oldest binding when MAX_BINDINGS exceeded' do
76
+ s = engine.register_stream(stream_type: :perception, content: 'base')
77
+ 201.times { engine.create_binding(stream_ids: [s.id], binding_type: :perceptual) }
78
+ expect(engine.to_h[:binding_count]).to eq(200)
79
+ end
80
+ end
81
+
82
+ describe '#reinforce_binding' do
83
+ it 'increases coherence' do
84
+ s = engine.register_stream(stream_type: :thought, content: 'q')
85
+ binding = engine.create_binding(stream_ids: [s.id], binding_type: :conceptual, attention_weight: 0.5)
86
+ initial_coherence = binding.coherence
87
+ engine.reinforce_binding(binding_id: binding.id)
88
+ expect(binding.coherence).to be > initial_coherence
89
+ end
90
+
91
+ it 'returns reinforced status with coherence' do
92
+ s = engine.register_stream(stream_type: :perception, content: 'r')
93
+ binding = engine.create_binding(stream_ids: [s.id], binding_type: :perceptual)
94
+ result = engine.reinforce_binding(binding_id: binding.id)
95
+ expect(result[:status]).to eq(:reinforced)
96
+ expect(result[:coherence]).to be_a(Float)
97
+ end
98
+
99
+ it 'returns not_found for unknown binding_id' do
100
+ result = engine.reinforce_binding(binding_id: 'no-such-id')
101
+ expect(result[:status]).to eq(:not_found)
102
+ end
103
+ end
104
+
105
+ describe '#dissolve_binding' do
106
+ it 'removes the binding' do
107
+ s = engine.register_stream(stream_type: :emotion, content: 'fear')
108
+ binding = engine.create_binding(stream_ids: [s.id], binding_type: :emotional)
109
+ engine.dissolve_binding(binding_id: binding.id)
110
+ expect(engine.to_h[:binding_count]).to eq(0)
111
+ end
112
+
113
+ it 'returns dissolved status' do
114
+ s = engine.register_stream(stream_type: :memory, content: 'past')
115
+ binding = engine.create_binding(stream_ids: [s.id], binding_type: :narrative)
116
+ result = engine.dissolve_binding(binding_id: binding.id)
117
+ expect(result[:status]).to eq(:dissolved)
118
+ end
119
+
120
+ it 'returns not_found for unknown binding_id' do
121
+ result = engine.dissolve_binding(binding_id: 'ghost')
122
+ expect(result[:status]).to eq(:not_found)
123
+ end
124
+ end
125
+
126
+ describe '#unified_experience' do
127
+ it 'returns nil when no bindings exist' do
128
+ expect(engine.unified_experience).to be_nil
129
+ end
130
+
131
+ it 'returns nil when no coherent bindings exist' do
132
+ s = engine.register_stream(stream_type: :perception, content: 'dim', salience: 0.1)
133
+ engine.create_binding(stream_ids: [s.id], binding_type: :perceptual)
134
+ expect(engine.unified_experience).to be_nil
135
+ end
136
+
137
+ it 'returns the strongest coherent binding' do
138
+ s1 = engine.register_stream(stream_type: :perception, content: 'a', salience: 0.8)
139
+ s2 = engine.register_stream(stream_type: :thought, content: 'b', salience: 0.9)
140
+ engine.create_binding(stream_ids: [s1.id], binding_type: :perceptual, attention_weight: 0.5)
141
+ b2 = engine.create_binding(stream_ids: [s2.id], binding_type: :conceptual, attention_weight: 0.9)
142
+ experience = engine.unified_experience
143
+ # b2 has higher coherence * attention_weight
144
+ expect(experience.id).to eq(b2.id)
145
+ end
146
+ end
147
+
148
+ describe '#fragmentation_index' do
149
+ it 'returns 0.0 when no streams registered' do
150
+ expect(engine.fragmentation_index).to eq(0.0)
151
+ end
152
+
153
+ it 'returns 1.0 when all streams are unbound' do
154
+ register_n_streams(3)
155
+ expect(engine.fragmentation_index).to eq(1.0)
156
+ end
157
+
158
+ it 'returns 0.0 when all streams are bound' do
159
+ streams = register_n_streams(2)
160
+ engine.create_binding(stream_ids: streams.map(&:id), binding_type: :perceptual)
161
+ expect(engine.fragmentation_index).to eq(0.0)
162
+ end
163
+
164
+ it 'returns partial fraction for partially bound streams' do
165
+ streams = register_n_streams(4)
166
+ engine.create_binding(stream_ids: [streams[0].id, streams[1].id], binding_type: :temporal)
167
+ expect(engine.fragmentation_index).to be_within(0.001).of(0.5)
168
+ end
169
+ end
170
+
171
+ describe '#binding_by_type' do
172
+ it 'returns bindings of the given type' do
173
+ s = engine.register_stream(stream_type: :prediction, content: 'p')
174
+ engine.create_binding(stream_ids: [s.id], binding_type: :temporal)
175
+ engine.create_binding(stream_ids: [s.id], binding_type: :perceptual)
176
+ result = engine.binding_by_type(binding_type: :temporal)
177
+ expect(result.size).to eq(1)
178
+ expect(result.first.binding_type).to eq(:temporal)
179
+ end
180
+
181
+ it 'returns empty array when no bindings of that type' do
182
+ expect(engine.binding_by_type(binding_type: :narrative)).to be_empty
183
+ end
184
+ end
185
+
186
+ describe '#streams_for_binding' do
187
+ it 'returns the streams belonging to a binding' do
188
+ s1 = engine.register_stream(stream_type: :emotion, content: 'warm')
189
+ s2 = engine.register_stream(stream_type: :memory, content: 'sunny')
190
+ binding = engine.create_binding(stream_ids: [s1.id, s2.id], binding_type: :emotional)
191
+ streams = engine.streams_for_binding(binding_id: binding.id)
192
+ expect(streams.map(&:id)).to contain_exactly(s1.id, s2.id)
193
+ end
194
+
195
+ it 'returns empty array for unknown binding_id' do
196
+ expect(engine.streams_for_binding(binding_id: 'ghost')).to be_empty
197
+ end
198
+ end
199
+
200
+ describe '#unbound_streams' do
201
+ it 'returns all streams when none are bound' do
202
+ register_n_streams(3)
203
+ expect(engine.unbound_streams.size).to eq(3)
204
+ end
205
+
206
+ it 'excludes streams that are in a binding' do
207
+ s1 = engine.register_stream(stream_type: :perception, content: 'bound')
208
+ engine.register_stream(stream_type: :thought, content: 'free')
209
+ engine.create_binding(stream_ids: [s1.id], binding_type: :perceptual)
210
+ unbound = engine.unbound_streams
211
+ expect(unbound.map(&:id)).not_to include(s1.id)
212
+ expect(unbound.size).to eq(1)
213
+ end
214
+ end
215
+
216
+ describe '#decay_all' do
217
+ it 'decreases coherence of all bindings' do
218
+ s = engine.register_stream(stream_type: :memory, content: 'm', salience: 0.9)
219
+ binding = engine.create_binding(stream_ids: [s.id], binding_type: :narrative)
220
+ initial = binding.coherence
221
+ engine.decay_all
222
+ expect(binding.coherence).to be < initial
223
+ end
224
+
225
+ it 'does not raise when no bindings exist' do
226
+ expect { engine.decay_all }.not_to raise_error
227
+ end
228
+ end
229
+
230
+ describe '#prune_incoherent' do
231
+ it 'removes bindings below COHERENCE_THRESHOLD' do
232
+ s = engine.register_stream(stream_type: :perception, content: 'faint', salience: 0.1)
233
+ engine.create_binding(stream_ids: [s.id], binding_type: :perceptual)
234
+ engine.prune_incoherent
235
+ expect(engine.to_h[:binding_count]).to eq(0)
236
+ end
237
+
238
+ it 'retains coherent bindings' do
239
+ s = engine.register_stream(stream_type: :intention, content: 'strong', salience: 0.9)
240
+ binding = engine.create_binding(stream_ids: [s.id], binding_type: :conceptual)
241
+ engine.prune_incoherent
242
+ expect(engine.to_h[:binding_count]).to eq(1)
243
+ expect(engine.streams_for_binding(binding_id: binding.id)).not_to be_empty
244
+ end
245
+ end
246
+
247
+ describe '#consciousness_report' do
248
+ it 'returns a hash with expected keys' do
249
+ report = engine.consciousness_report
250
+ expect(report.keys).to include(
251
+ :unified_experience, :fragmentation_index,
252
+ :coherence_distribution, :stream_count, :binding_count, :unbound_count
253
+ )
254
+ end
255
+
256
+ it 'includes nil unified_experience when no coherent bindings' do
257
+ report = engine.consciousness_report
258
+ expect(report[:unified_experience]).to be_nil
259
+ end
260
+
261
+ it 'includes a coherence_distribution hash with all labels' do
262
+ report = engine.consciousness_report
263
+ labels = report[:coherence_distribution].keys
264
+ expect(labels).to include(:unified, :coherent, :fragmented, :dissociated, :unbound)
265
+ end
266
+
267
+ it 'reports correct stream_count' do
268
+ register_n_streams(4)
269
+ report = engine.consciousness_report
270
+ expect(report[:stream_count]).to eq(4)
271
+ end
272
+
273
+ it 'includes unified_experience hash when a coherent binding exists' do
274
+ s = engine.register_stream(stream_type: :perception, content: 'vivid', salience: 0.9)
275
+ engine.create_binding(stream_ids: [s.id], binding_type: :perceptual)
276
+ report = engine.consciousness_report
277
+ expect(report[:unified_experience]).to be_a(Hash)
278
+ end
279
+ end
280
+
281
+ describe '#to_h' do
282
+ it 'returns engine stats' do
283
+ register_n_streams(2)
284
+ stats = engine.to_h
285
+ expect(stats[:stream_count]).to eq(2)
286
+ expect(stats[:binding_count]).to eq(0)
287
+ expect(stats[:unbound_count]).to eq(2)
288
+ end
289
+ end
290
+ end
@@ -0,0 +1,195 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Integration::PhenomenalBinding::Helpers::BindingUnit do
4
+ let(:stream_ids) { %w[stream-1 stream-2 stream-3] }
5
+ let(:binding) do
6
+ described_class.new(stream_ids: stream_ids, binding_type: :perceptual, coherence: 0.7)
7
+ end
8
+
9
+ describe '#initialize' do
10
+ it 'assigns a uuid id' do
11
+ expect(binding.id).to match(/\A[0-9a-f-]{36}\z/)
12
+ end
13
+
14
+ it 'assigns stream_ids' do
15
+ expect(binding.stream_ids).to eq(stream_ids)
16
+ end
17
+
18
+ it 'assigns binding_type' do
19
+ expect(binding.binding_type).to eq(:perceptual)
20
+ end
21
+
22
+ it 'assigns coherence' do
23
+ expect(binding.coherence).to eq(0.7)
24
+ end
25
+
26
+ it 'defaults attention_weight to 0.5' do
27
+ expect(binding.attention_weight).to eq(0.5)
28
+ end
29
+
30
+ it 'accepts custom attention_weight' do
31
+ b = described_class.new(stream_ids: [], binding_type: :temporal, coherence: 0.5, attention_weight: 0.9)
32
+ expect(b.attention_weight).to eq(0.9)
33
+ end
34
+
35
+ it 'clamps coherence above 1.0' do
36
+ b = described_class.new(stream_ids: [], binding_type: :conceptual, coherence: 1.5)
37
+ expect(b.coherence).to eq(1.0)
38
+ end
39
+
40
+ it 'clamps coherence below 0.0' do
41
+ b = described_class.new(stream_ids: [], binding_type: :narrative, coherence: -0.2)
42
+ expect(b.coherence).to eq(0.0)
43
+ end
44
+
45
+ it 'does not share the input array reference' do
46
+ original = %w[a b]
47
+ b = described_class.new(stream_ids: original, binding_type: :emotional, coherence: 0.5)
48
+ original << 'c'
49
+ expect(b.stream_ids.size).to eq(2)
50
+ end
51
+ end
52
+
53
+ describe '#add_stream' do
54
+ it 'adds a new stream_id' do
55
+ binding.add_stream(stream_id: 'stream-4')
56
+ expect(binding.stream_ids).to include('stream-4')
57
+ end
58
+
59
+ it 'does not duplicate an existing stream_id' do
60
+ binding.add_stream(stream_id: 'stream-1')
61
+ expect(binding.stream_ids.count('stream-1')).to eq(1)
62
+ end
63
+ end
64
+
65
+ describe '#remove_stream' do
66
+ it 'removes an existing stream_id' do
67
+ binding.remove_stream(stream_id: 'stream-2')
68
+ expect(binding.stream_ids).not_to include('stream-2')
69
+ end
70
+
71
+ it 'does not raise when removing a non-existent id' do
72
+ expect { binding.remove_stream(stream_id: 'ghost') }.not_to raise_error
73
+ end
74
+ end
75
+
76
+ describe '#reinforce!' do
77
+ it 'increases coherence by BINDING_BOOST' do
78
+ initial = binding.coherence
79
+ binding.reinforce!
80
+ expect(binding.coherence).to be_within(0.001).of(initial + 0.08)
81
+ end
82
+
83
+ it 'clamps coherence at 1.0' do
84
+ b = described_class.new(stream_ids: [], binding_type: :perceptual, coherence: 0.97)
85
+ b.reinforce!
86
+ expect(b.coherence).to eq(1.0)
87
+ end
88
+ end
89
+
90
+ describe '#decay!' do
91
+ it 'decreases coherence by BINDING_DECAY' do
92
+ initial = binding.coherence
93
+ binding.decay!
94
+ expect(binding.coherence).to be_within(0.001).of(initial - 0.03)
95
+ end
96
+
97
+ it 'clamps coherence at 0.0' do
98
+ b = described_class.new(stream_ids: [], binding_type: :perceptual, coherence: 0.01)
99
+ b.decay!
100
+ expect(b.coherence).to eq(0.0)
101
+ end
102
+ end
103
+
104
+ describe '#coherent?' do
105
+ it 'returns true when coherence >= COHERENCE_THRESHOLD' do
106
+ b = described_class.new(stream_ids: [], binding_type: :temporal, coherence: 0.7)
107
+ expect(b.coherent?).to be true
108
+ end
109
+
110
+ it 'returns true at the exact threshold' do
111
+ b = described_class.new(stream_ids: [], binding_type: :temporal, coherence: 0.6)
112
+ expect(b.coherent?).to be true
113
+ end
114
+
115
+ it 'returns false below the threshold' do
116
+ b = described_class.new(stream_ids: [], binding_type: :temporal, coherence: 0.5)
117
+ expect(b.coherent?).to be false
118
+ end
119
+ end
120
+
121
+ describe '#coherence_label' do
122
+ it 'returns :unified at 0.9' do
123
+ b = described_class.new(stream_ids: [], binding_type: :perceptual, coherence: 0.9)
124
+ expect(b.coherence_label).to eq(:unified)
125
+ end
126
+
127
+ it 'returns :coherent at 0.7' do
128
+ b = described_class.new(stream_ids: [], binding_type: :perceptual, coherence: 0.7)
129
+ expect(b.coherence_label).to eq(:coherent)
130
+ end
131
+
132
+ it 'returns :fragmented at 0.5' do
133
+ b = described_class.new(stream_ids: [], binding_type: :perceptual, coherence: 0.5)
134
+ expect(b.coherence_label).to eq(:fragmented)
135
+ end
136
+
137
+ it 'returns :dissociated at 0.3' do
138
+ b = described_class.new(stream_ids: [], binding_type: :perceptual, coherence: 0.3)
139
+ expect(b.coherence_label).to eq(:dissociated)
140
+ end
141
+
142
+ it 'returns :unbound at 0.1' do
143
+ b = described_class.new(stream_ids: [], binding_type: :perceptual, coherence: 0.1)
144
+ expect(b.coherence_label).to eq(:unbound)
145
+ end
146
+ end
147
+
148
+ describe '#stream_count' do
149
+ it 'returns the number of stream ids' do
150
+ expect(binding.stream_count).to eq(3)
151
+ end
152
+
153
+ it 'updates after adding a stream' do
154
+ binding.add_stream(stream_id: 'new')
155
+ expect(binding.stream_count).to eq(4)
156
+ end
157
+ end
158
+
159
+ describe '#to_h' do
160
+ subject(:hash) { binding.to_h }
161
+
162
+ it 'includes id' do
163
+ expect(hash[:id]).to be_a(String)
164
+ end
165
+
166
+ it 'includes stream_ids' do
167
+ expect(hash[:stream_ids]).to eq(stream_ids)
168
+ end
169
+
170
+ it 'includes binding_type' do
171
+ expect(hash[:binding_type]).to eq(:perceptual)
172
+ end
173
+
174
+ it 'includes coherence as a float' do
175
+ expect(hash[:coherence]).to be_a(Float)
176
+ end
177
+
178
+ it 'includes coherent boolean' do
179
+ expect(hash[:coherent]).to be true
180
+ end
181
+
182
+ it 'includes coherence_label' do
183
+ expect(hash[:coherence_label]).to eq(:coherent)
184
+ end
185
+
186
+ it 'includes stream_count' do
187
+ expect(hash[:stream_count]).to eq(3)
188
+ end
189
+
190
+ it 'does not share stream_ids array reference' do
191
+ hash[:stream_ids] << 'extra'
192
+ expect(binding.stream_ids.size).to eq(3)
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Integration::PhenomenalBinding::Helpers::Constants do
4
+ describe 'capacity constants' do
5
+ it 'defines MAX_STREAMS' do
6
+ expect(described_class::MAX_STREAMS).to eq(100)
7
+ end
8
+
9
+ it 'defines MAX_BINDINGS' do
10
+ expect(described_class::MAX_BINDINGS).to eq(200)
11
+ end
12
+ end
13
+
14
+ describe 'coherence constants' do
15
+ it 'defines COHERENCE_THRESHOLD' do
16
+ expect(described_class::COHERENCE_THRESHOLD).to eq(0.6)
17
+ end
18
+
19
+ it 'defines BINDING_DECAY' do
20
+ expect(described_class::BINDING_DECAY).to eq(0.03)
21
+ end
22
+
23
+ it 'defines BINDING_BOOST' do
24
+ expect(described_class::BINDING_BOOST).to eq(0.08)
25
+ end
26
+
27
+ it 'defines DEFAULT_SALIENCE' do
28
+ expect(described_class::DEFAULT_SALIENCE).to eq(0.5)
29
+ end
30
+ end
31
+
32
+ describe 'BINDING_TYPES' do
33
+ it 'includes all five types' do
34
+ expect(described_class::BINDING_TYPES).to contain_exactly(
35
+ :perceptual, :conceptual, :temporal, :narrative, :emotional
36
+ )
37
+ end
38
+
39
+ it 'is frozen' do
40
+ expect(described_class::BINDING_TYPES).to be_frozen
41
+ end
42
+ end
43
+
44
+ describe 'STREAM_TYPES' do
45
+ it 'includes all six types' do
46
+ expect(described_class::STREAM_TYPES).to contain_exactly(
47
+ :perception, :thought, :emotion, :memory, :intention, :prediction
48
+ )
49
+ end
50
+
51
+ it 'is frozen' do
52
+ expect(described_class::STREAM_TYPES).to be_frozen
53
+ end
54
+ end
55
+
56
+ describe 'COHERENCE_LABELS' do
57
+ it 'maps 0.9 to :unified' do
58
+ label = described_class::COHERENCE_LABELS.find { |range, _| range.cover?(0.9) }&.last
59
+ expect(label).to eq(:unified)
60
+ end
61
+
62
+ it 'maps 0.7 to :coherent' do
63
+ label = described_class::COHERENCE_LABELS.find { |range, _| range.cover?(0.7) }&.last
64
+ expect(label).to eq(:coherent)
65
+ end
66
+
67
+ it 'maps 0.5 to :fragmented' do
68
+ label = described_class::COHERENCE_LABELS.find { |range, _| range.cover?(0.5) }&.last
69
+ expect(label).to eq(:fragmented)
70
+ end
71
+
72
+ it 'maps 0.3 to :dissociated' do
73
+ label = described_class::COHERENCE_LABELS.find { |range, _| range.cover?(0.3) }&.last
74
+ expect(label).to eq(:dissociated)
75
+ end
76
+
77
+ it 'maps 0.1 to :unbound' do
78
+ label = described_class::COHERENCE_LABELS.find { |range, _| range.cover?(0.1) }&.last
79
+ expect(label).to eq(:unbound)
80
+ end
81
+
82
+ it 'maps exactly 0.8 to :unified' do
83
+ label = described_class::COHERENCE_LABELS.find { |range, _| range.cover?(0.8) }&.last
84
+ expect(label).to eq(:unified)
85
+ end
86
+
87
+ it 'maps exactly 0.6 to :coherent' do
88
+ label = described_class::COHERENCE_LABELS.find { |range, _| range.cover?(0.6) }&.last
89
+ expect(label).to eq(:coherent)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Integration::PhenomenalBinding::Helpers::Stream do
4
+ let(:stream) { described_class.new(stream_type: :perception, content: 'a red circle') }
5
+
6
+ describe '#initialize' do
7
+ it 'assigns a uuid id' do
8
+ expect(stream.id).to match(/\A[0-9a-f-]{36}\z/)
9
+ end
10
+
11
+ it 'assigns stream_type' do
12
+ expect(stream.stream_type).to eq(:perception)
13
+ end
14
+
15
+ it 'assigns content' do
16
+ expect(stream.content).to eq('a red circle')
17
+ end
18
+
19
+ it 'defaults salience to 0.5' do
20
+ expect(stream.salience).to eq(0.5)
21
+ end
22
+
23
+ it 'accepts custom salience' do
24
+ s = described_class.new(stream_type: :thought, content: 'deep', salience: 0.9)
25
+ expect(s.salience).to eq(0.9)
26
+ end
27
+
28
+ it 'clamps salience above 1.0' do
29
+ s = described_class.new(stream_type: :emotion, content: 'joy', salience: 1.5)
30
+ expect(s.salience).to eq(1.0)
31
+ end
32
+
33
+ it 'clamps salience below 0.0' do
34
+ s = described_class.new(stream_type: :memory, content: 'old', salience: -0.3)
35
+ expect(s.salience).to eq(0.0)
36
+ end
37
+
38
+ it 'assigns domain' do
39
+ s = described_class.new(stream_type: :intention, content: 'plan', domain: 'work')
40
+ expect(s.domain).to eq('work')
41
+ end
42
+
43
+ it 'defaults domain to nil' do
44
+ expect(stream.domain).to be_nil
45
+ end
46
+
47
+ it 'sets created_at to a Time' do
48
+ expect(stream.created_at).to be_a(Time)
49
+ end
50
+
51
+ it 'sets timestamp' do
52
+ expect(stream.timestamp).to be_a(Time)
53
+ end
54
+
55
+ it 'accepts explicit timestamp' do
56
+ t = Time.now.utc - 60
57
+ s = described_class.new(stream_type: :prediction, content: 'rain', timestamp: t)
58
+ expect(s.timestamp).to eq(t)
59
+ end
60
+ end
61
+
62
+ describe '#salient?' do
63
+ it 'returns true when salience >= 0.5' do
64
+ s = described_class.new(stream_type: :perception, content: 'x', salience: 0.7)
65
+ expect(s.salient?).to be true
66
+ end
67
+
68
+ it 'returns true when salience exactly 0.5' do
69
+ expect(stream.salient?).to be true
70
+ end
71
+
72
+ it 'returns false when salience < 0.5' do
73
+ s = described_class.new(stream_type: :thought, content: 'dim', salience: 0.3)
74
+ expect(s.salient?).to be false
75
+ end
76
+ end
77
+
78
+ describe '#fresh?' do
79
+ it 'returns true for a just-created stream' do
80
+ expect(stream.fresh?).to be true
81
+ end
82
+
83
+ it 'returns false for an old stream' do
84
+ allow(stream).to receive(:created_at).and_return(Time.now.utc - 60)
85
+ expect(stream.fresh?(window: 30)).to be false
86
+ end
87
+
88
+ it 'uses default window of 30 seconds' do
89
+ expect(stream.fresh?).to be true
90
+ end
91
+
92
+ it 'accepts a custom window' do
93
+ expect(stream.fresh?(window: 3600)).to be true
94
+ end
95
+ end
96
+
97
+ describe '#to_h' do
98
+ subject(:hash) { stream.to_h }
99
+
100
+ it 'includes id' do
101
+ expect(hash[:id]).to be_a(String)
102
+ end
103
+
104
+ it 'includes stream_type' do
105
+ expect(hash[:stream_type]).to eq(:perception)
106
+ end
107
+
108
+ it 'includes content' do
109
+ expect(hash[:content]).to eq('a red circle')
110
+ end
111
+
112
+ it 'includes salience as a rounded float' do
113
+ expect(hash[:salience]).to be_a(Float)
114
+ end
115
+
116
+ it 'includes domain' do
117
+ expect(hash).to have_key(:domain)
118
+ end
119
+
120
+ it 'includes timestamp' do
121
+ expect(hash[:timestamp]).to be_a(Time)
122
+ end
123
+
124
+ it 'includes created_at' do
125
+ expect(hash[:created_at]).to be_a(Time)
126
+ end
127
+ end
128
+ end