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,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Defense::Quicksand::Runners::CognitiveQuicksand do
4
+ let(:runner) do
5
+ obj = Object.new
6
+ obj.extend(described_class)
7
+ obj
8
+ end
9
+
10
+ let(:engine) { Legion::Extensions::Agentic::Defense::Quicksand::Helpers::QuicksandEngine.new }
11
+
12
+ describe '#create_trap' do
13
+ it 'returns success' do
14
+ result = runner.create_trap(trap_type: :overthinking, domain: :reasoning,
15
+ content: 'test', engine: engine)
16
+ expect(result[:success]).to be true
17
+ expect(result[:trap][:trap_type]).to eq(:overthinking)
18
+ end
19
+
20
+ it 'returns failure for invalid type' do
21
+ result = runner.create_trap(trap_type: :joy, domain: :t, content: 'x', engine: engine)
22
+ expect(result[:success]).to be false
23
+ end
24
+ end
25
+
26
+ describe '#create_pit' do
27
+ it 'returns success' do
28
+ result = runner.create_pit(engine: engine)
29
+ expect(result[:success]).to be true
30
+ expect(result[:pit]).to be_a(Hash)
31
+ end
32
+ end
33
+
34
+ describe '#struggle' do
35
+ it 'returns trap with struggle data' do
36
+ t = engine.create_trap(trap_type: :overthinking, domain: :t, content: 'x')
37
+ result = runner.struggle(trap_id: t.id, engine: engine)
38
+ expect(result[:success]).to be true
39
+ expect(result[:struggle_count]).to eq(1)
40
+ end
41
+
42
+ it 'returns failure for unknown trap' do
43
+ result = runner.struggle(trap_id: 'bad', engine: engine)
44
+ expect(result[:success]).to be false
45
+ end
46
+ end
47
+
48
+ describe '#calm_down' do
49
+ it 'decreases depth' do
50
+ t = engine.create_trap(trap_type: :rumination, domain: :t, content: 'x', depth: 0.5)
51
+ result = runner.calm_down(trap_id: t.id, engine: engine)
52
+ expect(result[:success]).to be true
53
+ expect(result[:trap][:depth]).to be < 0.5
54
+ end
55
+ end
56
+
57
+ describe '#attempt_escape' do
58
+ it 'returns escape result' do
59
+ t = engine.create_trap(trap_type: :indecision, domain: :t, content: 'x', depth: 0.4)
60
+ result = runner.attempt_escape(trap_id: t.id, engine: engine)
61
+ expect(result[:success]).to be true
62
+ expect(result[:result]).to eq(:escaped)
63
+ end
64
+ end
65
+
66
+ describe '#list_traps' do
67
+ before do
68
+ engine.create_trap(trap_type: :overthinking, domain: :t, content: 'a')
69
+ engine.create_trap(trap_type: :rumination, domain: :t, content: 'b')
70
+ end
71
+
72
+ it 'returns all traps' do
73
+ result = runner.list_traps(engine: engine)
74
+ expect(result[:count]).to eq(2)
75
+ end
76
+
77
+ it 'filters by trap_type' do
78
+ result = runner.list_traps(trap_type: :overthinking, engine: engine)
79
+ expect(result[:count]).to eq(1)
80
+ end
81
+ end
82
+
83
+ describe '#quicksand_status' do
84
+ it 'returns report' do
85
+ result = runner.quicksand_status(engine: engine)
86
+ expect(result[:success]).to be true
87
+ expect(result[:report]).to include(:total_traps)
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/defense/quicksilver/client'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Defense::Quicksilver::Client do
6
+ let(:client) { described_class.new }
7
+
8
+ it 'responds to all runner methods' do
9
+ %i[create_droplet create_pool shift_form merge split capture release add_to_pool
10
+ list_droplets quicksilver_status].each do |method|
11
+ expect(client).to respond_to(method)
12
+ end
13
+ end
14
+
15
+ it 'maintains isolated state per instance' do
16
+ client_a = described_class.new
17
+ client_b = described_class.new
18
+
19
+ client_a.create_droplet(form: :liquid, content: 'exclusive to a')
20
+ expect(client_a.quicksilver_status[:total_droplets]).to eq(1)
21
+ expect(client_b.quicksilver_status[:total_droplets]).to eq(0)
22
+ end
23
+
24
+ it 'round-trips a full quicksilver cycle' do
25
+ # Create droplet
26
+ d_result = client.create_droplet(form: :droplet, content: 'round trip idea')
27
+ expect(d_result[:success]).to be true
28
+ droplet_id = d_result[:droplet][:id]
29
+
30
+ # Create pool
31
+ p_result = client.create_pool(surface_type: :glass)
32
+ expect(p_result[:success]).to be true
33
+ pool_id = p_result[:pool][:id]
34
+
35
+ # Shift form
36
+ shift_result = client.shift_form(droplet_id: droplet_id, new_form: :liquid)
37
+ expect(shift_result[:droplet][:form]).to eq(:liquid)
38
+
39
+ # Add to pool
40
+ add_result = client.add_to_pool(droplet_id: droplet_id, pool_id: pool_id)
41
+ expect(add_result[:pool][:droplet_count]).to eq(1)
42
+
43
+ # Capture
44
+ cap_result = client.capture(droplet_id: droplet_id)
45
+ expect(cap_result[:droplet][:captured]).to be true
46
+
47
+ # Release
48
+ rel_result = client.release(droplet_id: droplet_id)
49
+ expect(rel_result[:droplet][:captured]).to be false
50
+
51
+ # Status report
52
+ status = client.quicksilver_status
53
+ expect(status[:total_droplets]).to eq(1)
54
+ expect(status[:total_pools]).to eq(1)
55
+ end
56
+
57
+ it 'handles merge and split lifecycle' do
58
+ a = client.create_droplet(form: :stream, content: 'stream a', mass: 0.4)
59
+ b = client.create_droplet(form: :stream, content: 'stream b', mass: 0.3)
60
+ a_id = a[:droplet][:id]
61
+ b_id = b[:droplet][:id]
62
+
63
+ merged = client.merge(droplet_a_id: a_id, droplet_b_id: b_id)
64
+ expect(merged[:success]).to be true
65
+ merged_mass = merged[:droplet][:mass]
66
+ expect(merged_mass).to be > 0.4
67
+
68
+ split = client.split(droplet_id: a_id)
69
+ expect(split[:success]).to be true
70
+ expect(split[:twin][:id]).not_to eq(a_id)
71
+ end
72
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Defense::Quicksilver::Helpers::Constants do
4
+ describe 'FORM_TYPES' do
5
+ it 'contains expected forms' do
6
+ expect(described_class::FORM_TYPES).to contain_exactly(:liquid, :droplet, :bead, :stream, :pool)
7
+ end
8
+
9
+ it 'is frozen' do
10
+ expect(described_class::FORM_TYPES).to be_frozen
11
+ end
12
+ end
13
+
14
+ describe 'SURFACE_TYPES' do
15
+ it 'contains expected surfaces' do
16
+ expect(described_class::SURFACE_TYPES).to contain_exactly(:glass, :metal, :wood, :stone, :fabric)
17
+ end
18
+
19
+ it 'is frozen' do
20
+ expect(described_class::SURFACE_TYPES).to be_frozen
21
+ end
22
+ end
23
+
24
+ describe 'numeric constants' do
25
+ it 'MAX_DROPLETS is 500' do
26
+ expect(described_class::MAX_DROPLETS).to eq(500)
27
+ end
28
+
29
+ it 'MAX_POOLS is 50' do
30
+ expect(described_class::MAX_POOLS).to eq(50)
31
+ end
32
+
33
+ it 'FLUIDITY_BASE is 0.8' do
34
+ expect(described_class::FLUIDITY_BASE).to eq(0.8)
35
+ end
36
+
37
+ it 'SURFACE_TENSION is 0.3' do
38
+ expect(described_class::SURFACE_TENSION).to eq(0.3)
39
+ end
40
+
41
+ it 'EVAPORATION_RATE is 0.02' do
42
+ expect(described_class::EVAPORATION_RATE).to eq(0.02)
43
+ end
44
+
45
+ it 'COALESCENCE_BONUS is 0.1' do
46
+ expect(described_class::COALESCENCE_BONUS).to eq(0.1)
47
+ end
48
+ end
49
+
50
+ describe '.label_for' do
51
+ context 'with COHESION_LABELS' do
52
+ it 'returns :unified for mass near 1.0' do
53
+ expect(described_class.label_for(described_class::COHESION_LABELS, 0.95)).to eq(:unified)
54
+ end
55
+
56
+ it 'returns :cohesive for mid-high mass' do
57
+ expect(described_class.label_for(described_class::COHESION_LABELS, 0.7)).to eq(:cohesive)
58
+ end
59
+
60
+ it 'returns :scattered for mid mass' do
61
+ expect(described_class.label_for(described_class::COHESION_LABELS, 0.5)).to eq(:scattered)
62
+ end
63
+
64
+ it 'returns :dispersed for lower mass' do
65
+ expect(described_class.label_for(described_class::COHESION_LABELS, 0.3)).to eq(:dispersed)
66
+ end
67
+
68
+ it 'returns :atomized for very low mass' do
69
+ expect(described_class.label_for(described_class::COHESION_LABELS, 0.05)).to eq(:atomized)
70
+ end
71
+ end
72
+
73
+ context 'with FLUIDITY_LABELS' do
74
+ it 'returns :liquid for high fluidity' do
75
+ expect(described_class.label_for(described_class::FLUIDITY_LABELS, 0.9)).to eq(:liquid)
76
+ end
77
+
78
+ it 'returns :flowing for upper-mid fluidity' do
79
+ expect(described_class.label_for(described_class::FLUIDITY_LABELS, 0.65)).to eq(:flowing)
80
+ end
81
+
82
+ it 'returns :viscous for mid fluidity' do
83
+ expect(described_class.label_for(described_class::FLUIDITY_LABELS, 0.5)).to eq(:viscous)
84
+ end
85
+
86
+ it 'returns :sluggish for lower-mid fluidity' do
87
+ expect(described_class.label_for(described_class::FLUIDITY_LABELS, 0.25)).to eq(:sluggish)
88
+ end
89
+
90
+ it 'returns :solid for very low fluidity' do
91
+ expect(described_class.label_for(described_class::FLUIDITY_LABELS, 0.05)).to eq(:solid)
92
+ end
93
+ end
94
+
95
+ it 'clamps values above 1.0' do
96
+ result = described_class.label_for(described_class::FLUIDITY_LABELS, 1.5)
97
+ expect(result).to eq(:liquid)
98
+ end
99
+
100
+ it 'clamps values below 0.0' do
101
+ result = described_class.label_for(described_class::FLUIDITY_LABELS, -0.5)
102
+ expect(result).to eq(:solid)
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,310 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Defense::Quicksilver::Helpers::Droplet do
4
+ let(:droplet) { described_class.new(form: :droplet, content: 'test idea') }
5
+
6
+ describe '#initialize' do
7
+ it 'assigns a uuid id' do
8
+ expect(droplet.id).to match(/\A[0-9a-f-]{36}\z/)
9
+ end
10
+
11
+ it 'sets form' do
12
+ expect(droplet.form).to eq(:droplet)
13
+ end
14
+
15
+ it 'sets content' do
16
+ expect(droplet.content).to eq('test idea')
17
+ end
18
+
19
+ it 'defaults mass to 0.3' do
20
+ expect(droplet.mass).to eq(0.3)
21
+ end
22
+
23
+ it 'defaults fluidity to FLUIDITY_BASE' do
24
+ expect(droplet.fluidity).to eq(Legion::Extensions::Agentic::Defense::Quicksilver::Helpers::Constants::FLUIDITY_BASE)
25
+ end
26
+
27
+ it 'defaults surface to :glass' do
28
+ expect(droplet.surface).to eq(:glass)
29
+ end
30
+
31
+ it 'defaults captured to false' do
32
+ expect(droplet.captured).to be false
33
+ end
34
+
35
+ it 'clamps mass above 1.0' do
36
+ d = described_class.new(form: :liquid, content: 'x', mass: 1.5)
37
+ expect(d.mass).to eq(1.0)
38
+ end
39
+
40
+ it 'clamps mass below 0.0' do
41
+ d = described_class.new(form: :liquid, content: 'x', mass: -0.5)
42
+ expect(d.mass).to eq(0.0)
43
+ end
44
+
45
+ it 'raises ArgumentError for invalid form' do
46
+ expect { described_class.new(form: :vapor, content: 'x') }.to raise_error(ArgumentError, /invalid form/)
47
+ end
48
+
49
+ it 'raises ArgumentError for invalid surface' do
50
+ expect do
51
+ described_class.new(form: :droplet, content: 'x', surface: :air)
52
+ end.to raise_error(ArgumentError, /invalid surface/)
53
+ end
54
+
55
+ it 'sets created_at' do
56
+ expect(droplet.created_at).to be_a(Time)
57
+ end
58
+ end
59
+
60
+ describe '#shift_form!' do
61
+ it 'changes the form' do
62
+ droplet.shift_form!(:liquid)
63
+ expect(droplet.form).to eq(:liquid)
64
+ end
65
+
66
+ it 'adjusts fluidity based on form' do
67
+ droplet.shift_form!(:liquid)
68
+ expect(droplet.fluidity).to eq(0.9)
69
+ end
70
+
71
+ it 'sets low fluidity for pool form' do
72
+ droplet.shift_form!(:pool)
73
+ expect(droplet.fluidity).to eq(0.3)
74
+ end
75
+
76
+ it 'returns self for chaining' do
77
+ expect(droplet.shift_form!(:bead)).to be(droplet)
78
+ end
79
+
80
+ it 'raises ArgumentError for invalid form' do
81
+ expect { droplet.shift_form!(:fog) }.to raise_error(ArgumentError, /invalid form/)
82
+ end
83
+ end
84
+
85
+ describe '#merge!' do
86
+ let(:other) { described_class.new(form: :liquid, content: 'other', mass: 0.4) }
87
+
88
+ it 'combines mass with coalescence bonus' do
89
+ bonus = Legion::Extensions::Agentic::Defense::Quicksilver::Helpers::Constants::COALESCENCE_BONUS
90
+ original_mass = droplet.mass
91
+ droplet.merge!(other)
92
+ expected = [original_mass + other.mass + bonus, 1.0].min
93
+ expect(droplet.mass).to be_within(0.001).of(expected)
94
+ end
95
+
96
+ it 'clamps merged mass to 1.0' do
97
+ heavy_a = described_class.new(form: :pool, content: 'a', mass: 0.7)
98
+ heavy_b = described_class.new(form: :pool, content: 'b', mass: 0.7)
99
+ heavy_a.merge!(heavy_b)
100
+ expect(heavy_a.mass).to eq(1.0)
101
+ end
102
+
103
+ it 'returns self for chaining' do
104
+ expect(droplet.merge!(other)).to be(droplet)
105
+ end
106
+
107
+ it 'averages fluidity' do
108
+ original_fluidity = droplet.fluidity
109
+ expected_fluidity = (original_fluidity + other.fluidity) / 2.0
110
+ droplet.merge!(other)
111
+ expect(droplet.fluidity).to be_within(0.001).of(expected_fluidity)
112
+ end
113
+ end
114
+
115
+ describe '#split!' do
116
+ context 'when mass is sufficient (> 0.2)' do
117
+ let(:heavy_droplet) { described_class.new(form: :droplet, content: 'big idea', mass: 0.6) }
118
+
119
+ it 'returns a twin droplet' do
120
+ twin = heavy_droplet.split!
121
+ expect(twin).to be_a(described_class)
122
+ end
123
+
124
+ it 'halves the original mass' do
125
+ heavy_droplet.split!
126
+ expect(heavy_droplet.mass).to be_within(0.001).of(0.3)
127
+ end
128
+
129
+ it 'twin has half the original mass' do
130
+ original_mass = heavy_droplet.mass
131
+ twin = heavy_droplet.split!
132
+ expect(twin.mass).to be_within(0.001).of(original_mass / 2.0)
133
+ end
134
+
135
+ it 'twin has a different id' do
136
+ twin = heavy_droplet.split!
137
+ expect(twin.id).not_to eq(heavy_droplet.id)
138
+ end
139
+
140
+ it 'twin inherits form and surface' do
141
+ twin = heavy_droplet.split!
142
+ expect(twin.form).to eq(heavy_droplet.form)
143
+ expect(twin.surface).to eq(heavy_droplet.surface)
144
+ end
145
+ end
146
+
147
+ context 'when mass is too small (<= 0.2)' do
148
+ let(:tiny_droplet) { described_class.new(form: :bead, content: 'tiny', mass: 0.15) }
149
+
150
+ it 'returns nil' do
151
+ expect(tiny_droplet.split!).to be_nil
152
+ end
153
+
154
+ it 'does not change the mass' do
155
+ original = tiny_droplet.mass
156
+ tiny_droplet.split!
157
+ expect(tiny_droplet.mass).to eq(original)
158
+ end
159
+ end
160
+ end
161
+
162
+ describe '#capture!' do
163
+ it 'sets captured to true' do
164
+ droplet.capture!
165
+ expect(droplet.captured).to be true
166
+ end
167
+
168
+ it 'halves the fluidity' do
169
+ original_fluidity = droplet.fluidity
170
+ droplet.capture!
171
+ expect(droplet.fluidity).to be_within(0.001).of(original_fluidity / 2.0)
172
+ end
173
+
174
+ it 'returns self' do
175
+ expect(droplet.capture!).to be(droplet)
176
+ end
177
+ end
178
+
179
+ describe '#release!' do
180
+ before { droplet.capture! }
181
+
182
+ it 'sets captured to false' do
183
+ droplet.release!
184
+ expect(droplet.captured).to be false
185
+ end
186
+
187
+ it 'restores fluidity (doubles it, clamped)' do
188
+ fluidity_after_capture = droplet.fluidity
189
+ droplet.release!
190
+ expect(droplet.fluidity).to be_within(0.001).of([fluidity_after_capture * 2.0, 1.0].min)
191
+ end
192
+
193
+ it 'returns self' do
194
+ expect(droplet.release!).to be(droplet)
195
+ end
196
+ end
197
+
198
+ describe '#evaporate!' do
199
+ it 'reduces mass by EVAPORATION_RATE' do
200
+ original_mass = droplet.mass
201
+ droplet.evaporate!
202
+ expected = original_mass - Legion::Extensions::Agentic::Defense::Quicksilver::Helpers::Constants::EVAPORATION_RATE
203
+ expect(droplet.mass).to be_within(0.001).of(expected)
204
+ end
205
+
206
+ it 'does not go below 0.0' do
207
+ tiny = described_class.new(form: :bead, content: 'x', mass: 0.01)
208
+ tiny.evaporate!
209
+ expect(tiny.mass).to be >= 0.0
210
+ end
211
+
212
+ it 'returns self' do
213
+ expect(droplet.evaporate!).to be(droplet)
214
+ end
215
+ end
216
+
217
+ describe '#elusive?' do
218
+ it 'returns true when fluidity >= 0.7 and not captured' do
219
+ d = described_class.new(form: :liquid, content: 'elusive', fluidity: 0.8)
220
+ expect(d.elusive?).to be true
221
+ end
222
+
223
+ it 'returns false when captured' do
224
+ droplet.capture!
225
+ expect(droplet.elusive?).to be false
226
+ end
227
+
228
+ it 'returns false when fluidity < 0.7' do
229
+ d = described_class.new(form: :pool, content: 'slow', fluidity: 0.3)
230
+ expect(d.elusive?).to be false
231
+ end
232
+ end
233
+
234
+ describe '#stable?' do
235
+ it 'returns true when captured' do
236
+ droplet.capture!
237
+ expect(droplet.stable?).to be true
238
+ end
239
+
240
+ it 'returns true when fluidity < 0.4' do
241
+ d = described_class.new(form: :pool, content: 'stable', fluidity: 0.2)
242
+ expect(d.stable?).to be true
243
+ end
244
+
245
+ it 'returns false for default high-fluidity uncaptured droplet' do
246
+ d = described_class.new(form: :liquid, content: 'free', fluidity: 0.8)
247
+ expect(d.stable?).to be false
248
+ end
249
+ end
250
+
251
+ describe '#vanishing?' do
252
+ it 'returns true when mass < 0.1' do
253
+ d = described_class.new(form: :bead, content: 'fading', mass: 0.05)
254
+ expect(d.vanishing?).to be true
255
+ end
256
+
257
+ it 'returns false for normal mass' do
258
+ expect(droplet.vanishing?).to be false
259
+ end
260
+ end
261
+
262
+ describe '#cohesion_label' do
263
+ it 'returns a symbol' do
264
+ expect(droplet.cohesion_label).to be_a(Symbol)
265
+ end
266
+
267
+ it 'returns :unified for mass 1.0' do
268
+ d = described_class.new(form: :pool, content: 'x', mass: 1.0)
269
+ expect(d.cohesion_label).to eq(:unified)
270
+ end
271
+
272
+ it 'returns :atomized for mass near 0.0' do
273
+ d = described_class.new(form: :bead, content: 'x', mass: 0.05)
274
+ expect(d.cohesion_label).to eq(:atomized)
275
+ end
276
+ end
277
+
278
+ describe '#fluidity_label' do
279
+ it 'returns a symbol' do
280
+ expect(droplet.fluidity_label).to be_a(Symbol)
281
+ end
282
+
283
+ it 'returns :liquid for fluidity 0.9' do
284
+ d = described_class.new(form: :liquid, content: 'x', fluidity: 0.9)
285
+ expect(d.fluidity_label).to eq(:liquid)
286
+ end
287
+
288
+ it 'returns :solid for fluidity 0.1' do
289
+ d = described_class.new(form: :bead, content: 'x', fluidity: 0.1)
290
+ expect(d.fluidity_label).to eq(:solid)
291
+ end
292
+ end
293
+
294
+ describe '#to_h' do
295
+ it 'returns a hash with all expected keys' do
296
+ h = droplet.to_h
297
+ expect(h.keys).to include(:id, :form, :content, :mass, :fluidity, :surface, :captured,
298
+ :elusive, :stable, :vanishing, :cohesion, :fluidity_lbl, :created_at)
299
+ end
300
+
301
+ it 'id matches droplet id' do
302
+ expect(droplet.to_h[:id]).to eq(droplet.id)
303
+ end
304
+
305
+ it 'reflects captured state' do
306
+ droplet.capture!
307
+ expect(droplet.to_h[:captured]).to be true
308
+ end
309
+ end
310
+ end