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,204 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/integration/situation_model/client'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Integration::SituationModel::Runners::SituationModel do
6
+ let(:client) { Legion::Extensions::Agentic::Integration::SituationModel::Client.new }
7
+
8
+ def create_model(label: 'test')
9
+ client.create_situation_model(label: label)
10
+ end
11
+
12
+ def add_event(model_id, content: 'test event', **dims)
13
+ defaults = { space: 0.5, time: 0.5, causation: 0.5, intentionality: 0.5, protagonist: 0.5 }
14
+ client.add_situation_event(model_id: model_id, content: content, **defaults.merge(dims))
15
+ end
16
+
17
+ describe '#create_situation_model' do
18
+ it 'returns success: true' do
19
+ result = create_model
20
+ expect(result[:success]).to be(true)
21
+ end
22
+
23
+ it 'returns a model hash with id and label' do
24
+ result = create_model(label: 'story')
25
+ expect(result[:model][:label]).to eq('story')
26
+ expect(result[:model][:id]).to match(/\A[0-9a-f-]{36}\z/)
27
+ end
28
+ end
29
+
30
+ describe '#add_situation_event' do
31
+ it 'returns success: true for known model' do
32
+ model_id = create_model[:model][:id]
33
+ result = add_event(model_id)
34
+ expect(result[:success]).to be(true)
35
+ end
36
+
37
+ it 'returns success: false for unknown model' do
38
+ result = add_event('nonexistent')
39
+ expect(result[:success]).to be(false)
40
+ expect(result[:error]).to eq('model not found')
41
+ end
42
+
43
+ it 'returns coherence after adding event' do
44
+ model_id = create_model[:model][:id]
45
+ result = add_event(model_id)
46
+ expect(result[:coherence]).to be_a(Float)
47
+ end
48
+
49
+ it 'accepts custom dimension values' do
50
+ model_id = create_model[:model][:id]
51
+ result = client.add_situation_event(model_id: model_id, content: 'scene',
52
+ space: 0.9, time: 0.1,
53
+ causation: 0.5, intentionality: 0.5, protagonist: 0.5)
54
+ expect(result[:success]).to be(true)
55
+ expect(result[:event][:dimension_values][:space]).to be_within(0.001).of(0.9)
56
+ end
57
+ end
58
+
59
+ describe '#situation_model_coherence' do
60
+ it 'returns success: true for known model' do
61
+ model_id = create_model[:model][:id]
62
+ result = client.situation_model_coherence(model_id: model_id)
63
+ expect(result[:success]).to be(true)
64
+ expect(result[:coherence]).to be_a(Float)
65
+ end
66
+
67
+ it 'returns success: false for unknown model' do
68
+ result = client.situation_model_coherence(model_id: 'bad')
69
+ expect(result[:success]).to be(false)
70
+ end
71
+ end
72
+
73
+ describe '#find_situation_boundaries' do
74
+ it 'returns success: true for known model' do
75
+ model_id = create_model[:model][:id]
76
+ add_event(model_id)
77
+ result = client.find_situation_boundaries(model_id: model_id)
78
+ expect(result[:success]).to be(true)
79
+ expect(result[:boundaries]).to be_an(Array)
80
+ end
81
+
82
+ it 'returns success: false for unknown model' do
83
+ result = client.find_situation_boundaries(model_id: 'bad')
84
+ expect(result[:success]).to be(false)
85
+ end
86
+
87
+ it 'uses custom threshold' do
88
+ model_id = create_model[:model][:id]
89
+ result = client.find_situation_boundaries(model_id: model_id, threshold: 0.5)
90
+ expect(result[:threshold]).to eq(0.5)
91
+ end
92
+
93
+ it 'detects discontinuity boundary' do
94
+ model_id = create_model[:model][:id]
95
+ client.add_situation_event(model_id: model_id, content: 'high',
96
+ space: 0.9, time: 0.9, causation: 0.9, intentionality: 0.9, protagonist: 0.9)
97
+ client.add_situation_event(model_id: model_id, content: 'low',
98
+ space: 0.0, time: 0.0, causation: 0.0, intentionality: 0.0, protagonist: 0.0)
99
+ result = client.find_situation_boundaries(model_id: model_id, threshold: 0.3)
100
+ expect(result[:boundaries]).to include(1)
101
+ end
102
+ end
103
+
104
+ describe '#situation_dimension_trajectory' do
105
+ it 'returns success: true for known model and dimension' do
106
+ model_id = create_model[:model][:id]
107
+ add_event(model_id, space: 0.7)
108
+ result = client.situation_dimension_trajectory(model_id: model_id, dimension: :space)
109
+ expect(result[:success]).to be(true)
110
+ expect(result[:trajectory]).to be_an(Array)
111
+ end
112
+
113
+ it 'accepts string dimensions (converts to symbol)' do
114
+ model_id = create_model[:model][:id]
115
+ add_event(model_id)
116
+ result = client.situation_dimension_trajectory(model_id: model_id, dimension: 'time')
117
+ expect(result[:success]).to be(true)
118
+ expect(result[:dimension]).to eq(:time)
119
+ end
120
+
121
+ it 'returns success: false for unknown model' do
122
+ result = client.situation_dimension_trajectory(model_id: 'bad', dimension: :space)
123
+ expect(result[:success]).to be(false)
124
+ end
125
+
126
+ it 'tracks trajectory over multiple events' do
127
+ model_id = create_model[:model][:id]
128
+ client.add_situation_event(model_id: model_id, content: 'a',
129
+ space: 0.2, time: 0.5, causation: 0.5, intentionality: 0.5, protagonist: 0.5)
130
+ client.add_situation_event(model_id: model_id, content: 'b',
131
+ space: 0.8, time: 0.5, causation: 0.5, intentionality: 0.5, protagonist: 0.5)
132
+ result = client.situation_dimension_trajectory(model_id: model_id, dimension: :space)
133
+ expect(result[:trajectory]).to eq([0.2, 0.8])
134
+ end
135
+ end
136
+
137
+ describe '#most_coherent_situations' do
138
+ it 'returns success: true' do
139
+ result = client.most_coherent_situations
140
+ expect(result[:success]).to be(true)
141
+ end
142
+
143
+ it 'returns models array' do
144
+ create_model(label: 'a')
145
+ create_model(label: 'b')
146
+ result = client.most_coherent_situations(limit: 2)
147
+ expect(result[:models]).to be_an(Array)
148
+ expect(result[:count]).to be <= 2
149
+ end
150
+
151
+ it 'respects limit' do
152
+ 5.times { |i| create_model(label: "m#{i}") }
153
+ result = client.most_coherent_situations(limit: 3)
154
+ expect(result[:models].size).to be <= 3
155
+ end
156
+ end
157
+
158
+ describe '#situations_by_label' do
159
+ it 'returns models with matching label' do
160
+ create_model(label: 'target')
161
+ create_model(label: 'target')
162
+ create_model(label: 'other')
163
+ result = client.situations_by_label(label: 'target')
164
+ expect(result[:success]).to be(true)
165
+ expect(result[:count]).to eq(2)
166
+ expect(result[:models].all? { |m| m[:label] == 'target' }).to be(true)
167
+ end
168
+
169
+ it 'returns empty for unknown label' do
170
+ result = client.situations_by_label(label: 'unknown')
171
+ expect(result[:count]).to eq(0)
172
+ end
173
+ end
174
+
175
+ describe '#update_situation_models' do
176
+ it 'returns success: true' do
177
+ result = client.update_situation_models
178
+ expect(result[:success]).to be(true)
179
+ end
180
+
181
+ it 'returns pruned_count' do
182
+ result = client.update_situation_models
183
+ expect(result[:pruned_count]).to be_a(Integer)
184
+ end
185
+ end
186
+
187
+ describe '#situation_model_stats' do
188
+ it 'returns success: true' do
189
+ result = client.situation_model_stats
190
+ expect(result[:success]).to be(true)
191
+ end
192
+
193
+ it 'includes model_count' do
194
+ create_model
195
+ result = client.situation_model_stats
196
+ expect(result[:model_count]).to be >= 1
197
+ end
198
+
199
+ it 'includes models array' do
200
+ result = client.situation_model_stats
201
+ expect(result[:models]).to be_an(Array)
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/integration/synthesis/client'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Integration::Synthesis::Client do
6
+ it 'responds to all runner methods' do
7
+ client = described_class.new
8
+ %i[add_stream remove_stream synthesize decay_streams check_conflict
9
+ dominant_stream synthesis_history average_coherence status].each do |method|
10
+ expect(client).to respond_to(method)
11
+ end
12
+ end
13
+
14
+ it 'accepts an injected engine' do
15
+ engine = Legion::Extensions::Agentic::Integration::Synthesis::Helpers::SynthesisEngine.new
16
+ client = described_class.new(engine: engine)
17
+ client.add_stream(stream_type: :emotional, content: {})
18
+ expect(engine.streams.size).to eq(1)
19
+ end
20
+
21
+ it 'creates its own engine when none injected' do
22
+ client = described_class.new
23
+ result = client.add_stream(stream_type: :emotional, content: {})
24
+ expect(result[:success]).to be true
25
+ end
26
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Integration::Synthesis::Helpers::Constants do
4
+ subject(:mod) { described_class }
5
+
6
+ describe 'MAX_STREAMS' do
7
+ it 'is 50' do
8
+ expect(mod::MAX_STREAMS).to eq(50)
9
+ end
10
+ end
11
+
12
+ describe 'MAX_SYNTHESES' do
13
+ it 'is 200' do
14
+ expect(mod::MAX_SYNTHESES).to eq(200)
15
+ end
16
+ end
17
+
18
+ describe 'DEFAULT_WEIGHT' do
19
+ it 'is 0.5' do
20
+ expect(mod::DEFAULT_WEIGHT).to eq(0.5)
21
+ end
22
+ end
23
+
24
+ describe 'COHERENCE_THRESHOLD' do
25
+ it 'is 0.6' do
26
+ expect(mod::COHERENCE_THRESHOLD).to eq(0.6)
27
+ end
28
+ end
29
+
30
+ describe 'NOVELTY_THRESHOLD' do
31
+ it 'is 0.7' do
32
+ expect(mod::NOVELTY_THRESHOLD).to eq(0.7)
33
+ end
34
+ end
35
+
36
+ describe 'FRESHNESS_DECAY' do
37
+ it 'is 0.02' do
38
+ expect(mod::FRESHNESS_DECAY).to eq(0.02)
39
+ end
40
+ end
41
+
42
+ describe 'MIN_STREAMS_FOR_SYNTHESIS' do
43
+ it 'is 2' do
44
+ expect(mod::MIN_STREAMS_FOR_SYNTHESIS).to eq(2)
45
+ end
46
+ end
47
+
48
+ describe 'STREAM_TYPES' do
49
+ it 'includes all expected types' do
50
+ expect(mod::STREAM_TYPES).to include(:emotional, :perceptual, :memorial, :predictive,
51
+ :reasoning, :social, :identity, :motor)
52
+ end
53
+
54
+ it 'is frozen' do
55
+ expect(mod::STREAM_TYPES).to be_frozen
56
+ end
57
+ end
58
+
59
+ describe 'COHERENCE_LABELS' do
60
+ it 'maps high values to :unified' do
61
+ label = mod::COHERENCE_LABELS.find { |range, _| range.cover?(0.9) }&.last
62
+ expect(label).to eq(:unified)
63
+ end
64
+
65
+ it 'maps low values to :chaotic' do
66
+ label = mod::COHERENCE_LABELS.find { |range, _| range.cover?(0.1) }&.last
67
+ expect(label).to eq(:chaotic)
68
+ end
69
+
70
+ it 'maps mid values to :coherent' do
71
+ label = mod::COHERENCE_LABELS.find { |range, _| range.cover?(0.7) }&.last
72
+ expect(label).to eq(:coherent)
73
+ end
74
+ end
75
+
76
+ describe 'CONFIDENCE_LABELS' do
77
+ it 'maps high values to :certain' do
78
+ label = mod::CONFIDENCE_LABELS.find { |range, _| range.cover?(0.95) }&.last
79
+ expect(label).to eq(:certain)
80
+ end
81
+
82
+ it 'maps low values to :guessing' do
83
+ label = mod::CONFIDENCE_LABELS.find { |range, _| range.cover?(0.05) }&.last
84
+ expect(label).to eq(:guessing)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,233 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Integration::Synthesis::Helpers::SynthesisEngine do
4
+ subject(:engine) { described_class.new }
5
+
6
+ def add_stream(type: :emotional, content: { value: 1 }, weight: 0.7, confidence: 0.8)
7
+ engine.add_stream(stream_type: type, content: content, weight: weight, confidence: confidence)
8
+ end
9
+
10
+ describe '#add_stream' do
11
+ it 'returns success with stream_id' do
12
+ result = add_stream
13
+ expect(result[:success]).to be true
14
+ expect(result[:stream_id]).to match(/\A[0-9a-f-]{36}\z/)
15
+ end
16
+
17
+ it 'stores stream in @streams' do
18
+ add_stream
19
+ expect(engine.streams.size).to eq(1)
20
+ end
21
+
22
+ it 'rejects invalid stream_type' do
23
+ result = engine.add_stream(stream_type: :invalid, content: {})
24
+ expect(result[:success]).to be false
25
+ expect(result[:error]).to eq(:invalid_stream_type)
26
+ end
27
+
28
+ it 'accepts all valid stream types' do
29
+ %i[emotional perceptual memorial predictive reasoning social identity motor].each do |type|
30
+ result = engine.add_stream(stream_type: type, content: {})
31
+ expect(result[:success]).to be true
32
+ end
33
+ end
34
+
35
+ it 'prunes when exceeding MAX_STREAMS' do
36
+ 51.times { add_stream }
37
+ expect(engine.streams.size).to eq(50)
38
+ end
39
+ end
40
+
41
+ describe '#remove_stream' do
42
+ it 'removes an existing stream' do
43
+ result = add_stream
44
+ remove = engine.remove_stream(stream_id: result[:stream_id])
45
+ expect(remove[:success]).to be true
46
+ expect(engine.streams).to be_empty
47
+ end
48
+
49
+ it 'returns not_found for missing stream' do
50
+ result = engine.remove_stream(stream_id: 'nonexistent')
51
+ expect(result[:success]).to be false
52
+ expect(result[:error]).to eq(:not_found)
53
+ end
54
+ end
55
+
56
+ describe '#synthesize!' do
57
+ it 'fails with fewer than MIN_STREAMS_FOR_SYNTHESIS active streams' do
58
+ add_stream
59
+ result = engine.synthesize!
60
+ expect(result[:success]).to be false
61
+ expect(result[:error]).to eq(:insufficient_streams)
62
+ end
63
+
64
+ it 'succeeds with enough streams' do
65
+ 2.times { add_stream }
66
+ result = engine.synthesize!
67
+ expect(result[:success]).to be true
68
+ expect(result[:synthesis]).to have_key(:id)
69
+ end
70
+
71
+ it 'stores synthesis in history' do
72
+ 2.times { add_stream }
73
+ engine.synthesize!
74
+ expect(engine.syntheses.size).to eq(1)
75
+ end
76
+
77
+ it 'includes coherence in result' do
78
+ 2.times { add_stream }
79
+ result = engine.synthesize!
80
+ expect(result[:synthesis][:coherence]).to be_between(0.0, 1.0)
81
+ end
82
+
83
+ it 'includes novelty in result' do
84
+ 2.times { add_stream }
85
+ result = engine.synthesize!
86
+ expect(result[:synthesis][:novelty]).to be_between(0.0, 1.0)
87
+ end
88
+
89
+ it 'includes confidence in result' do
90
+ 2.times { add_stream }
91
+ result = engine.synthesize!
92
+ expect(result[:synthesis][:confidence]).to be_between(0.0, 1.0)
93
+ end
94
+
95
+ it 'marks first synthesis as maximally novel (no prior)' do
96
+ 2.times { add_stream }
97
+ result = engine.synthesize!
98
+ expect(result[:synthesis][:novelty]).to eq(1.0)
99
+ end
100
+
101
+ it 'caps syntheses at MAX_SYNTHESES' do
102
+ 2.times { add_stream }
103
+ 201.times { engine.synthesize! }
104
+ expect(engine.syntheses.size).to eq(200)
105
+ end
106
+
107
+ it 'returns content with dominant_type' do
108
+ 2.times { add_stream }
109
+ result = engine.synthesize!
110
+ expect(result[:synthesis][:content]).to have_key(:dominant_type)
111
+ end
112
+ end
113
+
114
+ describe '#decay_all!' do
115
+ it 'returns success' do
116
+ result = engine.decay_all!
117
+ expect(result[:success]).to be true
118
+ end
119
+
120
+ it 'reduces freshness on all streams' do
121
+ add_stream
122
+ stream = engine.streams.values.first
123
+ before = stream.freshness
124
+ engine.decay_all!
125
+ expect(stream.freshness).to be < before
126
+ end
127
+
128
+ it 'removes stale streams' do
129
+ add_stream
130
+ stream = engine.streams.values.first
131
+ 46.times { stream.decay_freshness! }
132
+ engine.decay_all!
133
+ expect(engine.streams).to be_empty
134
+ end
135
+
136
+ it 'reports removed and remaining counts' do
137
+ 2.times { add_stream }
138
+ result = engine.decay_all!
139
+ expect(result).to have_key(:streams_removed)
140
+ expect(result).to have_key(:streams_remaining)
141
+ end
142
+ end
143
+
144
+ describe '#stream_conflict?' do
145
+ let(:id_a) { add_stream(weight: 0.9, content: { signal: :danger })[:stream_id] }
146
+ let(:id_b) { add_stream(weight: 0.1, content: { signal: :safe })[:stream_id] }
147
+
148
+ it 'detects weight opposition' do
149
+ result = engine.stream_conflict?(stream_id_a: id_a, stream_id_b: id_b)
150
+ expect(result[:success]).to be true
151
+ expect(result[:weight_opposition]).to be true
152
+ end
153
+
154
+ it 'detects content conflict on shared keys' do
155
+ result = engine.stream_conflict?(stream_id_a: id_a, stream_id_b: id_b)
156
+ expect(result[:content_conflict]).to be true
157
+ end
158
+
159
+ it 'reports no conflict for compatible streams' do
160
+ a = add_stream(weight: 0.6, content: { value: 1 })[:stream_id]
161
+ b = add_stream(weight: 0.7, content: { value: 1 })[:stream_id]
162
+ result = engine.stream_conflict?(stream_id_a: a, stream_id_b: b)
163
+ expect(result[:conflict]).to be false
164
+ end
165
+
166
+ it 'returns not_found for missing stream' do
167
+ result = engine.stream_conflict?(stream_id_a: 'x', stream_id_b: 'y')
168
+ expect(result[:success]).to be false
169
+ expect(result[:error]).to eq(:stream_not_found)
170
+ end
171
+ end
172
+
173
+ describe '#dominant_stream' do
174
+ it 'returns no_streams error when empty' do
175
+ result = engine.dominant_stream
176
+ expect(result[:success]).to be false
177
+ expect(result[:error]).to eq(:no_streams)
178
+ end
179
+
180
+ it 'returns the stream with highest effective_weight' do
181
+ add_stream(weight: 0.3, confidence: 0.3)
182
+ id = add_stream(weight: 0.9, confidence: 0.95)[:stream_id]
183
+ result = engine.dominant_stream
184
+ expect(result[:success]).to be true
185
+ expect(result[:stream][:id]).to eq(id)
186
+ end
187
+ end
188
+
189
+ describe '#synthesis_history' do
190
+ it 'returns empty list when no syntheses' do
191
+ result = engine.synthesis_history
192
+ expect(result[:syntheses]).to be_empty
193
+ expect(result[:count]).to eq(0)
194
+ end
195
+
196
+ it 'returns last N syntheses' do
197
+ 2.times { add_stream }
198
+ 5.times { engine.synthesize! }
199
+ result = engine.synthesis_history(limit: 3)
200
+ expect(result[:count]).to eq(3)
201
+ end
202
+ end
203
+
204
+ describe '#average_coherence' do
205
+ it 'returns 0.0 when no syntheses' do
206
+ result = engine.average_coherence
207
+ expect(result[:average_coherence]).to eq(0.0)
208
+ expect(result[:sample_size]).to eq(0)
209
+ end
210
+
211
+ it 'computes average coherence over recent syntheses' do
212
+ 2.times { add_stream }
213
+ 3.times { engine.synthesize! }
214
+ result = engine.average_coherence
215
+ expect(result[:average_coherence]).to be_between(0.0, 1.0)
216
+ expect(result[:sample_size]).to eq(3)
217
+ end
218
+ end
219
+
220
+ describe '#to_h' do
221
+ it 'returns summary stats hash' do
222
+ add_stream(type: :emotional)
223
+ result = engine.to_h
224
+ expect(result).to include(:stream_count, :synthesis_count, :active_streams,
225
+ :stale_streams, :average_coherence)
226
+ end
227
+
228
+ it 'reflects current stream count' do
229
+ 3.times { add_stream }
230
+ expect(engine.to_h[:stream_count]).to eq(3)
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Integration::Synthesis::Helpers::Synthesis do
4
+ subject(:synthesis) do
5
+ described_class.new(
6
+ streams: %w[abc def],
7
+ coherence: 0.75,
8
+ novelty: 0.8,
9
+ confidence: 0.7,
10
+ content: { dominant_type: :emotional, stream_count: 2 }
11
+ )
12
+ end
13
+
14
+ describe '#initialize' do
15
+ it 'assigns a UUID id' do
16
+ expect(synthesis.id).to match(/\A[0-9a-f-]{36}\z/)
17
+ end
18
+
19
+ it 'stores stream ids' do
20
+ expect(synthesis.streams).to eq(%w[abc def])
21
+ end
22
+
23
+ it 'clamps coherence to 0-1' do
24
+ s = described_class.new(streams: [], coherence: 1.5, novelty: 0.5, confidence: 0.5, content: {})
25
+ expect(s.coherence).to eq(1.0)
26
+ end
27
+
28
+ it 'clamps novelty to 0-1' do
29
+ s = described_class.new(streams: [], coherence: 0.5, novelty: -0.5, confidence: 0.5, content: {})
30
+ expect(s.novelty).to eq(0.0)
31
+ end
32
+
33
+ it 'clamps confidence to 0-1' do
34
+ s = described_class.new(streams: [], coherence: 0.5, novelty: 0.5, confidence: 2.0, content: {})
35
+ expect(s.confidence).to eq(1.0)
36
+ end
37
+
38
+ it 'sets created_at as UTC time' do
39
+ expect(synthesis.created_at).to be_a(Time)
40
+ end
41
+ end
42
+
43
+ describe '#fragmented?' do
44
+ it 'returns false when coherence >= COHERENCE_THRESHOLD' do
45
+ expect(synthesis.fragmented?).to be false
46
+ end
47
+
48
+ it 'returns true when coherence < COHERENCE_THRESHOLD' do
49
+ s = described_class.new(streams: [], coherence: 0.4, novelty: 0.5, confidence: 0.5, content: {})
50
+ expect(s.fragmented?).to be true
51
+ end
52
+ end
53
+
54
+ describe '#novel?' do
55
+ it 'returns true when novelty > NOVELTY_THRESHOLD' do
56
+ expect(synthesis.novel?).to be true
57
+ end
58
+
59
+ it 'returns false when novelty <= NOVELTY_THRESHOLD' do
60
+ s = described_class.new(streams: [], coherence: 0.5, novelty: 0.5, confidence: 0.5, content: {})
61
+ expect(s.novel?).to be false
62
+ end
63
+ end
64
+
65
+ describe '#coherence_label' do
66
+ it 'returns :coherent for coherence 0.75' do
67
+ expect(synthesis.coherence_label).to eq(:coherent)
68
+ end
69
+
70
+ it 'returns :unified for coherence 0.9' do
71
+ s = described_class.new(streams: [], coherence: 0.9, novelty: 0.5, confidence: 0.5, content: {})
72
+ expect(s.coherence_label).to eq(:unified)
73
+ end
74
+ end
75
+
76
+ describe '#confidence_label' do
77
+ it 'returns :confident for confidence 0.7' do
78
+ expect(synthesis.confidence_label).to eq(:confident)
79
+ end
80
+ end
81
+
82
+ describe '#to_h' do
83
+ let(:hash) { synthesis.to_h }
84
+
85
+ it 'includes all expected keys' do
86
+ %i[id streams coherence novelty confidence content fragmented novel
87
+ coherence_label confidence_label created_at].each do |key|
88
+ expect(hash).to have_key(key)
89
+ end
90
+ end
91
+
92
+ it 'reflects fragmented state' do
93
+ s = described_class.new(streams: [], coherence: 0.3, novelty: 0.5, confidence: 0.5, content: {})
94
+ expect(s.to_h[:fragmented]).to be true
95
+ end
96
+
97
+ it 'reflects novel state' do
98
+ expect(hash[:novel]).to be true
99
+ end
100
+ end
101
+ end