lex-agentic-learning 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 (192) 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-learning.gemspec +30 -0
  7. data/lib/legion/extensions/agentic/learning/anchoring/client.rb +26 -0
  8. data/lib/legion/extensions/agentic/learning/anchoring/helpers/anchor.rb +65 -0
  9. data/lib/legion/extensions/agentic/learning/anchoring/helpers/anchor_store.rb +132 -0
  10. data/lib/legion/extensions/agentic/learning/anchoring/helpers/constants.rb +31 -0
  11. data/lib/legion/extensions/agentic/learning/anchoring/runners/anchoring.rb +100 -0
  12. data/lib/legion/extensions/agentic/learning/anchoring/version.rb +13 -0
  13. data/lib/legion/extensions/agentic/learning/anchoring.rb +19 -0
  14. data/lib/legion/extensions/agentic/learning/catalyst/client.rb +15 -0
  15. data/lib/legion/extensions/agentic/learning/catalyst/helpers/catalyst.rb +87 -0
  16. data/lib/legion/extensions/agentic/learning/catalyst/helpers/catalyst_engine.rb +153 -0
  17. data/lib/legion/extensions/agentic/learning/catalyst/helpers/constants.rb +55 -0
  18. data/lib/legion/extensions/agentic/learning/catalyst/helpers/reaction.rb +87 -0
  19. data/lib/legion/extensions/agentic/learning/catalyst/runners/cognitive_catalyst.rb +103 -0
  20. data/lib/legion/extensions/agentic/learning/catalyst/version.rb +13 -0
  21. data/lib/legion/extensions/agentic/learning/catalyst.rb +22 -0
  22. data/lib/legion/extensions/agentic/learning/chrysalis/client.rb +22 -0
  23. data/lib/legion/extensions/agentic/learning/chrysalis/helpers/chrysalis.rb +137 -0
  24. data/lib/legion/extensions/agentic/learning/chrysalis/helpers/cocoon.rb +89 -0
  25. data/lib/legion/extensions/agentic/learning/chrysalis/helpers/constants.rb +49 -0
  26. data/lib/legion/extensions/agentic/learning/chrysalis/helpers/metamorphosis_engine.rb +157 -0
  27. data/lib/legion/extensions/agentic/learning/chrysalis/runners/cognitive_chrysalis.rb +129 -0
  28. data/lib/legion/extensions/agentic/learning/chrysalis/version.rb +13 -0
  29. data/lib/legion/extensions/agentic/learning/chrysalis.rb +21 -0
  30. data/lib/legion/extensions/agentic/learning/curiosity/client.rb +28 -0
  31. data/lib/legion/extensions/agentic/learning/curiosity/helpers/constants.rb +30 -0
  32. data/lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb +167 -0
  33. data/lib/legion/extensions/agentic/learning/curiosity/helpers/wonder.rb +73 -0
  34. data/lib/legion/extensions/agentic/learning/curiosity/helpers/wonder_store.rb +149 -0
  35. data/lib/legion/extensions/agentic/learning/curiosity/runners/curiosity.rb +163 -0
  36. data/lib/legion/extensions/agentic/learning/curiosity/version.rb +13 -0
  37. data/lib/legion/extensions/agentic/learning/curiosity.rb +21 -0
  38. data/lib/legion/extensions/agentic/learning/epistemic_curiosity/client.rb +28 -0
  39. data/lib/legion/extensions/agentic/learning/epistemic_curiosity/helpers/constants.rb +31 -0
  40. data/lib/legion/extensions/agentic/learning/epistemic_curiosity/helpers/curiosity_engine.rb +122 -0
  41. data/lib/legion/extensions/agentic/learning/epistemic_curiosity/helpers/knowledge_gap.rb +70 -0
  42. data/lib/legion/extensions/agentic/learning/epistemic_curiosity/runners/epistemic_curiosity.rb +106 -0
  43. data/lib/legion/extensions/agentic/learning/epistemic_curiosity/version.rb +13 -0
  44. data/lib/legion/extensions/agentic/learning/epistemic_curiosity.rb +19 -0
  45. data/lib/legion/extensions/agentic/learning/fermentation/client.rb +19 -0
  46. data/lib/legion/extensions/agentic/learning/fermentation/helpers/batch.rb +75 -0
  47. data/lib/legion/extensions/agentic/learning/fermentation/helpers/constants.rb +78 -0
  48. data/lib/legion/extensions/agentic/learning/fermentation/helpers/fermentation_engine.rb +147 -0
  49. data/lib/legion/extensions/agentic/learning/fermentation/helpers/substrate.rb +108 -0
  50. data/lib/legion/extensions/agentic/learning/fermentation/runners/cognitive_fermentation.rb +60 -0
  51. data/lib/legion/extensions/agentic/learning/fermentation/version.rb +13 -0
  52. data/lib/legion/extensions/agentic/learning/fermentation.rb +22 -0
  53. data/lib/legion/extensions/agentic/learning/habit/client.rb +26 -0
  54. data/lib/legion/extensions/agentic/learning/habit/helpers/action_sequence.rb +120 -0
  55. data/lib/legion/extensions/agentic/learning/habit/helpers/constants.rb +44 -0
  56. data/lib/legion/extensions/agentic/learning/habit/helpers/habit_store.rb +148 -0
  57. data/lib/legion/extensions/agentic/learning/habit/runners/habit.rb +86 -0
  58. data/lib/legion/extensions/agentic/learning/habit/version.rb +13 -0
  59. data/lib/legion/extensions/agentic/learning/habit.rb +19 -0
  60. data/lib/legion/extensions/agentic/learning/hebbian/actors/decay.rb +45 -0
  61. data/lib/legion/extensions/agentic/learning/hebbian/client.rb +29 -0
  62. data/lib/legion/extensions/agentic/learning/hebbian/helpers/assembly.rb +82 -0
  63. data/lib/legion/extensions/agentic/learning/hebbian/helpers/assembly_network.rb +190 -0
  64. data/lib/legion/extensions/agentic/learning/hebbian/helpers/constants.rb +50 -0
  65. data/lib/legion/extensions/agentic/learning/hebbian/helpers/unit.rb +94 -0
  66. data/lib/legion/extensions/agentic/learning/hebbian/runners/hebbian_assembly.rb +94 -0
  67. data/lib/legion/extensions/agentic/learning/hebbian/version.rb +13 -0
  68. data/lib/legion/extensions/agentic/learning/hebbian.rb +20 -0
  69. data/lib/legion/extensions/agentic/learning/learning_rate/client.rb +25 -0
  70. data/lib/legion/extensions/agentic/learning/learning_rate/helpers/constants.rb +35 -0
  71. data/lib/legion/extensions/agentic/learning/learning_rate/helpers/rate_model.rb +133 -0
  72. data/lib/legion/extensions/agentic/learning/learning_rate/runners/learning_rate.rb +85 -0
  73. data/lib/legion/extensions/agentic/learning/learning_rate/version.rb +13 -0
  74. data/lib/legion/extensions/agentic/learning/learning_rate.rb +18 -0
  75. data/lib/legion/extensions/agentic/learning/meta_learning/client.rb +27 -0
  76. data/lib/legion/extensions/agentic/learning/meta_learning/helpers/constants.rb +46 -0
  77. data/lib/legion/extensions/agentic/learning/meta_learning/helpers/learning_domain.rb +85 -0
  78. data/lib/legion/extensions/agentic/learning/meta_learning/helpers/meta_learning_engine.rb +202 -0
  79. data/lib/legion/extensions/agentic/learning/meta_learning/helpers/strategy.rb +62 -0
  80. data/lib/legion/extensions/agentic/learning/meta_learning/runners/meta_learning.rb +118 -0
  81. data/lib/legion/extensions/agentic/learning/meta_learning/version.rb +13 -0
  82. data/lib/legion/extensions/agentic/learning/meta_learning.rb +20 -0
  83. data/lib/legion/extensions/agentic/learning/plasticity/client.rb +15 -0
  84. data/lib/legion/extensions/agentic/learning/plasticity/helpers/constants.rb +45 -0
  85. data/lib/legion/extensions/agentic/learning/plasticity/helpers/neural_pathway.rb +85 -0
  86. data/lib/legion/extensions/agentic/learning/plasticity/helpers/plasticity_engine.rb +130 -0
  87. data/lib/legion/extensions/agentic/learning/plasticity/runners/cognitive_plasticity.rb +85 -0
  88. data/lib/legion/extensions/agentic/learning/plasticity/version.rb +13 -0
  89. data/lib/legion/extensions/agentic/learning/plasticity.rb +19 -0
  90. data/lib/legion/extensions/agentic/learning/preference_learning/actors/decay.rb +45 -0
  91. data/lib/legion/extensions/agentic/learning/preference_learning/client.rb +28 -0
  92. data/lib/legion/extensions/agentic/learning/preference_learning/helpers/constants.rb +35 -0
  93. data/lib/legion/extensions/agentic/learning/preference_learning/helpers/option.rb +78 -0
  94. data/lib/legion/extensions/agentic/learning/preference_learning/helpers/preference_engine.rb +121 -0
  95. data/lib/legion/extensions/agentic/learning/preference_learning/runners/preference_learning.rb +84 -0
  96. data/lib/legion/extensions/agentic/learning/preference_learning/version.rb +13 -0
  97. data/lib/legion/extensions/agentic/learning/preference_learning.rb +19 -0
  98. data/lib/legion/extensions/agentic/learning/procedural/client.rb +19 -0
  99. data/lib/legion/extensions/agentic/learning/procedural/helpers/constants.rb +46 -0
  100. data/lib/legion/extensions/agentic/learning/procedural/helpers/learning_engine.rb +160 -0
  101. data/lib/legion/extensions/agentic/learning/procedural/helpers/production.rb +66 -0
  102. data/lib/legion/extensions/agentic/learning/procedural/helpers/skill.rb +101 -0
  103. data/lib/legion/extensions/agentic/learning/procedural/runners/procedural_learning.rb +96 -0
  104. data/lib/legion/extensions/agentic/learning/procedural/version.rb +13 -0
  105. data/lib/legion/extensions/agentic/learning/procedural.rb +20 -0
  106. data/lib/legion/extensions/agentic/learning/scaffolding/client.rb +26 -0
  107. data/lib/legion/extensions/agentic/learning/scaffolding/helpers/constants.rb +42 -0
  108. data/lib/legion/extensions/agentic/learning/scaffolding/helpers/scaffold.rb +136 -0
  109. data/lib/legion/extensions/agentic/learning/scaffolding/helpers/scaffolding_engine.rb +112 -0
  110. data/lib/legion/extensions/agentic/learning/scaffolding/runners/cognitive_scaffolding.rb +107 -0
  111. data/lib/legion/extensions/agentic/learning/scaffolding/version.rb +13 -0
  112. data/lib/legion/extensions/agentic/learning/scaffolding.rb +19 -0
  113. data/lib/legion/extensions/agentic/learning/version.rb +11 -0
  114. data/lib/legion/extensions/agentic/learning.rb +31 -0
  115. data/spec/legion/extensions/agentic/learning/anchoring/client_spec.rb +32 -0
  116. data/spec/legion/extensions/agentic/learning/anchoring/helpers/anchor_spec.rb +130 -0
  117. data/spec/legion/extensions/agentic/learning/anchoring/helpers/anchor_store_spec.rb +201 -0
  118. data/spec/legion/extensions/agentic/learning/anchoring/helpers/constants_spec.rb +63 -0
  119. data/spec/legion/extensions/agentic/learning/anchoring/runners/anchoring_spec.rb +199 -0
  120. data/spec/legion/extensions/agentic/learning/catalyst/client_spec.rb +58 -0
  121. data/spec/legion/extensions/agentic/learning/catalyst/cognitive_catalyst_spec.rb +49 -0
  122. data/spec/legion/extensions/agentic/learning/catalyst/helpers/catalyst_engine_spec.rb +263 -0
  123. data/spec/legion/extensions/agentic/learning/catalyst/helpers/catalyst_spec.rb +214 -0
  124. data/spec/legion/extensions/agentic/learning/catalyst/helpers/reaction_spec.rb +223 -0
  125. data/spec/legion/extensions/agentic/learning/catalyst/runners/cognitive_catalyst_spec.rb +217 -0
  126. data/spec/legion/extensions/agentic/learning/chrysalis/client_spec.rb +83 -0
  127. data/spec/legion/extensions/agentic/learning/chrysalis/cognitive_chrysalis_spec.rb +15 -0
  128. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/chrysalis_engine_spec.rb +57 -0
  129. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/chrysalis_spec.rb +305 -0
  130. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/cocoon_spec.rb +206 -0
  131. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/constants_spec.rb +109 -0
  132. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/metamorphic_cycle_spec.rb +76 -0
  133. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/metamorphosis_engine_spec.rb +247 -0
  134. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/transformation_phase_spec.rb +98 -0
  135. data/spec/legion/extensions/agentic/learning/chrysalis/runners/cognitive_chrysalis_spec.rb +180 -0
  136. data/spec/legion/extensions/agentic/learning/chrysalis/runners/reporting_spec.rb +81 -0
  137. data/spec/legion/extensions/agentic/learning/chrysalis/runners/transformation_spec.rb +74 -0
  138. data/spec/legion/extensions/agentic/learning/curiosity/client_spec.rb +27 -0
  139. data/spec/legion/extensions/agentic/learning/curiosity/helpers/gap_detector_spec.rb +118 -0
  140. data/spec/legion/extensions/agentic/learning/curiosity/helpers/wonder_spec.rb +130 -0
  141. data/spec/legion/extensions/agentic/learning/curiosity/helpers/wonder_store_spec.rb +136 -0
  142. data/spec/legion/extensions/agentic/learning/curiosity/runners/curiosity_spec.rb +159 -0
  143. data/spec/legion/extensions/agentic/learning/epistemic_curiosity/client_spec.rb +47 -0
  144. data/spec/legion/extensions/agentic/learning/epistemic_curiosity/helpers/constants_spec.rb +45 -0
  145. data/spec/legion/extensions/agentic/learning/epistemic_curiosity/helpers/curiosity_engine_spec.rb +229 -0
  146. data/spec/legion/extensions/agentic/learning/epistemic_curiosity/helpers/knowledge_gap_spec.rb +188 -0
  147. data/spec/legion/extensions/agentic/learning/epistemic_curiosity/runners/epistemic_curiosity_spec.rb +175 -0
  148. data/spec/legion/extensions/agentic/learning/fermentation/client_spec.rb +36 -0
  149. data/spec/legion/extensions/agentic/learning/fermentation/helpers/batch_spec.rb +72 -0
  150. data/spec/legion/extensions/agentic/learning/fermentation/helpers/fermentation_engine_spec.rb +138 -0
  151. data/spec/legion/extensions/agentic/learning/fermentation/helpers/substrate_spec.rb +146 -0
  152. data/spec/legion/extensions/agentic/learning/habit/client_spec.rb +50 -0
  153. data/spec/legion/extensions/agentic/learning/habit/helpers/action_sequence_spec.rb +276 -0
  154. data/spec/legion/extensions/agentic/learning/habit/helpers/constants_spec.rb +115 -0
  155. data/spec/legion/extensions/agentic/learning/habit/helpers/habit_store_spec.rb +274 -0
  156. data/spec/legion/extensions/agentic/learning/habit/runners/habit_spec.rb +228 -0
  157. data/spec/legion/extensions/agentic/learning/hebbian/client_spec.rb +38 -0
  158. data/spec/legion/extensions/agentic/learning/hebbian/helpers/assembly_network_spec.rb +142 -0
  159. data/spec/legion/extensions/agentic/learning/hebbian/helpers/assembly_spec.rb +89 -0
  160. data/spec/legion/extensions/agentic/learning/hebbian/helpers/unit_spec.rb +119 -0
  161. data/spec/legion/extensions/agentic/learning/hebbian/runners/hebbian_assembly_spec.rb +109 -0
  162. data/spec/legion/extensions/agentic/learning/learning_rate/client_spec.rb +51 -0
  163. data/spec/legion/extensions/agentic/learning/learning_rate/helpers/constants_spec.rb +29 -0
  164. data/spec/legion/extensions/agentic/learning/learning_rate/helpers/rate_model_spec.rb +151 -0
  165. data/spec/legion/extensions/agentic/learning/learning_rate/runners/learning_rate_spec.rb +92 -0
  166. data/spec/legion/extensions/agentic/learning/meta_learning/client_spec.rb +27 -0
  167. data/spec/legion/extensions/agentic/learning/meta_learning/helpers/constants_spec.rb +43 -0
  168. data/spec/legion/extensions/agentic/learning/meta_learning/helpers/learning_domain_spec.rb +146 -0
  169. data/spec/legion/extensions/agentic/learning/meta_learning/helpers/meta_learning_engine_spec.rb +309 -0
  170. data/spec/legion/extensions/agentic/learning/meta_learning/helpers/strategy_spec.rb +82 -0
  171. data/spec/legion/extensions/agentic/learning/meta_learning/runners/meta_learning_spec.rb +185 -0
  172. data/spec/legion/extensions/agentic/learning/plasticity/helpers/constants_spec.rb +54 -0
  173. data/spec/legion/extensions/agentic/learning/plasticity/helpers/neural_pathway_spec.rb +136 -0
  174. data/spec/legion/extensions/agentic/learning/plasticity/helpers/plasticity_engine_spec.rb +157 -0
  175. data/spec/legion/extensions/agentic/learning/plasticity/runners/cognitive_plasticity_spec.rb +83 -0
  176. data/spec/legion/extensions/agentic/learning/preference_learning/client_spec.rb +17 -0
  177. data/spec/legion/extensions/agentic/learning/preference_learning/helpers/constants_spec.rb +67 -0
  178. data/spec/legion/extensions/agentic/learning/preference_learning/helpers/option_spec.rb +104 -0
  179. data/spec/legion/extensions/agentic/learning/preference_learning/helpers/preference_engine_spec.rb +151 -0
  180. data/spec/legion/extensions/agentic/learning/preference_learning/runners/preference_learning_spec.rb +86 -0
  181. data/spec/legion/extensions/agentic/learning/procedural/client_spec.rb +22 -0
  182. data/spec/legion/extensions/agentic/learning/procedural/helpers/learning_engine_spec.rb +135 -0
  183. data/spec/legion/extensions/agentic/learning/procedural/helpers/production_spec.rb +66 -0
  184. data/spec/legion/extensions/agentic/learning/procedural/helpers/skill_spec.rb +102 -0
  185. data/spec/legion/extensions/agentic/learning/procedural/runners/procedural_learning_spec.rb +94 -0
  186. data/spec/legion/extensions/agentic/learning/scaffolding/client_spec.rb +20 -0
  187. data/spec/legion/extensions/agentic/learning/scaffolding/helpers/constants_spec.rb +36 -0
  188. data/spec/legion/extensions/agentic/learning/scaffolding/helpers/scaffold_spec.rb +187 -0
  189. data/spec/legion/extensions/agentic/learning/scaffolding/helpers/scaffolding_engine_spec.rb +159 -0
  190. data/spec/legion/extensions/agentic/learning/scaffolding/runners/cognitive_scaffolding_spec.rb +163 -0
  191. data/spec/spec_helper.rb +46 -0
  192. metadata +277 -0
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Learning::Fermentation::Helpers::FermentationEngine do
4
+ subject(:engine) { described_class.new }
5
+
6
+ describe '#create_substrate' do
7
+ it 'creates and returns a substrate' do
8
+ sub = engine.create_substrate(substrate_type: :raw_idea, domain: :cognitive)
9
+ expect(sub).to be_a(Legion::Extensions::Agentic::Learning::Fermentation::Helpers::Substrate)
10
+ end
11
+
12
+ it 'adds substrate to a batch' do
13
+ engine.create_substrate(substrate_type: :raw_idea, domain: :cognitive)
14
+ report = engine.fermentation_report
15
+ expect(report[:total_batches]).to eq(1)
16
+ end
17
+
18
+ it 'reuses batch for same domain' do
19
+ engine.create_substrate(substrate_type: :raw_idea, domain: :cognitive)
20
+ engine.create_substrate(substrate_type: :dormant_association, domain: :cognitive)
21
+ expect(engine.fermentation_report[:total_batches]).to eq(1)
22
+ end
23
+
24
+ it 'creates separate batches per domain' do
25
+ engine.create_substrate(substrate_type: :raw_idea, domain: :cognitive)
26
+ engine.create_substrate(substrate_type: :raw_idea, domain: :emotional)
27
+ expect(engine.fermentation_report[:total_batches]).to eq(2)
28
+ end
29
+ end
30
+
31
+ describe '#ferment' do
32
+ it 'ferments an existing substrate' do
33
+ sub = engine.create_substrate(substrate_type: :raw_idea, domain: :cognitive)
34
+ result = engine.ferment(substrate_id: sub.id)
35
+ expect(result.maturity).to be > 0.0
36
+ end
37
+
38
+ it 'returns nil for unknown substrate' do
39
+ expect(engine.ferment(substrate_id: 'nonexistent')).to be_nil
40
+ end
41
+ end
42
+
43
+ describe '#catalyze' do
44
+ it 'catalyzes an existing substrate' do
45
+ sub = engine.create_substrate(substrate_type: :raw_idea, domain: :cognitive, potency: 0.3)
46
+ result = engine.catalyze(substrate_id: sub.id, catalyst_type: :analogy)
47
+ expect(result.potency).to be > 0.3
48
+ end
49
+
50
+ it 'returns nil for unknown substrate' do
51
+ expect(engine.catalyze(substrate_id: 'x', catalyst_type: :analogy)).to be_nil
52
+ end
53
+ end
54
+
55
+ describe '#ferment_all!' do
56
+ it 'advances all substrates' do
57
+ s1 = engine.create_substrate(substrate_type: :raw_idea, domain: :cognitive)
58
+ s2 = engine.create_substrate(substrate_type: :raw_idea, domain: :emotional)
59
+ engine.ferment_all!
60
+ expect(s1.maturity).to be > 0.0
61
+ expect(s2.maturity).to be > 0.0
62
+ end
63
+ end
64
+
65
+ describe 'query methods' do
66
+ before do
67
+ engine.create_substrate(substrate_type: :raw_idea, domain: :cognitive, potency: 0.5)
68
+ engine.create_substrate(substrate_type: :dormant_association, domain: :emotional, potency: 0.3)
69
+ engine.create_substrate(substrate_type: :raw_idea, domain: :cognitive, potency: 0.8)
70
+ end
71
+
72
+ it 'filters by domain' do
73
+ expect(engine.substrates_by_domain(domain: :cognitive).size).to eq(2)
74
+ end
75
+
76
+ it 'filters by type' do
77
+ expect(engine.substrates_by_type(type: :raw_idea).size).to eq(2)
78
+ end
79
+
80
+ it 'filters by stage' do
81
+ expect(engine.substrates_by_stage(stage: :inoculation).size).to eq(3)
82
+ end
83
+
84
+ it 'returns raw substrates' do
85
+ expect(engine.raw_substrates.size).to eq(3)
86
+ end
87
+
88
+ it 'returns most potent' do
89
+ results = engine.most_potent(limit: 2)
90
+ expect(results.size).to eq(2)
91
+ expect(results.first.potency).to be >= results.last.potency
92
+ end
93
+ end
94
+
95
+ describe 'aggregate metrics' do
96
+ it 'returns 0.0 overall_potency for empty engine' do
97
+ expect(engine.overall_potency).to eq(0.0)
98
+ end
99
+
100
+ it 'computes overall_potency' do
101
+ engine.create_substrate(substrate_type: :raw_idea, domain: :cognitive, potency: 0.6)
102
+ expect(engine.overall_potency).to be > 0.0
103
+ end
104
+
105
+ it 'computes overall_maturity' do
106
+ sub = engine.create_substrate(substrate_type: :raw_idea, domain: :cognitive)
107
+ engine.ferment(substrate_id: sub.id)
108
+ expect(engine.overall_maturity).to be > 0.0
109
+ end
110
+
111
+ it 'computes yield_rate' do
112
+ expect(engine.yield_rate).to eq(0.0)
113
+ end
114
+
115
+ it 'computes stage_distribution' do
116
+ engine.create_substrate(substrate_type: :raw_idea, domain: :cognitive)
117
+ dist = engine.stage_distribution
118
+ expect(dist[:inoculation]).to eq(1)
119
+ end
120
+ end
121
+
122
+ describe '#fermentation_report' do
123
+ it 'returns comprehensive report' do
124
+ engine.create_substrate(substrate_type: :raw_idea, domain: :cognitive, potency: 0.5)
125
+ report = engine.fermentation_report
126
+ expect(report).to include(:total_substrates, :total_batches, :overall_potency,
127
+ :potency_label, :overall_maturity, :maturity_label,
128
+ :yield_rate, :stage_distribution, :batches, :most_potent)
129
+ end
130
+ end
131
+
132
+ describe '#to_h' do
133
+ it 'returns summary hash' do
134
+ h = engine.to_h
135
+ expect(h).to include(:total_substrates, :total_batches, :potency, :maturity, :volatility)
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Learning::Fermentation::Helpers::Substrate do
4
+ subject(:substrate) do
5
+ described_class.new(substrate_type: :raw_idea, domain: :cognitive, content: 'test idea')
6
+ end
7
+
8
+ describe '#initialize' do
9
+ it 'assigns a uuid id' do
10
+ expect(substrate.id).to match(/\A[0-9a-f-]{36}\z/)
11
+ end
12
+
13
+ it 'stores substrate_type as symbol' do
14
+ expect(substrate.substrate_type).to eq(:raw_idea)
15
+ end
16
+
17
+ it 'stores domain as symbol' do
18
+ expect(substrate.domain).to eq(:cognitive)
19
+ end
20
+
21
+ it 'stores content' do
22
+ expect(substrate.content).to eq('test idea')
23
+ end
24
+
25
+ it 'defaults potency' do
26
+ expect(substrate.potency).to eq(0.3)
27
+ end
28
+
29
+ it 'starts at inoculation stage' do
30
+ expect(substrate.stage).to eq(:inoculation)
31
+ end
32
+
33
+ it 'starts with zero maturity' do
34
+ expect(substrate.maturity).to eq(0.0)
35
+ end
36
+
37
+ it 'starts with empty catalysts' do
38
+ expect(substrate.catalysts_applied).to be_empty
39
+ end
40
+
41
+ it 'clamps potency to valid range' do
42
+ s = described_class.new(substrate_type: :raw_idea, domain: :cognitive, potency: 1.5)
43
+ expect(s.potency).to eq(1.0)
44
+ end
45
+ end
46
+
47
+ describe '#ferment!' do
48
+ it 'increases maturity' do
49
+ substrate.ferment!
50
+ expect(substrate.maturity).to be > 0.0
51
+ end
52
+
53
+ it 'decreases volatility' do
54
+ initial = substrate.volatility
55
+ substrate.ferment!
56
+ expect(substrate.volatility).to be < initial
57
+ end
58
+
59
+ it 'advances stage with enough fermentation' do
60
+ 5.times { substrate.ferment!(0.1) }
61
+ expect(substrate.stage).not_to eq(:inoculation)
62
+ end
63
+
64
+ it 'reaches peak stage' do
65
+ 18.times { substrate.ferment!(0.05) }
66
+ expect(substrate.stage).to eq(:peak)
67
+ end
68
+
69
+ it 'reaches over_fermented stage' do
70
+ 25.times { substrate.ferment!(0.05) }
71
+ expect(substrate.stage).to eq(:over_fermented)
72
+ end
73
+ end
74
+
75
+ describe '#catalyze!' do
76
+ it 'boosts potency' do
77
+ initial = substrate.potency
78
+ substrate.catalyze!(:analogy)
79
+ expect(substrate.potency).to be > initial
80
+ end
81
+
82
+ it 'records catalyst type' do
83
+ substrate.catalyze!(:dream_residue)
84
+ expect(substrate.catalysts_applied).to include(:dream_residue)
85
+ end
86
+
87
+ it 'slightly increases volatility' do
88
+ initial = substrate.volatility
89
+ substrate.catalyze!(:analogy)
90
+ expect(substrate.volatility).to be > initial
91
+ end
92
+ end
93
+
94
+ describe '#spoil!' do
95
+ it 'halves potency' do
96
+ initial = substrate.potency
97
+ substrate.spoil!
98
+ expect(substrate.potency).to be < initial
99
+ end
100
+
101
+ it 'sets stage to over_fermented' do
102
+ substrate.spoil!
103
+ expect(substrate.stage).to eq(:over_fermented)
104
+ end
105
+ end
106
+
107
+ describe 'predicate methods' do
108
+ it 'reports ripe when potency and maturity are high' do
109
+ s = described_class.new(substrate_type: :raw_idea, domain: :cognitive, potency: 0.8)
110
+ 10.times { s.ferment!(0.06) }
111
+ expect(s).to be_ripe
112
+ end
113
+
114
+ it 'reports spoiled when potency is very low' do
115
+ s = described_class.new(substrate_type: :raw_idea, domain: :cognitive, potency: 0.05)
116
+ s.spoil!
117
+ expect(s).to be_spoiled
118
+ end
119
+
120
+ it 'reports raw at inoculation' do
121
+ expect(substrate).to be_raw
122
+ end
123
+
124
+ it 'reports multi_catalyzed with 3+ catalysts' do
125
+ substrate.catalyze!(:analogy)
126
+ substrate.catalyze!(:contrast)
127
+ substrate.catalyze!(:dream_residue)
128
+ expect(substrate).to be_multi_catalyzed
129
+ end
130
+ end
131
+
132
+ describe '#age' do
133
+ it 'returns age in minutes' do
134
+ expect(substrate.age).to be >= 0.0
135
+ end
136
+ end
137
+
138
+ describe '#to_h' do
139
+ it 'returns hash with all fields' do
140
+ h = substrate.to_h
141
+ expect(h).to include(:id, :substrate_type, :domain, :content, :potency,
142
+ :maturity, :volatility, :stage, :ripe, :peak,
143
+ :spoiled, :catalysts_applied, :created_at)
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/learning/habit/client'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Learning::Habit::Client do
6
+ describe '#initialize' do
7
+ it 'creates a default HabitStore when none provided' do
8
+ client = described_class.new
9
+ expect(client.habit_store).to be_a(Legion::Extensions::Agentic::Learning::Habit::Helpers::HabitStore)
10
+ end
11
+
12
+ it 'uses an injected habit_store' do
13
+ store = Legion::Extensions::Agentic::Learning::Habit::Helpers::HabitStore.new
14
+ client = described_class.new(habit_store: store)
15
+ expect(client.habit_store).to be(store)
16
+ end
17
+ end
18
+
19
+ describe 'runner methods' do
20
+ let(:client) { described_class.new }
21
+
22
+ it 'responds to #observe_action' do
23
+ expect(client).to respond_to(:observe_action)
24
+ end
25
+
26
+ it 'responds to #suggest_habit' do
27
+ expect(client).to respond_to(:suggest_habit)
28
+ end
29
+
30
+ it 'responds to #execute_habit' do
31
+ expect(client).to respond_to(:execute_habit)
32
+ end
33
+
34
+ it 'responds to #decay_habits' do
35
+ expect(client).to respond_to(:decay_habits)
36
+ end
37
+
38
+ it 'responds to #merge_habits' do
39
+ expect(client).to respond_to(:merge_habits)
40
+ end
41
+
42
+ it 'responds to #habit_stats' do
43
+ expect(client).to respond_to(:habit_stats)
44
+ end
45
+
46
+ it 'responds to #habit_repertoire' do
47
+ expect(client).to respond_to(:habit_repertoire)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,276 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Learning::Habit::Helpers::ActionSequence do
4
+ let(:actions) { %i[fetch parse cache] }
5
+ let(:context) { { domain: :api, mood: :neutral } }
6
+ let(:sequence) { described_class.new(actions: actions, context: context) }
7
+
8
+ describe '#initialize' do
9
+ it 'assigns a uuid id' do
10
+ expect(sequence.id).to match(/\A[0-9a-f-]{36}\z/)
11
+ end
12
+
13
+ it 'stores actions as symbols' do
14
+ expect(sequence.actions).to eq(%i[fetch parse cache])
15
+ end
16
+
17
+ it 'stores context' do
18
+ expect(sequence.context).to eq(context)
19
+ end
20
+
21
+ it 'starts with zero execution count' do
22
+ expect(sequence.execution_count).to eq(0)
23
+ end
24
+
25
+ it 'starts with zero success count' do
26
+ expect(sequence.success_count).to eq(0)
27
+ end
28
+
29
+ it 'starts with strength 0.3' do
30
+ expect(sequence.strength).to eq(0.3)
31
+ end
32
+
33
+ it 'starts at :novel maturity' do
34
+ expect(sequence.maturity).to eq(:novel)
35
+ end
36
+
37
+ it 'has nil last_executed' do
38
+ expect(sequence.last_executed).to be_nil
39
+ end
40
+
41
+ it 'records created_at' do
42
+ expect(sequence.created_at).to be_a(Time)
43
+ end
44
+
45
+ it 'converts string actions to symbols' do
46
+ seq = described_class.new(actions: %w[fetch parse])
47
+ expect(seq.actions).to eq(%i[fetch parse])
48
+ end
49
+ end
50
+
51
+ describe '#record_execution' do
52
+ context 'when success: true' do
53
+ it 'increments execution_count' do
54
+ sequence.record_execution(success: true)
55
+ expect(sequence.execution_count).to eq(1)
56
+ end
57
+
58
+ it 'increments success_count' do
59
+ sequence.record_execution(success: true)
60
+ expect(sequence.success_count).to eq(1)
61
+ end
62
+
63
+ it 'increases strength' do
64
+ before = sequence.strength
65
+ sequence.record_execution(success: true)
66
+ expect(sequence.strength).to be > before
67
+ end
68
+
69
+ it 'sets last_executed' do
70
+ sequence.record_execution(success: true)
71
+ expect(sequence.last_executed).to be_a(Time)
72
+ end
73
+ end
74
+
75
+ context 'when success: false' do
76
+ it 'increments execution_count' do
77
+ sequence.record_execution(success: false)
78
+ expect(sequence.execution_count).to eq(1)
79
+ end
80
+
81
+ it 'does not increment success_count' do
82
+ sequence.record_execution(success: false)
83
+ expect(sequence.success_count).to eq(0)
84
+ end
85
+
86
+ it 'decreases strength' do
87
+ before = sequence.strength
88
+ sequence.record_execution(success: false)
89
+ expect(sequence.strength).to be < before
90
+ end
91
+ end
92
+
93
+ it 'does not exceed strength 1.0' do
94
+ 20.times { sequence.record_execution(success: true) }
95
+ expect(sequence.strength).to be <= 1.0
96
+ end
97
+
98
+ it 'does not go below strength 0.0' do
99
+ 20.times { sequence.record_execution(success: false) }
100
+ expect(sequence.strength).to be >= 0.0
101
+ end
102
+ end
103
+
104
+ describe '#maturity progression' do
105
+ it 'advances to :learning after enough executions' do
106
+ threshold = Legion::Extensions::Agentic::Learning::Habit::Helpers::Constants::MATURITY_THRESHOLDS[:learning]
107
+ threshold.times { sequence.record_execution(success: true) }
108
+ expect(sequence.maturity).to eq(:learning)
109
+ end
110
+
111
+ it 'advances to :practiced after enough executions' do
112
+ threshold = Legion::Extensions::Agentic::Learning::Habit::Helpers::Constants::MATURITY_THRESHOLDS[:practiced]
113
+ threshold.times { sequence.record_execution(success: true) }
114
+ expect(sequence.maturity).to eq(:practiced)
115
+ end
116
+
117
+ it 'advances to :habitual after enough executions' do
118
+ threshold = Legion::Extensions::Agentic::Learning::Habit::Helpers::Constants::MATURITY_THRESHOLDS[:habitual]
119
+ threshold.times { sequence.record_execution(success: true) }
120
+ expect(sequence.maturity).to eq(:habitual)
121
+ end
122
+
123
+ it 'advances to :automatic after enough executions' do
124
+ threshold = Legion::Extensions::Agentic::Learning::Habit::Helpers::Constants::MATURITY_THRESHOLDS[:automatic]
125
+ threshold.times { sequence.record_execution(success: true) }
126
+ expect(sequence.maturity).to eq(:automatic)
127
+ end
128
+ end
129
+
130
+ describe '#cognitive_cost' do
131
+ it 'returns the cost for the current maturity stage' do
132
+ cost = Legion::Extensions::Agentic::Learning::Habit::Helpers::Constants::COGNITIVE_COST[:novel]
133
+ expect(sequence.cognitive_cost).to eq(cost)
134
+ end
135
+
136
+ it 'decreases as maturity increases' do
137
+ novel_cost = sequence.cognitive_cost
138
+ threshold = Legion::Extensions::Agentic::Learning::Habit::Helpers::Constants::MATURITY_THRESHOLDS[:automatic]
139
+ threshold.times { sequence.record_execution(success: true) }
140
+ expect(sequence.cognitive_cost).to be < novel_cost
141
+ end
142
+ end
143
+
144
+ describe '#success_rate' do
145
+ it 'returns 0.0 when no executions' do
146
+ expect(sequence.success_rate).to eq(0.0)
147
+ end
148
+
149
+ it 'returns 1.0 when all executions are successful' do
150
+ 3.times { sequence.record_execution(success: true) }
151
+ expect(sequence.success_rate).to eq(1.0)
152
+ end
153
+
154
+ it 'returns partial rate for mixed executions' do
155
+ sequence.record_execution(success: true)
156
+ sequence.record_execution(success: false)
157
+ expect(sequence.success_rate).to eq(0.5)
158
+ end
159
+ end
160
+
161
+ describe '#matches_context?' do
162
+ it 'returns true for empty context' do
163
+ expect(sequence.matches_context?({})).to be true
164
+ end
165
+
166
+ it 'returns true when majority of dimensions match' do
167
+ expect(sequence.matches_context?({ domain: :api, mood: :neutral })).to be true
168
+ end
169
+
170
+ it 'returns false when majority of dimensions do not match' do
171
+ expect(sequence.matches_context?({ domain: :db, mood: :stressed })).to be false
172
+ end
173
+
174
+ it 'returns true for partial match meeting 50% threshold' do
175
+ # context has domain: :api, mood: :neutral
176
+ # querying with domain: :api only — 100% match on stored dims
177
+ expect(sequence.matches_context?({ domain: :api })).to be true
178
+ end
179
+ end
180
+
181
+ describe '#decay' do
182
+ it 'reduces strength by DECAY_RATE' do
183
+ before = sequence.strength
184
+ sequence.decay
185
+ expect(sequence.strength).to be_within(0.001).of(before - Legion::Extensions::Agentic::Learning::Habit::Helpers::Constants::DECAY_RATE)
186
+ end
187
+
188
+ it 'returns true when strength is above floor' do
189
+ expect(sequence.decay).to be true
190
+ end
191
+
192
+ it 'returns false when strength falls to or below floor' do
193
+ floor = Legion::Extensions::Agentic::Learning::Habit::Helpers::Constants::HABIT_STRENGTH_FLOOR
194
+ # Set strength just below floor + decay_rate so one decay pushes it under
195
+ rate = Legion::Extensions::Agentic::Learning::Habit::Helpers::Constants::DECAY_RATE
196
+ seq = described_class.new(actions: %i[a b])
197
+ seq.instance_variable_set(:@strength, floor + (rate * 0.5))
198
+ expect(seq.decay).to be false
199
+ end
200
+ end
201
+
202
+ describe '#mature?' do
203
+ it 'returns false for :novel stage' do
204
+ expect(sequence.mature?).to be false
205
+ end
206
+
207
+ it 'returns true for :habitual stage' do
208
+ threshold = Legion::Extensions::Agentic::Learning::Habit::Helpers::Constants::MATURITY_THRESHOLDS[:habitual]
209
+ threshold.times { sequence.record_execution(success: true) }
210
+ expect(sequence.mature?).to be true
211
+ end
212
+
213
+ it 'returns true for :automatic stage' do
214
+ threshold = Legion::Extensions::Agentic::Learning::Habit::Helpers::Constants::MATURITY_THRESHOLDS[:automatic]
215
+ threshold.times { sequence.record_execution(success: true) }
216
+ expect(sequence.mature?).to be true
217
+ end
218
+ end
219
+
220
+ describe '#stale?' do
221
+ it 'returns true when never executed' do
222
+ expect(sequence.stale?).to be true
223
+ end
224
+
225
+ it 'returns false when executed recently' do
226
+ sequence.record_execution(success: true)
227
+ expect(sequence.stale?(3600)).to be false
228
+ end
229
+
230
+ it 'returns true when last executed beyond threshold' do
231
+ sequence.record_execution(success: true)
232
+ sequence.instance_variable_set(:@last_executed, Time.now.utc - 7200)
233
+ expect(sequence.stale?(3600)).to be true
234
+ end
235
+ end
236
+
237
+ describe '#similarity' do
238
+ it 'returns 1.0 for identical sequences' do
239
+ other = described_class.new(actions: actions)
240
+ expect(sequence.similarity(other)).to eq(1.0)
241
+ end
242
+
243
+ it 'returns 0.0 for completely different sequences' do
244
+ other = described_class.new(actions: %i[open write close])
245
+ expect(sequence.similarity(other)).to eq(0.0)
246
+ end
247
+
248
+ it 'returns a partial score for overlapping sequences' do
249
+ other = described_class.new(actions: %i[fetch transform store])
250
+ score = sequence.similarity(other)
251
+ expect(score).to be > 0.0
252
+ expect(score).to be < 1.0
253
+ end
254
+ end
255
+
256
+ describe '#to_h' do
257
+ it 'returns a hash with all expected keys' do
258
+ h = sequence.to_h
259
+ expect(h).to have_key(:id)
260
+ expect(h).to have_key(:actions)
261
+ expect(h).to have_key(:context)
262
+ expect(h).to have_key(:execution_count)
263
+ expect(h).to have_key(:success_count)
264
+ expect(h).to have_key(:strength)
265
+ expect(h).to have_key(:maturity)
266
+ expect(h).to have_key(:cognitive_cost)
267
+ expect(h).to have_key(:success_rate)
268
+ expect(h).to have_key(:last_executed)
269
+ expect(h).to have_key(:created_at)
270
+ end
271
+
272
+ it 'includes the correct maturity' do
273
+ expect(sequence.to_h[:maturity]).to eq(:novel)
274
+ end
275
+ end
276
+ end