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,198 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Self::MetacognitiveMonitoring::Helpers::MonitoringEngine do
4
+ subject(:engine) { described_class.new }
5
+
6
+ describe '#initialize' do
7
+ it 'starts with empty judgments' do
8
+ expect(engine.judgments).to be_empty
9
+ end
10
+
11
+ it 'starts with a CalibrationTracker' do
12
+ expect(engine.calibration).to be_a(
13
+ Legion::Extensions::Agentic::Self::MetacognitiveMonitoring::Helpers::CalibrationTracker
14
+ )
15
+ end
16
+
17
+ it 'starts with no domain calibrations' do
18
+ expect(engine.domain_calibrations).to be_empty
19
+ end
20
+ end
21
+
22
+ describe '#record_judgment' do
23
+ it 'creates and stores a MonitoringJudgment' do
24
+ judgment = engine.record_judgment(type: :feeling_of_knowing, domain: :episodic)
25
+ expect(engine.judgments[judgment.id]).to eq(judgment)
26
+ end
27
+
28
+ it 'returns a MonitoringJudgment' do
29
+ result = engine.record_judgment(type: :confidence_rating, domain: :semantic)
30
+ expect(result).to be_a(Legion::Extensions::Agentic::Self::MetacognitiveMonitoring::Helpers::MonitoringJudgment)
31
+ end
32
+
33
+ it 'stores predicted_confidence on the judgment' do
34
+ j = engine.record_judgment(type: :effort_estimate, domain: :test, predicted_confidence: 0.8)
35
+ expect(j.predicted_confidence).to eq(0.8)
36
+ end
37
+
38
+ it 'stores effort on the judgment' do
39
+ j = engine.record_judgment(type: :effort_estimate, domain: :test, effort: 0.9)
40
+ expect(j.effort_level).to eq(0.9)
41
+ end
42
+ end
43
+
44
+ describe '#resolve_judgment' do
45
+ let!(:judgment) { engine.record_judgment(type: :feeling_of_knowing, domain: :episodic, predicted_confidence: 0.7) }
46
+
47
+ it 'resolves the judgment' do
48
+ engine.resolve_judgment(judgment_id: judgment.id, actual_outcome: 0.6)
49
+ expect(judgment.resolved).to be true
50
+ end
51
+
52
+ it 'updates overall calibration tracker' do
53
+ engine.resolve_judgment(judgment_id: judgment.id, actual_outcome: 0.6)
54
+ expect(engine.calibration.count).to eq(1)
55
+ end
56
+
57
+ it 'updates domain calibration tracker' do
58
+ engine.resolve_judgment(judgment_id: judgment.id, actual_outcome: 0.6)
59
+ expect(engine.domain_calibrations[:episodic]).not_to be_nil
60
+ expect(engine.domain_calibrations[:episodic].count).to eq(1)
61
+ end
62
+
63
+ it 'returns nil for unknown judgment_id' do
64
+ result = engine.resolve_judgment(judgment_id: 'nonexistent', actual_outcome: 0.5)
65
+ expect(result).to be_nil
66
+ end
67
+
68
+ it 'returns the resolved judgment' do
69
+ result = engine.resolve_judgment(judgment_id: judgment.id, actual_outcome: 0.5)
70
+ expect(result).to eq(judgment)
71
+ end
72
+ end
73
+
74
+ describe '#feeling_of_knowing' do
75
+ it 'returns a MonitoringJudgment with type :feeling_of_knowing' do
76
+ j = engine.feeling_of_knowing(domain: :semantic)
77
+ expect(j.judgment_type).to eq(:feeling_of_knowing)
78
+ end
79
+
80
+ it 'uses query richness to influence confidence' do
81
+ short = engine.feeling_of_knowing(domain: :test, query: 'x')
82
+ long = engine.feeling_of_knowing(domain: :test, query: 'one two three four five six seven')
83
+ expect(long.predicted_confidence).to be >= short.predicted_confidence
84
+ end
85
+
86
+ it 'records the judgment in the engine' do
87
+ j = engine.feeling_of_knowing(domain: :test)
88
+ expect(engine.judgments[j.id]).to eq(j)
89
+ end
90
+ end
91
+
92
+ describe '#judgment_of_learning' do
93
+ it 'returns a MonitoringJudgment with type :judgment_of_learning' do
94
+ j = engine.judgment_of_learning(domain: :procedural)
95
+ expect(j.judgment_type).to eq(:judgment_of_learning)
96
+ end
97
+
98
+ it 'uses content length to influence confidence' do
99
+ short_j = engine.judgment_of_learning(domain: :test, content: 'hi')
100
+ long_j = engine.judgment_of_learning(domain: :test, content: 'x' * 200)
101
+ expect(long_j.predicted_confidence).to be >= short_j.predicted_confidence
102
+ end
103
+ end
104
+
105
+ describe '#detect_overconfidence' do
106
+ it 'returns empty when no resolved judgments' do
107
+ engine.record_judgment(type: :confidence_rating, domain: :test)
108
+ expect(engine.detect_overconfidence).to be_empty
109
+ end
110
+
111
+ it 'returns overconfident judgments' do
112
+ j = engine.record_judgment(type: :confidence_rating, domain: :test, predicted_confidence: 0.9)
113
+ engine.resolve_judgment(judgment_id: j.id, actual_outcome: 0.1)
114
+ expect(engine.detect_overconfidence).to include(j)
115
+ end
116
+
117
+ it 'does not include well-calibrated judgments' do
118
+ j = engine.record_judgment(type: :confidence_rating, domain: :test, predicted_confidence: 0.7)
119
+ engine.resolve_judgment(judgment_id: j.id, actual_outcome: 0.65)
120
+ expect(engine.detect_overconfidence).not_to include(j)
121
+ end
122
+ end
123
+
124
+ describe '#detect_underconfidence' do
125
+ it 'returns underconfident judgments' do
126
+ j = engine.record_judgment(type: :confidence_rating, domain: :test, predicted_confidence: 0.2)
127
+ engine.resolve_judgment(judgment_id: j.id, actual_outcome: 0.9)
128
+ expect(engine.detect_underconfidence).to include(j)
129
+ end
130
+ end
131
+
132
+ describe '#average_effort' do
133
+ it 'returns 0.0 when no judgments' do
134
+ expect(engine.average_effort).to eq(0.0)
135
+ end
136
+
137
+ it 'computes mean effort across judgments' do
138
+ engine.record_judgment(type: :effort_estimate, domain: :test, effort: 0.4)
139
+ engine.record_judgment(type: :effort_estimate, domain: :test, effort: 0.6)
140
+ expect(engine.average_effort).to eq(0.5)
141
+ end
142
+ end
143
+
144
+ describe '#calibration_report' do
145
+ it 'includes overall calibration' do
146
+ report = engine.calibration_report
147
+ expect(report).to have_key(:overall)
148
+ end
149
+
150
+ it 'includes by_domain breakdown' do
151
+ j = engine.record_judgment(type: :feeling_of_knowing, domain: :episodic, predicted_confidence: 0.7)
152
+ engine.resolve_judgment(judgment_id: j.id, actual_outcome: 0.6)
153
+ report = engine.calibration_report
154
+ expect(report[:by_domain]).to have_key(:episodic)
155
+ end
156
+
157
+ it 'reports total_resolved count' do
158
+ j = engine.record_judgment(type: :feeling_of_knowing, domain: :test, predicted_confidence: 0.7)
159
+ engine.resolve_judgment(judgment_id: j.id, actual_outcome: 0.6)
160
+ expect(engine.calibration_report[:total_resolved]).to eq(1)
161
+ end
162
+ end
163
+
164
+ describe '#monitoring_report' do
165
+ before do
166
+ j1 = engine.record_judgment(type: :feeling_of_knowing, domain: :test, predicted_confidence: 0.7)
167
+ engine.resolve_judgment(judgment_id: j1.id, actual_outcome: 0.3)
168
+ engine.record_judgment(type: :confidence_rating, domain: :test)
169
+ end
170
+
171
+ it 'includes total_judgments' do
172
+ expect(engine.monitoring_report[:total_judgments]).to eq(2)
173
+ end
174
+
175
+ it 'includes resolved_count' do
176
+ expect(engine.monitoring_report[:resolved_count]).to eq(1)
177
+ end
178
+
179
+ it 'includes unresolved_count' do
180
+ expect(engine.monitoring_report[:unresolved_count]).to eq(1)
181
+ end
182
+
183
+ it 'includes overconfident_count' do
184
+ expect(engine.monitoring_report).to have_key(:overconfident_count)
185
+ end
186
+
187
+ it 'includes domain_count' do
188
+ expect(engine.monitoring_report[:domain_count]).to eq(1)
189
+ end
190
+ end
191
+
192
+ describe '#to_h' do
193
+ it 'returns a summary hash' do
194
+ h = engine.to_h
195
+ expect(h).to include(:judgment_count, :calibration, :domain_count, :average_effort)
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Self::MetacognitiveMonitoring::Helpers::MonitoringJudgment do
4
+ let(:judgment) do
5
+ described_class.new(
6
+ judgment_type: :feeling_of_knowing,
7
+ domain: :episodic,
8
+ predicted_confidence: 0.7,
9
+ effort_level: 0.4
10
+ )
11
+ end
12
+
13
+ describe '#initialize' do
14
+ it 'assigns a UUID id' do
15
+ expect(judgment.id).to match(/\A[0-9a-f-]{36}\z/)
16
+ end
17
+
18
+ it 'stores judgment_type' do
19
+ expect(judgment.judgment_type).to eq(:feeling_of_knowing)
20
+ end
21
+
22
+ it 'stores domain' do
23
+ expect(judgment.domain).to eq(:episodic)
24
+ end
25
+
26
+ it 'stores predicted_confidence' do
27
+ expect(judgment.predicted_confidence).to eq(0.7)
28
+ end
29
+
30
+ it 'stores effort_level' do
31
+ expect(judgment.effort_level).to eq(0.4)
32
+ end
33
+
34
+ it 'starts unresolved' do
35
+ expect(judgment.resolved).to be false
36
+ end
37
+
38
+ it 'starts with nil actual_outcome' do
39
+ expect(judgment.actual_outcome).to be_nil
40
+ end
41
+
42
+ it 'clamps predicted_confidence above 1.0 to 1.0' do
43
+ j = described_class.new(judgment_type: :confidence_rating, domain: :test, predicted_confidence: 1.5)
44
+ expect(j.predicted_confidence).to eq(1.0)
45
+ end
46
+
47
+ it 'clamps predicted_confidence below 0.0 to 0.0' do
48
+ j = described_class.new(judgment_type: :confidence_rating, domain: :test, predicted_confidence: -0.3)
49
+ expect(j.predicted_confidence).to eq(0.0)
50
+ end
51
+
52
+ it 'clamps effort_level above 1.0 to 1.0' do
53
+ j = described_class.new(judgment_type: :effort_estimate, domain: :test, effort_level: 2.0)
54
+ expect(j.effort_level).to eq(1.0)
55
+ end
56
+ end
57
+
58
+ describe '#resolve!' do
59
+ it 'sets actual_outcome' do
60
+ judgment.resolve!(actual: 0.6)
61
+ expect(judgment.actual_outcome).to eq(0.6)
62
+ end
63
+
64
+ it 'marks as resolved' do
65
+ judgment.resolve!(actual: 0.6)
66
+ expect(judgment.resolved).to be true
67
+ end
68
+
69
+ it 'returns self' do
70
+ result = judgment.resolve!(actual: 0.6)
71
+ expect(result).to eq(judgment)
72
+ end
73
+
74
+ it 'clamps actual_outcome to 0.0..1.0' do
75
+ judgment.resolve!(actual: 1.5)
76
+ expect(judgment.actual_outcome).to eq(1.0)
77
+ end
78
+ end
79
+
80
+ describe '#calibration_error' do
81
+ it 'returns nil when unresolved' do
82
+ expect(judgment.calibration_error).to be_nil
83
+ end
84
+
85
+ it 'returns predicted - actual when resolved' do
86
+ judgment.resolve!(actual: 0.5)
87
+ expect(judgment.calibration_error).to eq(0.2)
88
+ end
89
+
90
+ it 'returns negative when underconfident' do
91
+ judgment.resolve!(actual: 0.9)
92
+ expect(judgment.calibration_error).to be < 0
93
+ end
94
+ end
95
+
96
+ describe '#overconfident?' do
97
+ it 'returns false when unresolved' do
98
+ expect(judgment.overconfident?).to be false
99
+ end
100
+
101
+ it 'returns true when calibration_error > OVERCONFIDENCE_THRESHOLD' do
102
+ judgment.resolve!(actual: 0.1)
103
+ expect(judgment.overconfident?).to be true
104
+ end
105
+
106
+ it 'returns false when calibration_error is within range' do
107
+ judgment.resolve!(actual: 0.65)
108
+ expect(judgment.overconfident?).to be false
109
+ end
110
+ end
111
+
112
+ describe '#underconfident?' do
113
+ it 'returns false when unresolved' do
114
+ expect(judgment.underconfident?).to be false
115
+ end
116
+
117
+ it 'returns true when actual >> predicted' do
118
+ j = described_class.new(judgment_type: :feeling_of_knowing, domain: :test, predicted_confidence: 0.2)
119
+ j.resolve!(actual: 0.9)
120
+ expect(j.underconfident?).to be true
121
+ end
122
+
123
+ it 'returns false for well-calibrated judgment' do
124
+ judgment.resolve!(actual: 0.65)
125
+ expect(judgment.underconfident?).to be false
126
+ end
127
+ end
128
+
129
+ describe '#confidence_label' do
130
+ it 'returns :very_high for 0.9' do
131
+ j = described_class.new(judgment_type: :confidence_rating, domain: :test, predicted_confidence: 0.9)
132
+ expect(j.confidence_label).to eq(:very_high)
133
+ end
134
+
135
+ it 'returns :moderate for 0.5' do
136
+ j = described_class.new(judgment_type: :confidence_rating, domain: :test, predicted_confidence: 0.5)
137
+ expect(j.confidence_label).to eq(:moderate)
138
+ end
139
+
140
+ it 'returns :very_low for 0.1' do
141
+ j = described_class.new(judgment_type: :confidence_rating, domain: :test, predicted_confidence: 0.1)
142
+ expect(j.confidence_label).to eq(:very_low)
143
+ end
144
+ end
145
+
146
+ describe '#effort_label' do
147
+ it 'returns :extreme for 0.9' do
148
+ j = described_class.new(judgment_type: :effort_estimate, domain: :test, effort_level: 0.9)
149
+ expect(j.effort_label).to eq(:extreme)
150
+ end
151
+
152
+ it 'returns :minimal for 0.1' do
153
+ j = described_class.new(judgment_type: :effort_estimate, domain: :test, effort_level: 0.1)
154
+ expect(j.effort_label).to eq(:minimal)
155
+ end
156
+ end
157
+
158
+ describe '#to_h' do
159
+ it 'includes all key fields' do
160
+ h = judgment.to_h
161
+ expect(h).to include(:id, :judgment_type, :domain, :predicted_confidence, :actual_outcome,
162
+ :effort_level, :resolved, :calibration_error, :confidence_label, :effort_label, :created_at)
163
+ end
164
+
165
+ it 'reflects resolved state after resolution' do
166
+ judgment.resolve!(actual: 0.6)
167
+ h = judgment.to_h
168
+ expect(h[:resolved]).to be true
169
+ expect(h[:actual_outcome]).to eq(0.6)
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,244 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/self/metacognitive_monitoring/client'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Self::MetacognitiveMonitoring::Runners::MetacognitiveMonitoring do
6
+ let(:engine) { Legion::Extensions::Agentic::Self::MetacognitiveMonitoring::Helpers::MonitoringEngine.new }
7
+ let(:client) { Legion::Extensions::Agentic::Self::MetacognitiveMonitoring::Client.new(engine: engine) }
8
+
9
+ describe '#record_judgment' do
10
+ it 'succeeds with valid type' do
11
+ result = client.record_judgment(type: :feeling_of_knowing, domain: :episodic)
12
+ expect(result[:success]).to be true
13
+ end
14
+
15
+ it 'returns a judgment_id' do
16
+ result = client.record_judgment(type: :feeling_of_knowing, domain: :episodic)
17
+ expect(result[:judgment_id]).to match(/\A[0-9a-f-]{36}\z/)
18
+ end
19
+
20
+ it 'returns the judgment hash' do
21
+ result = client.record_judgment(type: :feeling_of_knowing, domain: :episodic)
22
+ expect(result[:judgment]).to include(:id, :judgment_type, :domain)
23
+ end
24
+
25
+ it 'rejects invalid judgment type' do
26
+ result = client.record_judgment(type: :nonexistent, domain: :test)
27
+ expect(result[:success]).to be false
28
+ expect(result[:error]).to eq(:invalid_judgment_type)
29
+ end
30
+
31
+ it 'includes valid_types in error response' do
32
+ result = client.record_judgment(type: :bad_type, domain: :test)
33
+ expect(result[:valid_types]).to include(:feeling_of_knowing)
34
+ end
35
+
36
+ it 'accepts string type and converts to symbol' do
37
+ result = client.record_judgment(type: 'confidence_rating', domain: :test)
38
+ expect(result[:success]).to be true
39
+ end
40
+
41
+ it 'records judgment in engine' do
42
+ result = client.record_judgment(type: :effort_estimate, domain: :test)
43
+ expect(engine.judgments[result[:judgment_id]]).not_to be_nil
44
+ end
45
+
46
+ it 'uses provided predicted_confidence' do
47
+ result = client.record_judgment(type: :confidence_rating, domain: :test, predicted_confidence: 0.85)
48
+ expect(result[:judgment][:predicted_confidence]).to eq(0.85)
49
+ end
50
+ end
51
+
52
+ describe '#resolve_judgment' do
53
+ let!(:recorded) { client.record_judgment(type: :feeling_of_knowing, domain: :episodic, predicted_confidence: 0.7) }
54
+
55
+ it 'succeeds for existing judgment' do
56
+ result = client.resolve_judgment(judgment_id: recorded[:judgment_id], actual_outcome: 0.6)
57
+ expect(result[:success]).to be true
58
+ end
59
+
60
+ it 'returns the judgment_id' do
61
+ result = client.resolve_judgment(judgment_id: recorded[:judgment_id], actual_outcome: 0.6)
62
+ expect(result[:judgment_id]).to eq(recorded[:judgment_id])
63
+ end
64
+
65
+ it 'returns the resolved judgment hash' do
66
+ result = client.resolve_judgment(judgment_id: recorded[:judgment_id], actual_outcome: 0.6)
67
+ expect(result[:judgment][:resolved]).to be true
68
+ end
69
+
70
+ it 'returns failure for unknown id' do
71
+ result = client.resolve_judgment(judgment_id: 'no-such-id', actual_outcome: 0.5)
72
+ expect(result[:success]).to be false
73
+ expect(result[:error]).to eq(:not_found)
74
+ end
75
+ end
76
+
77
+ describe '#feeling_of_knowing' do
78
+ it 'succeeds' do
79
+ result = client.feeling_of_knowing(domain: :episodic)
80
+ expect(result[:success]).to be true
81
+ end
82
+
83
+ it 'returns judgment_id' do
84
+ result = client.feeling_of_knowing(domain: :episodic)
85
+ expect(result[:judgment_id]).to match(/\A[0-9a-f-]{36}\z/)
86
+ end
87
+
88
+ it 'returns predicted_confidence' do
89
+ result = client.feeling_of_knowing(domain: :episodic)
90
+ expect(result[:predicted_confidence]).to be_between(0.0, 1.0)
91
+ end
92
+
93
+ it 'returns a confidence_label' do
94
+ result = client.feeling_of_knowing(domain: :episodic)
95
+ expect(result[:confidence_label]).to be_a(Symbol)
96
+ end
97
+
98
+ it 'reflects query in domain' do
99
+ result = client.feeling_of_knowing(domain: :semantic, query: 'what is ruby')
100
+ expect(result[:domain]).to eq(:semantic)
101
+ end
102
+ end
103
+
104
+ describe '#judgment_of_learning' do
105
+ it 'succeeds' do
106
+ result = client.judgment_of_learning(domain: :procedural)
107
+ expect(result[:success]).to be true
108
+ end
109
+
110
+ it 'returns judgment_id' do
111
+ result = client.judgment_of_learning(domain: :procedural)
112
+ expect(result[:judgment_id]).to match(/\A[0-9a-f-]{36}\z/)
113
+ end
114
+
115
+ it 'returns confidence_label' do
116
+ result = client.judgment_of_learning(domain: :procedural, content: 'some content to assess')
117
+ expect(result[:confidence_label]).to be_a(Symbol)
118
+ end
119
+ end
120
+
121
+ describe '#detect_overconfidence' do
122
+ context 'with no overconfident judgments' do
123
+ it 'returns empty findings' do
124
+ result = client.detect_overconfidence
125
+ expect(result[:count]).to eq(0)
126
+ expect(result[:findings]).to be_empty
127
+ end
128
+ end
129
+
130
+ context 'with overconfident judgments' do
131
+ before do
132
+ r = client.record_judgment(type: :confidence_rating, domain: :test, predicted_confidence: 0.95)
133
+ client.resolve_judgment(judgment_id: r[:judgment_id], actual_outcome: 0.1)
134
+ end
135
+
136
+ it 'detects overconfidence' do
137
+ result = client.detect_overconfidence
138
+ expect(result[:count]).to eq(1)
139
+ end
140
+
141
+ it 'includes finding details' do
142
+ result = client.detect_overconfidence
143
+ expect(result[:findings].first).to include(:id, :predicted_confidence, :actual_outcome)
144
+ end
145
+ end
146
+ end
147
+
148
+ describe '#detect_underconfidence' do
149
+ context 'with underconfident judgment' do
150
+ before do
151
+ r = client.record_judgment(type: :confidence_rating, domain: :test, predicted_confidence: 0.1)
152
+ client.resolve_judgment(judgment_id: r[:judgment_id], actual_outcome: 0.9)
153
+ end
154
+
155
+ it 'detects underconfidence' do
156
+ result = client.detect_underconfidence
157
+ expect(result[:count]).to eq(1)
158
+ expect(result[:success]).to be true
159
+ end
160
+ end
161
+ end
162
+
163
+ describe '#calibration_report' do
164
+ it 'returns success' do
165
+ result = client.calibration_report
166
+ expect(result[:success]).to be true
167
+ end
168
+
169
+ it 'includes report' do
170
+ result = client.calibration_report
171
+ expect(result[:report]).to include(:overall, :by_domain, :total_resolved)
172
+ end
173
+
174
+ it 'populates by_domain after resolving a domain judgment' do
175
+ r = client.record_judgment(type: :feeling_of_knowing, domain: :episodic, predicted_confidence: 0.7)
176
+ client.resolve_judgment(judgment_id: r[:judgment_id], actual_outcome: 0.6)
177
+ report = client.calibration_report
178
+ expect(report[:report][:by_domain]).to have_key(:episodic)
179
+ end
180
+ end
181
+
182
+ describe '#monitoring_report' do
183
+ before do
184
+ r = client.record_judgment(type: :feeling_of_knowing, domain: :test, predicted_confidence: 0.7)
185
+ client.resolve_judgment(judgment_id: r[:judgment_id], actual_outcome: 0.5)
186
+ client.record_judgment(type: :effort_estimate, domain: :test)
187
+ end
188
+
189
+ it 'returns success' do
190
+ expect(client.monitoring_report[:success]).to be true
191
+ end
192
+
193
+ it 'includes total_judgments' do
194
+ expect(client.monitoring_report[:report][:total_judgments]).to eq(2)
195
+ end
196
+
197
+ it 'includes resolved and unresolved counts' do
198
+ report = client.monitoring_report[:report]
199
+ expect(report[:resolved_count]).to eq(1)
200
+ expect(report[:unresolved_count]).to eq(1)
201
+ end
202
+ end
203
+
204
+ describe '#average_effort' do
205
+ it 'returns success' do
206
+ result = client.average_effort
207
+ expect(result[:success]).to be true
208
+ end
209
+
210
+ it 'returns 0.0 when no judgments' do
211
+ result = client.average_effort
212
+ expect(result[:average_effort]).to eq(0.0)
213
+ end
214
+
215
+ it 'includes effort_label' do
216
+ client.record_judgment(type: :effort_estimate, domain: :test, effort: 0.9)
217
+ result = client.average_effort
218
+ expect(result[:effort_label]).to be_a(Symbol)
219
+ end
220
+
221
+ it 'reflects window parameter' do
222
+ result = client.average_effort(window: 10)
223
+ expect(result[:window]).to eq(10)
224
+ end
225
+ end
226
+
227
+ describe '#calibration_curve' do
228
+ it 'returns success' do
229
+ expect(client.calibration_curve[:success]).to be true
230
+ end
231
+
232
+ it 'returns requested number of bins' do
233
+ result = client.calibration_curve(bins: 5)
234
+ expect(result[:bins]).to eq(5)
235
+ end
236
+
237
+ it 'returns curve array' do
238
+ r = client.record_judgment(type: :confidence_rating, domain: :test, predicted_confidence: 0.7)
239
+ client.resolve_judgment(judgment_id: r[:judgment_id], actual_outcome: 0.6)
240
+ result = client.calibration_curve(bins: 5)
241
+ expect(result[:curve]).to be_an(Array)
242
+ end
243
+ end
244
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/self/narrative_arc/client'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Self::NarrativeArc::Client do
6
+ it 'responds to all narrative runner methods' do
7
+ client = described_class.new
8
+ expect(client).to respond_to(:create_arc)
9
+ expect(client).to respond_to(:add_beat)
10
+ expect(client).to respond_to(:get_arc)
11
+ expect(client).to respond_to(:active_arcs)
12
+ expect(client).to respond_to(:completed_arcs)
13
+ expect(client).to respond_to(:most_dramatic_arc)
14
+ expect(client).to respond_to(:arc_report)
15
+ end
16
+
17
+ it 'initializes with an arc engine' do
18
+ client = described_class.new
19
+ result = client.arc_report
20
+ expect(result[:report][:total_arcs]).to eq(0)
21
+ end
22
+ end