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,247 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Learning::Chrysalis::Helpers::MetamorphosisEngine do
4
+ subject(:engine) { described_class.new }
5
+
6
+ let(:chrysalis_id) { engine.create_chrysalis(chrysalis_type: :silk, content: 'raw idea')[:chrysalis][:id] }
7
+ let(:cocoon_id) { engine.create_cocoon(environment: 'garden')[:cocoon][:id] }
8
+
9
+ describe '#create_chrysalis' do
10
+ it 'returns success: true' do
11
+ result = engine.create_chrysalis(chrysalis_type: :silk, content: 'test')
12
+ expect(result[:success]).to be true
13
+ end
14
+
15
+ it 'returns a chrysalis hash' do
16
+ result = engine.create_chrysalis(chrysalis_type: :bark, content: 'test')
17
+ expect(result[:chrysalis]).to be_a(Hash)
18
+ expect(result[:chrysalis][:chrysalis_type]).to eq(:bark)
19
+ end
20
+
21
+ it 'returns success: false when at capacity' do
22
+ stub_const('Legion::Extensions::Agentic::Learning::Chrysalis::Helpers::Constants::MAX_CHRYSALISES', 1)
23
+ engine.create_chrysalis(chrysalis_type: :silk, content: 'first')
24
+ result = engine.create_chrysalis(chrysalis_type: :paper, content: 'second')
25
+ expect(result[:success]).to be false
26
+ expect(result[:reason]).to eq(:capacity_exceeded)
27
+ end
28
+ end
29
+
30
+ describe '#create_cocoon' do
31
+ it 'returns success: true' do
32
+ result = engine.create_cocoon(environment: 'meadow')
33
+ expect(result[:success]).to be true
34
+ end
35
+
36
+ it 'includes cocoon hash with environment' do
37
+ result = engine.create_cocoon(environment: 'meadow')
38
+ expect(result[:cocoon][:environment]).to eq('meadow')
39
+ end
40
+ end
41
+
42
+ describe '#spin' do
43
+ it 'transitions the chrysalis to :spinning' do
44
+ result = engine.spin(chrysalis_id: chrysalis_id)
45
+ expect(result[:success]).to be true
46
+ expect(result[:stage]).to eq(:spinning)
47
+ end
48
+
49
+ it 'returns failure for unknown chrysalis_id' do
50
+ result = engine.spin(chrysalis_id: 'bad-id')
51
+ expect(result[:success]).to be false
52
+ expect(result[:reason]).to eq(:not_found)
53
+ end
54
+
55
+ it 'returns failure when already spun' do
56
+ engine.spin(chrysalis_id: chrysalis_id)
57
+ result = engine.spin(chrysalis_id: chrysalis_id)
58
+ expect(result[:success]).to be false
59
+ end
60
+ end
61
+
62
+ describe '#enclose' do
63
+ it 'spins, cocoons, and shelters the chrysalis' do
64
+ result = engine.enclose(chrysalis_id: chrysalis_id, cocoon_id: cocoon_id)
65
+ expect(result[:success]).to be true
66
+ expect(result[:stage]).to eq(:cocooned)
67
+ end
68
+
69
+ it 'returns failure for unknown chrysalis' do
70
+ result = engine.enclose(chrysalis_id: 'bad-id', cocoon_id: cocoon_id)
71
+ expect(result[:success]).to be false
72
+ expect(result[:reason]).to eq(:chrysalis_not_found)
73
+ end
74
+
75
+ it 'returns failure for unknown cocoon' do
76
+ result = engine.enclose(chrysalis_id: chrysalis_id, cocoon_id: 'bad-cocoon')
77
+ expect(result[:success]).to be false
78
+ expect(result[:reason]).to eq(:cocoon_not_found)
79
+ end
80
+ end
81
+
82
+ describe '#incubate' do
83
+ before { engine.enclose(chrysalis_id: chrysalis_id, cocoon_id: cocoon_id) }
84
+
85
+ it 'increases transformation_progress' do
86
+ result = engine.incubate(chrysalis_id: chrysalis_id)
87
+ expect(result[:success]).to be true
88
+ expect(result[:progress]).to be > 0.0
89
+ end
90
+
91
+ it 'returns failure for unknown chrysalis' do
92
+ result = engine.incubate(chrysalis_id: 'bad-id')
93
+ expect(result[:success]).to be false
94
+ end
95
+
96
+ it 'returns failure for an already-butterfly chrysalis' do
97
+ 12.times { engine.incubate(chrysalis_id: chrysalis_id) }
98
+ engine.force_emerge(chrysalis_id: chrysalis_id)
99
+ result = engine.incubate(chrysalis_id: chrysalis_id)
100
+ expect(result[:success]).to be false
101
+ expect(result[:reason]).to eq(:already_butterfly)
102
+ end
103
+
104
+ it 'applies cocoon growth_modifier for ideal conditions' do
105
+ ideal_cocoon = engine.create_cocoon(environment: 'ideal')[:cocoon][:id]
106
+ engine.enclose(chrysalis_id: engine.create_chrysalis(chrysalis_type: :leaf, content: 'c2')[:chrysalis][:id],
107
+ cocoon_id: ideal_cocoon)
108
+ cid2 = engine.instance_variable_get(:@chrysalises).values.last.id
109
+ result = engine.incubate(chrysalis_id: cid2)
110
+ expect(result[:progress]).to be_within(0.01).of(0.18)
111
+ end
112
+ end
113
+
114
+ describe '#force_emerge' do
115
+ it 'forces butterfly regardless of progress' do
116
+ result = engine.force_emerge(chrysalis_id: chrysalis_id)
117
+ expect(result[:success]).to be true
118
+ expect(result[:stage]).to eq(:butterfly)
119
+ end
120
+
121
+ it 'returns failure for unknown chrysalis' do
122
+ result = engine.force_emerge(chrysalis_id: 'bad-id')
123
+ expect(result[:success]).to be false
124
+ end
125
+ end
126
+
127
+ describe '#natural_emerge' do
128
+ context 'when not ready' do
129
+ it 'returns failure with :not_ready reason' do
130
+ result = engine.natural_emerge(chrysalis_id: chrysalis_id)
131
+ expect(result[:success]).to be false
132
+ expect(result[:reason]).to eq(:not_ready)
133
+ end
134
+ end
135
+
136
+ context 'when ready (progress >= EMERGENCE_THRESHOLD)' do
137
+ before do
138
+ engine.enclose(chrysalis_id: chrysalis_id, cocoon_id: cocoon_id)
139
+ 12.times { engine.incubate(chrysalis_id: chrysalis_id) }
140
+ end
141
+
142
+ it 'successfully emerges when progress is sufficient' do
143
+ result = engine.natural_emerge(chrysalis_id: chrysalis_id)
144
+ expect(result[:success]).to be true
145
+ expect(result[:stage]).to eq(:butterfly)
146
+ end
147
+ end
148
+
149
+ it 'returns failure for unknown chrysalis' do
150
+ result = engine.natural_emerge(chrysalis_id: 'bad-id')
151
+ expect(result[:success]).to be false
152
+ expect(result[:reason]).to eq(:not_found)
153
+ end
154
+ end
155
+
156
+ describe '#disturb_cocoon' do
157
+ before do
158
+ engine.enclose(chrysalis_id: chrysalis_id, cocoon_id: cocoon_id)
159
+ end
160
+
161
+ it 'reduces protection of sheltered chrysalises' do
162
+ engine.disturb_cocoon(cocoon_id: cocoon_id, force: 0.1)
163
+ c = engine.instance_variable_get(:@chrysalises)[chrysalis_id]
164
+ expect(c.protection).to be < 0.8
165
+ end
166
+
167
+ it 'returns disturbed array with chrysalis info' do
168
+ result = engine.disturb_cocoon(cocoon_id: cocoon_id, force: 0.1)
169
+ expect(result[:disturbed]).not_to be_empty
170
+ expect(result[:disturbed].first[:chrysalis_id]).to eq(chrysalis_id)
171
+ end
172
+
173
+ it 'returns failure for unknown cocoon' do
174
+ result = engine.disturb_cocoon(cocoon_id: 'bad-id', force: 0.1)
175
+ expect(result[:success]).to be false
176
+ end
177
+ end
178
+
179
+ describe '#incubate_all!' do
180
+ it 'incubates all cocooned and transforming chrysalises' do
181
+ cid2 = engine.create_chrysalis(chrysalis_type: :bark, content: 'idea 2')[:chrysalis][:id]
182
+ cid3 = engine.create_chrysalis(chrysalis_type: :leaf, content: 'idea 3')[:chrysalis][:id]
183
+ coc2 = engine.create_cocoon(environment: 'forest')[:cocoon][:id]
184
+ engine.enclose(chrysalis_id: cid2, cocoon_id: coc2)
185
+ engine.enclose(chrysalis_id: cid3, cocoon_id: coc2)
186
+ result = engine.incubate_all!
187
+ expect(result[:success]).to be true
188
+ expect(result[:incubated]).to eq(2)
189
+ end
190
+
191
+ it 'returns empty results when no eligible chrysalises' do
192
+ result = engine.incubate_all!
193
+ expect(result[:incubated]).to eq(0)
194
+ end
195
+ end
196
+
197
+ describe '#butterflies' do
198
+ it 'returns empty array initially' do
199
+ expect(engine.butterflies).to eq([])
200
+ end
201
+
202
+ it 'returns emerged butterflies' do
203
+ engine.force_emerge(chrysalis_id: chrysalis_id)
204
+ expect(engine.butterflies.size).to eq(1)
205
+ end
206
+ end
207
+
208
+ describe '#metamorphosis_report' do
209
+ it 'returns a hash with report keys' do
210
+ result = engine.metamorphosis_report
211
+ expect(result).to include(
212
+ :total_chrysalises,
213
+ :total_cocoons,
214
+ :butterflies_count,
215
+ :premature_count,
216
+ :avg_beauty,
217
+ :avg_progress,
218
+ :cocooned_count,
219
+ :transforming_count
220
+ )
221
+ end
222
+
223
+ it 'reflects created chrysalises and cocoons' do
224
+ engine.create_chrysalis(chrysalis_type: :silk, content: 'counted')
225
+ engine.create_cocoon(environment: 'a')
226
+ engine.create_cocoon(environment: 'b')
227
+ result = engine.metamorphosis_report
228
+ expect(result[:total_chrysalises]).to eq(1)
229
+ expect(result[:total_cocoons]).to eq(2)
230
+ end
231
+
232
+ it 'counts butterflies correctly' do
233
+ engine.force_emerge(chrysalis_id: chrysalis_id)
234
+ report = engine.metamorphosis_report
235
+ expect(report[:butterflies_count]).to eq(1)
236
+ end
237
+
238
+ it 'returns avg_beauty 0.0 when no butterflies' do
239
+ expect(engine.metamorphosis_report[:avg_beauty]).to eq(0.0)
240
+ end
241
+
242
+ it 'returns avg_progress 0.0 when no chrysalises' do
243
+ empty_engine = described_class.new
244
+ expect(empty_engine.metamorphosis_report[:avg_progress]).to eq(0.0)
245
+ end
246
+ end
247
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Tests focused on Cocoon environment modifiers and their effect on transformation
4
+ RSpec.describe 'Cocoon environment effects' do
5
+ let(:engine) { Legion::Extensions::Agentic::Learning::Chrysalis::Helpers::MetamorphosisEngine.new }
6
+
7
+ describe 'ideal cocoon' do
8
+ let(:ideal_cocoon) do
9
+ Legion::Extensions::Agentic::Learning::Chrysalis::Helpers::Cocoon.new(
10
+ environment: 'greenhouse',
11
+ temperature: 0.55,
12
+ humidity: 0.55
13
+ )
14
+ end
15
+
16
+ it 'reports ideal? true' do
17
+ expect(ideal_cocoon.ideal?).to be true
18
+ end
19
+
20
+ it 'has growth_modifier of 0.1' do
21
+ expect(ideal_cocoon.growth_modifier).to eq(0.1)
22
+ end
23
+
24
+ it 'accelerates transformation compared to bare incubation' do
25
+ base_rate = Legion::Extensions::Agentic::Learning::Chrysalis::Helpers::Constants::TRANSFORMATION_RATE
26
+ rate_with_cocoon = base_rate + ideal_cocoon.growth_modifier
27
+ expect(rate_with_cocoon).to be > base_rate
28
+ end
29
+ end
30
+
31
+ describe 'hostile cocoon' do
32
+ let(:hostile_cocoon) do
33
+ Legion::Extensions::Agentic::Learning::Chrysalis::Helpers::Cocoon.new(
34
+ environment: 'furnace',
35
+ temperature: 0.95,
36
+ humidity: 0.5
37
+ )
38
+ end
39
+
40
+ it 'reports hostile? true' do
41
+ expect(hostile_cocoon.hostile?).to be true
42
+ end
43
+
44
+ it 'has negative growth_modifier' do
45
+ expect(hostile_cocoon.growth_modifier).to be < 0
46
+ end
47
+
48
+ it 'slows transformation below base rate' do
49
+ base_rate = Legion::Extensions::Agentic::Learning::Chrysalis::Helpers::Constants::TRANSFORMATION_RATE
50
+ rate_with_cocoon = base_rate + hostile_cocoon.growth_modifier
51
+ expect(rate_with_cocoon).to be < base_rate
52
+ end
53
+ end
54
+
55
+ describe 'neutral cocoon' do
56
+ let(:neutral_cocoon) do
57
+ Legion::Extensions::Agentic::Learning::Chrysalis::Helpers::Cocoon.new(
58
+ environment: 'plain',
59
+ temperature: 0.2,
60
+ humidity: 0.5
61
+ )
62
+ end
63
+
64
+ it 'has growth_modifier of 0.0' do
65
+ expect(neutral_cocoon.growth_modifier).to eq(0.0)
66
+ end
67
+
68
+ it 'does not accelerate or slow transformation' do
69
+ base_rate = Legion::Extensions::Agentic::Learning::Chrysalis::Helpers::Constants::TRANSFORMATION_RATE
70
+ expect(base_rate + neutral_cocoon.growth_modifier).to eq(base_rate)
71
+ end
72
+ end
73
+
74
+ describe 'temperature and humidity adjustments' do
75
+ let(:cocoon) { Legion::Extensions::Agentic::Learning::Chrysalis::Helpers::Cocoon.new(environment: 'test') }
76
+
77
+ it 'moisten and dry are inverse operations (roughly)' do
78
+ original = cocoon.humidity
79
+ cocoon.moisten!
80
+ cocoon.dry!
81
+ expect(cocoon.humidity).to be_within(0.001).of(original)
82
+ end
83
+
84
+ it 'warm and cool are inverse operations (roughly)' do
85
+ original = cocoon.temperature
86
+ cocoon.warm!
87
+ cocoon.cool!
88
+ expect(cocoon.temperature).to be_within(0.001).of(original)
89
+ end
90
+
91
+ it 'sheltering and exposing a chrysalis affects chrysalis_ids' do
92
+ cocoon.shelter('c-1')
93
+ expect(cocoon.chrysalis_ids).to include('c-1')
94
+ cocoon.expose('c-1')
95
+ expect(cocoon.chrysalis_ids).not_to include('c-1')
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Learning::Chrysalis::Runners::CognitiveChrysalis do
4
+ subject(:runner) { described_class }
5
+
6
+ let(:engine) { Legion::Extensions::Agentic::Learning::Chrysalis::Helpers::MetamorphosisEngine.new }
7
+
8
+ describe '.create_chrysalis' do
9
+ it 'returns success: true with a valid type' do
10
+ result = runner.create_chrysalis(chrysalis_type: :silk, content: 'thought', engine: engine)
11
+ expect(result[:success]).to be true
12
+ end
13
+
14
+ it 'returns success: false for an invalid type' do
15
+ result = runner.create_chrysalis(chrysalis_type: :invalid, content: 'x', engine: engine)
16
+ expect(result[:success]).to be false
17
+ expect(result[:reason]).to eq(:invalid_type)
18
+ end
19
+
20
+ it 'returns the valid_types list on invalid type' do
21
+ result = runner.create_chrysalis(chrysalis_type: :invalid, content: 'x', engine: engine)
22
+ expect(result[:valid_types]).to eq(Legion::Extensions::Agentic::Learning::Chrysalis::Helpers::Constants::CHRYSALIS_TYPES)
23
+ end
24
+
25
+ it 'defaults chrysalis_type to :silk when not provided' do
26
+ result = runner.create_chrysalis(content: 'default', engine: engine)
27
+ expect(result[:success]).to be true
28
+ end
29
+ end
30
+
31
+ describe '.create_cocoon' do
32
+ it 'returns success: true' do
33
+ result = runner.create_cocoon(environment: 'meadow', engine: engine)
34
+ expect(result[:success]).to be true
35
+ end
36
+
37
+ it 'defaults environment to "default"' do
38
+ result = runner.create_cocoon(engine: engine)
39
+ expect(result[:success]).to be true
40
+ end
41
+ end
42
+
43
+ describe '.spin' do
44
+ let(:chrysalis_id) { runner.create_chrysalis(chrysalis_type: :silk, content: 'c', engine: engine)[:chrysalis][:id] }
45
+
46
+ it 'returns success: true for a valid chrysalis' do
47
+ result = runner.spin(chrysalis_id: chrysalis_id, engine: engine)
48
+ expect(result[:success]).to be true
49
+ end
50
+
51
+ it 'returns failure for missing chrysalis_id' do
52
+ result = runner.spin(chrysalis_id: nil, engine: engine)
53
+ expect(result[:success]).to be false
54
+ expect(result[:reason]).to eq(:missing_chrysalis_id)
55
+ end
56
+
57
+ it 'returns failure for unknown chrysalis_id' do
58
+ result = runner.spin(chrysalis_id: 'ghost', engine: engine)
59
+ expect(result[:success]).to be false
60
+ end
61
+ end
62
+
63
+ describe '.enclose' do
64
+ let(:chrysalis_id) { runner.create_chrysalis(chrysalis_type: :leaf, content: 'c', engine: engine)[:chrysalis][:id] }
65
+ let(:cocoon_id) { runner.create_cocoon(environment: 'wood', engine: engine)[:cocoon][:id] }
66
+
67
+ it 'encloses a chrysalis in a cocoon' do
68
+ result = runner.enclose(chrysalis_id: chrysalis_id, cocoon_id: cocoon_id, engine: engine)
69
+ expect(result[:success]).to be true
70
+ expect(result[:stage]).to eq(:cocooned)
71
+ end
72
+
73
+ it 'returns failure for missing chrysalis_id' do
74
+ result = runner.enclose(chrysalis_id: nil, cocoon_id: cocoon_id, engine: engine)
75
+ expect(result[:success]).to be false
76
+ end
77
+
78
+ it 'returns failure for missing cocoon_id' do
79
+ result = runner.enclose(chrysalis_id: chrysalis_id, cocoon_id: nil, engine: engine)
80
+ expect(result[:success]).to be false
81
+ end
82
+ end
83
+
84
+ describe '.incubate' do
85
+ let(:chrysalis_id) do
86
+ runner.create_chrysalis(chrysalis_type: :paper, content: 'c', engine: engine)[:chrysalis][:id]
87
+ end
88
+ let(:cocoon_id) { runner.create_cocoon(environment: 'nest', engine: engine)[:cocoon][:id] }
89
+
90
+ before { runner.enclose(chrysalis_id: chrysalis_id, cocoon_id: cocoon_id, engine: engine) }
91
+
92
+ it 'increases progress' do
93
+ result = runner.incubate(chrysalis_id: chrysalis_id, engine: engine)
94
+ expect(result[:success]).to be true
95
+ expect(result[:progress]).to be > 0.0
96
+ end
97
+
98
+ it 'returns failure for missing chrysalis_id' do
99
+ result = runner.incubate(chrysalis_id: nil, engine: engine)
100
+ expect(result[:success]).to be false
101
+ end
102
+ end
103
+
104
+ describe '.emerge' do
105
+ let(:chrysalis_id) { runner.create_chrysalis(chrysalis_type: :bark, content: 'c', engine: engine)[:chrysalis][:id] }
106
+ let(:cocoon_id) { runner.create_cocoon(environment: 'oak', engine: engine)[:cocoon][:id] }
107
+
108
+ before do
109
+ runner.enclose(chrysalis_id: chrysalis_id, cocoon_id: cocoon_id, engine: engine)
110
+ 12.times { runner.incubate(chrysalis_id: chrysalis_id, engine: engine) }
111
+ end
112
+
113
+ it 'emerges naturally when ready' do
114
+ result = runner.emerge(chrysalis_id: chrysalis_id, engine: engine)
115
+ expect(result[:success]).to be true
116
+ expect(result[:stage]).to eq(:butterfly)
117
+ end
118
+
119
+ it 'force-emerges when force: true' do
120
+ cid2 = runner.create_chrysalis(chrysalis_type: :silk, content: 'fresh', engine: engine)[:chrysalis][:id]
121
+ result = runner.emerge(chrysalis_id: cid2, force: true, engine: engine)
122
+ expect(result[:success]).to be true
123
+ expect(result[:stage]).to eq(:butterfly)
124
+ end
125
+
126
+ it 'returns failure for missing chrysalis_id' do
127
+ result = runner.emerge(chrysalis_id: nil, engine: engine)
128
+ expect(result[:success]).to be false
129
+ end
130
+ end
131
+
132
+ describe '.disturb' do
133
+ let(:chrysalis_id) { runner.create_chrysalis(chrysalis_type: :silk, content: 'c', engine: engine)[:chrysalis][:id] }
134
+ let(:cocoon_id) { runner.create_cocoon(environment: 'pond', engine: engine)[:cocoon][:id] }
135
+
136
+ before { runner.enclose(chrysalis_id: chrysalis_id, cocoon_id: cocoon_id, engine: engine) }
137
+
138
+ it 'disturbs the cocoon' do
139
+ result = runner.disturb(cocoon_id: cocoon_id, force: 0.1, engine: engine)
140
+ expect(result[:success]).to be true
141
+ expect(result[:disturbed]).not_to be_empty
142
+ end
143
+
144
+ it 'returns failure for missing cocoon_id' do
145
+ result = runner.disturb(cocoon_id: nil, engine: engine)
146
+ expect(result[:success]).to be false
147
+ end
148
+ end
149
+
150
+ describe '.list_chrysalises' do
151
+ it 'returns success: true' do
152
+ result = runner.list_chrysalises(engine: engine)
153
+ expect(result[:success]).to be true
154
+ end
155
+
156
+ it 'returns empty array when no chrysalises' do
157
+ result = runner.list_chrysalises(engine: engine)
158
+ expect(result[:chrysalises]).to eq([])
159
+ end
160
+
161
+ it 'lists all created chrysalises' do
162
+ runner.create_chrysalis(chrysalis_type: :silk, content: 'one', engine: engine)
163
+ runner.create_chrysalis(chrysalis_type: :paper, content: 'two', engine: engine)
164
+ result = runner.list_chrysalises(engine: engine)
165
+ expect(result[:count]).to eq(2)
166
+ end
167
+ end
168
+
169
+ describe '.metamorphosis_status' do
170
+ it 'returns success: true' do
171
+ result = runner.metamorphosis_status(engine: engine)
172
+ expect(result[:success]).to be true
173
+ end
174
+
175
+ it 'includes report keys' do
176
+ result = runner.metamorphosis_status(engine: engine)
177
+ expect(result).to include(:total_chrysalises, :butterflies_count, :avg_beauty)
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Tests focused on status reporting and list operations
4
+ RSpec.describe 'Metamorphosis status reporting' do
5
+ let(:engine) { Legion::Extensions::Agentic::Learning::Chrysalis::Helpers::MetamorphosisEngine.new }
6
+ let(:runner) { Legion::Extensions::Agentic::Learning::Chrysalis::Runners::CognitiveChrysalis }
7
+
8
+ describe 'list_chrysalises' do
9
+ it 'returns empty list for a fresh engine' do
10
+ result = runner.list_chrysalises(engine: engine)
11
+ expect(result[:success]).to be true
12
+ expect(result[:chrysalises]).to eq([])
13
+ expect(result[:count]).to eq(0)
14
+ end
15
+
16
+ it 'lists all created chrysalises with their hashes' do
17
+ runner.create_chrysalis(chrysalis_type: :silk, content: 'alpha', engine: engine)
18
+ runner.create_chrysalis(chrysalis_type: :paper, content: 'beta', engine: engine)
19
+ result = runner.list_chrysalises(engine: engine)
20
+ expect(result[:count]).to eq(2)
21
+ types = result[:chrysalises].map { |c| c[:chrysalis_type] }
22
+ expect(types).to contain_exactly(:silk, :paper)
23
+ end
24
+
25
+ it 'includes chrysalis details in each entry' do
26
+ runner.create_chrysalis(chrysalis_type: :bark, content: 'idea', engine: engine)
27
+ c = runner.list_chrysalises(engine: engine)[:chrysalises].first
28
+ expect(c).to have_key(:id)
29
+ expect(c).to have_key(:stage)
30
+ expect(c).to have_key(:transformation_progress)
31
+ expect(c).to have_key(:beauty)
32
+ end
33
+ end
34
+
35
+ describe 'metamorphosis_status' do
36
+ it 'returns success: true' do
37
+ expect(runner.metamorphosis_status(engine: engine)[:success]).to be true
38
+ end
39
+
40
+ it 'shows zeroed counts for empty engine' do
41
+ status = runner.metamorphosis_status(engine: engine)
42
+ expect(status[:total_chrysalises]).to eq(0)
43
+ expect(status[:butterflies_count]).to eq(0)
44
+ expect(status[:premature_count]).to eq(0)
45
+ end
46
+
47
+ it 'shows correct butterflies_count after natural emergence' do
48
+ cid = runner.create_chrysalis(chrysalis_type: :leaf, content: 'c', engine: engine)[:chrysalis][:id]
49
+ coc = runner.create_cocoon(environment: 'park', engine: engine)[:cocoon][:id]
50
+ runner.enclose(chrysalis_id: cid, cocoon_id: coc, engine: engine)
51
+ 12.times { runner.incubate(chrysalis_id: cid, engine: engine) }
52
+ runner.emerge(chrysalis_id: cid, engine: engine)
53
+ expect(runner.metamorphosis_status(engine: engine)[:butterflies_count]).to eq(1)
54
+ end
55
+
56
+ it 'shows correct premature_count after forced emergence' do
57
+ cid = runner.create_chrysalis(chrysalis_type: :silk, content: 'forced', engine: engine)[:chrysalis][:id]
58
+ runner.emerge(chrysalis_id: cid, force: true, engine: engine)
59
+ expect(runner.metamorphosis_status(engine: engine)[:premature_count]).to eq(1)
60
+ end
61
+
62
+ it 'includes total_cocoons' do
63
+ runner.create_cocoon(environment: 'a', engine: engine)
64
+ runner.create_cocoon(environment: 'b', engine: engine)
65
+ expect(runner.metamorphosis_status(engine: engine)[:total_cocoons]).to eq(2)
66
+ end
67
+
68
+ it 'includes avg_beauty as 0.0 when no butterflies' do
69
+ runner.create_chrysalis(chrysalis_type: :bark, content: 'c', engine: engine)
70
+ expect(runner.metamorphosis_status(engine: engine)[:avg_beauty]).to eq(0.0)
71
+ end
72
+
73
+ it 'includes avg_progress > 0.0 after incubation' do
74
+ cid = runner.create_chrysalis(chrysalis_type: :paper, content: 'p', engine: engine)[:chrysalis][:id]
75
+ coc = runner.create_cocoon(environment: 'env', engine: engine)[:cocoon][:id]
76
+ runner.enclose(chrysalis_id: cid, cocoon_id: coc, engine: engine)
77
+ runner.incubate(chrysalis_id: cid, engine: engine)
78
+ expect(runner.metamorphosis_status(engine: engine)[:avg_progress]).to be > 0.0
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Integration tests for the full metamorphosis pipeline
4
+ RSpec.describe 'Full metamorphosis pipeline integration' do
5
+ let(:engine) { Legion::Extensions::Agentic::Learning::Chrysalis::Helpers::MetamorphosisEngine.new }
6
+ let(:runner) { Legion::Extensions::Agentic::Learning::Chrysalis::Runners::CognitiveChrysalis }
7
+
8
+ it 'creates and fully transforms a chrysalis through the natural emergence path' do
9
+ cid = runner.create_chrysalis(chrysalis_type: :silk, content: 'deep insight', engine: engine)[:chrysalis][:id]
10
+ coc = runner.create_cocoon(environment: 'sacred_grove', engine: engine)[:cocoon][:id]
11
+ runner.enclose(chrysalis_id: cid, cocoon_id: coc, engine: engine)
12
+
13
+ # Incubate until ready (needs ~12 steps with base rate 0.08 to exceed 0.9)
14
+ result = nil
15
+ 12.times { result = runner.incubate(chrysalis_id: cid, engine: engine) }
16
+ expect(result[:progress]).to be >= Legion::Extensions::Agentic::Learning::Chrysalis::Helpers::Constants::EMERGENCE_THRESHOLD
17
+
18
+ result = runner.emerge(chrysalis_id: cid, engine: engine)
19
+ expect(result[:success]).to be true
20
+ expect(result[:stage]).to eq(:butterfly)
21
+ expect(result[:premature]).to be false
22
+ end
23
+
24
+ it 'reports correct stats after full transformation' do
25
+ cid = runner.create_chrysalis(chrysalis_type: :leaf, content: 'wisdom', engine: engine)[:chrysalis][:id]
26
+ coc = runner.create_cocoon(environment: 'canopy', engine: engine)[:cocoon][:id]
27
+ runner.enclose(chrysalis_id: cid, cocoon_id: coc, engine: engine)
28
+ 12.times { runner.incubate(chrysalis_id: cid, engine: engine) }
29
+ runner.emerge(chrysalis_id: cid, engine: engine)
30
+
31
+ status = runner.metamorphosis_status(engine: engine)
32
+ expect(status[:butterflies_count]).to eq(1)
33
+ expect(status[:premature_count]).to eq(0)
34
+ expect(status[:avg_beauty]).to eq(1.0)
35
+ end
36
+
37
+ it 'handles multiple concurrent chrysalises in the same engine' do
38
+ ids = (1..3).map do |i|
39
+ cid = runner.create_chrysalis(chrysalis_type: :bark, content: "idea #{i}", engine: engine)[:chrysalis][:id]
40
+ coc = runner.create_cocoon(environment: "env_#{i}", engine: engine)[:cocoon][:id]
41
+ runner.enclose(chrysalis_id: cid, cocoon_id: coc, engine: engine)
42
+ cid
43
+ end
44
+
45
+ 12.times { runner.incubate_all(engine: engine) }
46
+ ids.each { |cid| runner.emerge(chrysalis_id: cid, engine: engine) }
47
+
48
+ status = runner.metamorphosis_status(engine: engine)
49
+ expect(status[:butterflies_count]).to eq(3)
50
+ end
51
+
52
+ it 'incubate_all applies modifiers from individual cocoons' do
53
+ cid1 = runner.create_chrysalis(chrysalis_type: :silk, content: 'c1', engine: engine)[:chrysalis][:id]
54
+ coc1 = runner.create_cocoon(environment: 'ideal', temperature: 0.55, humidity: 0.55, engine: engine)[:cocoon][:id]
55
+ runner.enclose(chrysalis_id: cid1, cocoon_id: coc1, engine: engine)
56
+
57
+ result = runner.incubate_all(engine: engine)
58
+ expect(result[:incubated]).to eq(1)
59
+ c = engine.instance_variable_get(:@chrysalises)[cid1]
60
+ expect(c.transformation_progress).to be > Legion::Extensions::Agentic::Learning::Chrysalis::Helpers::Constants::TRANSFORMATION_RATE
61
+ end
62
+
63
+ it 'disturbing a cocoon mid-transformation can force premature emergence' do
64
+ cid = runner.create_chrysalis(chrysalis_type: :underground, content: 'fragile', engine: engine)[:chrysalis][:id]
65
+ coc = runner.create_cocoon(environment: 'unstable', engine: engine)[:cocoon][:id]
66
+ runner.enclose(chrysalis_id: cid, cocoon_id: coc, engine: engine)
67
+ 3.times { runner.incubate(chrysalis_id: cid, engine: engine) }
68
+ runner.disturb(cocoon_id: coc, force: 1.0, engine: engine)
69
+
70
+ c = engine.instance_variable_get(:@chrysalises)[cid]
71
+ expect(c.butterfly?).to be true
72
+ expect(c.premature?).to be true
73
+ end
74
+ end