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,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Self::Reflection::Helpers::ReflectionStore do
4
+ subject(:store) { described_class.new }
5
+
6
+ let(:reflection) do
7
+ Legion::Extensions::Agentic::Self::Reflection::Helpers::ReflectionFactory.new_reflection(
8
+ category: :prediction_calibration,
9
+ observation: 'Low accuracy',
10
+ severity: :significant,
11
+ recommendation: :increase_curiosity
12
+ )
13
+ end
14
+
15
+ describe '#store and #get' do
16
+ it 'stores and retrieves a reflection' do
17
+ store.store(reflection)
18
+ expect(store.get(reflection[:reflection_id])).to eq(reflection)
19
+ end
20
+
21
+ it 'increments total_generated' do
22
+ store.store(reflection)
23
+ expect(store.total_generated).to eq(1)
24
+ end
25
+ end
26
+
27
+ describe '#recent' do
28
+ it 'returns reflections in reverse chronological order' do
29
+ r1 = Legion::Extensions::Agentic::Self::Reflection::Helpers::ReflectionFactory.new_reflection(
30
+ category: :trust_drift, observation: 'first'
31
+ )
32
+ store.store(r1)
33
+ r2 = Legion::Extensions::Agentic::Self::Reflection::Helpers::ReflectionFactory.new_reflection(
34
+ category: :memory_health, observation: 'second'
35
+ )
36
+ store.store(r2)
37
+
38
+ recent = store.recent(limit: 2)
39
+ expect(recent.first[:observation]).to eq('second')
40
+ end
41
+ end
42
+
43
+ describe '#by_category' do
44
+ it 'filters by category' do
45
+ store.store(reflection)
46
+ other = Legion::Extensions::Agentic::Self::Reflection::Helpers::ReflectionFactory.new_reflection(
47
+ category: :trust_drift, observation: 'other'
48
+ )
49
+ store.store(other)
50
+
51
+ results = store.by_category(:prediction_calibration)
52
+ expect(results.size).to eq(1)
53
+ expect(results.first[:category]).to eq(:prediction_calibration)
54
+ end
55
+ end
56
+
57
+ describe '#mark_acted_on' do
58
+ it 'marks a reflection as acted on' do
59
+ store.store(reflection)
60
+ store.mark_acted_on(reflection[:reflection_id])
61
+ expect(store.get(reflection[:reflection_id])[:acted_on]).to be true
62
+ end
63
+
64
+ it 'returns nil for unknown id' do
65
+ expect(store.mark_acted_on('nonexistent')).to be_nil
66
+ end
67
+ end
68
+
69
+ describe '#cognitive_health' do
70
+ it 'returns 1.0 when all scores are at default' do
71
+ expect(store.cognitive_health).to eq(1.0)
72
+ end
73
+
74
+ it 'decreases when category scores drop' do
75
+ store.update_category_score(:prediction_calibration, 0.3)
76
+ expect(store.cognitive_health).to be < 1.0
77
+ end
78
+ end
79
+
80
+ describe '#severity_counts' do
81
+ it 'counts reflections by severity' do
82
+ store.store(reflection)
83
+ critical = Legion::Extensions::Agentic::Self::Reflection::Helpers::ReflectionFactory.new_reflection(
84
+ category: :cognitive_load, observation: 'overload', severity: :critical
85
+ )
86
+ store.store(critical)
87
+
88
+ counts = store.severity_counts
89
+ expect(counts[:significant]).to eq(1)
90
+ expect(counts[:critical]).to eq(1)
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,204 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Self::Reflection::Runners::Reflection do
4
+ let(:client) { Legion::Extensions::Agentic::Self::Reflection::Client.new }
5
+
6
+ describe '#reflect' do
7
+ it 'returns results with no tick data' do
8
+ result = client.reflect(tick_results: {})
9
+ expect(result[:reflections_generated]).to eq(0)
10
+ expect(result[:cognitive_health]).to eq(1.0)
11
+ end
12
+
13
+ it 'generates reflections from problematic tick results' do
14
+ result = client.reflect(tick_results: {
15
+ prediction_engine: { confidence: 0.2 },
16
+ emotional_evaluation: { stability: 0.1 },
17
+ memory_consolidation: { pruned: 90, total: 100 }
18
+ })
19
+ expect(result[:reflections_generated]).to be >= 2
20
+ expect(result[:cognitive_health]).to be < 1.0
21
+ end
22
+
23
+ it 'accumulates metric history over multiple calls' do
24
+ 5.times { client.reflect(tick_results: { prediction_engine: { confidence: 0.9 } }) }
25
+ 5.times { client.reflect(tick_results: { prediction_engine: { confidence: 0.4 } }) }
26
+
27
+ # Should detect trend
28
+ result = client.reflect(tick_results: { prediction_engine: { confidence: 0.3 } })
29
+ predictions = result[:new_reflections].select { |r| r[:category] == :prediction_calibration }
30
+ expect(predictions).not_to be_empty
31
+ end
32
+ end
33
+
34
+ describe '#cognitive_health' do
35
+ it 'returns full health with no data' do
36
+ result = client.cognitive_health
37
+ expect(result[:health]).to eq(1.0)
38
+ end
39
+
40
+ it 'degrades with bad tick results' do
41
+ client.reflect(tick_results: {
42
+ prediction_engine: { confidence: 0.2 },
43
+ emotional_evaluation: { stability: 0.1 }
44
+ })
45
+ result = client.cognitive_health
46
+ expect(result[:health]).to be < 1.0
47
+ expect(result[:category_scores][:prediction_calibration]).to eq(0.2)
48
+ end
49
+ end
50
+
51
+ describe '#recent_reflections' do
52
+ it 'returns recent reflections' do
53
+ client.reflect(tick_results: { prediction_engine: { confidence: 0.1 } })
54
+ result = client.recent_reflections(limit: 5)
55
+ expect(result[:reflections]).not_to be_empty
56
+ end
57
+ end
58
+
59
+ describe '#reflections_by_category' do
60
+ it 'filters by category' do
61
+ client.reflect(tick_results: {
62
+ prediction_engine: { confidence: 0.1 },
63
+ emotional_evaluation: { stability: 0.1 }
64
+ })
65
+ result = client.reflections_by_category(category: :emotional_stability)
66
+ result[:reflections].each do |r|
67
+ expect(r[:category]).to eq(:emotional_stability)
68
+ end
69
+ end
70
+ end
71
+
72
+ describe '#adapt' do
73
+ it 'marks a reflection as acted upon' do
74
+ client.reflect(tick_results: { prediction_engine: { confidence: 0.1 } })
75
+ reflections = client.recent_reflections[:reflections]
76
+ id = reflections.first[:reflection_id]
77
+
78
+ result = client.adapt(reflection_id: id)
79
+ expect(result[:adapted]).to be true
80
+ end
81
+
82
+ it 'returns error for unknown id' do
83
+ result = client.adapt(reflection_id: 'nonexistent')
84
+ expect(result[:error]).to eq(:not_found)
85
+ end
86
+ end
87
+
88
+ describe '#reflection_stats' do
89
+ it 'returns comprehensive stats' do
90
+ client.reflect(tick_results: { prediction_engine: { confidence: 0.1 } })
91
+ stats = client.reflection_stats
92
+ expect(stats[:total_generated]).to be >= 1
93
+ expect(stats[:cognitive_health]).to be_a(Float)
94
+ expect(stats[:severity_counts]).to be_a(Hash)
95
+ end
96
+ end
97
+
98
+ describe '#reflect with LLM enhancement' do
99
+ let(:fake_chat) { double }
100
+ let(:llm_observation) { 'My prediction confidence is critically low at 10%, strongly correlated with declining memory health.' }
101
+ let(:fake_response) do
102
+ double(content: <<~TEXT)
103
+ EMOTION: Emotional state appears stable this tick.
104
+ PREDICTION: #{llm_observation}
105
+ MEMORY: Memory health is nominal.
106
+ TRUST: Trust scores unchanged.
107
+ CURIOSITY: Curiosity levels normal.
108
+ IDENTITY: Identity entropy within bounds.
109
+ TEXT
110
+ end
111
+
112
+ before do
113
+ stub_const('Legion::LLM', double(respond_to?: true, started?: true))
114
+ allow(Legion::LLM).to receive(:chat).and_return(fake_chat)
115
+ allow(fake_chat).to receive(:with_instructions)
116
+ allow(fake_chat).to receive(:ask).and_return(fake_response)
117
+ end
118
+
119
+ it 'replaces mechanical observation text with LLM-generated text' do
120
+ result = client.reflect(tick_results: { prediction_engine: { confidence: 0.1 } })
121
+ prediction_reflection = result[:new_reflections].find { |r| r[:category] == :prediction_calibration }
122
+ expect(prediction_reflection).not_to be_nil
123
+ expect(prediction_reflection[:observation]).to include('critically low')
124
+ end
125
+
126
+ it 'does not change recommendation symbols when using LLM' do
127
+ result = client.reflect(tick_results: { prediction_engine: { confidence: 0.1 } })
128
+ prediction_reflection = result[:new_reflections].find { |r| r[:category] == :prediction_calibration }
129
+ expect(prediction_reflection).not_to be_nil
130
+ expect(prediction_reflection[:recommendation]).to eq(:increase_curiosity)
131
+ end
132
+
133
+ it 'falls back to mechanical observations when LLM returns nil' do
134
+ allow(fake_chat).to receive(:ask).and_return(double(content: nil))
135
+
136
+ result = client.reflect(tick_results: {
137
+ prediction_engine: { confidence: 0.2 },
138
+ emotional_evaluation: { stability: 0.1 }
139
+ })
140
+ expect(result[:reflections_generated]).to be >= 1
141
+ # Mechanical observations still present (LLM returned nil)
142
+ result[:new_reflections].each do |r|
143
+ expect(r[:observation]).to be_a(String)
144
+ expect(r[:observation]).not_to be_empty
145
+ end
146
+ end
147
+ end
148
+
149
+ describe '#reflect_on_dream' do
150
+ context 'without LLM' do
151
+ it 'uses mechanical fallback for empty dream results' do
152
+ result = client.reflect_on_dream(dream_results: {})
153
+ expect(result[:reflection]).to eq('Dream cycle completed.')
154
+ expect(result[:source]).to eq(:mechanical)
155
+ end
156
+
157
+ it 'builds mechanical reflection from dream phase data' do
158
+ result = client.reflect_on_dream(dream_results: {
159
+ memory_audit: { decayed: 3, unresolved_count: 2 },
160
+ contradiction_resolution: { detected: 1, resolved: 1 },
161
+ agenda_formation: { agenda_items: 3 }
162
+ })
163
+ expect(result[:reflection]).to include('Memory audit')
164
+ expect(result[:source]).to eq(:mechanical)
165
+ end
166
+ end
167
+
168
+ context 'with LLM available' do
169
+ let(:fake_chat) { double }
170
+ let(:fake_response) do
171
+ double(content: <<~TEXT)
172
+ REFLECTION: The dream cycle consolidated 3 traces and resolved a contradiction in the identity domain. I notice a strengthening of procedural memory pathways.
173
+ TEXT
174
+ end
175
+
176
+ before do
177
+ stub_const('Legion::LLM', double(respond_to?: true, started?: true))
178
+ allow(Legion::LLM).to receive(:chat).and_return(fake_chat)
179
+ allow(fake_chat).to receive(:with_instructions)
180
+ allow(fake_chat).to receive(:ask).and_return(fake_response)
181
+ end
182
+
183
+ it 'uses LLM-generated reflection when available' do
184
+ result = client.reflect_on_dream(dream_results: {
185
+ memory_audit: { decayed: 3, unresolved_count: 2 },
186
+ contradiction_resolution: { detected: 1, resolved: 1 }
187
+ })
188
+ expect(result[:reflection]).to include('consolidated 3 traces')
189
+ expect(result[:source]).to eq(:llm)
190
+ end
191
+
192
+ it 'falls back to mechanical when LLM returns nil' do
193
+ allow(fake_chat).to receive(:ask).and_return(double(content: nil))
194
+
195
+ result = client.reflect_on_dream(dream_results: {
196
+ memory_audit: { decayed: 1, unresolved_count: 0 }
197
+ })
198
+ expect(result[:source]).to eq(:mechanical)
199
+ expect(result[:reflection]).to be_a(String)
200
+ expect(result[:reflection]).not_to be_empty
201
+ end
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Self::SelfModel::Client do
4
+ subject(:client) { described_class.new }
5
+
6
+ it 'full lifecycle: add capability and knowledge, predict, record, introspect' do
7
+ cap_result = client.add_self_capability(name: 'problem solving', domain: :cognitive, competence: 0.6)
8
+ expect(cap_result[:success]).to be true
9
+
10
+ dom_result = client.add_self_knowledge(name: 'algorithms', depth: 0.5, breadth: 0.4)
11
+ expect(dom_result[:success]).to be true
12
+
13
+ pred = client.predict_own_success(capability_id: cap_result[:capability_id])
14
+ expect(pred[:success]).to be true
15
+ expect(pred[:predicted_probability]).to be_a(Float)
16
+
17
+ outcome = client.record_self_outcome(
18
+ capability_id: cap_result[:capability_id],
19
+ predicted: true,
20
+ actual: true
21
+ )
22
+ expect(outcome[:success]).to be true
23
+
24
+ introspection = client.self_introspection
25
+ expect(introspection[:success]).to be true
26
+ expect(introspection[:strengths]).to be_an(Array)
27
+
28
+ report = client.self_calibration_report
29
+ expect(report[:success]).to be true
30
+
31
+ stats = client.self_model_stats
32
+ expect(stats[:capability_count]).to eq(1)
33
+ expect(stats[:knowledge_domain_count]).to eq(1)
34
+ end
35
+
36
+ it 'accepts injected model' do
37
+ injected = Legion::Extensions::Agentic::Self::SelfModel::Helpers::SelfModel.new
38
+ c = described_class.new(model: injected)
39
+ c.add_self_capability(name: 'testing')
40
+ expect(injected.capabilities.size).to eq(1)
41
+ end
42
+
43
+ it 'tracks strengths and weaknesses separately' do
44
+ client.add_self_capability(name: 'expert task', competence: 0.9)
45
+ client.add_self_capability(name: 'beginner task', competence: 0.1)
46
+
47
+ strengths = client.self_strengths
48
+ weaknesses = client.self_weaknesses
49
+
50
+ expect(strengths[:count]).to eq(1)
51
+ expect(weaknesses[:count]).to eq(1)
52
+ expect(strengths[:strengths].first[:name]).to eq('expert task')
53
+ expect(weaknesses[:weaknesses].first[:name]).to eq('beginner task')
54
+ end
55
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Self::SelfModel::Helpers::Capability do
4
+ subject(:cap) do
5
+ described_class.new(id: :cap_one, name: 'write code', domain: :engineering, competence: 0.6)
6
+ end
7
+
8
+ describe '#initialize' do
9
+ it 'sets id, name, domain' do
10
+ expect(cap.id).to eq(:cap_one)
11
+ expect(cap.name).to eq('write code')
12
+ expect(cap.domain).to eq(:engineering)
13
+ end
14
+
15
+ it 'sets competence' do
16
+ expect(cap.competence).to eq(0.6)
17
+ end
18
+
19
+ it 'starts with zero attempts and successes' do
20
+ expect(cap.attempts).to eq(0)
21
+ expect(cap.successes).to eq(0)
22
+ end
23
+
24
+ it 'starts with zero calibration error' do
25
+ expect(cap.calibration_error).to eq(0.0)
26
+ end
27
+
28
+ it 'clamps competence to floor' do
29
+ c = described_class.new(id: :x, name: 'x', competence: -1.0)
30
+ expect(c.competence).to eq(Legion::Extensions::Agentic::Self::SelfModel::Helpers::Constants::COMPETENCE_FLOOR)
31
+ end
32
+
33
+ it 'clamps competence to ceiling' do
34
+ c = described_class.new(id: :x, name: 'x', competence: 2.0)
35
+ expect(c.competence).to eq(Legion::Extensions::Agentic::Self::SelfModel::Helpers::Constants::COMPETENCE_CEILING)
36
+ end
37
+
38
+ it 'sets initial state based on competence' do
39
+ expect(cap.state).to eq(:competent)
40
+ end
41
+ end
42
+
43
+ describe '#record_attempt' do
44
+ it 'increments attempts' do
45
+ cap.record_attempt(predicted_success: true, actual_success: true)
46
+ expect(cap.attempts).to eq(1)
47
+ end
48
+
49
+ it 'increments successes on actual success' do
50
+ cap.record_attempt(predicted_success: true, actual_success: true)
51
+ expect(cap.successes).to eq(1)
52
+ end
53
+
54
+ it 'does not increment successes on failure' do
55
+ cap.record_attempt(predicted_success: true, actual_success: false)
56
+ expect(cap.successes).to eq(0)
57
+ end
58
+
59
+ it 'updates competence toward actual outcome' do
60
+ original = cap.competence
61
+ cap.record_attempt(predicted_success: true, actual_success: false)
62
+ expect(cap.competence).to be < original
63
+ end
64
+
65
+ it 'updates calibration error toward prediction error' do
66
+ cap.record_attempt(predicted_success: true, actual_success: false)
67
+ expect(cap.calibration_error).not_to eq(0.0)
68
+ end
69
+
70
+ it 'keeps competence within floor/ceiling' do
71
+ low_cap = described_class.new(id: :x, name: 'x', competence: 0.06)
72
+ 20.times { low_cap.record_attempt(predicted_success: false, actual_success: false) }
73
+ expect(low_cap.competence).to be >= Legion::Extensions::Agentic::Self::SelfModel::Helpers::Constants::COMPETENCE_FLOOR
74
+ end
75
+ end
76
+
77
+ describe '#state transitions' do
78
+ it 'returns :unknown for competence < 0.2' do
79
+ c = described_class.new(id: :x, name: 'x', competence: 0.1)
80
+ expect(c.state).to eq(:unknown)
81
+ end
82
+
83
+ it 'returns :developing for competence 0.2-0.5' do
84
+ c = described_class.new(id: :x, name: 'x', competence: 0.35)
85
+ expect(c.state).to eq(:developing)
86
+ end
87
+
88
+ it 'returns :competent for competence 0.5-0.8' do
89
+ expect(cap.state).to eq(:competent)
90
+ end
91
+
92
+ it 'returns :expert for competence >= 0.8' do
93
+ c = described_class.new(id: :x, name: 'x', competence: 0.9)
94
+ expect(c.state).to eq(:expert)
95
+ end
96
+ end
97
+
98
+ describe '#competence_label' do
99
+ it 'returns a symbol' do
100
+ expect(cap.competence_label).to be_a(Symbol)
101
+ end
102
+
103
+ it 'returns :moderate for 0.6' do
104
+ expect(cap.competence_label).to eq(:moderate)
105
+ end
106
+
107
+ it 'returns :high for 0.8' do
108
+ c = described_class.new(id: :x, name: 'x', competence: 0.8)
109
+ expect(c.competence_label).to eq(:high)
110
+ end
111
+ end
112
+
113
+ describe '#calibrated?' do
114
+ it 'returns true when calibration_error is within threshold' do
115
+ expect(cap.calibrated?).to be true
116
+ end
117
+
118
+ it 'returns false after many overconfident predictions' do
119
+ c = described_class.new(id: :x, name: 'x', competence: 0.9)
120
+ 20.times { c.record_attempt(predicted_success: true, actual_success: false) }
121
+ expect(c.calibrated?).to be false
122
+ end
123
+ end
124
+
125
+ describe '#overconfident?' do
126
+ it 'returns false initially' do
127
+ expect(cap.overconfident?).to be false
128
+ end
129
+
130
+ it 'returns true after sustained overconfident predictions' do
131
+ c = described_class.new(id: :x, name: 'x', competence: 0.9)
132
+ 30.times { c.record_attempt(predicted_success: true, actual_success: false) }
133
+ expect(c.overconfident?).to be true
134
+ end
135
+ end
136
+
137
+ describe '#underconfident?' do
138
+ it 'returns false initially' do
139
+ expect(cap.underconfident?).to be false
140
+ end
141
+
142
+ it 'returns true after sustained underconfident predictions' do
143
+ c = described_class.new(id: :x, name: 'x', competence: 0.1)
144
+ 30.times { c.record_attempt(predicted_success: false, actual_success: true) }
145
+ expect(c.underconfident?).to be true
146
+ end
147
+ end
148
+
149
+ describe '#to_h' do
150
+ it 'returns expected keys' do
151
+ h = cap.to_h
152
+ expect(h).to include(:id, :name, :domain, :competence, :state, :attempts, :successes,
153
+ :calibration_error, :calibrated, :overconfident, :underconfident)
154
+ end
155
+
156
+ it 'rounds competence to 4 decimal places' do
157
+ expect(cap.to_h[:competence]).to eq(cap.competence.round(4))
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Self::SelfModel::Helpers::KnowledgeDomain do
4
+ subject(:dom) do
5
+ described_class.new(id: :dom_one, name: 'machine learning', depth: 0.4, breadth: 0.3)
6
+ end
7
+
8
+ describe '#initialize' do
9
+ it 'sets id and name' do
10
+ expect(dom.id).to eq(:dom_one)
11
+ expect(dom.name).to eq('machine learning')
12
+ end
13
+
14
+ it 'sets depth and breadth' do
15
+ expect(dom.depth).to be_within(0.001).of(0.4)
16
+ expect(dom.breadth).to be_within(0.001).of(0.3)
17
+ end
18
+
19
+ it 'computes confidence as average of depth and breadth' do
20
+ expect(dom.confidence).to be_within(0.001).of(0.35)
21
+ end
22
+
23
+ it 'starts with nil last_accessed' do
24
+ expect(dom.last_accessed).to be_nil
25
+ end
26
+
27
+ it 'clamps depth to 0-1' do
28
+ d = described_class.new(id: :x, name: 'x', depth: 2.0, breadth: 0.0)
29
+ expect(d.depth).to eq(1.0)
30
+ end
31
+
32
+ it 'clamps breadth to 0-1' do
33
+ d = described_class.new(id: :x, name: 'x', depth: 0.0, breadth: -0.5)
34
+ expect(d.breadth).to eq(0.0)
35
+ end
36
+ end
37
+
38
+ describe '#deepen' do
39
+ it 'increases depth' do
40
+ original = dom.depth
41
+ dom.deepen(amount: 0.2)
42
+ expect(dom.depth).to be > original
43
+ end
44
+
45
+ it 'updates confidence after deepening' do
46
+ original = dom.confidence
47
+ dom.deepen(amount: 0.3)
48
+ expect(dom.confidence).to be > original
49
+ end
50
+
51
+ it 'clamps depth at 1.0' do
52
+ dom.deepen(amount: 5.0)
53
+ expect(dom.depth).to eq(1.0)
54
+ end
55
+
56
+ it 'updates state after deepening' do
57
+ d = described_class.new(id: :x, name: 'x', depth: 0.0, breadth: 0.0)
58
+ d.deepen(amount: 0.9)
59
+ expect(d.state).not_to eq(:ignorant)
60
+ end
61
+ end
62
+
63
+ describe '#broaden' do
64
+ it 'increases breadth' do
65
+ original = dom.breadth
66
+ dom.broaden(amount: 0.2)
67
+ expect(dom.breadth).to be > original
68
+ end
69
+
70
+ it 'updates confidence after broadening' do
71
+ original = dom.confidence
72
+ dom.broaden(amount: 0.3)
73
+ expect(dom.confidence).to be > original
74
+ end
75
+
76
+ it 'clamps breadth at 1.0' do
77
+ dom.broaden(amount: 5.0)
78
+ expect(dom.breadth).to eq(1.0)
79
+ end
80
+ end
81
+
82
+ describe '#access!' do
83
+ it 'sets last_accessed to current time' do
84
+ dom.access!
85
+ expect(dom.last_accessed).not_to be_nil
86
+ expect(dom.last_accessed).to be_a(Time)
87
+ end
88
+ end
89
+
90
+ describe '#state' do
91
+ it 'returns :ignorant for very low scores' do
92
+ d = described_class.new(id: :x, name: 'x', depth: 0.05, breadth: 0.05)
93
+ expect(d.state).to eq(:ignorant)
94
+ end
95
+
96
+ it 'returns :aware for low scores' do
97
+ d = described_class.new(id: :x, name: 'x', depth: 0.3, breadth: 0.3)
98
+ expect(d.state).to eq(:aware)
99
+ end
100
+
101
+ it 'returns :familiar for moderate scores' do
102
+ expect(dom.state).to eq(:aware)
103
+ end
104
+
105
+ it 'returns :expert for high scores' do
106
+ d = described_class.new(id: :x, name: 'x', depth: 0.9, breadth: 0.9)
107
+ expect(d.state).to eq(:expert)
108
+ end
109
+ end
110
+
111
+ describe '#knowledge_label' do
112
+ it 'returns a symbol' do
113
+ expect(dom.knowledge_label).to be_a(Symbol)
114
+ end
115
+ end
116
+
117
+ describe '#to_h' do
118
+ it 'returns expected keys' do
119
+ h = dom.to_h
120
+ expect(h).to include(:id, :name, :depth, :breadth, :confidence, :state, :knowledge_label, :last_accessed)
121
+ end
122
+
123
+ it 'rounds depth and breadth' do
124
+ expect(dom.to_h[:depth]).to eq(dom.depth.round(4))
125
+ expect(dom.to_h[:breadth]).to eq(dom.breadth.round(4))
126
+ end
127
+ end
128
+ end