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,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Defense::Avalanche::Helpers::Snowpack do
4
+ subject(:snowpack) do
5
+ described_class.new(snowpack_type: :ideas, domain: :reasoning, content: 'recursive self-improvement')
6
+ end
7
+
8
+ describe '#initialize' do
9
+ it 'assigns a uuid id' do
10
+ expect(snowpack.id).to match(/\A[0-9a-f-]{36}\z/)
11
+ end
12
+
13
+ it 'sets snowpack_type' do
14
+ expect(snowpack.snowpack_type).to eq(:ideas)
15
+ end
16
+
17
+ it 'sets domain' do
18
+ expect(snowpack.domain).to eq(:reasoning)
19
+ end
20
+
21
+ it 'sets content' do
22
+ expect(snowpack.content).to eq('recursive self-improvement')
23
+ end
24
+
25
+ it 'defaults depth to 0.0' do
26
+ expect(snowpack.depth).to eq(0.0)
27
+ end
28
+
29
+ it 'defaults stability to 1.0' do
30
+ expect(snowpack.stability).to eq(1.0)
31
+ end
32
+
33
+ it 'records created_at timestamp' do
34
+ expect(snowpack.created_at).to be_a(Time)
35
+ end
36
+
37
+ it 'clamps depth above 1.0 to 1.0' do
38
+ sp = described_class.new(snowpack_type: :emotions, domain: :test, content: 'x', depth: 2.5)
39
+ expect(sp.depth).to eq(1.0)
40
+ end
41
+
42
+ it 'clamps stability below 0.0 to 0.0' do
43
+ sp = described_class.new(snowpack_type: :emotions, domain: :test, content: 'x', stability: -0.5)
44
+ expect(sp.stability).to eq(0.0)
45
+ end
46
+
47
+ it 'raises ArgumentError for unknown snowpack_type' do
48
+ expect do
49
+ described_class.new(snowpack_type: :unknown_type, domain: :test, content: 'x')
50
+ end.to raise_error(ArgumentError, /unknown snowpack_type/)
51
+ end
52
+
53
+ it 'accepts all valid snowpack_types' do
54
+ %i[ideas emotions memories associations impulses].each do |type|
55
+ expect do
56
+ described_class.new(snowpack_type: type, domain: :test, content: 'x')
57
+ end.not_to raise_error
58
+ end
59
+ end
60
+ end
61
+
62
+ describe '#accumulate!' do
63
+ it 'increases depth by default rate' do
64
+ expect { snowpack.accumulate! }.to change(snowpack, :depth).by(0.06)
65
+ end
66
+
67
+ it 'increases depth by given rate' do
68
+ expect { snowpack.accumulate!(0.1) }.to change(snowpack, :depth).by(0.1)
69
+ end
70
+
71
+ it 'clamps depth at 1.0' do
72
+ sp = described_class.new(snowpack_type: :ideas, domain: :test, content: 'x', depth: 0.98)
73
+ sp.accumulate!(0.1)
74
+ expect(sp.depth).to eq(1.0)
75
+ end
76
+
77
+ it 'uses absolute value of rate' do
78
+ before = snowpack.depth
79
+ snowpack.accumulate!(-0.05)
80
+ expect(snowpack.depth).to eq((before + 0.05).round(10))
81
+ end
82
+ end
83
+
84
+ describe '#compact!' do
85
+ it 'increases stability by 0.05' do
86
+ sp = described_class.new(snowpack_type: :ideas, domain: :test, content: 'x', stability: 0.5)
87
+ before = sp.stability
88
+ sp.compact!
89
+ expect(sp.stability).to be_within(0.0001).of(before + 0.05)
90
+ end
91
+
92
+ it 'clamps stability at 1.0' do
93
+ sp = described_class.new(snowpack_type: :ideas, domain: :test, content: 'x', stability: 0.98)
94
+ sp.compact!
95
+ expect(sp.stability).to eq(1.0)
96
+ end
97
+ end
98
+
99
+ describe '#destabilize!' do
100
+ it 'reduces stability by force' do
101
+ sp = described_class.new(snowpack_type: :ideas, domain: :test, content: 'x', stability: 0.8)
102
+ before = sp.stability
103
+ sp.destabilize!(0.2)
104
+ expect(sp.stability).to be_within(0.0001).of(before - 0.2)
105
+ end
106
+
107
+ it 'clamps stability at 0.0' do
108
+ sp = described_class.new(snowpack_type: :ideas, domain: :test, content: 'x', stability: 0.1)
109
+ sp.destabilize!(0.5)
110
+ expect(sp.stability).to eq(0.0)
111
+ end
112
+
113
+ it 'uses absolute value of force' do
114
+ sp = described_class.new(snowpack_type: :ideas, domain: :test, content: 'x', stability: 0.8)
115
+ sp.destabilize!(-0.2)
116
+ expect(sp.stability).to eq(0.6)
117
+ end
118
+ end
119
+
120
+ describe '#stable?' do
121
+ it 'returns true when stability >= 0.6' do
122
+ sp = described_class.new(snowpack_type: :ideas, domain: :test, content: 'x', stability: 0.7)
123
+ expect(sp.stable?).to be(true)
124
+ end
125
+
126
+ it 'returns false when stability < 0.6' do
127
+ sp = described_class.new(snowpack_type: :ideas, domain: :test, content: 'x', stability: 0.5)
128
+ expect(sp.stable?).to be(false)
129
+ end
130
+
131
+ it 'returns true at exactly 0.6' do
132
+ sp = described_class.new(snowpack_type: :ideas, domain: :test, content: 'x', stability: 0.6)
133
+ expect(sp.stable?).to be(true)
134
+ end
135
+ end
136
+
137
+ describe '#unstable?' do
138
+ it 'returns true when stability < 0.4' do
139
+ sp = described_class.new(snowpack_type: :ideas, domain: :test, content: 'x', stability: 0.3)
140
+ expect(sp.unstable?).to be(true)
141
+ end
142
+
143
+ it 'returns false when stability >= 0.4' do
144
+ sp = described_class.new(snowpack_type: :ideas, domain: :test, content: 'x', stability: 0.5)
145
+ expect(sp.unstable?).to be(false)
146
+ end
147
+ end
148
+
149
+ describe '#critical?' do
150
+ it 'returns true when stability < 0.2' do
151
+ sp = described_class.new(snowpack_type: :ideas, domain: :test, content: 'x', stability: 0.1)
152
+ expect(sp.critical?).to be(true)
153
+ end
154
+
155
+ it 'returns false when stability >= 0.2' do
156
+ sp = described_class.new(snowpack_type: :ideas, domain: :test, content: 'x', stability: 0.2)
157
+ expect(sp.critical?).to be(false)
158
+ end
159
+ end
160
+
161
+ describe '#stability_label' do
162
+ it 'returns :catastrophic for stability 0.1' do
163
+ sp = described_class.new(snowpack_type: :ideas, domain: :test, content: 'x', stability: 0.1)
164
+ expect(sp.stability_label).to eq(:catastrophic)
165
+ end
166
+
167
+ it 'returns :bedrock for stability 1.0' do
168
+ sp = described_class.new(snowpack_type: :ideas, domain: :test, content: 'x', stability: 1.0)
169
+ expect(sp.stability_label).to eq(:bedrock)
170
+ end
171
+ end
172
+
173
+ describe '#instability' do
174
+ it 'returns 1.0 - stability' do
175
+ sp = described_class.new(snowpack_type: :ideas, domain: :test, content: 'x', stability: 0.7)
176
+ expect(sp.instability).to eq(0.3)
177
+ end
178
+
179
+ it 'returns 0.0 when fully stable' do
180
+ sp = described_class.new(snowpack_type: :ideas, domain: :test, content: 'x', stability: 1.0)
181
+ expect(sp.instability).to eq(0.0)
182
+ end
183
+ end
184
+
185
+ describe '#to_h' do
186
+ it 'includes all expected keys' do
187
+ h = snowpack.to_h
188
+ expect(h.keys).to include(:id, :snowpack_type, :domain, :content, :depth, :stability,
189
+ :stable, :unstable, :critical, :stability_label, :created_at)
190
+ end
191
+
192
+ it 'reflects current stability state' do
193
+ h = snowpack.to_h
194
+ expect(h[:stable]).to eq(snowpack.stable?)
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,211 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Defense::Avalanche::Runners::CognitiveAvalanche do
4
+ let(:engine) { Legion::Extensions::Agentic::Defense::Avalanche::Helpers::AvalancheEngine.new }
5
+ let(:runner) { Object.new.extend(described_module) }
6
+
7
+ def described_module
8
+ Legion::Extensions::Agentic::Defense::Avalanche::Runners::CognitiveAvalanche
9
+ end
10
+
11
+ let(:unstable_snowpack_id) do
12
+ engine.create_snowpack(
13
+ snowpack_type: :ideas,
14
+ domain: :creativity,
15
+ content: 'cascading concept',
16
+ stability: 0.1
17
+ )[:snowpack_id]
18
+ end
19
+
20
+ let(:stable_snowpack_id) do
21
+ engine.create_snowpack(
22
+ snowpack_type: :memories,
23
+ domain: :identity,
24
+ content: 'core belief',
25
+ stability: 0.95
26
+ )[:snowpack_id]
27
+ end
28
+
29
+ describe '#create_snowpack' do
30
+ it 'creates a snowpack with valid params' do
31
+ result = runner.create_snowpack(snowpack_type: :emotions, domain: :fear, content: 'baseline anxiety', engine: engine)
32
+ expect(result[:success]).to be(true)
33
+ expect(result[:snowpack_id]).not_to be_nil
34
+ end
35
+
36
+ it 'returns failure when snowpack_type is missing' do
37
+ result = runner.create_snowpack(domain: :test, content: 'x', engine: engine)
38
+ expect(result[:success]).to be(false)
39
+ expect(result[:error]).to match(/snowpack_type/)
40
+ end
41
+
42
+ it 'returns failure when domain is missing' do
43
+ result = runner.create_snowpack(snowpack_type: :ideas, content: 'x', engine: engine)
44
+ expect(result[:success]).to be(false)
45
+ expect(result[:error]).to match(/domain/)
46
+ end
47
+
48
+ it 'returns failure when content is missing' do
49
+ result = runner.create_snowpack(snowpack_type: :ideas, domain: :test, engine: engine)
50
+ expect(result[:success]).to be(false)
51
+ expect(result[:error]).to match(/content/)
52
+ end
53
+
54
+ it 'passes depth to engine' do
55
+ result = runner.create_snowpack(snowpack_type: :ideas, domain: :test, content: 'x', depth: 0.4, engine: engine)
56
+ expect(engine.snowpacks[result[:snowpack_id]].depth).to eq(0.4)
57
+ end
58
+
59
+ it 'passes stability to engine' do
60
+ result = runner.create_snowpack(snowpack_type: :ideas, domain: :test, content: 'x', stability: 0.3, engine: engine)
61
+ expect(engine.snowpacks[result[:snowpack_id]].stability).to eq(0.3)
62
+ end
63
+
64
+ it 'accepts extra kwargs without error' do
65
+ result = runner.create_snowpack(
66
+ snowpack_type: :impulses, domain: :test, content: 'x', engine: engine, irrelevant: true
67
+ )
68
+ expect(result[:success]).to be(true)
69
+ end
70
+ end
71
+
72
+ describe '#trigger' do
73
+ before { unstable_snowpack_id && stable_snowpack_id }
74
+
75
+ it 'triggers a cascade for an unstable snowpack' do
76
+ result = runner.trigger(snowpack_id: unstable_snowpack_id, force: 0.5, engine: engine)
77
+ expect(result[:success]).to be(true)
78
+ expect(result[:triggered]).to be(true)
79
+ end
80
+
81
+ it 'returns no trigger for stable snowpack with low force' do
82
+ result = runner.trigger(snowpack_id: stable_snowpack_id, force: 0.01, engine: engine)
83
+ expect(result[:success]).to be(true)
84
+ expect(result[:triggered]).to be(false)
85
+ end
86
+
87
+ it 'returns failure when snowpack_id is missing' do
88
+ result = runner.trigger(force: 0.5, engine: engine)
89
+ expect(result[:success]).to be(false)
90
+ expect(result[:error]).to match(/snowpack_id/)
91
+ end
92
+
93
+ it 'returns failure for unknown snowpack_id' do
94
+ result = runner.trigger(snowpack_id: 'ghost', force: 0.5, engine: engine)
95
+ expect(result[:success]).to be(false)
96
+ end
97
+
98
+ it 'defaults force to 0.5' do
99
+ result = runner.trigger(snowpack_id: unstable_snowpack_id, engine: engine)
100
+ expect(result[:success]).to be(true)
101
+ end
102
+
103
+ it 'accepts custom cascade_type' do
104
+ result = runner.trigger(
105
+ snowpack_id: unstable_snowpack_id, force: 0.5,
106
+ cascade_type: :analytical, engine: engine
107
+ )
108
+ expect(result[:cascade][:cascade_type]).to eq(:analytical) if result[:triggered]
109
+ end
110
+
111
+ it 'accepts extra kwargs without error' do
112
+ result = runner.trigger(snowpack_id: unstable_snowpack_id, force: 0.5, engine: engine, trace: 'debug')
113
+ expect(result[:success]).to be(true)
114
+ end
115
+ end
116
+
117
+ describe '#accumulate' do
118
+ before { unstable_snowpack_id }
119
+
120
+ it 'returns success' do
121
+ result = runner.accumulate(engine: engine)
122
+ expect(result[:success]).to be(true)
123
+ end
124
+
125
+ it 'reports packs_accumulated' do
126
+ result = runner.accumulate(engine: engine)
127
+ expect(result[:packs_accumulated]).to eq(1)
128
+ end
129
+
130
+ it 'accepts custom rate' do
131
+ result = runner.accumulate(rate: 0.1, engine: engine)
132
+ expect(result[:rate]).to eq(0.1)
133
+ end
134
+
135
+ it 'uses default ACCUMULATION_RATE when not specified' do
136
+ result = runner.accumulate(engine: engine)
137
+ expect(result[:rate]).to eq(Legion::Extensions::Agentic::Defense::Avalanche::Helpers::Constants::ACCUMULATION_RATE)
138
+ end
139
+ end
140
+
141
+ describe '#list_snowpacks' do
142
+ before { unstable_snowpack_id && stable_snowpack_id }
143
+
144
+ it 'returns success' do
145
+ result = runner.list_snowpacks(engine: engine)
146
+ expect(result[:success]).to be(true)
147
+ end
148
+
149
+ it 'includes snowpacks array' do
150
+ result = runner.list_snowpacks(engine: engine)
151
+ expect(result[:snowpacks]).to be_an(Array)
152
+ expect(result[:snowpacks].size).to eq(2)
153
+ end
154
+
155
+ it 'includes count' do
156
+ result = runner.list_snowpacks(engine: engine)
157
+ expect(result[:count]).to eq(2)
158
+ end
159
+
160
+ it 'returns hash representations of snowpacks' do
161
+ result = runner.list_snowpacks(engine: engine)
162
+ expect(result[:snowpacks].first).to have_key(:id)
163
+ expect(result[:snowpacks].first).to have_key(:stability)
164
+ end
165
+
166
+ it 'returns empty list when no snowpacks' do
167
+ empty_engine = Legion::Extensions::Agentic::Defense::Avalanche::Helpers::AvalancheEngine.new
168
+ result = runner.list_snowpacks(engine: empty_engine)
169
+ expect(result[:count]).to eq(0)
170
+ end
171
+ end
172
+
173
+ describe '#terrain_status' do
174
+ before { unstable_snowpack_id && stable_snowpack_id }
175
+
176
+ it 'returns success' do
177
+ result = runner.terrain_status(engine: engine)
178
+ expect(result[:success]).to be(true)
179
+ end
180
+
181
+ it 'includes total_snowpacks' do
182
+ result = runner.terrain_status(engine: engine)
183
+ expect(result[:total_snowpacks]).to eq(2)
184
+ end
185
+
186
+ it 'includes cascade_history' do
187
+ runner.trigger(snowpack_id: unstable_snowpack_id, force: 0.5, engine: engine)
188
+ result = runner.terrain_status(engine: engine)
189
+ expect(result[:cascade_history]).to be >= 1
190
+ end
191
+
192
+ it 'includes avg_stability' do
193
+ result = runner.terrain_status(engine: engine)
194
+ expect(result[:avg_stability]).to be_a(Float)
195
+ end
196
+
197
+ it 'includes most_unstable_id' do
198
+ result = runner.terrain_status(engine: engine)
199
+ expect(result[:most_unstable_id]).to eq(unstable_snowpack_id)
200
+ end
201
+ end
202
+
203
+ describe 'default engine isolation' do
204
+ it 'each runner instance has its own default engine' do
205
+ r1 = Object.new.extend(described_module)
206
+ r2 = Object.new.extend(described_module)
207
+ r1.create_snowpack(snowpack_type: :ideas, domain: :test, content: 'isolated')
208
+ expect(r2.terrain_status[:total_snowpacks]).to eq(0)
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/defense/bias/client'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Defense::Bias::Client do
6
+ let(:client) { described_class.new }
7
+
8
+ it 'responds to all runner methods' do
9
+ expect(client).to respond_to(:check_for_bias)
10
+ expect(client).to respond_to(:record_anchor)
11
+ expect(client).to respond_to(:update_bias)
12
+ expect(client).to respond_to(:bias_report)
13
+ expect(client).to respond_to(:susceptibility_profile)
14
+ expect(client).to respond_to(:bias_stats)
15
+ end
16
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/defense/bias/helpers/constants'
4
+ require 'legion/extensions/agentic/defense/bias/helpers/bias_detector'
5
+
6
+ RSpec.describe Legion::Extensions::Agentic::Defense::Bias::Helpers::BiasDetector do
7
+ subject(:detector) { described_class.new }
8
+
9
+ describe '#susceptibility_for' do
10
+ it 'returns DEFAULT_SUSCEPTIBILITY for all bias types at init' do
11
+ Legion::Extensions::Agentic::Defense::Bias::Helpers::Constants::BIAS_TYPES.each do |bt|
12
+ expect(detector.susceptibility_for(bt)).to eq(
13
+ Legion::Extensions::Agentic::Defense::Bias::Helpers::Constants::DEFAULT_SUSCEPTIBILITY
14
+ )
15
+ end
16
+ end
17
+ end
18
+
19
+ describe '#detect_anchoring' do
20
+ it 'returns high magnitude when current value is very close to anchor' do
21
+ mag = detector.detect_anchoring(current_value: 100.0, anchor_value: 100.0)
22
+ expect(mag).to be_within(0.01).of(0.5) # pull=1.0 * susceptibility=0.5
23
+ end
24
+
25
+ it 'returns low magnitude when current value is far from anchor' do
26
+ mag = detector.detect_anchoring(current_value: 200.0, anchor_value: 100.0)
27
+ expect(mag).to be < 0.3
28
+ end
29
+
30
+ it 'returns 0.0 when anchor_value is zero' do
31
+ mag = detector.detect_anchoring(current_value: 50.0, anchor_value: 0.0)
32
+ expect(mag).to eq(0.0)
33
+ end
34
+
35
+ it 'returns 0.0 when anchor_value is nil' do
36
+ mag = detector.detect_anchoring(current_value: 50.0, anchor_value: nil)
37
+ expect(mag).to eq(0.0)
38
+ end
39
+
40
+ it 'clamps result to [0, 1]' do
41
+ mag = detector.detect_anchoring(current_value: 100.0, anchor_value: 100.0)
42
+ expect(mag).to be_between(0.0, 1.0)
43
+ end
44
+ end
45
+
46
+ describe '#detect_confirmation' do
47
+ it 'returns higher magnitude when evidence matches hypothesis' do
48
+ mag_match = detector.detect_confirmation(
49
+ evidence_direction: :positive,
50
+ hypothesis_direction: :positive
51
+ )
52
+ mag_mismatch = detector.detect_confirmation(
53
+ evidence_direction: :negative,
54
+ hypothesis_direction: :positive
55
+ )
56
+ expect(mag_match).to be > mag_mismatch
57
+ end
58
+
59
+ it 'returns magnitude >= CONFIRMATION_WEIGHT * susceptibility when matching' do
60
+ mag = detector.detect_confirmation(
61
+ evidence_direction: :up,
62
+ hypothesis_direction: :up
63
+ )
64
+ expected = Legion::Extensions::Agentic::Defense::Bias::Helpers::Constants::CONFIRMATION_WEIGHT *
65
+ Legion::Extensions::Agentic::Defense::Bias::Helpers::Constants::DEFAULT_SUSCEPTIBILITY
66
+ expect(mag).to be_within(0.001).of(expected)
67
+ end
68
+ end
69
+
70
+ describe '#detect_availability' do
71
+ it 'returns higher magnitude with more recent events' do
72
+ mag_full = detector.detect_availability(recent_events: Array.new(10, :event))
73
+ mag_none = detector.detect_availability(recent_events: [])
74
+ expect(mag_full).to be > mag_none
75
+ end
76
+
77
+ it 'returns 0 with empty recent_events' do
78
+ mag = detector.detect_availability(recent_events: [])
79
+ expect(mag).to eq(0.0)
80
+ end
81
+ end
82
+
83
+ describe '#detect_recency' do
84
+ it 'returns 0 with fewer than 2 data points' do
85
+ mag = detector.detect_recency(data_points: [1.0])
86
+ expect(mag).to eq(0.0)
87
+ end
88
+
89
+ it 'returns 0 when all values are equal (zero range)' do
90
+ mag = detector.detect_recency(data_points: [5.0, 5.0, 5.0, 5.0])
91
+ expect(mag).to eq(0.0)
92
+ end
93
+
94
+ it 'returns higher magnitude when recent half differs significantly from earlier half' do
95
+ early = [1.0, 1.0, 1.0, 1.0]
96
+ recent = [9.0, 9.0, 9.0, 9.0]
97
+ mag = detector.detect_recency(data_points: early + recent)
98
+ expect(mag).to be > 0.3
99
+ end
100
+ end
101
+
102
+ describe '#detect_sunk_cost' do
103
+ it 'returns 0 when invested is 0' do
104
+ mag = detector.detect_sunk_cost(invested: 0, expected_return: 100)
105
+ expect(mag).to eq(0.0)
106
+ end
107
+
108
+ it 'returns higher magnitude with large investment and low expected return' do
109
+ mag = detector.detect_sunk_cost(invested: 1_000_000, expected_return: 1)
110
+ expect(mag).to be > 0.3
111
+ end
112
+
113
+ it 'returns lower magnitude with small investment and large expected return' do
114
+ mag = detector.detect_sunk_cost(invested: 1, expected_return: 1_000_000)
115
+ expect(mag).to be < 0.1
116
+ end
117
+ end
118
+
119
+ describe '#correction_for' do
120
+ it 'applies CORRECTION_FACTOR to magnitude' do
121
+ result = detector.correction_for(0.6)
122
+ expect(result).to be_within(0.001).of(
123
+ 0.6 * Legion::Extensions::Agentic::Defense::Bias::Helpers::Constants::CORRECTION_FACTOR
124
+ )
125
+ end
126
+
127
+ it 'clamps to [0, 1]' do
128
+ expect(detector.correction_for(2.0)).to eq(1.0)
129
+ expect(detector.correction_for(-1.0)).to eq(0.0)
130
+ end
131
+ end
132
+
133
+ describe '#update_susceptibility' do
134
+ it 'increases susceptibility toward 1.0 when repeatedly detected' do
135
+ initial = detector.susceptibility_for(:anchoring)
136
+ 10.times { detector.update_susceptibility(:anchoring, detected: true) }
137
+ expect(detector.susceptibility_for(:anchoring)).to be > initial
138
+ end
139
+
140
+ it 'decreases susceptibility toward 0.0 when not detected' do
141
+ initial = detector.susceptibility_for(:anchoring)
142
+ 10.times { detector.update_susceptibility(:anchoring, detected: false) }
143
+ expect(detector.susceptibility_for(:anchoring)).to be < initial
144
+ end
145
+
146
+ it 'ignores unknown bias types' do
147
+ expect { detector.update_susceptibility(:unknown_bias, detected: true) }.not_to raise_error
148
+ end
149
+ end
150
+
151
+ describe '#to_h' do
152
+ it 'returns susceptibility hash' do
153
+ h = detector.to_h
154
+ expect(h[:susceptibility]).to be_a(Hash)
155
+ expect(h[:susceptibility].keys).to match_array(
156
+ Legion::Extensions::Agentic::Defense::Bias::Helpers::Constants::BIAS_TYPES
157
+ )
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/defense/bias/helpers/bias_event'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Defense::Bias::Helpers::BiasEvent do
6
+ let(:event) do
7
+ described_class.new(
8
+ bias_type: :anchoring,
9
+ domain: :finance,
10
+ magnitude: 0.6,
11
+ corrected: true,
12
+ correction_applied: 0.3,
13
+ context: { current_value: 100 }
14
+ )
15
+ end
16
+
17
+ describe '#initialize' do
18
+ it 'assigns a uuid id' do
19
+ expect(event.id).to match(/\A[0-9a-f-]{36}\z/)
20
+ end
21
+
22
+ it 'assigns bias_type' do
23
+ expect(event.bias_type).to eq(:anchoring)
24
+ end
25
+
26
+ it 'assigns domain' do
27
+ expect(event.domain).to eq(:finance)
28
+ end
29
+
30
+ it 'assigns magnitude' do
31
+ expect(event.magnitude).to eq(0.6)
32
+ end
33
+
34
+ it 'assigns corrected flag' do
35
+ expect(event.corrected).to be true
36
+ end
37
+
38
+ it 'assigns correction_applied' do
39
+ expect(event.correction_applied).to eq(0.3)
40
+ end
41
+
42
+ it 'assigns context' do
43
+ expect(event.context).to eq({ current_value: 100 })
44
+ end
45
+
46
+ it 'sets timestamp to a Time' do
47
+ expect(event.timestamp).to be_a(Time)
48
+ end
49
+ end
50
+
51
+ describe '#to_h' do
52
+ it 'returns a hash with all fields' do
53
+ h = event.to_h
54
+ expect(h[:id]).to eq(event.id)
55
+ expect(h[:bias_type]).to eq(:anchoring)
56
+ expect(h[:domain]).to eq(:finance)
57
+ expect(h[:magnitude]).to eq(0.6)
58
+ expect(h[:corrected]).to be true
59
+ expect(h[:correction_applied]).to eq(0.3)
60
+ expect(h[:context]).to eq({ current_value: 100 })
61
+ expect(h[:timestamp]).to be_a(Time)
62
+ end
63
+ end
64
+ end