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,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Self::Fingerprint::Helpers::Constants do
4
+ describe 'TRAIT_CATEGORIES' do
5
+ it 'contains 8 categories' do
6
+ expect(described_class::TRAIT_CATEGORIES.size).to eq(8)
7
+ end
8
+
9
+ it 'includes all expected categories' do
10
+ expect(described_class::TRAIT_CATEGORIES).to include(
11
+ :processing_speed, :accuracy, :creativity, :caution,
12
+ :thoroughness, :risk_tolerance, :abstraction_preference, :social_orientation
13
+ )
14
+ end
15
+
16
+ it 'is frozen' do
17
+ expect(described_class::TRAIT_CATEGORIES).to be_frozen
18
+ end
19
+ end
20
+
21
+ describe 'MAX_TRAITS' do
22
+ it 'equals 100' do
23
+ expect(described_class::MAX_TRAITS).to eq(100)
24
+ end
25
+ end
26
+
27
+ describe 'MAX_SAMPLES' do
28
+ it 'equals 500' do
29
+ expect(described_class::MAX_SAMPLES).to eq(500)
30
+ end
31
+ end
32
+
33
+ describe 'EMA_ALPHA' do
34
+ it 'equals 0.15' do
35
+ expect(described_class::EMA_ALPHA).to eq(0.15)
36
+ end
37
+ end
38
+
39
+ describe 'DEVIATION_THRESHOLD' do
40
+ it 'equals 0.3' do
41
+ expect(described_class::DEVIATION_THRESHOLD).to eq(0.3)
42
+ end
43
+ end
44
+
45
+ describe '.identity_label_for' do
46
+ it 'returns :certain for high confidence' do
47
+ expect(described_class.identity_label_for(0.9)).to eq(:certain)
48
+ end
49
+
50
+ it 'returns :certain at exactly 0.85' do
51
+ expect(described_class.identity_label_for(0.85)).to eq(:certain)
52
+ end
53
+
54
+ it 'returns :confident for 0.65-0.85 range' do
55
+ expect(described_class.identity_label_for(0.75)).to eq(:confident)
56
+ end
57
+
58
+ it 'returns :developing for 0.40-0.65 range' do
59
+ expect(described_class.identity_label_for(0.5)).to eq(:developing)
60
+ end
61
+
62
+ it 'returns :uncertain for 0.20-0.40 range' do
63
+ expect(described_class.identity_label_for(0.3)).to eq(:uncertain)
64
+ end
65
+
66
+ it 'returns :unknown for low confidence' do
67
+ expect(described_class.identity_label_for(0.1)).to eq(:unknown)
68
+ end
69
+
70
+ it 'returns :unknown for exactly 0.0' do
71
+ expect(described_class.identity_label_for(0.0)).to eq(:unknown)
72
+ end
73
+
74
+ it 'returns :certain for exactly 1.0' do
75
+ expect(described_class.identity_label_for(1.0)).to eq(:certain)
76
+ end
77
+ end
78
+
79
+ describe '.trait_strength_label_for' do
80
+ it 'returns :dominant for high values' do
81
+ expect(described_class.trait_strength_label_for(0.9)).to eq(:dominant)
82
+ end
83
+
84
+ it 'returns :dominant at exactly 0.80' do
85
+ expect(described_class.trait_strength_label_for(0.80)).to eq(:dominant)
86
+ end
87
+
88
+ it 'returns :strong for 0.60-0.80 range' do
89
+ expect(described_class.trait_strength_label_for(0.7)).to eq(:strong)
90
+ end
91
+
92
+ it 'returns :moderate for 0.40-0.60 range' do
93
+ expect(described_class.trait_strength_label_for(0.5)).to eq(:moderate)
94
+ end
95
+
96
+ it 'returns :weak for 0.20-0.40 range' do
97
+ expect(described_class.trait_strength_label_for(0.3)).to eq(:weak)
98
+ end
99
+
100
+ it 'returns :absent for low values' do
101
+ expect(described_class.trait_strength_label_for(0.1)).to eq(:absent)
102
+ end
103
+
104
+ it 'returns :absent for 0.0' do
105
+ expect(described_class.trait_strength_label_for(0.0)).to eq(:absent)
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,318 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Self::Fingerprint::Helpers::FingerprintEngine do
4
+ let(:engine) { described_class.new }
5
+
6
+ def observe(category, value)
7
+ engine.record_observation(category: category, value: value)
8
+ end
9
+
10
+ describe '#record_observation' do
11
+ it 'returns status :recorded for valid category' do
12
+ result = observe(:accuracy, 0.8)
13
+ expect(result[:status]).to eq(:recorded)
14
+ end
15
+
16
+ it 'returns status :invalid_category for unknown category' do
17
+ result = observe(:nonexistent, 0.5)
18
+ expect(result[:status]).to eq(:invalid_category)
19
+ end
20
+
21
+ it 'stores trait on first observation' do
22
+ observe(:accuracy, 0.7)
23
+ expect(engine.trait_count).to eq(1)
24
+ end
25
+
26
+ it 'returns baseline after observation' do
27
+ result = observe(:creativity, 0.8)
28
+ expect(result[:baseline]).to be > 0.0
29
+ end
30
+
31
+ it 'increments samples on each call' do
32
+ observe(:accuracy, 0.5)
33
+ observe(:accuracy, 0.7)
34
+ result = observe(:accuracy, 0.6)
35
+ expect(result[:samples]).to eq(3)
36
+ end
37
+
38
+ it 'clamps values above 1.0' do
39
+ result = observe(:accuracy, 5.0)
40
+ expect(result[:baseline]).to be <= 1.0
41
+ end
42
+
43
+ it 'clamps values below 0.0' do
44
+ result = observe(:accuracy, -1.0)
45
+ expect(result[:baseline]).to be >= 0.0
46
+ end
47
+
48
+ it 'accumulates global sample log' do
49
+ observe(:accuracy, 0.5)
50
+ observe(:creativity, 0.6)
51
+ expect(engine.sample_count).to eq(2)
52
+ end
53
+
54
+ it 'caps global samples at MAX_SAMPLES' do
55
+ max = Legion::Extensions::Agentic::Self::Fingerprint::Helpers::Constants::MAX_SAMPLES
56
+ (max + 20).times { observe(:accuracy, 0.5) }
57
+ expect(engine.sample_count).to eq(max)
58
+ end
59
+ end
60
+
61
+ describe '#verify_identity' do
62
+ context 'with no traits built' do
63
+ it 'returns insufficient_data verdict' do
64
+ result = engine.verify_identity(observations: [{ category: :accuracy, value: 0.8 }])
65
+ expect(result[:verdict]).to eq(:insufficient_data)
66
+ end
67
+ end
68
+
69
+ context 'with observations that match baseline' do
70
+ before do
71
+ 10.times { observe(:accuracy, 0.8) }
72
+ 10.times { observe(:creativity, 0.6) }
73
+ end
74
+
75
+ it 'returns :verified for matching observations' do
76
+ result = engine.verify_identity(observations: [
77
+ { category: :accuracy, value: 0.8 },
78
+ { category: :creativity, value: 0.6 }
79
+ ])
80
+ expect(result[:verdict]).to eq(:verified)
81
+ end
82
+
83
+ it 'returns a match_score between 0 and 1' do
84
+ result = engine.verify_identity(observations: [{ category: :accuracy, value: 0.8 }])
85
+ expect(result[:match_score]).to be_between(0.0, 1.0)
86
+ end
87
+
88
+ it 'returns observations_checked count' do
89
+ result = engine.verify_identity(observations: [
90
+ { category: :accuracy, value: 0.8 },
91
+ { category: :creativity, value: 0.6 }
92
+ ])
93
+ expect(result[:observations_checked]).to eq(2)
94
+ end
95
+ end
96
+
97
+ context 'with mismatched observations' do
98
+ before { 20.times { observe(:accuracy, 0.8) } }
99
+
100
+ it 'returns :mismatch for very deviant observations' do
101
+ result = engine.verify_identity(observations: [{ category: :accuracy, value: 0.0 }])
102
+ expect(result[:verdict]).to eq(:mismatch)
103
+ end
104
+ end
105
+
106
+ it 'returns insufficient_data for empty observations' do
107
+ observe(:accuracy, 0.5)
108
+ result = engine.verify_identity(observations: [])
109
+ expect(result[:verdict]).to eq(:insufficient_data)
110
+ end
111
+
112
+ it 'skips observations for unknown categories' do
113
+ observe(:accuracy, 0.5)
114
+ result = engine.verify_identity(observations: [{ category: :unknown_cat, value: 0.5 }])
115
+ expect(result[:verdict]).to eq(:insufficient_data)
116
+ end
117
+ end
118
+
119
+ describe '#trait_profile' do
120
+ it 'returns empty hash when no traits' do
121
+ expect(engine.trait_profile).to eq({})
122
+ end
123
+
124
+ it 'returns category => baseline mapping' do
125
+ observe(:accuracy, 0.8)
126
+ observe(:creativity, 0.4)
127
+ profile = engine.trait_profile
128
+ expect(profile).to have_key(:accuracy)
129
+ expect(profile).to have_key(:creativity)
130
+ end
131
+
132
+ it 'values are floats between 0 and 1' do
133
+ observe(:caution, 0.7)
134
+ engine.trait_profile.each_value do |v|
135
+ expect(v).to be_between(0.0, 1.0)
136
+ end
137
+ end
138
+ end
139
+
140
+ describe '#strongest_traits' do
141
+ before do
142
+ observe(:accuracy, 0.9)
143
+ observe(:creativity, 0.5)
144
+ observe(:caution, 0.1)
145
+ end
146
+
147
+ it 'returns traits sorted by baseline descending' do
148
+ strongest = engine.strongest_traits(2)
149
+ expect(strongest.first[:baseline]).to be >= strongest.last[:baseline]
150
+ end
151
+
152
+ it 'respects n parameter' do
153
+ expect(engine.strongest_traits(1).size).to eq(1)
154
+ end
155
+
156
+ it 'returns hashes with required keys' do
157
+ traits = engine.strongest_traits(1)
158
+ expect(traits.first).to have_key(:category)
159
+ expect(traits.first).to have_key(:baseline)
160
+ end
161
+ end
162
+
163
+ describe '#weakest_traits' do
164
+ before do
165
+ observe(:accuracy, 0.9)
166
+ observe(:creativity, 0.5)
167
+ observe(:caution, 0.1)
168
+ end
169
+
170
+ it 'returns traits sorted by baseline ascending' do
171
+ weakest = engine.weakest_traits(2)
172
+ expect(weakest.first[:baseline]).to be <= weakest.last[:baseline]
173
+ end
174
+
175
+ it 'respects n parameter' do
176
+ expect(engine.weakest_traits(1).size).to eq(1)
177
+ end
178
+ end
179
+
180
+ describe '#identity_confidence' do
181
+ it 'returns 0.0 with no traits' do
182
+ expect(engine.identity_confidence).to eq(0.0)
183
+ end
184
+
185
+ it 'returns a value between 0 and 1 with data' do
186
+ observe(:accuracy, 0.7)
187
+ expect(engine.identity_confidence).to be_between(0.0, 1.0)
188
+ end
189
+
190
+ it 'increases as more categories are covered' do
191
+ conf1 = engine.identity_confidence
192
+ observe(:accuracy, 0.7)
193
+ conf2 = engine.identity_confidence
194
+ observe(:creativity, 0.6)
195
+ conf3 = engine.identity_confidence
196
+ expect(conf3).to be >= conf2
197
+ expect(conf2).to be >= conf1
198
+ end
199
+ end
200
+
201
+ describe '#identity_label' do
202
+ it 'returns :unknown with no traits' do
203
+ expect(engine.identity_label).to eq(:unknown)
204
+ end
205
+
206
+ it 'returns a valid confidence label' do
207
+ observe(:accuracy, 0.7)
208
+ valid = %i[certain confident developing uncertain unknown]
209
+ expect(valid).to include(engine.identity_label)
210
+ end
211
+ end
212
+
213
+ describe '#anomaly_check' do
214
+ context 'with no baseline' do
215
+ it 'returns anomaly: false and reason: :no_baseline' do
216
+ result = engine.anomaly_check(category: :accuracy, value: 0.5)
217
+ expect(result[:anomaly]).to be false
218
+ expect(result[:reason]).to eq(:no_baseline)
219
+ end
220
+ end
221
+
222
+ context 'with established baseline' do
223
+ before { 10.times { observe(:accuracy, 0.8) } }
224
+
225
+ it 'returns anomaly: false for value near baseline' do
226
+ result = engine.anomaly_check(category: :accuracy, value: 0.8)
227
+ expect(result[:anomaly]).to be false
228
+ end
229
+
230
+ it 'returns anomaly: true for value far from baseline' do
231
+ result = engine.anomaly_check(category: :accuracy, value: 0.0)
232
+ expect(result[:anomaly]).to be true
233
+ end
234
+
235
+ it 'includes deviation in result' do
236
+ result = engine.anomaly_check(category: :accuracy, value: 0.5)
237
+ expect(result[:deviation]).to be >= 0.0
238
+ end
239
+
240
+ it 'includes baseline in result' do
241
+ result = engine.anomaly_check(category: :accuracy, value: 0.5)
242
+ expect(result[:baseline]).to be > 0.0
243
+ end
244
+
245
+ it 'includes threshold in result' do
246
+ result = engine.anomaly_check(category: :accuracy, value: 0.5)
247
+ expect(result[:threshold]).to eq(0.3)
248
+ end
249
+ end
250
+ end
251
+
252
+ describe '#fingerprint_hash' do
253
+ it 'returns nil with no traits' do
254
+ expect(engine.fingerprint_hash).to be_nil
255
+ end
256
+
257
+ it 'returns a 16-char hex string with data' do
258
+ observe(:accuracy, 0.7)
259
+ expect(engine.fingerprint_hash).to match(/\A[0-9a-f]{16}\z/)
260
+ end
261
+
262
+ it 'is deterministic for same baseline values' do
263
+ observe(:accuracy, 0.7)
264
+ h1 = engine.fingerprint_hash
265
+ h2 = engine.fingerprint_hash
266
+ expect(h1).to eq(h2)
267
+ end
268
+
269
+ it 'changes when baseline changes meaningfully' do
270
+ observe(:accuracy, 0.5)
271
+ h1 = engine.fingerprint_hash
272
+ 20.times { observe(:accuracy, 1.0) }
273
+ h2 = engine.fingerprint_hash
274
+ expect(h1).not_to eq(h2)
275
+ end
276
+ end
277
+
278
+ describe '#fingerprint_report' do
279
+ it 'returns a hash with required keys' do
280
+ %i[fingerprint_hash identity_confidence identity_label trait_count sample_count traits].each do |key|
281
+ expect(engine.fingerprint_report).to have_key(key)
282
+ end
283
+ end
284
+
285
+ it 'reflects accumulated state' do
286
+ observe(:accuracy, 0.7)
287
+ observe(:creativity, 0.5)
288
+ report = engine.fingerprint_report
289
+ expect(report[:trait_count]).to eq(2)
290
+ expect(report[:sample_count]).to eq(2)
291
+ end
292
+ end
293
+
294
+ describe '#to_h' do
295
+ it 'delegates to fingerprint_report' do
296
+ observe(:accuracy, 0.7)
297
+ expect(engine.to_h).to eq(engine.fingerprint_report)
298
+ end
299
+ end
300
+
301
+ describe '#trait_count' do
302
+ it 'returns 0 initially' do
303
+ expect(engine.trait_count).to eq(0)
304
+ end
305
+
306
+ it 'counts unique categories' do
307
+ observe(:accuracy, 0.5)
308
+ observe(:creativity, 0.5)
309
+ expect(engine.trait_count).to eq(2)
310
+ end
311
+
312
+ it 'does not double-count same category' do
313
+ observe(:accuracy, 0.5)
314
+ observe(:accuracy, 0.7)
315
+ expect(engine.trait_count).to eq(1)
316
+ end
317
+ end
318
+ end
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/self/fingerprint/client'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Self::Fingerprint::Runners::CognitiveFingerprint do
6
+ let(:client) { Legion::Extensions::Agentic::Self::Fingerprint::Client.new }
7
+
8
+ def seed_trait(category, value, count = 10)
9
+ count.times { client.record_observation(category: category, value: value) }
10
+ end
11
+
12
+ describe '#record_observation' do
13
+ it 'returns status :recorded for valid category' do
14
+ result = client.record_observation(category: :accuracy, value: 0.7)
15
+ expect(result[:status]).to eq(:recorded)
16
+ end
17
+
18
+ it 'accepts string category (coerced to symbol)' do
19
+ result = client.record_observation(category: 'accuracy', value: 0.7)
20
+ expect(result[:status]).to eq(:recorded)
21
+ end
22
+
23
+ it 'returns status :invalid_category for unknown category' do
24
+ result = client.record_observation(category: :fake, value: 0.5)
25
+ expect(result[:status]).to eq(:invalid_category)
26
+ end
27
+
28
+ it 'includes category in result' do
29
+ result = client.record_observation(category: :creativity, value: 0.6)
30
+ expect(result[:category]).to eq(:creativity)
31
+ end
32
+
33
+ it 'includes baseline in result' do
34
+ result = client.record_observation(category: :caution, value: 0.4)
35
+ expect(result[:baseline]).to be_a(Float)
36
+ end
37
+
38
+ it 'increments samples on repeated calls' do
39
+ client.record_observation(category: :accuracy, value: 0.5)
40
+ result = client.record_observation(category: :accuracy, value: 0.6)
41
+ expect(result[:samples]).to eq(2)
42
+ end
43
+ end
44
+
45
+ describe '#verify_identity' do
46
+ context 'with matching observations' do
47
+ before { seed_trait(:accuracy, 0.8) }
48
+
49
+ it 'returns :verified for close observations' do
50
+ result = client.verify_identity(observations: [{ category: :accuracy, value: 0.8 }])
51
+ expect(result[:verdict]).to eq(:verified)
52
+ end
53
+
54
+ it 'returns match_score between 0 and 1' do
55
+ result = client.verify_identity(observations: [{ category: :accuracy, value: 0.8 }])
56
+ expect(result[:match_score]).to be_between(0.0, 1.0)
57
+ end
58
+ end
59
+
60
+ context 'with mismatched observations' do
61
+ before { seed_trait(:accuracy, 0.9, 20) }
62
+
63
+ it 'returns :mismatch for large deviations' do
64
+ result = client.verify_identity(observations: [{ category: :accuracy, value: 0.0 }])
65
+ expect(result[:verdict]).to eq(:mismatch)
66
+ end
67
+ end
68
+
69
+ it 'returns insufficient_data with no prior traits' do
70
+ result = client.verify_identity(observations: [{ category: :accuracy, value: 0.5 }])
71
+ expect(result[:verdict]).to eq(:insufficient_data)
72
+ end
73
+
74
+ it 'returns insufficient_data for empty observations list' do
75
+ seed_trait(:accuracy, 0.7)
76
+ result = client.verify_identity(observations: [])
77
+ expect(result[:verdict]).to eq(:insufficient_data)
78
+ end
79
+ end
80
+
81
+ describe '#anomaly_check' do
82
+ it 'returns no_baseline when category has no data' do
83
+ result = client.anomaly_check(category: :accuracy, value: 0.5)
84
+ expect(result[:reason]).to eq(:no_baseline)
85
+ end
86
+
87
+ context 'with established baseline' do
88
+ before { seed_trait(:accuracy, 0.8) }
89
+
90
+ it 'returns anomaly: false for value near baseline' do
91
+ result = client.anomaly_check(category: :accuracy, value: 0.8)
92
+ expect(result[:anomaly]).to be false
93
+ end
94
+
95
+ it 'returns anomaly: true for outlier value' do
96
+ result = client.anomaly_check(category: :accuracy, value: 0.0)
97
+ expect(result[:anomaly]).to be true
98
+ end
99
+
100
+ it 'accepts string category' do
101
+ result = client.anomaly_check(category: 'accuracy', value: 0.8)
102
+ expect(result).to have_key(:anomaly)
103
+ end
104
+ end
105
+ end
106
+
107
+ describe '#trait_profile' do
108
+ it 'returns empty profile initially' do
109
+ result = client.trait_profile
110
+ expect(result[:profile]).to eq({})
111
+ end
112
+
113
+ it 'returns profile with baselines after observations' do
114
+ seed_trait(:accuracy, 0.7)
115
+ seed_trait(:creativity, 0.4)
116
+ profile = client.trait_profile[:profile]
117
+ expect(profile).to have_key(:accuracy)
118
+ expect(profile).to have_key(:creativity)
119
+ end
120
+ end
121
+
122
+ describe '#strongest_traits' do
123
+ before do
124
+ seed_trait(:accuracy, 0.9)
125
+ seed_trait(:creativity, 0.5)
126
+ seed_trait(:caution, 0.2)
127
+ end
128
+
129
+ it 'returns top 3 traits by default' do
130
+ result = client.strongest_traits
131
+ expect(result[:traits].size).to be <= 3
132
+ end
133
+
134
+ it 'respects top_n keyword' do
135
+ result = client.strongest_traits(top_n: 1)
136
+ expect(result[:traits].size).to eq(1)
137
+ end
138
+
139
+ it 'sorted by baseline descending' do
140
+ result = client.strongest_traits(top_n: 2)
141
+ traits = result[:traits]
142
+ expect(traits.first[:baseline]).to be >= traits.last[:baseline]
143
+ end
144
+ end
145
+
146
+ describe '#weakest_traits' do
147
+ before do
148
+ seed_trait(:accuracy, 0.9)
149
+ seed_trait(:creativity, 0.5)
150
+ seed_trait(:caution, 0.2)
151
+ end
152
+
153
+ it 'returns bottom 3 traits by default' do
154
+ result = client.weakest_traits
155
+ expect(result[:traits].size).to be <= 3
156
+ end
157
+
158
+ it 'sorted by baseline ascending' do
159
+ result = client.weakest_traits(top_n: 2)
160
+ traits = result[:traits]
161
+ expect(traits.first[:baseline]).to be <= traits.last[:baseline]
162
+ end
163
+ end
164
+
165
+ describe '#identity_confidence' do
166
+ it 'returns confidence: 0.0 with no traits' do
167
+ result = client.identity_confidence
168
+ expect(result[:confidence]).to eq(0.0)
169
+ end
170
+
171
+ it 'returns label: :unknown with no traits' do
172
+ result = client.identity_confidence
173
+ expect(result[:label]).to eq(:unknown)
174
+ end
175
+
176
+ it 'returns a valid label after observations' do
177
+ seed_trait(:accuracy, 0.7)
178
+ result = client.identity_confidence
179
+ valid = %i[certain confident developing uncertain unknown]
180
+ expect(valid).to include(result[:label])
181
+ end
182
+
183
+ it 'confidence is between 0 and 1' do
184
+ seed_trait(:accuracy, 0.7)
185
+ result = client.identity_confidence
186
+ expect(result[:confidence]).to be_between(0.0, 1.0)
187
+ end
188
+ end
189
+
190
+ describe '#fingerprint_hash' do
191
+ it 'returns nil hash with no traits' do
192
+ result = client.fingerprint_hash
193
+ expect(result[:fingerprint_hash]).to be_nil
194
+ end
195
+
196
+ it 'returns a hex string after observations' do
197
+ seed_trait(:accuracy, 0.7)
198
+ result = client.fingerprint_hash
199
+ expect(result[:fingerprint_hash]).to match(/\A[0-9a-f]{16}\z/)
200
+ end
201
+ end
202
+
203
+ describe '#fingerprint_report' do
204
+ it 'returns a complete report hash' do
205
+ %i[fingerprint_hash identity_confidence identity_label trait_count sample_count traits].each do |key|
206
+ expect(client.fingerprint_report).to have_key(key)
207
+ end
208
+ end
209
+
210
+ it 'reflects accumulated observations' do
211
+ seed_trait(:accuracy, 0.7)
212
+ report = client.fingerprint_report
213
+ expect(report[:trait_count]).to eq(1)
214
+ end
215
+ end
216
+
217
+ describe '#fingerprint_status' do
218
+ it 'returns trait_count, sample_count, label' do
219
+ result = client.fingerprint_status
220
+ expect(result).to have_key(:trait_count)
221
+ expect(result).to have_key(:sample_count)
222
+ expect(result).to have_key(:label)
223
+ end
224
+
225
+ it 'reflects current state' do
226
+ seed_trait(:accuracy, 0.7)
227
+ result = client.fingerprint_status
228
+ expect(result[:trait_count]).to eq(1)
229
+ expect(result[:sample_count]).to eq(10)
230
+ end
231
+ end
232
+ end