lex-agentic-defense 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 (219) 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-defense.gemspec +30 -0
  7. data/lib/legion/extensions/agentic/defense/avalanche/client.rb +22 -0
  8. data/lib/legion/extensions/agentic/defense/avalanche/helpers/avalanche_engine.rb +132 -0
  9. data/lib/legion/extensions/agentic/defense/avalanche/helpers/cascade.rb +76 -0
  10. data/lib/legion/extensions/agentic/defense/avalanche/helpers/constants.rb +44 -0
  11. data/lib/legion/extensions/agentic/defense/avalanche/helpers/snowpack.rb +86 -0
  12. data/lib/legion/extensions/agentic/defense/avalanche/runners/cognitive_avalanche.rb +75 -0
  13. data/lib/legion/extensions/agentic/defense/avalanche/version.rb +13 -0
  14. data/lib/legion/extensions/agentic/defense/avalanche.rb +22 -0
  15. data/lib/legion/extensions/agentic/defense/bias/actors/update.rb +45 -0
  16. data/lib/legion/extensions/agentic/defense/bias/client.rb +30 -0
  17. data/lib/legion/extensions/agentic/defense/bias/helpers/bias_detector.rb +107 -0
  18. data/lib/legion/extensions/agentic/defense/bias/helpers/bias_event.rb +44 -0
  19. data/lib/legion/extensions/agentic/defense/bias/helpers/bias_store.rb +84 -0
  20. data/lib/legion/extensions/agentic/defense/bias/helpers/constants.rb +28 -0
  21. data/lib/legion/extensions/agentic/defense/bias/runners/bias.rb +151 -0
  22. data/lib/legion/extensions/agentic/defense/bias/version.rb +13 -0
  23. data/lib/legion/extensions/agentic/defense/bias.rb +20 -0
  24. data/lib/legion/extensions/agentic/defense/confabulation/actors/decay.rb +45 -0
  25. data/lib/legion/extensions/agentic/defense/confabulation/client.rb +28 -0
  26. data/lib/legion/extensions/agentic/defense/confabulation/helpers/claim.rb +67 -0
  27. data/lib/legion/extensions/agentic/defense/confabulation/helpers/confabulation_engine.rb +120 -0
  28. data/lib/legion/extensions/agentic/defense/confabulation/helpers/constants.rb +29 -0
  29. data/lib/legion/extensions/agentic/defense/confabulation/runners/confabulation.rb +74 -0
  30. data/lib/legion/extensions/agentic/defense/confabulation/version.rb +13 -0
  31. data/lib/legion/extensions/agentic/defense/confabulation.rb +19 -0
  32. data/lib/legion/extensions/agentic/defense/dissonance/client.rb +32 -0
  33. data/lib/legion/extensions/agentic/defense/dissonance/helpers/belief.rb +46 -0
  34. data/lib/legion/extensions/agentic/defense/dissonance/helpers/constants.rb +27 -0
  35. data/lib/legion/extensions/agentic/defense/dissonance/helpers/dissonance_event.rb +52 -0
  36. data/lib/legion/extensions/agentic/defense/dissonance/helpers/dissonance_model.rb +159 -0
  37. data/lib/legion/extensions/agentic/defense/dissonance/runners/dissonance.rb +163 -0
  38. data/lib/legion/extensions/agentic/defense/dissonance/version.rb +13 -0
  39. data/lib/legion/extensions/agentic/defense/dissonance.rb +20 -0
  40. data/lib/legion/extensions/agentic/defense/epistemic_vigilance/actors/update.rb +45 -0
  41. data/lib/legion/extensions/agentic/defense/epistemic_vigilance/client.rb +27 -0
  42. data/lib/legion/extensions/agentic/defense/epistemic_vigilance/helpers/claim.rb +78 -0
  43. data/lib/legion/extensions/agentic/defense/epistemic_vigilance/helpers/client.rb +23 -0
  44. data/lib/legion/extensions/agentic/defense/epistemic_vigilance/helpers/constants.rb +37 -0
  45. data/lib/legion/extensions/agentic/defense/epistemic_vigilance/helpers/source.rb +64 -0
  46. data/lib/legion/extensions/agentic/defense/epistemic_vigilance/helpers/vigilance_engine.rb +195 -0
  47. data/lib/legion/extensions/agentic/defense/epistemic_vigilance/runners/epistemic_vigilance.rb +91 -0
  48. data/lib/legion/extensions/agentic/defense/epistemic_vigilance/version.rb +13 -0
  49. data/lib/legion/extensions/agentic/defense/epistemic_vigilance.rb +20 -0
  50. data/lib/legion/extensions/agentic/defense/erosion/client.rb +23 -0
  51. data/lib/legion/extensions/agentic/defense/erosion/helpers/channel.rb +84 -0
  52. data/lib/legion/extensions/agentic/defense/erosion/helpers/constants.rb +47 -0
  53. data/lib/legion/extensions/agentic/defense/erosion/helpers/erosion_engine.rb +134 -0
  54. data/lib/legion/extensions/agentic/defense/erosion/helpers/formation.rb +100 -0
  55. data/lib/legion/extensions/agentic/defense/erosion/runners/cognitive_erosion.rb +93 -0
  56. data/lib/legion/extensions/agentic/defense/erosion/version.rb +13 -0
  57. data/lib/legion/extensions/agentic/defense/erosion.rb +21 -0
  58. data/lib/legion/extensions/agentic/defense/error_monitoring/actors/tick.rb +45 -0
  59. data/lib/legion/extensions/agentic/defense/error_monitoring/client.rb +28 -0
  60. data/lib/legion/extensions/agentic/defense/error_monitoring/helpers/constants.rb +50 -0
  61. data/lib/legion/extensions/agentic/defense/error_monitoring/helpers/error_monitor.rb +174 -0
  62. data/lib/legion/extensions/agentic/defense/error_monitoring/helpers/error_signal.rb +60 -0
  63. data/lib/legion/extensions/agentic/defense/error_monitoring/runners/error_monitoring.rb +102 -0
  64. data/lib/legion/extensions/agentic/defense/error_monitoring/version.rb +13 -0
  65. data/lib/legion/extensions/agentic/defense/error_monitoring.rb +19 -0
  66. data/lib/legion/extensions/agentic/defense/extinction/actors/protocol_monitor.rb +45 -0
  67. data/lib/legion/extensions/agentic/defense/extinction/client.rb +27 -0
  68. data/lib/legion/extensions/agentic/defense/extinction/helpers/levels.rb +43 -0
  69. data/lib/legion/extensions/agentic/defense/extinction/helpers/protocol_state.rb +125 -0
  70. data/lib/legion/extensions/agentic/defense/extinction/local_migrations/20260316000040_create_extinction_state.rb +13 -0
  71. data/lib/legion/extensions/agentic/defense/extinction/runners/extinction.rb +130 -0
  72. data/lib/legion/extensions/agentic/defense/extinction/version.rb +13 -0
  73. data/lib/legion/extensions/agentic/defense/extinction.rb +25 -0
  74. data/lib/legion/extensions/agentic/defense/friction/client.rb +15 -0
  75. data/lib/legion/extensions/agentic/defense/friction/helpers/constants.rb +38 -0
  76. data/lib/legion/extensions/agentic/defense/friction/helpers/friction_engine.rb +131 -0
  77. data/lib/legion/extensions/agentic/defense/friction/helpers/state_transition.rb +73 -0
  78. data/lib/legion/extensions/agentic/defense/friction/runners/cognitive_friction.rb +82 -0
  79. data/lib/legion/extensions/agentic/defense/friction/version.rb +13 -0
  80. data/lib/legion/extensions/agentic/defense/friction.rb +19 -0
  81. data/lib/legion/extensions/agentic/defense/immune_response/client.rb +19 -0
  82. data/lib/legion/extensions/agentic/defense/immune_response/helpers/antibody.rb +72 -0
  83. data/lib/legion/extensions/agentic/defense/immune_response/helpers/antigen.rb +87 -0
  84. data/lib/legion/extensions/agentic/defense/immune_response/helpers/constants.rb +75 -0
  85. data/lib/legion/extensions/agentic/defense/immune_response/helpers/immune_engine.rb +184 -0
  86. data/lib/legion/extensions/agentic/defense/immune_response/helpers/immune_response.rb +76 -0
  87. data/lib/legion/extensions/agentic/defense/immune_response/runners/cognitive_immune_response.rb +114 -0
  88. data/lib/legion/extensions/agentic/defense/immune_response/version.rb +13 -0
  89. data/lib/legion/extensions/agentic/defense/immune_response.rb +21 -0
  90. data/lib/legion/extensions/agentic/defense/immunology/client.rb +29 -0
  91. data/lib/legion/extensions/agentic/defense/immunology/helpers/antibody.rb +55 -0
  92. data/lib/legion/extensions/agentic/defense/immunology/helpers/constants.rb +43 -0
  93. data/lib/legion/extensions/agentic/defense/immunology/helpers/immune_engine.rb +187 -0
  94. data/lib/legion/extensions/agentic/defense/immunology/helpers/threat.rb +67 -0
  95. data/lib/legion/extensions/agentic/defense/immunology/runners/cognitive_immunology.rb +92 -0
  96. data/lib/legion/extensions/agentic/defense/immunology/version.rb +13 -0
  97. data/lib/legion/extensions/agentic/defense/immunology.rb +20 -0
  98. data/lib/legion/extensions/agentic/defense/phantom/client.rb +29 -0
  99. data/lib/legion/extensions/agentic/defense/phantom/helpers/constants.rb +54 -0
  100. data/lib/legion/extensions/agentic/defense/phantom/helpers/phantom_engine.rb +106 -0
  101. data/lib/legion/extensions/agentic/defense/phantom/helpers/phantom_limb.rb +103 -0
  102. data/lib/legion/extensions/agentic/defense/phantom/helpers/phantom_signal.rb +40 -0
  103. data/lib/legion/extensions/agentic/defense/phantom/runners/cognitive_phantom.rb +79 -0
  104. data/lib/legion/extensions/agentic/defense/phantom/version.rb +13 -0
  105. data/lib/legion/extensions/agentic/defense/phantom.rb +21 -0
  106. data/lib/legion/extensions/agentic/defense/quicksand/client.rb +15 -0
  107. data/lib/legion/extensions/agentic/defense/quicksand/helpers/constants.rb +48 -0
  108. data/lib/legion/extensions/agentic/defense/quicksand/helpers/pit.rb +82 -0
  109. data/lib/legion/extensions/agentic/defense/quicksand/helpers/quicksand_engine.rb +137 -0
  110. data/lib/legion/extensions/agentic/defense/quicksand/helpers/trap.rb +101 -0
  111. data/lib/legion/extensions/agentic/defense/quicksand/runners/cognitive_quicksand.rb +84 -0
  112. data/lib/legion/extensions/agentic/defense/quicksand/version.rb +13 -0
  113. data/lib/legion/extensions/agentic/defense/quicksand.rb +22 -0
  114. data/lib/legion/extensions/agentic/defense/quicksilver/client.rb +29 -0
  115. data/lib/legion/extensions/agentic/defense/quicksilver/helpers/constants.rb +50 -0
  116. data/lib/legion/extensions/agentic/defense/quicksilver/helpers/droplet.rb +126 -0
  117. data/lib/legion/extensions/agentic/defense/quicksilver/helpers/pool.rb +83 -0
  118. data/lib/legion/extensions/agentic/defense/quicksilver/helpers/quicksilver_engine.rb +124 -0
  119. data/lib/legion/extensions/agentic/defense/quicksilver/runners/cognitive_quicksilver.rb +130 -0
  120. data/lib/legion/extensions/agentic/defense/quicksilver/version.rb +13 -0
  121. data/lib/legion/extensions/agentic/defense/quicksilver.rb +21 -0
  122. data/lib/legion/extensions/agentic/defense/version.rb +11 -0
  123. data/lib/legion/extensions/agentic/defense/whirlpool/client.rb +65 -0
  124. data/lib/legion/extensions/agentic/defense/whirlpool/helpers/captured_thought.rb +67 -0
  125. data/lib/legion/extensions/agentic/defense/whirlpool/helpers/constants.rb +45 -0
  126. data/lib/legion/extensions/agentic/defense/whirlpool/helpers/vortex.rb +91 -0
  127. data/lib/legion/extensions/agentic/defense/whirlpool/helpers/whirlpool_engine.rb +92 -0
  128. data/lib/legion/extensions/agentic/defense/whirlpool/runners/cognitive_whirlpool.rb +117 -0
  129. data/lib/legion/extensions/agentic/defense/whirlpool/version.rb +13 -0
  130. data/lib/legion/extensions/agentic/defense/whirlpool.rb +22 -0
  131. data/lib/legion/extensions/agentic/defense.rb +32 -0
  132. data/spec/legion/extensions/agentic/defense/avalanche/client_spec.rb +96 -0
  133. data/spec/legion/extensions/agentic/defense/avalanche/helpers/avalanche_engine_spec.rb +276 -0
  134. data/spec/legion/extensions/agentic/defense/avalanche/helpers/cascade_spec.rb +190 -0
  135. data/spec/legion/extensions/agentic/defense/avalanche/helpers/constants_spec.rb +129 -0
  136. data/spec/legion/extensions/agentic/defense/avalanche/helpers/snowpack_spec.rb +197 -0
  137. data/spec/legion/extensions/agentic/defense/avalanche/runners/cognitive_avalanche_spec.rb +211 -0
  138. data/spec/legion/extensions/agentic/defense/bias/client_spec.rb +16 -0
  139. data/spec/legion/extensions/agentic/defense/bias/helpers/bias_detector_spec.rb +160 -0
  140. data/spec/legion/extensions/agentic/defense/bias/helpers/bias_event_spec.rb +64 -0
  141. data/spec/legion/extensions/agentic/defense/bias/helpers/bias_store_spec.rb +143 -0
  142. data/spec/legion/extensions/agentic/defense/bias/runners/bias_spec.rb +155 -0
  143. data/spec/legion/extensions/agentic/defense/confabulation/client_spec.rb +34 -0
  144. data/spec/legion/extensions/agentic/defense/confabulation/helpers/claim_spec.rb +119 -0
  145. data/spec/legion/extensions/agentic/defense/confabulation/helpers/confabulation_engine_spec.rb +163 -0
  146. data/spec/legion/extensions/agentic/defense/confabulation/helpers/constants_spec.rb +55 -0
  147. data/spec/legion/extensions/agentic/defense/confabulation/runners/confabulation_spec.rb +119 -0
  148. data/spec/legion/extensions/agentic/defense/dissonance/client_spec.rb +51 -0
  149. data/spec/legion/extensions/agentic/defense/dissonance/helpers/belief_spec.rb +103 -0
  150. data/spec/legion/extensions/agentic/defense/dissonance/helpers/constants_spec.rb +60 -0
  151. data/spec/legion/extensions/agentic/defense/dissonance/helpers/dissonance_event_spec.rb +113 -0
  152. data/spec/legion/extensions/agentic/defense/dissonance/helpers/dissonance_model_spec.rb +252 -0
  153. data/spec/legion/extensions/agentic/defense/dissonance/runners/dissonance_spec.rb +323 -0
  154. data/spec/legion/extensions/agentic/defense/epistemic_vigilance/client_spec.rb +28 -0
  155. data/spec/legion/extensions/agentic/defense/epistemic_vigilance/helpers/claim_spec.rb +135 -0
  156. data/spec/legion/extensions/agentic/defense/epistemic_vigilance/helpers/constants_spec.rb +59 -0
  157. data/spec/legion/extensions/agentic/defense/epistemic_vigilance/helpers/source_spec.rb +117 -0
  158. data/spec/legion/extensions/agentic/defense/epistemic_vigilance/helpers/vigilance_engine_spec.rb +273 -0
  159. data/spec/legion/extensions/agentic/defense/epistemic_vigilance/runners/epistemic_vigilance_spec.rb +157 -0
  160. data/spec/legion/extensions/agentic/defense/erosion/client_spec.rb +90 -0
  161. data/spec/legion/extensions/agentic/defense/erosion/helpers/channel_spec.rb +173 -0
  162. data/spec/legion/extensions/agentic/defense/erosion/helpers/constants_spec.rb +137 -0
  163. data/spec/legion/extensions/agentic/defense/erosion/helpers/erosion_engine_spec.rb +263 -0
  164. data/spec/legion/extensions/agentic/defense/erosion/helpers/formation_spec.rb +206 -0
  165. data/spec/legion/extensions/agentic/defense/erosion/runners/cognitive_erosion_spec.rb +153 -0
  166. data/spec/legion/extensions/agentic/defense/error_monitoring/client_spec.rb +40 -0
  167. data/spec/legion/extensions/agentic/defense/error_monitoring/helpers/error_monitor_spec.rb +178 -0
  168. data/spec/legion/extensions/agentic/defense/error_monitoring/helpers/error_signal_spec.rb +76 -0
  169. data/spec/legion/extensions/agentic/defense/error_monitoring/runners/error_monitoring_spec.rb +87 -0
  170. data/spec/legion/extensions/agentic/defense/extinction/actors/protocol_monitor_spec.rb +45 -0
  171. data/spec/legion/extensions/agentic/defense/extinction/client_spec.rb +13 -0
  172. data/spec/legion/extensions/agentic/defense/extinction/helpers/levels_spec.rb +180 -0
  173. data/spec/legion/extensions/agentic/defense/extinction/helpers/protocol_state_spec.rb +291 -0
  174. data/spec/legion/extensions/agentic/defense/extinction/local_persistence_spec.rb +188 -0
  175. data/spec/legion/extensions/agentic/defense/extinction/runners/extinction_spec.rb +114 -0
  176. data/spec/legion/extensions/agentic/defense/friction/helpers/constants_spec.rb +46 -0
  177. data/spec/legion/extensions/agentic/defense/friction/helpers/friction_engine_spec.rb +175 -0
  178. data/spec/legion/extensions/agentic/defense/friction/helpers/state_transition_spec.rb +124 -0
  179. data/spec/legion/extensions/agentic/defense/friction/runners/cognitive_friction_spec.rb +89 -0
  180. data/spec/legion/extensions/agentic/defense/immune_response/client_spec.rb +32 -0
  181. data/spec/legion/extensions/agentic/defense/immune_response/cognitive_immune_response_spec.rb +7 -0
  182. data/spec/legion/extensions/agentic/defense/immune_response/helpers/antibody_spec.rb +117 -0
  183. data/spec/legion/extensions/agentic/defense/immune_response/helpers/antigen_spec.rb +125 -0
  184. data/spec/legion/extensions/agentic/defense/immune_response/helpers/constants_spec.rb +45 -0
  185. data/spec/legion/extensions/agentic/defense/immune_response/helpers/immune_engine_spec.rb +222 -0
  186. data/spec/legion/extensions/agentic/defense/immune_response/helpers/immune_response_spec.rb +84 -0
  187. data/spec/legion/extensions/agentic/defense/immune_response/runners_spec.rb +141 -0
  188. data/spec/legion/extensions/agentic/defense/immunology/client_spec.rb +61 -0
  189. data/spec/legion/extensions/agentic/defense/immunology/helpers/antibody_spec.rb +98 -0
  190. data/spec/legion/extensions/agentic/defense/immunology/helpers/constants_spec.rb +86 -0
  191. data/spec/legion/extensions/agentic/defense/immunology/helpers/immune_engine_spec.rb +275 -0
  192. data/spec/legion/extensions/agentic/defense/immunology/helpers/threat_spec.rb +133 -0
  193. data/spec/legion/extensions/agentic/defense/immunology/runners/cognitive_immunology_spec.rb +177 -0
  194. data/spec/legion/extensions/agentic/defense/phantom/client_spec.rb +53 -0
  195. data/spec/legion/extensions/agentic/defense/phantom/helpers/constants_spec.rb +87 -0
  196. data/spec/legion/extensions/agentic/defense/phantom/helpers/phantom_engine_spec.rb +222 -0
  197. data/spec/legion/extensions/agentic/defense/phantom/helpers/phantom_limb_spec.rb +180 -0
  198. data/spec/legion/extensions/agentic/defense/phantom/helpers/phantom_signal_spec.rb +59 -0
  199. data/spec/legion/extensions/agentic/defense/phantom/runners/cognitive_phantom_spec.rb +193 -0
  200. data/spec/legion/extensions/agentic/defense/quicksand/client_spec.rb +35 -0
  201. data/spec/legion/extensions/agentic/defense/quicksand/helpers/constants_spec.rb +58 -0
  202. data/spec/legion/extensions/agentic/defense/quicksand/helpers/pit_spec.rb +103 -0
  203. data/spec/legion/extensions/agentic/defense/quicksand/helpers/quicksand_engine_spec.rb +153 -0
  204. data/spec/legion/extensions/agentic/defense/quicksand/helpers/trap_spec.rb +166 -0
  205. data/spec/legion/extensions/agentic/defense/quicksand/runners/cognitive_quicksand_spec.rb +90 -0
  206. data/spec/legion/extensions/agentic/defense/quicksilver/client_spec.rb +72 -0
  207. data/spec/legion/extensions/agentic/defense/quicksilver/helpers/constants_spec.rb +105 -0
  208. data/spec/legion/extensions/agentic/defense/quicksilver/helpers/droplet_spec.rb +310 -0
  209. data/spec/legion/extensions/agentic/defense/quicksilver/helpers/pool_spec.rb +174 -0
  210. data/spec/legion/extensions/agentic/defense/quicksilver/helpers/quicksilver_engine_spec.rb +226 -0
  211. data/spec/legion/extensions/agentic/defense/quicksilver/runners/cognitive_quicksilver_spec.rb +227 -0
  212. data/spec/legion/extensions/agentic/defense/whirlpool/client_spec.rb +63 -0
  213. data/spec/legion/extensions/agentic/defense/whirlpool/helpers/captured_thought_spec.rb +171 -0
  214. data/spec/legion/extensions/agentic/defense/whirlpool/helpers/constants_spec.rb +65 -0
  215. data/spec/legion/extensions/agentic/defense/whirlpool/helpers/vortex_spec.rb +189 -0
  216. data/spec/legion/extensions/agentic/defense/whirlpool/helpers/whirlpool_engine_spec.rb +227 -0
  217. data/spec/legion/extensions/agentic/defense/whirlpool/runners/cognitive_whirlpool_spec.rb +226 -0
  218. data/spec/spec_helper.rb +46 -0
  219. metadata +303 -0
