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,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/defense/epistemic_vigilance/client'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Defense::EpistemicVigilance::Runners::EpistemicVigilance do
6
+ let(:client) { Legion::Extensions::Agentic::Defense::EpistemicVigilance::Client.new }
7
+
8
+ let(:source_id) do
9
+ client.register_epistemic_source(name: 'TestSource', domain: :testing)[:id]
10
+ end
11
+
12
+ let(:claim_id) do
13
+ client.submit_epistemic_claim(content: 'test claim', source_id: source_id, domain: :testing)[:claim][:id]
14
+ end
15
+
16
+ describe '#register_epistemic_source' do
17
+ it 'returns a hash with id and name' do
18
+ result = client.register_epistemic_source(name: 'ScienceBot', domain: :science)
19
+ expect(result).to include(:id, :name, :domain)
20
+ expect(result[:name]).to eq('ScienceBot')
21
+ end
22
+ end
23
+
24
+ describe '#submit_epistemic_claim' do
25
+ it 'returns claim and assessment' do
26
+ result = client.submit_epistemic_claim(content: 'sky is blue', source_id: source_id, domain: :testing)
27
+ expect(result).to include(:claim, :assessment)
28
+ end
29
+
30
+ it 'returns error for unknown source' do
31
+ result = client.submit_epistemic_claim(content: 'x', source_id: 'bad', domain: :testing)
32
+ expect(result[:error]).to eq(:source_not_found)
33
+ end
34
+ end
35
+
36
+ describe '#assess_epistemic_claim' do
37
+ it 'returns assessment with all score fields' do
38
+ cid = claim_id
39
+ result = client.assess_epistemic_claim(claim_id: cid)
40
+ expect(result).to include(:source_score, :consistency_score, :coherence_score,
41
+ :total_score, :recommended_verdict)
42
+ end
43
+
44
+ it 'returns error for unknown claim' do
45
+ result = client.assess_epistemic_claim(claim_id: 'bad')
46
+ expect(result[:error]).to eq(:claim_not_found)
47
+ end
48
+ end
49
+
50
+ describe '#support_epistemic_claim' do
51
+ it 'increments evidence_for' do
52
+ cid = claim_id
53
+ result = client.support_epistemic_claim(claim_id: cid)
54
+ expect(result[:evidence_for]).to eq(1)
55
+ end
56
+
57
+ it 'increases confidence' do
58
+ cid = claim_id
59
+ result = client.support_epistemic_claim(claim_id: cid)
60
+ expect(result[:confidence]).to be > 0.5
61
+ end
62
+ end
63
+
64
+ describe '#challenge_epistemic_claim' do
65
+ it 'increments evidence_against' do
66
+ cid = claim_id
67
+ result = client.challenge_epistemic_claim(claim_id: cid)
68
+ expect(result[:evidence_against]).to eq(1)
69
+ end
70
+
71
+ it 'decreases confidence' do
72
+ cid = claim_id
73
+ result = client.challenge_epistemic_claim(claim_id: cid)
74
+ expect(result[:confidence]).to be < 0.5
75
+ end
76
+ end
77
+
78
+ describe '#adjudicate_epistemic_claim' do
79
+ it 'sets the verdict to accepted' do
80
+ cid = claim_id
81
+ result = client.adjudicate_epistemic_claim(claim_id: cid, verdict: :accepted)
82
+ expect(result[:verdict]).to eq(:accepted)
83
+ end
84
+
85
+ it 'sets the verdict to rejected' do
86
+ cid = claim_id
87
+ result = client.adjudicate_epistemic_claim(claim_id: cid, verdict: :rejected)
88
+ expect(result[:verdict]).to eq(:rejected)
89
+ end
90
+ end
91
+
92
+ describe '#source_reliability_report' do
93
+ it 'returns reliability and label' do
94
+ sid = source_id
95
+ result = client.source_reliability_report(source_id: sid)
96
+ expect(result).to include(:reliability, :label)
97
+ end
98
+
99
+ it 'returns error for unknown source' do
100
+ result = client.source_reliability_report(source_id: 'bad')
101
+ expect(result[:error]).to eq(:source_not_found)
102
+ end
103
+ end
104
+
105
+ describe '#contested_claims_report' do
106
+ it 'returns contested claims list and count' do
107
+ result = client.contested_claims_report
108
+ expect(result).to include(:contested, :count)
109
+ end
110
+
111
+ it 'count matches contested array size' do
112
+ result = client.contested_claims_report
113
+ expect(result[:count]).to eq(result[:contested].size)
114
+ end
115
+ end
116
+
117
+ describe '#domain_vigilance_report' do
118
+ it 'returns domain, vigilance_level, claims, count' do
119
+ result = client.domain_vigilance_report(domain: :testing)
120
+ expect(result).to include(:domain, :vigilance_level, :claims, :count)
121
+ end
122
+
123
+ it 'includes submitted claims in domain' do
124
+ claim_id # force creation
125
+ result = client.domain_vigilance_report(domain: :testing)
126
+ expect(result[:count]).to be >= 1
127
+ end
128
+ end
129
+
130
+ describe '#update_epistemic_vigilance' do
131
+ it 'returns decay and prune results' do
132
+ result = client.update_epistemic_vigilance
133
+ expect(result).to include(:decay, :prune)
134
+ end
135
+
136
+ it 'decays existing claims' do
137
+ claim_id # create a claim
138
+ result = client.update_epistemic_vigilance
139
+ expect(result[:decay][:decayed]).to be >= 1
140
+ end
141
+ end
142
+
143
+ describe '#epistemic_vigilance_stats' do
144
+ it 'returns stats hash' do
145
+ result = client.epistemic_vigilance_stats
146
+ expect(result).to include(:sources_count, :claims_count, :contested, :by_verdict)
147
+ end
148
+
149
+ it 'reflects registered sources and submitted claims' do
150
+ sid = source_id
151
+ client.submit_epistemic_claim(content: 'test', source_id: sid, domain: :testing)
152
+ stats = client.epistemic_vigilance_stats
153
+ expect(stats[:sources_count]).to be >= 1
154
+ expect(stats[:claims_count]).to be >= 1
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Defense::Erosion::Client do
4
+ let(:engine) { Legion::Extensions::Agentic::Defense::Erosion::Helpers::ErosionEngine.new }
5
+ let(:client) { described_class.new(engine: engine) }
6
+
7
+ describe '#initialize' do
8
+ it 'accepts injected engine' do
9
+ expect(client).to respond_to(:create_formation)
10
+ end
11
+
12
+ it 'creates its own engine when none injected' do
13
+ c = described_class.new
14
+ result = c.create_formation(material_type: :limestone, domain: 'd', content: 'c')
15
+ expect(result[:success]).to be(true)
16
+ end
17
+ end
18
+
19
+ describe '#create_formation' do
20
+ it 'creates a formation' do
21
+ result = client.create_formation(material_type: :sandstone, domain: 'morality', content: 'lying is wrong')
22
+ expect(result[:success]).to be(true)
23
+ expect(result[:formation_id]).to match(/\A[0-9a-f-]{36}\z/)
24
+ end
25
+ end
26
+
27
+ describe '#erode' do
28
+ it 'erodes an existing formation' do
29
+ id = client.create_formation(material_type: :chalk, domain: 'taste', content: 'jazz is noise')[:formation_id]
30
+ result = client.erode(formation_id: id, agent: :water, force: 0.4)
31
+ expect(result[:success]).to be(true)
32
+ end
33
+ end
34
+
35
+ describe '#weather_all' do
36
+ it 'weathers all formations in the engine' do
37
+ client.create_formation(material_type: :clay, domain: 'd1', content: 'c1')
38
+ client.create_formation(material_type: :chalk, domain: 'd2', content: 'c2')
39
+ result = client.weather_all
40
+ expect(result[:weathered]).to eq(2)
41
+ end
42
+ end
43
+
44
+ describe '#deepest_channels' do
45
+ it 'returns channels' do
46
+ result = client.deepest_channels
47
+ expect(result[:channels]).to be_an(Array)
48
+ end
49
+ end
50
+
51
+ describe '#most_eroded' do
52
+ it 'returns formations' do
53
+ result = client.most_eroded
54
+ expect(result[:formations]).to be_an(Array)
55
+ end
56
+ end
57
+
58
+ describe '#erosion_report' do
59
+ it 'returns a report' do
60
+ result = client.erosion_report
61
+ expect(result[:success]).to be(true)
62
+ expect(result).to include(:total_formations, :total_channels)
63
+ end
64
+ end
65
+
66
+ describe 'full erosion lifecycle' do
67
+ it 'creates formation, erodes repeatedly, and reports canyon' do
68
+ id = client.create_formation(material_type: :clay, domain: 'belief', content: 'hard work always pays off')[:formation_id]
69
+
70
+ 10.times { client.erode(formation_id: id, agent: :water, force: 0.8) }
71
+
72
+ report = client.erosion_report
73
+ expect(report[:canyons]).to be >= 1
74
+
75
+ channels = client.deepest_channels(limit: 3)
76
+ expect(channels[:channels].first[:depth]).to be > 0.3
77
+ end
78
+
79
+ it 'tracks multiple independent erosion channels per formation' do
80
+ id = client.create_formation(material_type: :limestone, domain: 'politics', content: 'markets self-regulate')[:formation_id]
81
+
82
+ client.erode(formation_id: id, agent: :water, force: 0.3)
83
+ client.erode(formation_id: id, agent: :wind, force: 0.3)
84
+ client.erode(formation_id: id, agent: :chemical, force: 0.3)
85
+
86
+ report = client.erosion_report
87
+ expect(report[:total_channels]).to be >= 3
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Defense::Erosion::Helpers::Channel do
4
+ let(:formation_id) { 'test-formation-uuid-1234' }
5
+ let(:channel) { described_class.new(formation_id: formation_id, agent: :water) }
6
+
7
+ describe '#initialize' do
8
+ it 'assigns a UUID channel_id' do
9
+ expect(channel.channel_id).to match(/\A[0-9a-f-]{36}\z/)
10
+ end
11
+
12
+ it 'sets formation_id' do
13
+ expect(channel.formation_id).to eq(formation_id)
14
+ end
15
+
16
+ it 'sets agent' do
17
+ expect(channel.agent).to eq(:water)
18
+ end
19
+
20
+ it 'starts with depth 0.0' do
21
+ expect(channel.depth).to eq(0.0)
22
+ end
23
+
24
+ it 'starts with default width 0.1' do
25
+ expect(channel.width).to eq(0.1)
26
+ end
27
+
28
+ it 'starts with flow_rate 0.0' do
29
+ expect(channel.flow_rate).to eq(0.0)
30
+ end
31
+
32
+ it 'accepts custom initial depth' do
33
+ ch = described_class.new(formation_id: formation_id, agent: :wind, depth: 0.4)
34
+ expect(ch.depth).to eq(0.4)
35
+ end
36
+
37
+ it 'clamps depth to 0.0-1.0 range' do
38
+ ch = described_class.new(formation_id: formation_id, agent: :wind, depth: 5.0)
39
+ expect(ch.depth).to eq(1.0)
40
+ end
41
+
42
+ it 'raises ArgumentError for invalid agent' do
43
+ expect do
44
+ described_class.new(formation_id: formation_id, agent: :laser)
45
+ end.to raise_error(ArgumentError, /invalid agent/)
46
+ end
47
+
48
+ it 'last_active_at starts as nil' do
49
+ expect(channel.last_active_at).to be_nil
50
+ end
51
+ end
52
+
53
+ describe '#deepen!' do
54
+ it 'increases depth' do
55
+ channel.deepen!(0.3)
56
+ expect(channel.depth).to be > 0.0
57
+ end
58
+
59
+ it 'increases flow_rate' do
60
+ channel.deepen!(0.5)
61
+ expect(channel.flow_rate).to be > 0.0
62
+ end
63
+
64
+ it 'returns hash with depth and flow_rate' do
65
+ result = channel.deepen!(0.2)
66
+ expect(result).to include(:depth, :flow_rate)
67
+ end
68
+
69
+ it 'clamps force to 0.0 minimum' do
70
+ channel.deepen!(-1.0)
71
+ expect(channel.depth).to eq(0.0)
72
+ end
73
+
74
+ it 'clamps depth at 1.0 maximum' do
75
+ 5.times { channel.deepen!(1.0) }
76
+ expect(channel.depth).to eq(1.0)
77
+ end
78
+
79
+ it 'sets last_active_at' do
80
+ channel.deepen!(0.1)
81
+ expect(channel.last_active_at).not_to be_nil
82
+ end
83
+
84
+ it 'updates updated_at' do
85
+ original = channel.updated_at
86
+ sleep(0.001)
87
+ channel.deepen!(0.1)
88
+ expect(channel.updated_at).to be >= original
89
+ end
90
+ end
91
+
92
+ describe '#widen!' do
93
+ it 'increases width' do
94
+ before = channel.width
95
+ channel.widen!
96
+ expect(channel.width).to be > before
97
+ end
98
+
99
+ it 'accepts custom amount' do
100
+ channel.widen!(0.2)
101
+ expect(channel.width).to be_within(0.001).of(0.3)
102
+ end
103
+
104
+ it 'clamps width at 1.0' do
105
+ 10.times { channel.widen!(1.0) }
106
+ expect(channel.width).to eq(1.0)
107
+ end
108
+
109
+ it 'returns hash with width' do
110
+ result = channel.widen!
111
+ expect(result).to include(:width)
112
+ end
113
+ end
114
+
115
+ describe '#dormant?' do
116
+ it 'is true when never activated' do
117
+ expect(channel.dormant?).to be(true)
118
+ end
119
+
120
+ it 'is false immediately after deepen!' do
121
+ channel.deepen!(0.1)
122
+ expect(channel.dormant?).to be(false)
123
+ end
124
+ end
125
+
126
+ describe '#active?' do
127
+ it 'is false when never activated' do
128
+ expect(channel.active?).to be(false)
129
+ end
130
+
131
+ it 'is true immediately after deepen!' do
132
+ channel.deepen!(0.1)
133
+ expect(channel.active?).to be(true)
134
+ end
135
+ end
136
+
137
+ describe '#depth_label' do
138
+ it 'returns :surface_scratch for fresh channel' do
139
+ expect(channel.depth_label).to eq(:surface_scratch)
140
+ end
141
+
142
+ it 'returns :canyon for deeply carved channel' do
143
+ ch = described_class.new(formation_id: formation_id, agent: :water, depth: 0.95)
144
+ expect(ch.depth_label).to eq(:canyon)
145
+ end
146
+
147
+ it 'returns :carved_channel at mid depth' do
148
+ ch = described_class.new(formation_id: formation_id, agent: :ice, depth: 0.4)
149
+ expect(ch.depth_label).to eq(:carved_channel)
150
+ end
151
+ end
152
+
153
+ describe '#to_h' do
154
+ it 'returns a hash with all required keys' do
155
+ h = channel.to_h
156
+ expect(h).to include(
157
+ :channel_id, :formation_id, :agent, :depth, :width,
158
+ :flow_rate, :depth_label, :dormant, :active, :created_at,
159
+ :updated_at, :last_active_at
160
+ )
161
+ end
162
+
163
+ it 'reflects current depth after deepen!' do
164
+ channel.deepen!(0.4)
165
+ expect(channel.to_h[:depth]).to be > 0.0
166
+ end
167
+
168
+ it 'reflects active state after deepen!' do
169
+ channel.deepen!(0.1)
170
+ expect(channel.to_h[:active]).to be(true)
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Defense::Erosion::Helpers::Constants do
4
+ describe 'MATERIAL_TYPES' do
5
+ it 'includes granite' do
6
+ expect(described_class::MATERIAL_TYPES).to include(:granite)
7
+ end
8
+
9
+ it 'includes sandstone' do
10
+ expect(described_class::MATERIAL_TYPES).to include(:sandstone)
11
+ end
12
+
13
+ it 'includes limestone' do
14
+ expect(described_class::MATERIAL_TYPES).to include(:limestone)
15
+ end
16
+
17
+ it 'includes chalk' do
18
+ expect(described_class::MATERIAL_TYPES).to include(:chalk)
19
+ end
20
+
21
+ it 'includes clay' do
22
+ expect(described_class::MATERIAL_TYPES).to include(:clay)
23
+ end
24
+
25
+ it 'is frozen' do
26
+ expect(described_class::MATERIAL_TYPES).to be_frozen
27
+ end
28
+
29
+ it 'has 5 material types' do
30
+ expect(described_class::MATERIAL_TYPES.size).to eq(5)
31
+ end
32
+ end
33
+
34
+ describe 'EROSION_AGENTS' do
35
+ it 'includes water' do
36
+ expect(described_class::EROSION_AGENTS).to include(:water)
37
+ end
38
+
39
+ it 'includes wind' do
40
+ expect(described_class::EROSION_AGENTS).to include(:wind)
41
+ end
42
+
43
+ it 'includes ice' do
44
+ expect(described_class::EROSION_AGENTS).to include(:ice)
45
+ end
46
+
47
+ it 'includes pressure' do
48
+ expect(described_class::EROSION_AGENTS).to include(:pressure)
49
+ end
50
+
51
+ it 'includes chemical' do
52
+ expect(described_class::EROSION_AGENTS).to include(:chemical)
53
+ end
54
+
55
+ it 'is frozen' do
56
+ expect(described_class::EROSION_AGENTS).to be_frozen
57
+ end
58
+
59
+ it 'has 5 agents' do
60
+ expect(described_class::EROSION_AGENTS.size).to eq(5)
61
+ end
62
+ end
63
+
64
+ describe 'RESISTANCE' do
65
+ it 'granite has highest resistance' do
66
+ expect(described_class::RESISTANCE[:granite]).to eq(0.9)
67
+ end
68
+
69
+ it 'clay has lowest resistance' do
70
+ expect(described_class::RESISTANCE[:clay]).to eq(0.1)
71
+ end
72
+
73
+ it 'sandstone resistance is between granite and limestone' do
74
+ expect(described_class::RESISTANCE[:sandstone]).to be_between(
75
+ described_class::RESISTANCE[:limestone],
76
+ described_class::RESISTANCE[:granite]
77
+ )
78
+ end
79
+
80
+ it 'chalk has lower resistance than limestone' do
81
+ expect(described_class::RESISTANCE[:chalk]).to be < described_class::RESISTANCE[:limestone]
82
+ end
83
+
84
+ it 'covers all material types' do
85
+ described_class::MATERIAL_TYPES.each do |mat|
86
+ expect(described_class::RESISTANCE).to have_key(mat)
87
+ end
88
+ end
89
+ end
90
+
91
+ describe 'MAX_FORMATIONS' do
92
+ it 'is 200' do
93
+ expect(described_class::MAX_FORMATIONS).to eq(200)
94
+ end
95
+ end
96
+
97
+ describe 'CHANNEL_DEPTH_LABELS' do
98
+ it 'labels depth 0.0 as surface_scratch' do
99
+ label = described_class::CHANNEL_DEPTH_LABELS.find { |range, _| range.cover?(0.0) }&.last
100
+ expect(label).to eq(:surface_scratch)
101
+ end
102
+
103
+ it 'labels depth 0.95 as canyon' do
104
+ label = described_class::CHANNEL_DEPTH_LABELS.find { |range, _| range.cover?(0.95) }&.last
105
+ expect(label).to eq(:canyon)
106
+ end
107
+
108
+ it 'labels depth 0.4 as carved_channel' do
109
+ label = described_class::CHANNEL_DEPTH_LABELS.find { |range, _| range.cover?(0.4) }&.last
110
+ expect(label).to eq(:carved_channel)
111
+ end
112
+
113
+ it 'covers the full 0.0-1.0 range' do
114
+ [0.0, 0.15, 0.35, 0.55, 0.75, 0.95, 1.0].each do |val|
115
+ found = described_class::CHANNEL_DEPTH_LABELS.any? { |range, _| range.cover?(val) }
116
+ expect(found).to be(true), "no label covers depth #{val}"
117
+ end
118
+ end
119
+ end
120
+
121
+ describe 'FORMATION_LABELS' do
122
+ it 'labels integrity 1.0 as pristine' do
123
+ label = described_class::FORMATION_LABELS.find { |range, _| range.cover?(1.0) }&.last
124
+ expect(label).to eq(:pristine)
125
+ end
126
+
127
+ it 'labels integrity 0.0 as ruins' do
128
+ label = described_class::FORMATION_LABELS.find { |range, _| range.cover?(0.0) }&.last
129
+ expect(label).to eq(:ruins)
130
+ end
131
+
132
+ it 'labels integrity 0.55 as weathered' do
133
+ label = described_class::FORMATION_LABELS.find { |range, _| range.cover?(0.55) }&.last
134
+ expect(label).to eq(:weathered)
135
+ end
136
+ end
137
+ end