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,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Defense::EpistemicVigilance::Helpers::Claim do
4
+ subject(:claim) do
5
+ described_class.new(content: 'the sky is blue', source_id: 'src-1', domain: :weather, confidence: 0.5)
6
+ end
7
+
8
+ describe '#initialize' do
9
+ it 'generates a UUID id' do
10
+ expect(claim.id).to match(/\A[0-9a-f-]{36}\z/)
11
+ end
12
+
13
+ it 'starts with :suspended verdict' do
14
+ expect(claim.verdict).to eq(:suspended)
15
+ end
16
+
17
+ it 'starts with zero evidence' do
18
+ expect(claim.evidence_for).to eq(0)
19
+ expect(claim.evidence_against).to eq(0)
20
+ end
21
+
22
+ it 'clamps confidence to 0..1' do
23
+ over = described_class.new(content: 'x', source_id: 's', domain: :d, confidence: 1.5)
24
+ under = described_class.new(content: 'x', source_id: 's', domain: :d, confidence: -0.5)
25
+ expect(over.confidence).to eq(1.0)
26
+ expect(under.confidence).to eq(0.0)
27
+ end
28
+ end
29
+
30
+ describe '#support!' do
31
+ it 'increments evidence_for' do
32
+ expect { claim.support! }.to change(claim, :evidence_for).by(1)
33
+ end
34
+
35
+ it 'increases confidence by 0.05' do
36
+ before = claim.confidence
37
+ claim.support!
38
+ expect(claim.confidence).to be_within(0.001).of(before + 0.05)
39
+ end
40
+
41
+ it 'clamps confidence at 1.0' do
42
+ strong = described_class.new(content: 'x', source_id: 's', domain: :d, confidence: 0.98)
43
+ strong.support!
44
+ expect(strong.confidence).to eq(1.0)
45
+ end
46
+ end
47
+
48
+ describe '#challenge!' do
49
+ it 'increments evidence_against' do
50
+ expect { claim.challenge! }.to change(claim, :evidence_against).by(1)
51
+ end
52
+
53
+ it 'decreases confidence by 0.08' do
54
+ before = claim.confidence
55
+ claim.challenge!
56
+ expect(claim.confidence).to be_within(0.001).of(before - 0.08)
57
+ end
58
+
59
+ it 'clamps confidence at 0.0' do
60
+ weak = described_class.new(content: 'x', source_id: 's', domain: :d, confidence: 0.03)
61
+ weak.challenge!
62
+ expect(weak.confidence).to eq(0.0)
63
+ end
64
+ end
65
+
66
+ describe '#credibility_ratio' do
67
+ it 'returns 0.0 with no evidence' do
68
+ expect(claim.credibility_ratio).to eq(0.0 / 1.0)
69
+ end
70
+
71
+ it 'is higher with more evidence_for' do
72
+ 3.times { claim.support! }
73
+ claim.challenge!
74
+ expect(claim.credibility_ratio).to be > 0.5
75
+ end
76
+ end
77
+
78
+ describe '#contested?' do
79
+ it 'is false with no challenges' do
80
+ 3.times { claim.support! }
81
+ expect(claim.contested?).to be false
82
+ end
83
+
84
+ it 'is true when challenged and credibility_ratio < 0.6' do
85
+ claim.support!
86
+ 3.times { claim.challenge! }
87
+ expect(claim.contested?).to be true
88
+ end
89
+ end
90
+
91
+ describe '#well_supported?' do
92
+ it 'is false initially' do
93
+ expect(claim.well_supported?).to be false
94
+ end
95
+
96
+ it 'is true with 3+ for and credibility > 0.7' do
97
+ 4.times { claim.support! }
98
+ expect(claim.well_supported?).to be true
99
+ end
100
+
101
+ it 'is false when challenged enough to lower credibility_ratio' do
102
+ 3.times { claim.support! }
103
+ 5.times { claim.challenge! }
104
+ expect(claim.well_supported?).to be false
105
+ end
106
+ end
107
+
108
+ describe '#adjudicate!' do
109
+ it 'sets the verdict' do
110
+ claim.adjudicate!(verdict: :accepted)
111
+ expect(claim.verdict).to eq(:accepted)
112
+ end
113
+
114
+ it 'can be set to rejected' do
115
+ claim.adjudicate!(verdict: :rejected)
116
+ expect(claim.verdict).to eq(:rejected)
117
+ end
118
+ end
119
+
120
+ describe '#to_h' do
121
+ it 'returns a hash with all expected keys' do
122
+ h = claim.to_h
123
+ expect(h).to include(:id, :content, :source_id, :domain, :confidence, :verdict,
124
+ :evidence_for, :evidence_against, :credibility_ratio,
125
+ :contested, :well_supported, :created_at)
126
+ end
127
+
128
+ it 'reflects current state' do
129
+ claim.support!
130
+ h = claim.to_h
131
+ expect(h[:evidence_for]).to eq(1)
132
+ expect(h[:confidence]).to be > 0.5
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Defense::EpistemicVigilance::Helpers::Constants do
4
+ subject(:mod) { described_class }
5
+
6
+ describe 'VIGILANCE_LEVELS' do
7
+ it 'defines four levels in order' do
8
+ expect(mod::VIGILANCE_LEVELS).to eq(%i[trusting cautious skeptical hostile])
9
+ end
10
+
11
+ it 'is frozen' do
12
+ expect(mod::VIGILANCE_LEVELS).to be_frozen
13
+ end
14
+ end
15
+
16
+ describe 'VIGILANCE_THRESHOLDS' do
17
+ it 'defines trusting at 0.8' do
18
+ expect(mod::VIGILANCE_THRESHOLDS[:trusting]).to eq(0.8)
19
+ end
20
+
21
+ it 'defines hostile at 0.0' do
22
+ expect(mod::VIGILANCE_THRESHOLDS[:hostile]).to eq(0.0)
23
+ end
24
+ end
25
+
26
+ describe 'CLAIM_VERDICTS' do
27
+ it 'includes accepted and rejected' do
28
+ expect(mod::CLAIM_VERDICTS).to include(:accepted, :rejected)
29
+ end
30
+
31
+ it 'is frozen' do
32
+ expect(mod::CLAIM_VERDICTS).to be_frozen
33
+ end
34
+ end
35
+
36
+ describe 'SOURCE_RELIABILITY_LABELS' do
37
+ it 'maps 0.9 to highly_reliable' do
38
+ label = mod::SOURCE_RELIABILITY_LABELS.find { |range, _| range.include?(0.9) }&.last
39
+ expect(label).to eq(:highly_reliable)
40
+ end
41
+
42
+ it 'maps 0.1 to deceptive' do
43
+ label = mod::SOURCE_RELIABILITY_LABELS.find { |range, _| range.include?(0.1) }&.last
44
+ expect(label).to eq(:deceptive)
45
+ end
46
+
47
+ it 'maps 0.5 to uncertain' do
48
+ label = mod::SOURCE_RELIABILITY_LABELS.find { |range, _| range.include?(0.5) }&.last
49
+ expect(label).to eq(:uncertain)
50
+ end
51
+ end
52
+
53
+ describe 'weights' do
54
+ it 'weights sum to 1.0' do
55
+ total = mod::CONSISTENCY_WEIGHT + mod::SOURCE_WEIGHT + mod::COHERENCE_WEIGHT
56
+ expect(total).to be_within(0.001).of(1.0)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Defense::EpistemicVigilance::Helpers::Source do
4
+ subject(:source) { described_class.new(name: 'NewsBot', domain: :news) }
5
+
6
+ let(:boost) { Legion::Extensions::Agentic::Defense::EpistemicVigilance::Helpers::Constants::RELIABILITY_BOOST }
7
+ let(:penalty) { Legion::Extensions::Agentic::Defense::EpistemicVigilance::Helpers::Constants::RELIABILITY_PENALTY }
8
+ let(:default) { Legion::Extensions::Agentic::Defense::EpistemicVigilance::Helpers::Constants::DEFAULT_SOURCE_RELIABILITY }
9
+
10
+ describe '#initialize' do
11
+ it 'generates a UUID id' do
12
+ expect(source.id).to match(/\A[0-9a-f-]{36}\z/)
13
+ end
14
+
15
+ it 'starts with default reliability 0.5' do
16
+ expect(source.reliability).to eq(default)
17
+ end
18
+
19
+ it 'starts with zero claim counts' do
20
+ expect(source.claims_made).to eq(0)
21
+ expect(source.claims_verified).to eq(0)
22
+ expect(source.claims_refuted).to eq(0)
23
+ end
24
+ end
25
+
26
+ describe '#record_verified!' do
27
+ it 'increments claims_verified' do
28
+ expect { source.record_verified! }.to change(source, :claims_verified).by(1)
29
+ end
30
+
31
+ it 'boosts reliability' do
32
+ before = source.reliability
33
+ source.record_verified!
34
+ expect(source.reliability).to be_within(0.001).of(before + boost)
35
+ end
36
+
37
+ it 'clamps reliability at 1.0' do
38
+ high = described_class.new(name: 'top', domain: :d)
39
+ high.reliability = 0.99
40
+ high.record_verified!
41
+ expect(high.reliability).to eq(1.0)
42
+ end
43
+ end
44
+
45
+ describe '#record_refuted!' do
46
+ it 'increments claims_refuted' do
47
+ expect { source.record_refuted! }.to change(source, :claims_refuted).by(1)
48
+ end
49
+
50
+ it 'penalizes reliability' do
51
+ before = source.reliability
52
+ source.record_refuted!
53
+ expect(source.reliability).to be_within(0.001).of(before - penalty)
54
+ end
55
+
56
+ it 'clamps reliability at 0.0' do
57
+ low = described_class.new(name: 'spammer', domain: :d)
58
+ low.reliability = 0.05
59
+ low.record_refuted!
60
+ expect(low.reliability).to eq(0.0)
61
+ end
62
+
63
+ it 'penalizes more than boost (asymmetric)' do
64
+ expect(penalty).to be > boost
65
+ end
66
+ end
67
+
68
+ describe '#reliability_label' do
69
+ it 'returns :highly_reliable at 0.9' do
70
+ source.reliability = 0.9
71
+ expect(source.reliability_label).to eq(:highly_reliable)
72
+ end
73
+
74
+ it 'returns :deceptive at 0.1' do
75
+ source.reliability = 0.1
76
+ expect(source.reliability_label).to eq(:deceptive)
77
+ end
78
+
79
+ it 'returns :uncertain at 0.5' do
80
+ expect(source.reliability_label).to eq(:uncertain)
81
+ end
82
+
83
+ it 'returns :reliable at 0.7' do
84
+ source.reliability = 0.7
85
+ expect(source.reliability_label).to eq(:reliable)
86
+ end
87
+
88
+ it 'returns :unreliable at 0.3' do
89
+ source.reliability = 0.3
90
+ expect(source.reliability_label).to eq(:unreliable)
91
+ end
92
+ end
93
+
94
+ describe '#track_record' do
95
+ it 'returns 0.0 / 1.0 with no activity' do
96
+ expect(source.track_record).to be_within(0.001).of(0.0)
97
+ end
98
+
99
+ it 'improves with verifications' do
100
+ 3.times { source.record_verified! }
101
+ expect(source.track_record).to be > 0.5
102
+ end
103
+
104
+ it 'decreases with refutations' do
105
+ 3.times { source.record_refuted! }
106
+ expect(source.track_record).to be_within(0.001).of(0.0)
107
+ end
108
+ end
109
+
110
+ describe '#to_h' do
111
+ it 'includes all expected keys' do
112
+ h = source.to_h
113
+ expect(h).to include(:id, :name, :domain, :reliability, :reliability_label,
114
+ :claims_made, :claims_verified, :claims_refuted, :track_record)
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,273 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Defense::EpistemicVigilance::Helpers::VigilanceEngine do
4
+ subject(:engine) { described_class.new }
5
+
6
+ let(:source_result) { engine.register_source(name: 'ResearchBot', domain: :science) }
7
+ let(:source_id) { source_result[:id] }
8
+
9
+ describe '#register_source' do
10
+ it 'returns a hash with id, name, domain' do
11
+ expect(source_result).to include(:id, :name, :domain)
12
+ end
13
+
14
+ it 'sets name and domain' do
15
+ expect(source_result[:name]).to eq('ResearchBot')
16
+ expect(source_result[:domain]).to eq(:science)
17
+ end
18
+
19
+ it 'starts with default reliability' do
20
+ expect(source_result[:reliability]).to eq(0.5)
21
+ end
22
+ end
23
+
24
+ describe '#submit_claim' do
25
+ it 'returns error when source not found' do
26
+ result = engine.submit_claim(content: 'x', source_id: 'bad', domain: :d)
27
+ expect(result[:error]).to eq(:source_not_found)
28
+ end
29
+
30
+ it 'returns claim and assessment' do
31
+ result = engine.submit_claim(content: 'water boils at 100C', source_id: source_id, domain: :science)
32
+ expect(result).to include(:claim, :assessment)
33
+ end
34
+
35
+ it 'includes recommended_verdict in assessment' do
36
+ result = engine.submit_claim(content: 'water boils at 100C', source_id: source_id, domain: :science)
37
+ expect(result[:assessment]).to include(:recommended_verdict)
38
+ end
39
+
40
+ it 'increments source claims_made' do
41
+ engine.submit_claim(content: 'water boils at 100C', source_id: source_id, domain: :science)
42
+ report = engine.source_reliability(source_id: source_id)
43
+ expect(report[:reliability]).to be >= 0.5
44
+ end
45
+ end
46
+
47
+ describe '#assess_claim' do
48
+ it 'returns error for missing claim' do
49
+ result = engine.assess_claim(claim_id: 'nonexistent')
50
+ expect(result[:error]).to eq(:claim_not_found)
51
+ end
52
+
53
+ it 'returns all score components' do
54
+ claim_result = engine.submit_claim(content: 'test', source_id: source_id, domain: :science)
55
+ claim_id = claim_result[:claim][:id]
56
+ assessment = engine.assess_claim(claim_id: claim_id)
57
+ expect(assessment).to include(:source_score, :consistency_score, :coherence_score,
58
+ :total_score, :recommended_verdict)
59
+ end
60
+
61
+ it 'source_score reflects source weight' do
62
+ claim_result = engine.submit_claim(content: 'test', source_id: source_id, domain: :science)
63
+ claim_id = claim_result[:claim][:id]
64
+ assessment = engine.assess_claim(claim_id: claim_id)
65
+ expected = 0.5 * Legion::Extensions::Agentic::Defense::EpistemicVigilance::Helpers::Constants::SOURCE_WEIGHT
66
+ expect(assessment[:source_score]).to be_within(0.001).of(expected)
67
+ end
68
+ end
69
+
70
+ describe '#support_claim' do
71
+ let(:claim_id) do
72
+ engine.submit_claim(content: 'test', source_id: source_id, domain: :science)[:claim][:id]
73
+ end
74
+
75
+ it 'returns error for unknown claim' do
76
+ expect(engine.support_claim(claim_id: 'bad')[:error]).to eq(:claim_not_found)
77
+ end
78
+
79
+ it 'increments evidence_for' do
80
+ result = engine.support_claim(claim_id: claim_id)
81
+ expect(result[:evidence_for]).to eq(1)
82
+ end
83
+
84
+ it 'increases confidence' do
85
+ engine.submit_claim(content: 'test', source_id: source_id, domain: :science)
86
+ result = engine.support_claim(claim_id: claim_id)
87
+ expect(result[:confidence]).to be > 0.5
88
+ end
89
+
90
+ it 'boosts source reliability' do
91
+ before = engine.source_reliability(source_id: source_id)[:reliability]
92
+ engine.support_claim(claim_id: claim_id)
93
+ after = engine.source_reliability(source_id: source_id)[:reliability]
94
+ expect(after).to be > before
95
+ end
96
+ end
97
+
98
+ describe '#challenge_claim' do
99
+ let(:claim_id) do
100
+ engine.submit_claim(content: 'test', source_id: source_id, domain: :science)[:claim][:id]
101
+ end
102
+
103
+ it 'returns error for unknown claim' do
104
+ expect(engine.challenge_claim(claim_id: 'bad')[:error]).to eq(:claim_not_found)
105
+ end
106
+
107
+ it 'increments evidence_against' do
108
+ result = engine.challenge_claim(claim_id: claim_id)
109
+ expect(result[:evidence_against]).to eq(1)
110
+ end
111
+
112
+ it 'decreases confidence' do
113
+ result = engine.challenge_claim(claim_id: claim_id)
114
+ expect(result[:confidence]).to be < 0.5
115
+ end
116
+
117
+ it 'penalizes source reliability' do
118
+ before = engine.source_reliability(source_id: source_id)[:reliability]
119
+ engine.challenge_claim(claim_id: claim_id)
120
+ after = engine.source_reliability(source_id: source_id)[:reliability]
121
+ expect(after).to be < before
122
+ end
123
+ end
124
+
125
+ describe '#adjudicate_claim' do
126
+ let(:claim_id) do
127
+ engine.submit_claim(content: 'test', source_id: source_id, domain: :science)[:claim][:id]
128
+ end
129
+
130
+ it 'returns error for unknown claim' do
131
+ expect(engine.adjudicate_claim(claim_id: 'bad', verdict: :accepted)[:error]).to eq(:claim_not_found)
132
+ end
133
+
134
+ it 'sets the verdict' do
135
+ result = engine.adjudicate_claim(claim_id: claim_id, verdict: :accepted)
136
+ expect(result[:verdict]).to eq(:accepted)
137
+ end
138
+
139
+ it 'boosts source reliability on accepted' do
140
+ before = engine.source_reliability(source_id: source_id)[:reliability]
141
+ engine.adjudicate_claim(claim_id: claim_id, verdict: :accepted)
142
+ after = engine.source_reliability(source_id: source_id)[:reliability]
143
+ expect(after).to be > before
144
+ end
145
+
146
+ it 'penalizes source reliability on rejected' do
147
+ before = engine.source_reliability(source_id: source_id)[:reliability]
148
+ engine.adjudicate_claim(claim_id: claim_id, verdict: :rejected)
149
+ after = engine.source_reliability(source_id: source_id)[:reliability]
150
+ expect(after).to be < before
151
+ end
152
+ end
153
+
154
+ describe '#source_reliability' do
155
+ it 'returns error for unknown source' do
156
+ expect(engine.source_reliability(source_id: 'bad')[:error]).to eq(:source_not_found)
157
+ end
158
+
159
+ it 'returns reliability and label' do
160
+ result = engine.source_reliability(source_id: source_id)
161
+ expect(result).to include(:reliability, :label)
162
+ end
163
+ end
164
+
165
+ describe '#contested_claims' do
166
+ it 'returns empty when no contested claims' do
167
+ expect(engine.contested_claims).to eq([])
168
+ end
169
+
170
+ it 'returns claims that are contested' do
171
+ claim_id = engine.submit_claim(content: 'test', source_id: source_id, domain: :science)[:claim][:id]
172
+ engine.support_claim(claim_id: claim_id)
173
+ 3.times { engine.challenge_claim(claim_id: claim_id) }
174
+ contested = engine.contested_claims
175
+ expect(contested.size).to eq(1)
176
+ end
177
+ end
178
+
179
+ describe '#claims_by_source' do
180
+ it 'returns claims for the given source' do
181
+ engine.submit_claim(content: 'a', source_id: source_id, domain: :science)
182
+ engine.submit_claim(content: 'b', source_id: source_id, domain: :science)
183
+ result = engine.claims_by_source(source_id: source_id)
184
+ expect(result.size).to eq(2)
185
+ end
186
+
187
+ it 'returns empty for unknown source' do
188
+ expect(engine.claims_by_source(source_id: 'nobody')).to eq([])
189
+ end
190
+ end
191
+
192
+ describe '#claims_by_domain' do
193
+ it 'returns claims for the given domain' do
194
+ engine.submit_claim(content: 'x', source_id: source_id, domain: :science)
195
+ engine.submit_claim(content: 'y', source_id: source_id, domain: :science)
196
+ result = engine.claims_by_domain(domain: :science)
197
+ expect(result.size).to eq(2)
198
+ end
199
+
200
+ it 'filters by domain' do
201
+ engine.submit_claim(content: 'x', source_id: source_id, domain: :science)
202
+ result = engine.claims_by_domain(domain: :other)
203
+ expect(result).to eq([])
204
+ end
205
+ end
206
+
207
+ describe '#domain_vigilance_level' do
208
+ it 'returns :skeptical for empty domain' do
209
+ expect(engine.domain_vigilance_level(domain: :unknown)).to eq(:skeptical)
210
+ end
211
+
212
+ it 'returns a valid vigilance level' do
213
+ engine.submit_claim(content: 'test', source_id: source_id, domain: :science)
214
+ level = engine.domain_vigilance_level(domain: :science)
215
+ expect(Legion::Extensions::Agentic::Defense::EpistemicVigilance::Helpers::Constants::VIGILANCE_LEVELS).to include(level)
216
+ end
217
+
218
+ it 'returns :hostile for low-confidence domain' do
219
+ claim_id = engine.submit_claim(content: 'test', source_id: source_id,
220
+ domain: :science, initial_confidence: 0.05)[:claim][:id]
221
+ 5.times { engine.challenge_claim(claim_id: claim_id) }
222
+ level = engine.domain_vigilance_level(domain: :science)
223
+ expect(%i[skeptical hostile]).to include(level)
224
+ end
225
+ end
226
+
227
+ describe '#decay_all' do
228
+ it 'returns count of decayed claims' do
229
+ engine.submit_claim(content: 'test', source_id: source_id, domain: :science)
230
+ result = engine.decay_all
231
+ expect(result[:decayed]).to eq(1)
232
+ end
233
+
234
+ it 'reduces claim confidence' do
235
+ engine.submit_claim(content: 'test', source_id: source_id, domain: :science)[:claim][:id]
236
+ before = engine.claims_by_domain(domain: :science).first[:confidence]
237
+ engine.decay_all
238
+ after = engine.claims_by_domain(domain: :science).first[:confidence]
239
+ expect(after).to be < before
240
+ end
241
+ end
242
+
243
+ describe '#prune_rejected' do
244
+ it 'removes rejected claims below 0.1 confidence' do
245
+ claim_id = engine.submit_claim(content: 'garbage', source_id: source_id,
246
+ domain: :science, initial_confidence: 0.05)[:claim][:id]
247
+ engine.adjudicate_claim(claim_id: claim_id, verdict: :rejected)
248
+ result = engine.prune_rejected
249
+ expect(result[:pruned]).to eq(1)
250
+ end
251
+
252
+ it 'keeps rejected claims above 0.1 confidence' do
253
+ claim_id = engine.submit_claim(content: 'maybe', source_id: source_id,
254
+ domain: :science, initial_confidence: 0.5)[:claim][:id]
255
+ engine.adjudicate_claim(claim_id: claim_id, verdict: :rejected)
256
+ engine.prune_rejected
257
+ expect(engine.claims_by_domain(domain: :science).size).to eq(1)
258
+ end
259
+ end
260
+
261
+ describe '#to_h' do
262
+ it 'returns stats hash' do
263
+ result = engine.to_h
264
+ expect(result).to include(:sources_count, :claims_count, :contested, :by_verdict)
265
+ end
266
+
267
+ it 'reflects current state' do
268
+ engine.submit_claim(content: 'test', source_id: source_id, domain: :science)
269
+ expect(engine.to_h[:claims_count]).to eq(1)
270
+ expect(engine.to_h[:sources_count]).to eq(1)
271
+ end
272
+ end
273
+ end