@@ -0,0 +1,276 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Defense::Avalanche::Helpers::AvalancheEngine do
4
+ subject(:engine) { described_class.new }
5
+
6
+ let(:snowpack_id) do
7
+ engine.create_snowpack(
8
+ snowpack_type: :ideas,
9
+ domain: :creativity,
10
+ content: 'divergent thinking spiral',
11
+ stability: 0.3
12
+ )[:snowpack_id]
13
+ end
14
+
15
+ let(:stable_snowpack_id) do
16
+ engine.create_snowpack(
17
+ snowpack_type: :memories,
18
+ domain: :identity,
19
+ content: 'foundational self-concept',
20
+ stability: 0.9
21
+ )[:snowpack_id]
22
+ end
23
+
24
+ describe '#create_snowpack' do
25
+ it 'creates a snowpack and returns success' do
26
+ result = engine.create_snowpack(snowpack_type: :emotions, domain: :fear, content: 'anticipatory dread')
27
+ expect(result[:success]).to be(true)
28
+ expect(result[:snowpack_id]).not_to be_nil
29
+ end
30
+
31
+ it 'returns a snowpack hash in the result' do
32
+ result = engine.create_snowpack(snowpack_type: :ideas, domain: :test, content: 'x')
33
+ expect(result[:snowpack]).to be_a(Hash)
34
+ expect(result[:snowpack][:domain]).to eq(:test)
35
+ end
36
+
37
+ it 'stores the snowpack in @snowpacks' do
38
+ result = engine.create_snowpack(snowpack_type: :impulses, domain: :action, content: 'do it now')
39
+ expect(engine.snowpacks).to have_key(result[:snowpack_id])
40
+ end
41
+
42
+ it 'returns failure for invalid snowpack_type' do
43
+ result = engine.create_snowpack(snowpack_type: :invalid, domain: :test, content: 'x')
44
+ expect(result[:success]).to be(false)
45
+ expect(result[:error]).to match(/unknown snowpack_type/)
46
+ end
47
+
48
+ it 'enforces MAX_SNOWPACKS limit' do
49
+ 100.times { |i| engine.create_snowpack(snowpack_type: :ideas, domain: :test, content: "x#{i}") }
50
+ result = engine.create_snowpack(snowpack_type: :ideas, domain: :test, content: 'overflow')
51
+ expect(result[:success]).to be(false)
52
+ expect(result[:error]).to match(/snowpack limit/)
53
+ end
54
+
55
+ it 'respects custom depth' do
56
+ result = engine.create_snowpack(snowpack_type: :ideas, domain: :test, content: 'x', depth: 0.5)
57
+ expect(engine.snowpacks[result[:snowpack_id]].depth).to eq(0.5)
58
+ end
59
+
60
+ it 'respects custom stability' do
61
+ result = engine.create_snowpack(snowpack_type: :ideas, domain: :test, content: 'x', stability: 0.2)
62
+ expect(engine.snowpacks[result[:snowpack_id]].stability).to eq(0.2)
63
+ end
64
+ end
65
+
66
+ describe '#trigger' do
67
+ it 'triggers a cascade when force + instability >= threshold' do
68
+ result = engine.trigger(snowpack_id: snowpack_id, force: 0.5)
69
+ expect(result[:success]).to be(true)
70
+ expect(result[:triggered]).to be(true)
71
+ expect(result[:cascade_id]).not_to be_nil
72
+ end
73
+
74
+ it 'returns no trigger when combined force+instability is below threshold' do
75
+ result = engine.trigger(snowpack_id: stable_snowpack_id, force: 0.01)
76
+ expect(result[:success]).to be(true)
77
+ expect(result[:triggered]).to be(false)
78
+ end
79
+
80
+ it 'returns failure for unknown snowpack_id' do
81
+ result = engine.trigger(snowpack_id: 'ghost-uuid', force: 0.5)
82
+ expect(result[:success]).to be(false)
83
+ expect(result[:error]).to match(/snowpack not found/)
84
+ end
85
+
86
+ it 'includes cascade hash in triggered result' do
87
+ result = engine.trigger(snowpack_id: snowpack_id, force: 0.5)
88
+ expect(result[:cascade]).to be_a(Hash)
89
+ expect(result[:cascade][:cascade_type]).not_to be_nil
90
+ end
91
+
92
+ it 'accepts custom cascade_type' do
93
+ result = engine.trigger(snowpack_id: snowpack_id, force: 0.5, cascade_type: :creative)
94
+ expect(result[:cascade][:cascade_type]).to eq(:creative)
95
+ end
96
+
97
+ it 'records cascade in cascade_history' do
98
+ engine.trigger(snowpack_id: snowpack_id, force: 0.5)
99
+ expect(engine.cascade_history).not_to be_empty
100
+ end
101
+
102
+ it 'destabilizes the snowpack' do
103
+ pack = engine.snowpacks[snowpack_id]
104
+ before_stability = pack.stability
105
+ engine.trigger(snowpack_id: snowpack_id, force: 0.2)
106
+ expect(pack.stability).to be < before_stability
107
+ end
108
+
109
+ it 'adds snowpack content to cascade debris' do
110
+ result = engine.trigger(snowpack_id: snowpack_id, force: 0.5)
111
+ expect(result[:cascade][:debris]).to include('divergent thinking spiral')
112
+ end
113
+
114
+ it 'includes instability in result' do
115
+ result = engine.trigger(snowpack_id: snowpack_id, force: 0.5)
116
+ expect(result).to have_key(:instability)
117
+ end
118
+ end
119
+
120
+ describe '#accumulate_all!' do
121
+ before { snowpack_id && stable_snowpack_id }
122
+
123
+ it 'returns success' do
124
+ result = engine.accumulate_all!
125
+ expect(result[:success]).to be(true)
126
+ end
127
+
128
+ it 'reports packs_accumulated count' do
129
+ result = engine.accumulate_all!
130
+ expect(result[:packs_accumulated]).to eq(2)
131
+ end
132
+
133
+ it 'increases depth of all snowpacks' do
134
+ packs = engine.snowpacks.values
135
+ before_depths = packs.map(&:depth)
136
+ engine.accumulate_all!
137
+ packs.each_with_index do |pack, i|
138
+ expect(pack.depth).to be >= before_depths[i]
139
+ end
140
+ end
141
+
142
+ it 'uses custom rate when provided' do
143
+ result = engine.accumulate_all!(rate: 0.1)
144
+ expect(result[:rate]).to eq(0.1)
145
+ end
146
+ end
147
+
148
+ describe '#melt_all!' do
149
+ before do
150
+ engine.create_snowpack(snowpack_type: :ideas, domain: :test, content: 'x', depth: 0.5)
151
+ engine.create_snowpack(snowpack_type: :emotions, domain: :test, content: 'y', depth: 0.5)
152
+ end
153
+
154
+ it 'returns success' do
155
+ result = engine.melt_all!
156
+ expect(result[:success]).to be(true)
157
+ end
158
+
159
+ it 'reports packs_melted count' do
160
+ result = engine.melt_all!
161
+ expect(result[:packs_melted]).to eq(2)
162
+ end
163
+
164
+ it 'decreases depth of all snowpacks' do
165
+ packs = engine.snowpacks.values
166
+ before = packs.map(&:depth)
167
+ engine.melt_all!
168
+ packs.each_with_index do |pack, i|
169
+ expect(pack.depth).to be <= before[i]
170
+ end
171
+ end
172
+ end
173
+
174
+ describe '#active_cascades' do
175
+ it 'returns empty array with no history' do
176
+ expect(engine.active_cascades).to eq([])
177
+ end
178
+
179
+ it 'returns only active cascades' do
180
+ engine.trigger(snowpack_id: snowpack_id, force: 0.5)
181
+ expect(engine.active_cascades).not_to be_empty
182
+ end
183
+ end
184
+
185
+ describe '#most_unstable' do
186
+ it 'returns nil when no snowpacks exist' do
187
+ expect(engine.most_unstable).to be_nil
188
+ end
189
+
190
+ it 'returns the snowpack with lowest stability' do
191
+ snowpack_id
192
+ stable_snowpack_id
193
+ expect(engine.most_unstable.id).to eq(snowpack_id)
194
+ end
195
+ end
196
+
197
+ describe '#terrain_report' do
198
+ before { snowpack_id && stable_snowpack_id }
199
+
200
+ it 'includes total_snowpacks' do
201
+ report = engine.terrain_report
202
+ expect(report[:total_snowpacks]).to eq(2)
203
+ end
204
+
205
+ it 'includes critical_count' do
206
+ report = engine.terrain_report
207
+ expect(report).to have_key(:critical_count)
208
+ end
209
+
210
+ it 'includes unstable_count' do
211
+ report = engine.terrain_report
212
+ expect(report).to have_key(:unstable_count)
213
+ end
214
+
215
+ it 'includes stable_count' do
216
+ report = engine.terrain_report
217
+ expect(report).to have_key(:stable_count)
218
+ end
219
+
220
+ it 'includes active_cascades count' do
221
+ report = engine.terrain_report
222
+ expect(report).to have_key(:active_cascades)
223
+ end
224
+
225
+ it 'includes cascade_history count' do
226
+ engine.trigger(snowpack_id: snowpack_id, force: 0.5)
227
+ report = engine.terrain_report
228
+ expect(report[:cascade_history]).to be >= 1
229
+ end
230
+
231
+ it 'includes avg_stability' do
232
+ report = engine.terrain_report
233
+ expect(report[:avg_stability]).to be_a(Float)
234
+ end
235
+
236
+ it 'includes avg_depth' do
237
+ report = engine.terrain_report
238
+ expect(report[:avg_depth]).to be_a(Float)
239
+ end
240
+
241
+ it 'includes most_unstable_id' do
242
+ report = engine.terrain_report
243
+ expect(report[:most_unstable_id]).to eq(snowpack_id)
244
+ end
245
+
246
+ it 'includes recent_cascades array' do
247
+ report = engine.terrain_report
248
+ expect(report[:recent_cascades]).to be_an(Array)
249
+ end
250
+
251
+ it 'caps recent_cascades at 5' do
252
+ 10.times { engine.trigger(snowpack_id: snowpack_id, force: 0.5) }
253
+ report = engine.terrain_report
254
+ expect(report[:recent_cascades].size).to be <= 5
255
+ end
256
+ end
257
+
258
+ describe 'cascade_history cap' do
259
+ it 'caps cascade_history at MAX_CASCADE_HISTORY' do
260
+ pack_id = engine.create_snowpack(
261
+ snowpack_type: :impulses, domain: :stress, content: 'persistent trigger', stability: 0.0
262
+ )[:snowpack_id]
263
+
264
+ 510.times do
265
+ engine.send(:record_cascade,
266
+ Legion::Extensions::Agentic::Defense::Avalanche::Helpers::Cascade.new(
267
+ cascade_type: :chaotic,
268
+ trigger_source: pack_id,
269
+ magnitude: 0.5
270
+ ))
271
+ end
272
+
273
+ expect(engine.cascade_history.size).to eq(500)
274
+ end
275
+ end
276
+ end
@@ -0,0 +1,190 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Defense::Avalanche::Helpers::Cascade do
4
+ subject(:cascade) do
5
+ described_class.new(cascade_type: :creative, trigger_source: 'sp-abc', magnitude: 0.5)
6
+ end
7
+
8
+ describe '#initialize' do
9
+ it 'assigns a uuid id' do
10
+ expect(cascade.id).to match(/\A[0-9a-f-]{36}\z/)
11
+ end
12
+
13
+ it 'sets cascade_type' do
14
+ expect(cascade.cascade_type).to eq(:creative)
15
+ end
16
+
17
+ it 'sets trigger_source' do
18
+ expect(cascade.trigger_source).to eq('sp-abc')
19
+ end
20
+
21
+ it 'sets magnitude' do
22
+ expect(cascade.magnitude).to eq(0.5)
23
+ end
24
+
25
+ it 'defaults propagation_speed to 0.5' do
26
+ expect(cascade.propagation_speed).to eq(0.5)
27
+ end
28
+
29
+ it 'defaults debris to empty array' do
30
+ expect(cascade.debris).to eq([])
31
+ end
32
+
33
+ it 'records started_at timestamp' do
34
+ expect(cascade.started_at).to be_a(Time)
35
+ end
36
+
37
+ it 'starts as active' do
38
+ expect(cascade.active?).to be(true)
39
+ end
40
+
41
+ it 'clamps magnitude above 1.0 to 1.0' do
42
+ c = described_class.new(cascade_type: :emotional, trigger_source: 'x', magnitude: 2.0)
43
+ expect(c.magnitude).to eq(1.0)
44
+ end
45
+
46
+ it 'clamps magnitude below 0.0 to 0.0' do
47
+ c = described_class.new(cascade_type: :emotional, trigger_source: 'x', magnitude: -0.5)
48
+ expect(c.magnitude).to eq(0.0)
49
+ end
50
+
51
+ it 'raises ArgumentError for unknown cascade_type' do
52
+ expect do
53
+ described_class.new(cascade_type: :volcanic, trigger_source: 'x', magnitude: 0.5)
54
+ end.to raise_error(ArgumentError, /unknown cascade_type/)
55
+ end
56
+
57
+ it 'accepts all valid cascade_types' do
58
+ %i[creative emotional analytical chaotic convergent].each do |type|
59
+ expect do
60
+ described_class.new(cascade_type: type, trigger_source: 'x', magnitude: 0.5)
61
+ end.not_to raise_error
62
+ end
63
+ end
64
+
65
+ it 'accepts pre-populated debris' do
66
+ c = described_class.new(cascade_type: :chaotic, trigger_source: 'x', magnitude: 0.5, debris: ['idea1'])
67
+ expect(c.debris).to include('idea1')
68
+ end
69
+ end
70
+
71
+ describe '#propagate!' do
72
+ it 'increases magnitude by default 0.1' do
73
+ before = cascade.magnitude
74
+ cascade.propagate!
75
+ expect(cascade.magnitude).to be_within(0.0001).of(before + 0.1)
76
+ end
77
+
78
+ it 'increases magnitude by given rate' do
79
+ before = cascade.magnitude
80
+ cascade.propagate!(0.2)
81
+ expect(cascade.magnitude).to be_within(0.0001).of(before + 0.2)
82
+ end
83
+
84
+ it 'clamps magnitude at 1.0' do
85
+ c = described_class.new(cascade_type: :chaotic, trigger_source: 'x', magnitude: 0.95)
86
+ c.propagate!(0.5)
87
+ expect(c.magnitude).to eq(1.0)
88
+ end
89
+
90
+ it 'does nothing when spent' do
91
+ c = described_class.new(cascade_type: :chaotic, trigger_source: 'x', magnitude: 0.5)
92
+ c.dissipate!(1.0)
93
+ before = c.magnitude
94
+ c.propagate!(0.1)
95
+ expect(c.magnitude).to eq(before)
96
+ end
97
+ end
98
+
99
+ describe '#dissipate!' do
100
+ it 'decreases magnitude by default 0.08' do
101
+ before = cascade.magnitude
102
+ cascade.dissipate!
103
+ expect(cascade.magnitude).to be_within(0.0001).of(before - 0.08)
104
+ end
105
+
106
+ it 'decreases magnitude by given rate' do
107
+ before = cascade.magnitude
108
+ cascade.dissipate!(0.1)
109
+ expect(cascade.magnitude).to be_within(0.0001).of(before - 0.1)
110
+ end
111
+
112
+ it 'clamps magnitude at 0.0' do
113
+ c = described_class.new(cascade_type: :chaotic, trigger_source: 'x', magnitude: 0.05)
114
+ c.dissipate!(0.5)
115
+ expect(c.magnitude).to eq(0.0)
116
+ end
117
+
118
+ it 'marks cascade as spent when magnitude reaches 0.0' do
119
+ c = described_class.new(cascade_type: :chaotic, trigger_source: 'x', magnitude: 0.05)
120
+ c.dissipate!(0.5)
121
+ expect(c.spent?).to be(true)
122
+ end
123
+ end
124
+
125
+ describe '#active?' do
126
+ it 'returns true when active and magnitude > 0.0' do
127
+ expect(cascade.active?).to be(true)
128
+ end
129
+
130
+ it 'returns false when fully dissipated' do
131
+ cascade.dissipate!(1.0)
132
+ expect(cascade.active?).to be(false)
133
+ end
134
+ end
135
+
136
+ describe '#spent?' do
137
+ it 'returns false for new cascade' do
138
+ expect(cascade.spent?).to be(false)
139
+ end
140
+
141
+ it 'returns true after full dissipation' do
142
+ cascade.dissipate!(1.0)
143
+ expect(cascade.spent?).to be(true)
144
+ end
145
+ end
146
+
147
+ describe '#magnitude_label' do
148
+ it 'returns :minor for magnitude 0.1' do
149
+ c = described_class.new(cascade_type: :chaotic, trigger_source: 'x', magnitude: 0.1)
150
+ expect(c.magnitude_label).to eq(:minor)
151
+ end
152
+
153
+ it 'returns :devastating for magnitude 0.9' do
154
+ c = described_class.new(cascade_type: :chaotic, trigger_source: 'x', magnitude: 0.9)
155
+ expect(c.magnitude_label).to eq(:devastating)
156
+ end
157
+ end
158
+
159
+ describe '#add_debris' do
160
+ it 'appends to debris array' do
161
+ cascade.add_debris('new thought fragment')
162
+ expect(cascade.debris).to include('new thought fragment')
163
+ end
164
+
165
+ it 'supports multiple debris items' do
166
+ cascade.add_debris('item1')
167
+ cascade.add_debris('item2')
168
+ expect(cascade.debris.size).to eq(2)
169
+ end
170
+ end
171
+
172
+ describe '#to_h' do
173
+ it 'includes all expected keys' do
174
+ h = cascade.to_h
175
+ expect(h.keys).to include(:id, :cascade_type, :trigger_source, :magnitude, :magnitude_label,
176
+ :propagation_speed, :debris, :active, :started_at)
177
+ end
178
+
179
+ it 'reflects current active state' do
180
+ cascade.dissipate!(1.0)
181
+ expect(cascade.to_h[:active]).to be(false)
182
+ end
183
+
184
+ it 'returns a duplicate of debris' do
185
+ h = cascade.to_h
186
+ h[:debris] << 'mutant'
187
+ expect(cascade.debris).not_to include('mutant')
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Defense::Avalanche::Helpers::Constants do
4
+ def described_module
5
+ Legion::Extensions::Agentic::Defense::Avalanche::Helpers::Constants
6
+ end
7
+
8
+ describe 'MAX_SNOWPACKS' do
9
+ it 'is 100' do
10
+ expect(described_module::MAX_SNOWPACKS).to eq(100)
11
+ end
12
+ end
13
+
14
+ describe 'MAX_CASCADE_HISTORY' do
15
+ it 'is 500' do
16
+ expect(described_module::MAX_CASCADE_HISTORY).to eq(500)
17
+ end
18
+ end
19
+
20
+ describe 'TRIGGER_THRESHOLD' do
21
+ it 'is 0.75' do
22
+ expect(described_module::TRIGGER_THRESHOLD).to eq(0.75)
23
+ end
24
+ end
25
+
26
+ describe 'ACCUMULATION_RATE' do
27
+ it 'is 0.06' do
28
+ expect(described_module::ACCUMULATION_RATE).to eq(0.06)
29
+ end
30
+ end
31
+
32
+ describe 'MELT_RATE' do
33
+ it 'is 0.02' do
34
+ expect(described_module::MELT_RATE).to eq(0.02)
35
+ end
36
+ end
37
+
38
+ describe 'SNOWPACK_TYPES' do
39
+ it 'contains all five types' do
40
+ expect(described_module::SNOWPACK_TYPES).to eq(%i[ideas emotions memories associations impulses])
41
+ end
42
+
43
+ it 'is frozen' do
44
+ expect(described_module::SNOWPACK_TYPES).to be_frozen
45
+ end
46
+ end
47
+
48
+ describe 'CASCADE_TYPES' do
49
+ it 'contains all five types' do
50
+ expect(described_module::CASCADE_TYPES).to eq(%i[creative emotional analytical chaotic convergent])
51
+ end
52
+
53
+ it 'is frozen' do
54
+ expect(described_module::CASCADE_TYPES).to be_frozen
55
+ end
56
+ end
57
+
58
+ describe 'STABILITY_LABELS' do
59
+ it 'is a hash' do
60
+ expect(described_module::STABILITY_LABELS).to be_a(Hash)
61
+ end
62
+
63
+ it 'covers catastrophic through bedrock' do
64
+ labels = described_module::STABILITY_LABELS.values
65
+ expect(labels).to include(:catastrophic, :critical, :unstable, :moderate, :bedrock)
66
+ end
67
+ end
68
+
69
+ describe 'MAGNITUDE_LABELS' do
70
+ it 'is a hash' do
71
+ expect(described_module::MAGNITUDE_LABELS).to be_a(Hash)
72
+ end
73
+
74
+ it 'covers minor through devastating' do
75
+ labels = described_module::MAGNITUDE_LABELS.values
76
+ expect(labels).to include(:minor, :moderate, :significant, :major, :devastating)
77
+ end
78
+ end
79
+
80
+ describe '.label_for' do
81
+ it 'returns :catastrophic for stability 0.1' do
82
+ expect(described_module.label_for(:STABILITY_LABELS, 0.1)).to eq(:catastrophic)
83
+ end
84
+
85
+ it 'returns :critical for stability 0.3' do
86
+ expect(described_module.label_for(:STABILITY_LABELS, 0.3)).to eq(:critical)
87
+ end
88
+
89
+ it 'returns :unstable for stability 0.5' do
90
+ expect(described_module.label_for(:STABILITY_LABELS, 0.5)).to eq(:unstable)
91
+ end
92
+
93
+ it 'returns :moderate for stability 0.7' do
94
+ expect(described_module.label_for(:STABILITY_LABELS, 0.7)).to eq(:moderate)
95
+ end
96
+
97
+ it 'returns :bedrock for stability 0.9' do
98
+ expect(described_module.label_for(:STABILITY_LABELS, 0.9)).to eq(:bedrock)
99
+ end
100
+
101
+ it 'returns :minor for magnitude 0.1' do
102
+ expect(described_module.label_for(:MAGNITUDE_LABELS, 0.1)).to eq(:minor)
103
+ end
104
+
105
+ it 'returns :moderate for magnitude 0.3' do
106
+ expect(described_module.label_for(:MAGNITUDE_LABELS, 0.3)).to eq(:moderate)
107
+ end
108
+
109
+ it 'returns :significant for magnitude 0.5' do
110
+ expect(described_module.label_for(:MAGNITUDE_LABELS, 0.5)).to eq(:significant)
111
+ end
112
+
113
+ it 'returns :major for magnitude 0.7' do
114
+ expect(described_module.label_for(:MAGNITUDE_LABELS, 0.7)).to eq(:major)
115
+ end
116
+
117
+ it 'returns :devastating for magnitude 0.9' do
118
+ expect(described_module.label_for(:MAGNITUDE_LABELS, 0.9)).to eq(:devastating)
119
+ end
120
+
121
+ it 'clamps values above 1.0 to 1.0' do
122
+ expect(described_module.label_for(:MAGNITUDE_LABELS, 1.5)).not_to eq(:unknown)
123
+ end
124
+
125
+ it 'clamps values below 0.0 to 0.0' do
126
+ expect(described_module.label_for(:MAGNITUDE_LABELS, -0.5)).not_to eq(:unknown)
127
+ end
128
+ end
129
+ end