lex-agentic-self 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 (249) 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-self.gemspec +31 -0
  7. data/lib/legion/extensions/agentic/self/agency/client.rb +21 -0
  8. data/lib/legion/extensions/agentic/self/agency/helpers/constants.rb +77 -0
  9. data/lib/legion/extensions/agentic/self/agency/helpers/efficacy_model.rb +136 -0
  10. data/lib/legion/extensions/agentic/self/agency/helpers/outcome_event.rb +52 -0
  11. data/lib/legion/extensions/agentic/self/agency/runners/agency.rb +117 -0
  12. data/lib/legion/extensions/agentic/self/agency/version.rb +13 -0
  13. data/lib/legion/extensions/agentic/self/agency.rb +19 -0
  14. data/lib/legion/extensions/agentic/self/anchor/client.rb +15 -0
  15. data/lib/legion/extensions/agentic/self/anchor/helpers/anchor.rb +92 -0
  16. data/lib/legion/extensions/agentic/self/anchor/helpers/anchor_engine.rb +123 -0
  17. data/lib/legion/extensions/agentic/self/anchor/helpers/chain.rb +93 -0
  18. data/lib/legion/extensions/agentic/self/anchor/helpers/constants.rb +46 -0
  19. data/lib/legion/extensions/agentic/self/anchor/runners/cognitive_anchor.rb +70 -0
  20. data/lib/legion/extensions/agentic/self/anchor/version.rb +13 -0
  21. data/lib/legion/extensions/agentic/self/anchor.rb +22 -0
  22. data/lib/legion/extensions/agentic/self/anosognosia/client.rb +28 -0
  23. data/lib/legion/extensions/agentic/self/anosognosia/helpers/anosognosia_engine.rb +153 -0
  24. data/lib/legion/extensions/agentic/self/anosognosia/helpers/cognitive_deficit.rb +71 -0
  25. data/lib/legion/extensions/agentic/self/anosognosia/helpers/constants.rb +29 -0
  26. data/lib/legion/extensions/agentic/self/anosognosia/runners/anosognosia.rb +98 -0
  27. data/lib/legion/extensions/agentic/self/anosognosia/version.rb +13 -0
  28. data/lib/legion/extensions/agentic/self/anosognosia.rb +19 -0
  29. data/lib/legion/extensions/agentic/self/architecture/client.rb +19 -0
  30. data/lib/legion/extensions/agentic/self/architecture/helpers/architecture_engine.rb +167 -0
  31. data/lib/legion/extensions/agentic/self/architecture/helpers/connection.rb +57 -0
  32. data/lib/legion/extensions/agentic/self/architecture/helpers/constants.rb +37 -0
  33. data/lib/legion/extensions/agentic/self/architecture/helpers/subsystem.rb +80 -0
  34. data/lib/legion/extensions/agentic/self/architecture/runners/cognitive_architecture.rb +125 -0
  35. data/lib/legion/extensions/agentic/self/architecture/version.rb +13 -0
  36. data/lib/legion/extensions/agentic/self/architecture.rb +20 -0
  37. data/lib/legion/extensions/agentic/self/default_mode_network/actors/idle.rb +45 -0
  38. data/lib/legion/extensions/agentic/self/default_mode_network/client.rb +28 -0
  39. data/lib/legion/extensions/agentic/self/default_mode_network/helpers/constants.rb +53 -0
  40. data/lib/legion/extensions/agentic/self/default_mode_network/helpers/dmn_engine.rb +221 -0
  41. data/lib/legion/extensions/agentic/self/default_mode_network/helpers/wandering_thought.rb +60 -0
  42. data/lib/legion/extensions/agentic/self/default_mode_network/runners/default_mode_network.rb +122 -0
  43. data/lib/legion/extensions/agentic/self/default_mode_network/version.rb +13 -0
  44. data/lib/legion/extensions/agentic/self/default_mode_network.rb +20 -0
  45. data/lib/legion/extensions/agentic/self/fingerprint/client.rb +28 -0
  46. data/lib/legion/extensions/agentic/self/fingerprint/helpers/cognitive_trait.rb +73 -0
  47. data/lib/legion/extensions/agentic/self/fingerprint/helpers/constants.rb +60 -0
  48. data/lib/legion/extensions/agentic/self/fingerprint/helpers/fingerprint_engine.rb +169 -0
  49. data/lib/legion/extensions/agentic/self/fingerprint/runners/cognitive_fingerprint.rb +86 -0
  50. data/lib/legion/extensions/agentic/self/fingerprint/version.rb +13 -0
  51. data/lib/legion/extensions/agentic/self/fingerprint.rb +19 -0
  52. data/lib/legion/extensions/agentic/self/identity/actors/credential_refresh.rb +49 -0
  53. data/lib/legion/extensions/agentic/self/identity/actors/orphan_check.rb +52 -0
  54. data/lib/legion/extensions/agentic/self/identity/client.rb +27 -0
  55. data/lib/legion/extensions/agentic/self/identity/helpers/dimensions.rb +75 -0
  56. data/lib/legion/extensions/agentic/self/identity/helpers/fingerprint.rb +170 -0
  57. data/lib/legion/extensions/agentic/self/identity/helpers/graph_client.rb +29 -0
  58. data/lib/legion/extensions/agentic/self/identity/helpers/graph_token.rb +36 -0
  59. data/lib/legion/extensions/agentic/self/identity/helpers/token_cache.rb +59 -0
  60. data/lib/legion/extensions/agentic/self/identity/helpers/vault_secrets.rb +80 -0
  61. data/lib/legion/extensions/agentic/self/identity/local_migrations/20260316000030_create_fingerprint.rb +20 -0
  62. data/lib/legion/extensions/agentic/self/identity/runners/entra.rb +402 -0
  63. data/lib/legion/extensions/agentic/self/identity/runners/identity.rb +90 -0
  64. data/lib/legion/extensions/agentic/self/identity/version.rb +13 -0
  65. data/lib/legion/extensions/agentic/self/identity.rb +28 -0
  66. data/lib/legion/extensions/agentic/self/metacognition/client.rb +27 -0
  67. data/lib/legion/extensions/agentic/self/metacognition/helpers/constants.rb +377 -0
  68. data/lib/legion/extensions/agentic/self/metacognition/helpers/narrator_bridge.rb +85 -0
  69. data/lib/legion/extensions/agentic/self/metacognition/helpers/registry_store.rb +70 -0
  70. data/lib/legion/extensions/agentic/self/metacognition/helpers/self_model.rb +160 -0
  71. data/lib/legion/extensions/agentic/self/metacognition/helpers/snapshot_store.rb +82 -0
  72. data/lib/legion/extensions/agentic/self/metacognition/runners/metacognition.rb +116 -0
  73. data/lib/legion/extensions/agentic/self/metacognition/runners/registry.rb +180 -0
  74. data/lib/legion/extensions/agentic/self/metacognition/version.rb +13 -0
  75. data/lib/legion/extensions/agentic/self/metacognition.rb +22 -0
  76. data/lib/legion/extensions/agentic/self/metacognitive_monitoring/client.rb +25 -0
  77. data/lib/legion/extensions/agentic/self/metacognitive_monitoring/helpers/calibration_tracker.rb +96 -0
  78. data/lib/legion/extensions/agentic/self/metacognitive_monitoring/helpers/constants.rb +47 -0
  79. data/lib/legion/extensions/agentic/self/metacognitive_monitoring/helpers/monitoring_engine.rb +141 -0
  80. data/lib/legion/extensions/agentic/self/metacognitive_monitoring/helpers/monitoring_judgment.rb +79 -0
  81. data/lib/legion/extensions/agentic/self/metacognitive_monitoring/runners/metacognitive_monitoring.rb +151 -0
  82. data/lib/legion/extensions/agentic/self/metacognitive_monitoring/version.rb +13 -0
  83. data/lib/legion/extensions/agentic/self/metacognitive_monitoring.rb +20 -0
  84. data/lib/legion/extensions/agentic/self/narrative_arc/client.rb +29 -0
  85. data/lib/legion/extensions/agentic/self/narrative_arc/helpers/arc.rb +137 -0
  86. data/lib/legion/extensions/agentic/self/narrative_arc/helpers/arc_engine.rb +119 -0
  87. data/lib/legion/extensions/agentic/self/narrative_arc/helpers/beat_event.rb +59 -0
  88. data/lib/legion/extensions/agentic/self/narrative_arc/helpers/constants.rb +66 -0
  89. data/lib/legion/extensions/agentic/self/narrative_arc/runners/narrative.rb +101 -0
  90. data/lib/legion/extensions/agentic/self/narrative_arc/version.rb +13 -0
  91. data/lib/legion/extensions/agentic/self/narrative_arc.rb +20 -0
  92. data/lib/legion/extensions/agentic/self/narrative_identity/actors/narrative_decay.rb +45 -0
  93. data/lib/legion/extensions/agentic/self/narrative_identity/client.rb +22 -0
  94. data/lib/legion/extensions/agentic/self/narrative_identity/helpers/chapter.rb +48 -0
  95. data/lib/legion/extensions/agentic/self/narrative_identity/helpers/constants.rb +62 -0
  96. data/lib/legion/extensions/agentic/self/narrative_identity/helpers/episode.rb +67 -0
  97. data/lib/legion/extensions/agentic/self/narrative_identity/helpers/narrative_engine.rb +187 -0
  98. data/lib/legion/extensions/agentic/self/narrative_identity/helpers/theme.rb +50 -0
  99. data/lib/legion/extensions/agentic/self/narrative_identity/runners/narrative_identity.rb +158 -0
  100. data/lib/legion/extensions/agentic/self/narrative_identity/version.rb +13 -0
  101. data/lib/legion/extensions/agentic/self/narrative_identity.rb +21 -0
  102. data/lib/legion/extensions/agentic/self/narrative_self/client.rb +27 -0
  103. data/lib/legion/extensions/agentic/self/narrative_self/helpers/autobiography.rb +187 -0
  104. data/lib/legion/extensions/agentic/self/narrative_self/helpers/constants.rb +42 -0
  105. data/lib/legion/extensions/agentic/self/narrative_self/helpers/episode.rb +81 -0
  106. data/lib/legion/extensions/agentic/self/narrative_self/helpers/narrative_thread.rb +65 -0
  107. data/lib/legion/extensions/agentic/self/narrative_self/runners/narrative_self.rb +86 -0
  108. data/lib/legion/extensions/agentic/self/narrative_self/version.rb +13 -0
  109. data/lib/legion/extensions/agentic/self/narrative_self.rb +20 -0
  110. data/lib/legion/extensions/agentic/self/personality/client.rb +21 -0
  111. data/lib/legion/extensions/agentic/self/personality/helpers/constants.rb +84 -0
  112. data/lib/legion/extensions/agentic/self/personality/helpers/personality_store.rb +126 -0
  113. data/lib/legion/extensions/agentic/self/personality/helpers/trait_model.rb +147 -0
  114. data/lib/legion/extensions/agentic/self/personality/runners/personality.rb +102 -0
  115. data/lib/legion/extensions/agentic/self/personality/version.rb +13 -0
  116. data/lib/legion/extensions/agentic/self/personality.rb +19 -0
  117. data/lib/legion/extensions/agentic/self/reflection/client.rb +27 -0
  118. data/lib/legion/extensions/agentic/self/reflection/helpers/constants.rb +66 -0
  119. data/lib/legion/extensions/agentic/self/reflection/helpers/llm_enhancer.rb +166 -0
  120. data/lib/legion/extensions/agentic/self/reflection/helpers/monitors.rb +186 -0
  121. data/lib/legion/extensions/agentic/self/reflection/helpers/reflection.rb +54 -0
  122. data/lib/legion/extensions/agentic/self/reflection/helpers/reflection_store.rb +99 -0
  123. data/lib/legion/extensions/agentic/self/reflection/runners/reflection.rb +199 -0
  124. data/lib/legion/extensions/agentic/self/reflection/version.rb +13 -0
  125. data/lib/legion/extensions/agentic/self/reflection.rb +21 -0
  126. data/lib/legion/extensions/agentic/self/self_model/client.rb +19 -0
  127. data/lib/legion/extensions/agentic/self/self_model/helpers/capability.rb +93 -0
  128. data/lib/legion/extensions/agentic/self/self_model/helpers/constants.rb +46 -0
  129. data/lib/legion/extensions/agentic/self/self_model/helpers/knowledge_domain.rb +82 -0
  130. data/lib/legion/extensions/agentic/self/self_model/helpers/self_model.rb +150 -0
  131. data/lib/legion/extensions/agentic/self/self_model/runners/self_model.rb +82 -0
  132. data/lib/legion/extensions/agentic/self/self_model/version.rb +13 -0
  133. data/lib/legion/extensions/agentic/self/self_model.rb +21 -0
  134. data/lib/legion/extensions/agentic/self/self_talk/actors/volume_decay.rb +45 -0
  135. data/lib/legion/extensions/agentic/self/self_talk/client.rb +30 -0
  136. data/lib/legion/extensions/agentic/self/self_talk/helpers/constants.rb +63 -0
  137. data/lib/legion/extensions/agentic/self/self_talk/helpers/dialogue.rb +114 -0
  138. data/lib/legion/extensions/agentic/self/self_talk/helpers/dialogue_turn.rb +43 -0
  139. data/lib/legion/extensions/agentic/self/self_talk/helpers/inner_voice.rb +77 -0
  140. data/lib/legion/extensions/agentic/self/self_talk/helpers/llm_enhancer.rb +135 -0
  141. data/lib/legion/extensions/agentic/self/self_talk/helpers/self_talk_engine.rb +160 -0
  142. data/lib/legion/extensions/agentic/self/self_talk/runners/self_talk.rb +172 -0
  143. data/lib/legion/extensions/agentic/self/self_talk/version.rb +13 -0
  144. data/lib/legion/extensions/agentic/self/self_talk.rb +22 -0
  145. data/lib/legion/extensions/agentic/self/version.rb +11 -0
  146. data/lib/legion/extensions/agentic/self.rb +33 -0
  147. data/spec/legion/extensions/agentic/self/agency/client_spec.rb +67 -0
  148. data/spec/legion/extensions/agentic/self/agency/helpers/constants_spec.rb +73 -0
  149. data/spec/legion/extensions/agentic/self/agency/helpers/efficacy_model_spec.rb +190 -0
  150. data/spec/legion/extensions/agentic/self/agency/helpers/outcome_event_spec.rb +85 -0
  151. data/spec/legion/extensions/agentic/self/agency/runners/agency_spec.rb +132 -0
  152. data/spec/legion/extensions/agentic/self/anchor/client_spec.rb +30 -0
  153. data/spec/legion/extensions/agentic/self/anchor/helpers/anchor_engine_spec.rb +109 -0
  154. data/spec/legion/extensions/agentic/self/anchor/helpers/anchor_spec.rb +124 -0
  155. data/spec/legion/extensions/agentic/self/anchor/helpers/chain_spec.rb +106 -0
  156. data/spec/legion/extensions/agentic/self/anchor/helpers/constants_spec.rb +53 -0
  157. data/spec/legion/extensions/agentic/self/anchor/runners/cognitive_anchor_spec.rb +70 -0
  158. data/spec/legion/extensions/agentic/self/anosognosia/anosognosia_spec.rb +15 -0
  159. data/spec/legion/extensions/agentic/self/anosognosia/client_spec.rb +50 -0
  160. data/spec/legion/extensions/agentic/self/anosognosia/helpers/anosognosia_engine_spec.rb +266 -0
  161. data/spec/legion/extensions/agentic/self/anosognosia/helpers/cognitive_deficit_spec.rb +150 -0
  162. data/spec/legion/extensions/agentic/self/anosognosia/helpers/constants_spec.rb +58 -0
  163. data/spec/legion/extensions/agentic/self/anosognosia/runners/anosognosia_spec.rb +225 -0
  164. data/spec/legion/extensions/agentic/self/architecture/client_spec.rb +51 -0
  165. data/spec/legion/extensions/agentic/self/architecture/helpers/architecture_engine_spec.rb +321 -0
  166. data/spec/legion/extensions/agentic/self/architecture/helpers/connection_spec.rb +118 -0
  167. data/spec/legion/extensions/agentic/self/architecture/helpers/subsystem_spec.rb +189 -0
  168. data/spec/legion/extensions/agentic/self/architecture/runners/cognitive_architecture_spec.rb +181 -0
  169. data/spec/legion/extensions/agentic/self/default_mode_network/client_spec.rb +69 -0
  170. data/spec/legion/extensions/agentic/self/default_mode_network/helpers/constants_spec.rb +76 -0
  171. data/spec/legion/extensions/agentic/self/default_mode_network/helpers/dmn_engine_spec.rb +321 -0
  172. data/spec/legion/extensions/agentic/self/default_mode_network/helpers/wandering_thought_spec.rb +145 -0
  173. data/spec/legion/extensions/agentic/self/default_mode_network/runners/default_mode_network_spec.rb +269 -0
  174. data/spec/legion/extensions/agentic/self/fingerprint/client_spec.rb +54 -0
  175. data/spec/legion/extensions/agentic/self/fingerprint/helpers/cognitive_trait_spec.rb +180 -0
  176. data/spec/legion/extensions/agentic/self/fingerprint/helpers/constants_spec.rb +108 -0
  177. data/spec/legion/extensions/agentic/self/fingerprint/helpers/fingerprint_engine_spec.rb +318 -0
  178. data/spec/legion/extensions/agentic/self/fingerprint/runners/cognitive_fingerprint_spec.rb +232 -0
  179. data/spec/legion/extensions/agentic/self/identity/actors/orphan_check_spec.rb +104 -0
  180. data/spec/legion/extensions/agentic/self/identity/client_spec.rb +32 -0
  181. data/spec/legion/extensions/agentic/self/identity/helpers/dimensions_spec.rb +51 -0
  182. data/spec/legion/extensions/agentic/self/identity/helpers/fingerprint_spec.rb +66 -0
  183. data/spec/legion/extensions/agentic/self/identity/helpers/graph_client_spec.rb +19 -0
  184. data/spec/legion/extensions/agentic/self/identity/helpers/graph_token_spec.rb +31 -0
  185. data/spec/legion/extensions/agentic/self/identity/helpers/token_cache_spec.rb +50 -0
  186. data/spec/legion/extensions/agentic/self/identity/local_persistence_spec.rb +329 -0
  187. data/spec/legion/extensions/agentic/self/identity/runners/entra_spec.rb +655 -0
  188. data/spec/legion/extensions/agentic/self/identity/runners/identity_spec.rb +61 -0
  189. data/spec/legion/extensions/agentic/self/metacognition/client_spec.rb +20 -0
  190. data/spec/legion/extensions/agentic/self/metacognition/helpers/constants_spec.rb +31 -0
  191. data/spec/legion/extensions/agentic/self/metacognition/helpers/narrator_bridge_spec.rb +102 -0
  192. data/spec/legion/extensions/agentic/self/metacognition/helpers/registry_store_spec.rb +227 -0
  193. data/spec/legion/extensions/agentic/self/metacognition/helpers/self_model_spec.rb +117 -0
  194. data/spec/legion/extensions/agentic/self/metacognition/helpers/snapshot_store_spec.rb +128 -0
  195. data/spec/legion/extensions/agentic/self/metacognition/runners/metacognition_spec.rb +110 -0
  196. data/spec/legion/extensions/agentic/self/metacognition/runners/registry_spec.rb +281 -0
  197. data/spec/legion/extensions/agentic/self/metacognitive_monitoring/client_spec.rb +59 -0
  198. data/spec/legion/extensions/agentic/self/metacognitive_monitoring/helpers/calibration_tracker_spec.rb +143 -0
  199. data/spec/legion/extensions/agentic/self/metacognitive_monitoring/helpers/constants_spec.rb +91 -0
  200. data/spec/legion/extensions/agentic/self/metacognitive_monitoring/helpers/monitoring_engine_spec.rb +198 -0
  201. data/spec/legion/extensions/agentic/self/metacognitive_monitoring/helpers/monitoring_judgment_spec.rb +172 -0
  202. data/spec/legion/extensions/agentic/self/metacognitive_monitoring/runners/metacognitive_monitoring_spec.rb +244 -0
  203. data/spec/legion/extensions/agentic/self/narrative_arc/client_spec.rb +22 -0
  204. data/spec/legion/extensions/agentic/self/narrative_arc/helpers/arc_engine_spec.rb +183 -0
  205. data/spec/legion/extensions/agentic/self/narrative_arc/helpers/arc_spec.rb +177 -0
  206. data/spec/legion/extensions/agentic/self/narrative_arc/helpers/beat_event_spec.rb +96 -0
  207. data/spec/legion/extensions/agentic/self/narrative_arc/helpers/constants_spec.rb +75 -0
  208. data/spec/legion/extensions/agentic/self/narrative_arc/runners/narrative_spec.rb +142 -0
  209. data/spec/legion/extensions/agentic/self/narrative_identity/client_spec.rb +69 -0
  210. data/spec/legion/extensions/agentic/self/narrative_identity/helpers/chapter_spec.rb +85 -0
  211. data/spec/legion/extensions/agentic/self/narrative_identity/helpers/constants_spec.rb +83 -0
  212. data/spec/legion/extensions/agentic/self/narrative_identity/helpers/episode_spec.rb +180 -0
  213. data/spec/legion/extensions/agentic/self/narrative_identity/helpers/narrative_engine_spec.rb +307 -0
  214. data/spec/legion/extensions/agentic/self/narrative_identity/helpers/theme_spec.rb +107 -0
  215. data/spec/legion/extensions/agentic/self/narrative_identity/runners/narrative_identity_spec.rb +240 -0
  216. data/spec/legion/extensions/agentic/self/narrative_self/client_spec.rb +67 -0
  217. data/spec/legion/extensions/agentic/self/narrative_self/helpers/autobiography_spec.rb +155 -0
  218. data/spec/legion/extensions/agentic/self/narrative_self/helpers/constants_spec.rb +28 -0
  219. data/spec/legion/extensions/agentic/self/narrative_self/helpers/episode_spec.rb +144 -0
  220. data/spec/legion/extensions/agentic/self/narrative_self/helpers/narrative_thread_spec.rb +87 -0
  221. data/spec/legion/extensions/agentic/self/narrative_self/runners/narrative_self_spec.rb +118 -0
  222. data/spec/legion/extensions/agentic/self/personality/client_spec.rb +20 -0
  223. data/spec/legion/extensions/agentic/self/personality/helpers/constants_spec.rb +41 -0
  224. data/spec/legion/extensions/agentic/self/personality/helpers/personality_store_spec.rb +66 -0
  225. data/spec/legion/extensions/agentic/self/personality/helpers/trait_model_spec.rb +148 -0
  226. data/spec/legion/extensions/agentic/self/personality/runners/personality_spec.rb +67 -0
  227. data/spec/legion/extensions/agentic/self/reflection/client_spec.rb +24 -0
  228. data/spec/legion/extensions/agentic/self/reflection/helpers/llm_enhancer_spec.rb +191 -0
  229. data/spec/legion/extensions/agentic/self/reflection/helpers/monitors_spec.rb +120 -0
  230. data/spec/legion/extensions/agentic/self/reflection/helpers/reflection_spec.rb +49 -0
  231. data/spec/legion/extensions/agentic/self/reflection/helpers/reflection_store_spec.rb +93 -0
  232. data/spec/legion/extensions/agentic/self/reflection/runners/reflection_spec.rb +204 -0
  233. data/spec/legion/extensions/agentic/self/self_model/client_spec.rb +55 -0
  234. data/spec/legion/extensions/agentic/self/self_model/helpers/capability_spec.rb +160 -0
  235. data/spec/legion/extensions/agentic/self/self_model/helpers/knowledge_domain_spec.rb +128 -0
  236. data/spec/legion/extensions/agentic/self/self_model/helpers/self_model_spec.rb +238 -0
  237. data/spec/legion/extensions/agentic/self/self_model/runners/self_model_spec.rb +143 -0
  238. data/spec/legion/extensions/agentic/self/self_talk/actors/volume_decay_spec.rb +46 -0
  239. data/spec/legion/extensions/agentic/self/self_talk/client_spec.rb +26 -0
  240. data/spec/legion/extensions/agentic/self/self_talk/helpers/constants_spec.rb +110 -0
  241. data/spec/legion/extensions/agentic/self/self_talk/helpers/dialogue_spec.rb +191 -0
  242. data/spec/legion/extensions/agentic/self/self_talk/helpers/dialogue_turn_spec.rb +78 -0
  243. data/spec/legion/extensions/agentic/self/self_talk/helpers/inner_voice_spec.rb +172 -0
  244. data/spec/legion/extensions/agentic/self/self_talk/helpers/llm_enhancer_spec.rb +206 -0
  245. data/spec/legion/extensions/agentic/self/self_talk/helpers/self_talk_engine_spec.rb +239 -0
  246. data/spec/legion/extensions/agentic/self/self_talk/runners/self_talk_llm_spec.rb +169 -0
  247. data/spec/legion/extensions/agentic/self/self_talk/runners/self_talk_spec.rb +196 -0
  248. data/spec/spec_helper.rb +46 -0
  249. metadata +347 -0
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Self::NarrativeSelf::Client do
4
+ let(:client) { described_class.new }
5
+
6
+ it 'can be instantiated' do
7
+ expect(client).to be_a(described_class)
8
+ end
9
+
10
+ it 'includes all runner methods' do
11
+ expect(client).to respond_to(:record_episode)
12
+ expect(client).to respond_to(:recent_episodes)
13
+ expect(client).to respond_to(:significant_episodes)
14
+ expect(client).to respond_to(:create_thread)
15
+ expect(client).to respond_to(:self_summary)
16
+ expect(client).to respond_to(:update_narrative_self)
17
+ expect(client).to respond_to(:narrative_self_stats)
18
+ end
19
+
20
+ it 'exposes the autobiography' do
21
+ expect(client.autobiography).to be_a(Legion::Extensions::Agentic::Self::NarrativeSelf::Helpers::Autobiography)
22
+ end
23
+
24
+ describe 'full lifecycle' do
25
+ it 'builds a narrative from episodes and threads' do
26
+ # Create narrative threads
27
+ client.create_thread(theme: :learning, domain: :cognition)
28
+ client.create_thread(theme: :mastery, domain: :technical)
29
+
30
+ # Record experiences
31
+ client.record_episode(
32
+ description: 'built first extension',
33
+ episode_type: :achievement,
34
+ domain: :technical,
35
+ emotional_valence: 0.7,
36
+ tags: %i[building mastery]
37
+ )
38
+ client.record_episode(
39
+ description: 'learned about EMA',
40
+ episode_type: :insight,
41
+ domain: :cognition,
42
+ tags: %i[learning math]
43
+ )
44
+ client.record_episode(
45
+ description: 'test failure taught me about edge cases',
46
+ episode_type: :discovery,
47
+ domain: :technical,
48
+ emotional_valence: -0.3,
49
+ tags: %i[learning debugging]
50
+ )
51
+
52
+ # Check timeline
53
+ tl = client.timeline
54
+ expect(tl[:count]).to eq(3)
55
+
56
+ # Check self-summary
57
+ summary = client.self_summary[:summary]
58
+ expect(summary[:total_episodes]).to eq(3)
59
+ expect(summary[:self_concept]).not_to be_empty
60
+
61
+ # Tick
62
+ client.update_narrative_self
63
+ stats = client.narrative_self_stats[:stats]
64
+ expect(stats[:episode_count]).to eq(3)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Self::NarrativeSelf::Helpers::Autobiography do
4
+ let(:auto) { described_class.new }
5
+
6
+ describe '#record_episode' do
7
+ it 'creates and stores an episode' do
8
+ episode = auto.record_episode(description: 'first task completed', episode_type: :achievement)
9
+ expect(episode).to be_a(Legion::Extensions::Agentic::Self::NarrativeSelf::Helpers::Episode)
10
+ expect(auto.episodes.size).to eq(1)
11
+ end
12
+
13
+ it 'updates self-concept with episode type' do
14
+ auto.record_episode(description: 'test', episode_type: :achievement)
15
+ expect(auto.self_concept).to have_key(:achievement)
16
+ end
17
+
18
+ it 'trims episodes when exceeding MAX_EPISODES' do
19
+ max = Legion::Extensions::Agentic::Self::NarrativeSelf::Helpers::Constants::MAX_EPISODES
20
+ (max + 5).times { |i| auto.record_episode(description: "episode #{i}") }
21
+ expect(auto.episodes.size).to eq(max)
22
+ end
23
+ end
24
+
25
+ describe '#find_episode' do
26
+ it 'finds by id' do
27
+ ep = auto.record_episode(description: 'test')
28
+ expect(auto.find_episode(ep.id)).to eq(ep)
29
+ end
30
+
31
+ it 'returns nil for unknown id' do
32
+ expect(auto.find_episode('nonexistent')).to be_nil
33
+ end
34
+ end
35
+
36
+ describe '#recent_episodes' do
37
+ it 'returns the last N episodes' do
38
+ 5.times { |i| auto.record_episode(description: "ep #{i}") }
39
+ recent = auto.recent_episodes(3)
40
+ expect(recent.size).to eq(3)
41
+ expect(recent.last.description).to eq('ep 4')
42
+ end
43
+ end
44
+
45
+ describe '#significant_episodes' do
46
+ it 'returns episodes above threshold' do
47
+ auto.record_episode(description: 'big deal', significance: 0.9)
48
+ auto.record_episode(description: 'small thing', significance: 0.2)
49
+ significant = auto.significant_episodes(min_significance: 0.5)
50
+ expect(significant.size).to eq(1)
51
+ expect(significant.first.description).to eq('big deal')
52
+ end
53
+ end
54
+
55
+ describe '#episodes_by_type' do
56
+ it 'filters by episode type' do
57
+ auto.record_episode(description: 'win', episode_type: :achievement)
58
+ auto.record_episode(description: 'loss', episode_type: :failure)
59
+ auto.record_episode(description: 'another win', episode_type: :achievement)
60
+ expect(auto.episodes_by_type(:achievement).size).to eq(2)
61
+ end
62
+ end
63
+
64
+ describe '#episodes_in_domain' do
65
+ it 'filters by domain' do
66
+ auto.record_episode(description: 'code', domain: :technical)
67
+ auto.record_episode(description: 'meeting', domain: :social)
68
+ expect(auto.episodes_in_domain(:technical).size).to eq(1)
69
+ end
70
+ end
71
+
72
+ describe '#create_thread' do
73
+ it 'creates a narrative thread' do
74
+ thread = auto.create_thread(theme: :growth, domain: :personal)
75
+ expect(thread.theme).to eq(:growth)
76
+ expect(auto.threads.size).to eq(1)
77
+ end
78
+ end
79
+
80
+ describe '#strongest_threads' do
81
+ it 'returns threads sorted by strength' do
82
+ auto.create_thread(theme: :growth)
83
+ t2 = auto.create_thread(theme: :mastery)
84
+ t2.reinforce
85
+ strongest = auto.strongest_threads(2)
86
+ expect(strongest.first).to eq(t2)
87
+ end
88
+ end
89
+
90
+ describe '#timeline' do
91
+ it 'returns recent episodes as hashes' do
92
+ auto.record_episode(description: 'first')
93
+ auto.record_episode(description: 'second')
94
+ tl = auto.timeline(window: 10)
95
+ expect(tl.size).to eq(2)
96
+ expect(tl.first).to be_a(Hash)
97
+ end
98
+ end
99
+
100
+ describe '#self_summary' do
101
+ it 'returns a comprehensive summary' do
102
+ auto.record_episode(description: 'a', episode_type: :achievement, domain: :tech)
103
+ auto.record_episode(description: 'b', episode_type: :insight, domain: :tech)
104
+ summary = auto.self_summary
105
+ expect(summary).to have_key(:total_episodes)
106
+ expect(summary).to have_key(:dominant_types)
107
+ expect(summary).to have_key(:dominant_domains)
108
+ expect(summary).to have_key(:self_concept)
109
+ expect(summary).to have_key(:narrative_richness)
110
+ expect(summary[:total_episodes]).to eq(2)
111
+ end
112
+ end
113
+
114
+ describe '#decay_all' do
115
+ it 'decays episodes and removes faded ones' do
116
+ ep = auto.record_episode(description: 'old', significance: 0.06)
117
+ 200.times { ep.decay }
118
+ auto.decay_all
119
+ expect(auto.episodes).not_to include(ep)
120
+ end
121
+
122
+ it 'decays threads and removes weak ones' do
123
+ t = auto.create_thread(theme: :test)
124
+ 100.times { t.decay }
125
+ auto.decay_all
126
+ expect(auto.threads).not_to include(t)
127
+ end
128
+ end
129
+
130
+ describe '#to_h' do
131
+ it 'returns summary statistics' do
132
+ auto.record_episode(description: 'test', episode_type: :insight, domain: :tech)
133
+ h = auto.to_h
134
+ expect(h).to have_key(:episode_count)
135
+ expect(h).to have_key(:thread_count)
136
+ expect(h).to have_key(:self_concept)
137
+ expect(h).to have_key(:by_type)
138
+ expect(h).to have_key(:by_domain)
139
+ expect(h).to have_key(:avg_significance)
140
+ end
141
+ end
142
+
143
+ describe 'auto-linking' do
144
+ it 'links episodes to matching threads' do
145
+ thread = auto.create_thread(theme: :learning, domain: :cognition)
146
+ episode = auto.record_episode(
147
+ description: 'learned something new',
148
+ domain: :cognition,
149
+ tags: %i[learning growth]
150
+ )
151
+ expect(thread.episode_ids).to include(episode.id)
152
+ expect(episode.thread_ids).to include(thread.id)
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Self::NarrativeSelf::Helpers::Constants do
4
+ it 'defines MAX_EPISODES' do
5
+ expect(described_class::MAX_EPISODES).to eq(500)
6
+ end
7
+
8
+ it 'defines MAX_THREADS' do
9
+ expect(described_class::MAX_THREADS).to eq(50)
10
+ end
11
+
12
+ it 'defines EPISODE_TYPES' do
13
+ expect(described_class::EPISODE_TYPES).to include(:achievement, :failure, :discovery, :insight)
14
+ end
15
+
16
+ it 'defines SIGNIFICANCE_LABELS covering 0.0..1.0' do
17
+ labels = described_class::SIGNIFICANCE_LABELS
18
+ [0.0, 0.1, 0.3, 0.5, 0.7, 0.9, 1.0].each do |val|
19
+ matched = labels.any? { |range, _| range.cover?(val) }
20
+ expect(matched).to be(true), "Expected #{val} to match a label range"
21
+ end
22
+ end
23
+
24
+ it 'has all expected SIGNIFICANCE_LABELS values' do
25
+ values = described_class::SIGNIFICANCE_LABELS.values
26
+ expect(values).to contain_exactly(:pivotal, :important, :routine, :minor)
27
+ end
28
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Self::NarrativeSelf::Helpers::Episode do
4
+ let(:episode) do
5
+ described_class.new(
6
+ description: 'learned to handle context switches',
7
+ episode_type: :insight,
8
+ domain: :cognition,
9
+ emotional_valence: 0.6,
10
+ tags: %i[learning context performance]
11
+ )
12
+ end
13
+
14
+ describe '#initialize' do
15
+ it 'sets description and type' do
16
+ expect(episode.description).to eq('learned to handle context switches')
17
+ expect(episode.episode_type).to eq(:insight)
18
+ end
19
+
20
+ it 'sets domain' do
21
+ expect(episode.domain).to eq(:cognition)
22
+ end
23
+
24
+ it 'starts with default significance' do
25
+ plain = described_class.new(description: 'test')
26
+ expect(plain.significance).to eq(Legion::Extensions::Agentic::Self::NarrativeSelf::Helpers::Constants::DEFAULT_SIGNIFICANCE)
27
+ end
28
+
29
+ it 'clamps emotional valence to [-1, 1]' do
30
+ extreme = described_class.new(description: 'extreme', emotional_valence: 5.0)
31
+ expect(extreme.emotional_valence).to eq(1.0)
32
+ end
33
+
34
+ it 'has a UUID id' do
35
+ expect(episode.id).to match(/\A[0-9a-f-]{36}\z/)
36
+ end
37
+
38
+ it 'stores tags' do
39
+ expect(episode.tags).to eq(%i[learning context performance])
40
+ end
41
+
42
+ it 'starts with empty thread_ids' do
43
+ expect(episode.thread_ids).to be_empty
44
+ end
45
+ end
46
+
47
+ describe '#boost' do
48
+ it 'increases significance' do
49
+ initial = episode.significance
50
+ episode.boost(0.1)
51
+ expect(episode.significance).to be > initial
52
+ end
53
+
54
+ it 'includes emotional factor' do
55
+ low_emotion = described_class.new(description: 'calm', emotional_valence: 0.0)
56
+ high_emotion = described_class.new(description: 'excited', emotional_valence: 0.9)
57
+ low_emotion.boost(0.1)
58
+ high_emotion.boost(0.1)
59
+ expect(high_emotion.significance).to be > low_emotion.significance
60
+ end
61
+
62
+ it 'caps at 1.0' do
63
+ 10.times { episode.boost(0.5) }
64
+ expect(episode.significance).to be <= 1.0
65
+ end
66
+ end
67
+
68
+ describe '#decay' do
69
+ it 'decreases significance' do
70
+ initial = episode.significance
71
+ episode.decay
72
+ expect(episode.significance).to be < initial
73
+ end
74
+
75
+ it 'does not go below zero' do
76
+ 200.times { episode.decay }
77
+ expect(episode.significance).to be >= 0.0
78
+ end
79
+ end
80
+
81
+ describe '#faded?' do
82
+ it 'is not faded when fresh' do
83
+ expect(episode.faded?).to be false
84
+ end
85
+
86
+ it 'becomes faded after heavy decay' do
87
+ 200.times { episode.decay }
88
+ expect(episode.faded?).to be true
89
+ end
90
+ end
91
+
92
+ describe '#label' do
93
+ it 'returns a symbol' do
94
+ expect(episode.label).to be_a(Symbol)
95
+ end
96
+
97
+ it 'returns :minor for low significance' do
98
+ 200.times { episode.decay }
99
+ expect(episode.label).to eq(:minor)
100
+ end
101
+ end
102
+
103
+ describe '#link_thread' do
104
+ it 'adds a thread id' do
105
+ episode.link_thread('thread-1')
106
+ expect(episode.thread_ids).to include('thread-1')
107
+ end
108
+
109
+ it 'does not duplicate' do
110
+ episode.link_thread('thread-1')
111
+ episode.link_thread('thread-1')
112
+ expect(episode.thread_ids.count('thread-1')).to eq(1)
113
+ end
114
+ end
115
+
116
+ describe '#matches_tags?' do
117
+ it 'returns match ratio for overlapping tags' do
118
+ score = episode.matches_tags?(%i[learning performance])
119
+ expect(score).to be_within(0.01).of(2.0 / 2.0)
120
+ end
121
+
122
+ it 'returns 0.0 for no overlap' do
123
+ expect(episode.matches_tags?(%i[sleep eat])).to eq(0.0)
124
+ end
125
+
126
+ it 'returns 0.0 for empty input' do
127
+ expect(episode.matches_tags?([])).to be_falsey
128
+ end
129
+ end
130
+
131
+ describe '#to_h' do
132
+ it 'contains all expected keys' do
133
+ h = episode.to_h
134
+ expect(h).to have_key(:id)
135
+ expect(h).to have_key(:description)
136
+ expect(h).to have_key(:episode_type)
137
+ expect(h).to have_key(:significance)
138
+ expect(h).to have_key(:emotional_valence)
139
+ expect(h).to have_key(:label)
140
+ expect(h).to have_key(:tags)
141
+ expect(h).to have_key(:thread_ids)
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Self::NarrativeSelf::Helpers::NarrativeThread do
4
+ let(:thread) { described_class.new(theme: :growth, domain: :cognition) }
5
+
6
+ describe '#initialize' do
7
+ it 'sets theme and domain' do
8
+ expect(thread.theme).to eq(:growth)
9
+ expect(thread.domain).to eq(:cognition)
10
+ end
11
+
12
+ it 'starts with strength 0.5' do
13
+ expect(thread.strength).to eq(0.5)
14
+ end
15
+
16
+ it 'starts with empty episode_ids' do
17
+ expect(thread.episode_ids).to be_empty
18
+ end
19
+
20
+ it 'has a UUID id' do
21
+ expect(thread.id).to match(/\A[0-9a-f-]{36}\z/)
22
+ end
23
+ end
24
+
25
+ describe '#add_episode' do
26
+ it 'adds an episode id' do
27
+ thread.add_episode('ep-1')
28
+ expect(thread.episode_ids).to include('ep-1')
29
+ end
30
+
31
+ it 'does not duplicate' do
32
+ thread.add_episode('ep-1')
33
+ thread.add_episode('ep-1')
34
+ expect(thread.episode_ids.count('ep-1')).to eq(1)
35
+ end
36
+
37
+ it 'caps at MAX_CHAPTER_SIZE' do
38
+ max = Legion::Extensions::Agentic::Self::NarrativeSelf::Helpers::Constants::MAX_CHAPTER_SIZE
39
+ (max + 5).times { |i| thread.add_episode("ep-#{i}") }
40
+ expect(thread.episode_ids.size).to eq(max)
41
+ end
42
+
43
+ it 'reinforces the thread' do
44
+ initial = thread.strength
45
+ thread.add_episode('ep-1')
46
+ expect(thread.strength).to be > initial
47
+ end
48
+ end
49
+
50
+ describe '#decay' do
51
+ it 'reduces strength' do
52
+ initial = thread.strength
53
+ thread.decay
54
+ expect(thread.strength).to be < initial
55
+ end
56
+ end
57
+
58
+ describe '#weak?' do
59
+ it 'is not weak when fresh' do
60
+ expect(thread.weak?).to be false
61
+ end
62
+
63
+ it 'becomes weak after heavy decay' do
64
+ 100.times { thread.decay }
65
+ expect(thread.weak?).to be true
66
+ end
67
+ end
68
+
69
+ describe '#size' do
70
+ it 'returns the episode count' do
71
+ thread.add_episode('ep-1')
72
+ thread.add_episode('ep-2')
73
+ expect(thread.size).to eq(2)
74
+ end
75
+ end
76
+
77
+ describe '#to_h' do
78
+ it 'contains expected keys' do
79
+ h = thread.to_h
80
+ expect(h).to have_key(:id)
81
+ expect(h).to have_key(:theme)
82
+ expect(h).to have_key(:domain)
83
+ expect(h).to have_key(:strength)
84
+ expect(h).to have_key(:episodes)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Self::NarrativeSelf::Runners::NarrativeSelf do
4
+ let(:client) { Legion::Extensions::Agentic::Self::NarrativeSelf::Client.new }
5
+
6
+ describe '#record_episode' do
7
+ it 'returns success with episode data' do
8
+ result = client.record_episode(description: 'completed first build', episode_type: :achievement)
9
+ expect(result[:success]).to be true
10
+ expect(result[:episode][:description]).to eq('completed first build')
11
+ expect(result[:episode][:episode_type]).to eq(:achievement)
12
+ end
13
+ end
14
+
15
+ describe '#recent_episodes' do
16
+ before do
17
+ 3.times { |i| client.record_episode(description: "event #{i}") }
18
+ end
19
+
20
+ it 'returns recent episodes' do
21
+ result = client.recent_episodes(count: 2)
22
+ expect(result[:success]).to be true
23
+ expect(result[:count]).to eq(2)
24
+ end
25
+ end
26
+
27
+ describe '#significant_episodes' do
28
+ before do
29
+ client.record_episode(description: 'major', significance: 0.9)
30
+ client.record_episode(description: 'minor', significance: 0.1)
31
+ end
32
+
33
+ it 'filters by significance' do
34
+ result = client.significant_episodes(min_significance: 0.5)
35
+ expect(result[:count]).to eq(1)
36
+ expect(result[:episodes].first[:description]).to eq('major')
37
+ end
38
+ end
39
+
40
+ describe '#episodes_by_type' do
41
+ before do
42
+ client.record_episode(description: 'win', episode_type: :achievement)
43
+ client.record_episode(description: 'learn', episode_type: :insight)
44
+ end
45
+
46
+ it 'filters by type' do
47
+ result = client.episodes_by_type(episode_type: :achievement)
48
+ expect(result[:count]).to eq(1)
49
+ end
50
+ end
51
+
52
+ describe '#create_thread' do
53
+ it 'returns success with thread data' do
54
+ result = client.create_thread(theme: :growth, domain: :personal)
55
+ expect(result[:success]).to be true
56
+ expect(result[:thread][:theme]).to eq(:growth)
57
+ end
58
+ end
59
+
60
+ describe '#strongest_threads' do
61
+ before do
62
+ client.create_thread(theme: :growth)
63
+ client.create_thread(theme: :mastery)
64
+ end
65
+
66
+ it 'returns threads sorted by strength' do
67
+ result = client.strongest_threads(count: 2)
68
+ expect(result[:success]).to be true
69
+ expect(result[:count]).to eq(2)
70
+ end
71
+ end
72
+
73
+ describe '#timeline' do
74
+ before do
75
+ 3.times { |i| client.record_episode(description: "event #{i}") }
76
+ end
77
+
78
+ it 'returns timeline entries' do
79
+ result = client.timeline(window: 10)
80
+ expect(result[:success]).to be true
81
+ expect(result[:count]).to eq(3)
82
+ expect(result[:timeline].first).to be_a(Hash)
83
+ end
84
+ end
85
+
86
+ describe '#self_summary' do
87
+ before do
88
+ client.record_episode(description: 'a', episode_type: :achievement, domain: :tech)
89
+ client.record_episode(description: 'b', episode_type: :insight, domain: :personal)
90
+ end
91
+
92
+ it 'returns comprehensive summary' do
93
+ result = client.self_summary
94
+ expect(result[:success]).to be true
95
+ expect(result[:summary][:total_episodes]).to eq(2)
96
+ expect(result[:summary]).to have_key(:narrative_richness)
97
+ expect(result[:summary]).to have_key(:self_concept)
98
+ end
99
+ end
100
+
101
+ describe '#update_narrative_self' do
102
+ it 'decays and reports counts' do
103
+ client.record_episode(description: 'test')
104
+ result = client.update_narrative_self
105
+ expect(result[:success]).to be true
106
+ expect(result[:episode_count]).to eq(1)
107
+ end
108
+ end
109
+
110
+ describe '#narrative_self_stats' do
111
+ it 'returns stats' do
112
+ result = client.narrative_self_stats
113
+ expect(result[:success]).to be true
114
+ expect(result[:stats]).to have_key(:episode_count)
115
+ expect(result[:stats]).to have_key(:thread_count)
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Self::Personality::Client do
4
+ it 'creates default personality store' do
5
+ client = described_class.new
6
+ expect(client.personality_store).to be_a(Legion::Extensions::Agentic::Self::Personality::Helpers::PersonalityStore)
7
+ end
8
+
9
+ it 'accepts injected personality store' do
10
+ store = Legion::Extensions::Agentic::Self::Personality::Helpers::PersonalityStore.new
11
+ client = described_class.new(personality_store: store)
12
+ expect(client.personality_store).to equal(store)
13
+ end
14
+
15
+ it 'includes Personality runner methods' do
16
+ client = described_class.new
17
+ expect(client).to respond_to(:update_personality, :personality_profile,
18
+ :describe_personality, :personality_stats)
19
+ end
20
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Self::Personality::Helpers::Constants do
4
+ it 'defines 5 traits (OCEAN)' do
5
+ expect(described_class::TRAITS.size).to eq(5)
6
+ end
7
+
8
+ it 'includes all Big Five traits' do
9
+ expect(described_class::TRAITS).to contain_exactly(
10
+ :openness, :conscientiousness, :extraversion, :agreeableness, :neuroticism
11
+ )
12
+ end
13
+
14
+ it 'has default trait value of 0.5' do
15
+ expect(described_class::DEFAULT_TRAIT_VALUE).to eq(0.5)
16
+ end
17
+
18
+ it 'has a very low trait alpha for stability' do
19
+ expect(described_class::TRAIT_ALPHA).to be < 0.1
20
+ end
21
+
22
+ it 'defines descriptors for every trait' do
23
+ described_class::TRAITS.each do |trait|
24
+ expect(described_class::TRAIT_DESCRIPTORS).to have_key(trait)
25
+ expect(described_class::TRAIT_DESCRIPTORS[trait]).to include(:high, :mid, :low)
26
+ end
27
+ end
28
+
29
+ it 'maps signals to traits' do
30
+ expect(described_class::SIGNAL_MAP).not_to be_empty
31
+ described_class::SIGNAL_MAP.each_value do |trait, direction, weight|
32
+ expect(described_class::TRAITS).to include(trait)
33
+ expect(%i[positive negative]).to include(direction)
34
+ expect(weight).to be_between(0.0, 1.0)
35
+ end
36
+ end
37
+
38
+ it 'has HIGH_THRESHOLD > LOW_THRESHOLD' do
39
+ expect(described_class::HIGH_THRESHOLD).to be > described_class::LOW_THRESHOLD
40
+ end
41
+ end