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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile +5 -0
- data/LICENSE +21 -0
- data/README.md +13 -0
- data/lex-agentic-defense.gemspec +30 -0
- data/lib/legion/extensions/agentic/defense/avalanche/client.rb +22 -0
- data/lib/legion/extensions/agentic/defense/avalanche/helpers/avalanche_engine.rb +132 -0
- data/lib/legion/extensions/agentic/defense/avalanche/helpers/cascade.rb +76 -0
- data/lib/legion/extensions/agentic/defense/avalanche/helpers/constants.rb +44 -0
- data/lib/legion/extensions/agentic/defense/avalanche/helpers/snowpack.rb +86 -0
- data/lib/legion/extensions/agentic/defense/avalanche/runners/cognitive_avalanche.rb +75 -0
- data/lib/legion/extensions/agentic/defense/avalanche/version.rb +13 -0
- data/lib/legion/extensions/agentic/defense/avalanche.rb +22 -0
- data/lib/legion/extensions/agentic/defense/bias/actors/update.rb +45 -0
- data/lib/legion/extensions/agentic/defense/bias/client.rb +30 -0
- data/lib/legion/extensions/agentic/defense/bias/helpers/bias_detector.rb +107 -0
- data/lib/legion/extensions/agentic/defense/bias/helpers/bias_event.rb +44 -0
- data/lib/legion/extensions/agentic/defense/bias/helpers/bias_store.rb +84 -0
- data/lib/legion/extensions/agentic/defense/bias/helpers/constants.rb +28 -0
- data/lib/legion/extensions/agentic/defense/bias/runners/bias.rb +151 -0
- data/lib/legion/extensions/agentic/defense/bias/version.rb +13 -0
- data/lib/legion/extensions/agentic/defense/bias.rb +20 -0
- data/lib/legion/extensions/agentic/defense/confabulation/actors/decay.rb +45 -0
- data/lib/legion/extensions/agentic/defense/confabulation/client.rb +28 -0
- data/lib/legion/extensions/agentic/defense/confabulation/helpers/claim.rb +67 -0
- data/lib/legion/extensions/agentic/defense/confabulation/helpers/confabulation_engine.rb +120 -0
- data/lib/legion/extensions/agentic/defense/confabulation/helpers/constants.rb +29 -0
- data/lib/legion/extensions/agentic/defense/confabulation/runners/confabulation.rb +74 -0
- data/lib/legion/extensions/agentic/defense/confabulation/version.rb +13 -0
- data/lib/legion/extensions/agentic/defense/confabulation.rb +19 -0
- data/lib/legion/extensions/agentic/defense/dissonance/client.rb +32 -0
- data/lib/legion/extensions/agentic/defense/dissonance/helpers/belief.rb +46 -0
- data/lib/legion/extensions/agentic/defense/dissonance/helpers/constants.rb +27 -0
- data/lib/legion/extensions/agentic/defense/dissonance/helpers/dissonance_event.rb +52 -0
- data/lib/legion/extensions/agentic/defense/dissonance/helpers/dissonance_model.rb +159 -0
- data/lib/legion/extensions/agentic/defense/dissonance/runners/dissonance.rb +163 -0
- data/lib/legion/extensions/agentic/defense/dissonance/version.rb +13 -0
- data/lib/legion/extensions/agentic/defense/dissonance.rb +20 -0
- data/lib/legion/extensions/agentic/defense/epistemic_vigilance/actors/update.rb +45 -0
- data/lib/legion/extensions/agentic/defense/epistemic_vigilance/client.rb +27 -0
- data/lib/legion/extensions/agentic/defense/epistemic_vigilance/helpers/claim.rb +78 -0
- data/lib/legion/extensions/agentic/defense/epistemic_vigilance/helpers/client.rb +23 -0
- data/lib/legion/extensions/agentic/defense/epistemic_vigilance/helpers/constants.rb +37 -0
- data/lib/legion/extensions/agentic/defense/epistemic_vigilance/helpers/source.rb +64 -0
- data/lib/legion/extensions/agentic/defense/epistemic_vigilance/helpers/vigilance_engine.rb +195 -0
- data/lib/legion/extensions/agentic/defense/epistemic_vigilance/runners/epistemic_vigilance.rb +91 -0
- data/lib/legion/extensions/agentic/defense/epistemic_vigilance/version.rb +13 -0
- data/lib/legion/extensions/agentic/defense/epistemic_vigilance.rb +20 -0
- data/lib/legion/extensions/agentic/defense/erosion/client.rb +23 -0
- data/lib/legion/extensions/agentic/defense/erosion/helpers/channel.rb +84 -0
- data/lib/legion/extensions/agentic/defense/erosion/helpers/constants.rb +47 -0
- data/lib/legion/extensions/agentic/defense/erosion/helpers/erosion_engine.rb +134 -0
- data/lib/legion/extensions/agentic/defense/erosion/helpers/formation.rb +100 -0
- data/lib/legion/extensions/agentic/defense/erosion/runners/cognitive_erosion.rb +93 -0
- data/lib/legion/extensions/agentic/defense/erosion/version.rb +13 -0
- data/lib/legion/extensions/agentic/defense/erosion.rb +21 -0
- data/lib/legion/extensions/agentic/defense/error_monitoring/actors/tick.rb +45 -0
- data/lib/legion/extensions/agentic/defense/error_monitoring/client.rb +28 -0
- data/lib/legion/extensions/agentic/defense/error_monitoring/helpers/constants.rb +50 -0
- data/lib/legion/extensions/agentic/defense/error_monitoring/helpers/error_monitor.rb +174 -0
- data/lib/legion/extensions/agentic/defense/error_monitoring/helpers/error_signal.rb +60 -0
- data/lib/legion/extensions/agentic/defense/error_monitoring/runners/error_monitoring.rb +102 -0
- data/lib/legion/extensions/agentic/defense/error_monitoring/version.rb +13 -0
- data/lib/legion/extensions/agentic/defense/error_monitoring.rb +19 -0
- data/lib/legion/extensions/agentic/defense/extinction/actors/protocol_monitor.rb +45 -0
- data/lib/legion/extensions/agentic/defense/extinction/client.rb +27 -0
- data/lib/legion/extensions/agentic/defense/extinction/helpers/levels.rb +43 -0
- data/lib/legion/extensions/agentic/defense/extinction/helpers/protocol_state.rb +125 -0
- data/lib/legion/extensions/agentic/defense/extinction/local_migrations/20260316000040_create_extinction_state.rb +13 -0
- data/lib/legion/extensions/agentic/defense/extinction/runners/extinction.rb +130 -0
- data/lib/legion/extensions/agentic/defense/extinction/version.rb +13 -0
- data/lib/legion/extensions/agentic/defense/extinction.rb +25 -0
- data/lib/legion/extensions/agentic/defense/friction/client.rb +15 -0
- data/lib/legion/extensions/agentic/defense/friction/helpers/constants.rb +38 -0
- data/lib/legion/extensions/agentic/defense/friction/helpers/friction_engine.rb +131 -0
- data/lib/legion/extensions/agentic/defense/friction/helpers/state_transition.rb +73 -0
- data/lib/legion/extensions/agentic/defense/friction/runners/cognitive_friction.rb +82 -0
- data/lib/legion/extensions/agentic/defense/friction/version.rb +13 -0
- data/lib/legion/extensions/agentic/defense/friction.rb +19 -0
- data/lib/legion/extensions/agentic/defense/immune_response/client.rb +19 -0
- data/lib/legion/extensions/agentic/defense/immune_response/helpers/antibody.rb +72 -0
- data/lib/legion/extensions/agentic/defense/immune_response/helpers/antigen.rb +87 -0
- data/lib/legion/extensions/agentic/defense/immune_response/helpers/constants.rb +75 -0
- data/lib/legion/extensions/agentic/defense/immune_response/helpers/immune_engine.rb +184 -0
- data/lib/legion/extensions/agentic/defense/immune_response/helpers/immune_response.rb +76 -0
- data/lib/legion/extensions/agentic/defense/immune_response/runners/cognitive_immune_response.rb +114 -0
- data/lib/legion/extensions/agentic/defense/immune_response/version.rb +13 -0
- data/lib/legion/extensions/agentic/defense/immune_response.rb +21 -0
- data/lib/legion/extensions/agentic/defense/immunology/client.rb +29 -0
- data/lib/legion/extensions/agentic/defense/immunology/helpers/antibody.rb +55 -0
- data/lib/legion/extensions/agentic/defense/immunology/helpers/constants.rb +43 -0
- data/lib/legion/extensions/agentic/defense/immunology/helpers/immune_engine.rb +187 -0
- data/lib/legion/extensions/agentic/defense/immunology/helpers/threat.rb +67 -0
- data/lib/legion/extensions/agentic/defense/immunology/runners/cognitive_immunology.rb +92 -0
- data/lib/legion/extensions/agentic/defense/immunology/version.rb +13 -0
- data/lib/legion/extensions/agentic/defense/immunology.rb +20 -0
- data/lib/legion/extensions/agentic/defense/phantom/client.rb +29 -0
- data/lib/legion/extensions/agentic/defense/phantom/helpers/constants.rb +54 -0
- data/lib/legion/extensions/agentic/defense/phantom/helpers/phantom_engine.rb +106 -0
- data/lib/legion/extensions/agentic/defense/phantom/helpers/phantom_limb.rb +103 -0
- data/lib/legion/extensions/agentic/defense/phantom/helpers/phantom_signal.rb +40 -0
- data/lib/legion/extensions/agentic/defense/phantom/runners/cognitive_phantom.rb +79 -0
- data/lib/legion/extensions/agentic/defense/phantom/version.rb +13 -0
- data/lib/legion/extensions/agentic/defense/phantom.rb +21 -0
- data/lib/legion/extensions/agentic/defense/quicksand/client.rb +15 -0
- data/lib/legion/extensions/agentic/defense/quicksand/helpers/constants.rb +48 -0
- data/lib/legion/extensions/agentic/defense/quicksand/helpers/pit.rb +82 -0
- data/lib/legion/extensions/agentic/defense/quicksand/helpers/quicksand_engine.rb +137 -0
- data/lib/legion/extensions/agentic/defense/quicksand/helpers/trap.rb +101 -0
- data/lib/legion/extensions/agentic/defense/quicksand/runners/cognitive_quicksand.rb +84 -0
- data/lib/legion/extensions/agentic/defense/quicksand/version.rb +13 -0
- data/lib/legion/extensions/agentic/defense/quicksand.rb +22 -0
- data/lib/legion/extensions/agentic/defense/quicksilver/client.rb +29 -0
- data/lib/legion/extensions/agentic/defense/quicksilver/helpers/constants.rb +50 -0
- data/lib/legion/extensions/agentic/defense/quicksilver/helpers/droplet.rb +126 -0
- data/lib/legion/extensions/agentic/defense/quicksilver/helpers/pool.rb +83 -0
- data/lib/legion/extensions/agentic/defense/quicksilver/helpers/quicksilver_engine.rb +124 -0
- data/lib/legion/extensions/agentic/defense/quicksilver/runners/cognitive_quicksilver.rb +130 -0
- data/lib/legion/extensions/agentic/defense/quicksilver/version.rb +13 -0
- data/lib/legion/extensions/agentic/defense/quicksilver.rb +21 -0
- data/lib/legion/extensions/agentic/defense/version.rb +11 -0
- data/lib/legion/extensions/agentic/defense/whirlpool/client.rb +65 -0
- data/lib/legion/extensions/agentic/defense/whirlpool/helpers/captured_thought.rb +67 -0
- data/lib/legion/extensions/agentic/defense/whirlpool/helpers/constants.rb +45 -0
- data/lib/legion/extensions/agentic/defense/whirlpool/helpers/vortex.rb +91 -0
- data/lib/legion/extensions/agentic/defense/whirlpool/helpers/whirlpool_engine.rb +92 -0
- data/lib/legion/extensions/agentic/defense/whirlpool/runners/cognitive_whirlpool.rb +117 -0
- data/lib/legion/extensions/agentic/defense/whirlpool/version.rb +13 -0
- data/lib/legion/extensions/agentic/defense/whirlpool.rb +22 -0
- data/lib/legion/extensions/agentic/defense.rb +32 -0
- data/spec/legion/extensions/agentic/defense/avalanche/client_spec.rb +96 -0
- data/spec/legion/extensions/agentic/defense/avalanche/helpers/avalanche_engine_spec.rb +276 -0
- data/spec/legion/extensions/agentic/defense/avalanche/helpers/cascade_spec.rb +190 -0
- data/spec/legion/extensions/agentic/defense/avalanche/helpers/constants_spec.rb +129 -0
- data/spec/legion/extensions/agentic/defense/avalanche/helpers/snowpack_spec.rb +197 -0
- data/spec/legion/extensions/agentic/defense/avalanche/runners/cognitive_avalanche_spec.rb +211 -0
- data/spec/legion/extensions/agentic/defense/bias/client_spec.rb +16 -0
- data/spec/legion/extensions/agentic/defense/bias/helpers/bias_detector_spec.rb +160 -0
- data/spec/legion/extensions/agentic/defense/bias/helpers/bias_event_spec.rb +64 -0
- data/spec/legion/extensions/agentic/defense/bias/helpers/bias_store_spec.rb +143 -0
- data/spec/legion/extensions/agentic/defense/bias/runners/bias_spec.rb +155 -0
- data/spec/legion/extensions/agentic/defense/confabulation/client_spec.rb +34 -0
- data/spec/legion/extensions/agentic/defense/confabulation/helpers/claim_spec.rb +119 -0
- data/spec/legion/extensions/agentic/defense/confabulation/helpers/confabulation_engine_spec.rb +163 -0
- data/spec/legion/extensions/agentic/defense/confabulation/helpers/constants_spec.rb +55 -0
- data/spec/legion/extensions/agentic/defense/confabulation/runners/confabulation_spec.rb +119 -0
- data/spec/legion/extensions/agentic/defense/dissonance/client_spec.rb +51 -0
- data/spec/legion/extensions/agentic/defense/dissonance/helpers/belief_spec.rb +103 -0
- data/spec/legion/extensions/agentic/defense/dissonance/helpers/constants_spec.rb +60 -0
- data/spec/legion/extensions/agentic/defense/dissonance/helpers/dissonance_event_spec.rb +113 -0
- data/spec/legion/extensions/agentic/defense/dissonance/helpers/dissonance_model_spec.rb +252 -0
- data/spec/legion/extensions/agentic/defense/dissonance/runners/dissonance_spec.rb +323 -0
- data/spec/legion/extensions/agentic/defense/epistemic_vigilance/client_spec.rb +28 -0
- data/spec/legion/extensions/agentic/defense/epistemic_vigilance/helpers/claim_spec.rb +135 -0
- data/spec/legion/extensions/agentic/defense/epistemic_vigilance/helpers/constants_spec.rb +59 -0
- data/spec/legion/extensions/agentic/defense/epistemic_vigilance/helpers/source_spec.rb +117 -0
- data/spec/legion/extensions/agentic/defense/epistemic_vigilance/helpers/vigilance_engine_spec.rb +273 -0
- data/spec/legion/extensions/agentic/defense/epistemic_vigilance/runners/epistemic_vigilance_spec.rb +157 -0
- data/spec/legion/extensions/agentic/defense/erosion/client_spec.rb +90 -0
- data/spec/legion/extensions/agentic/defense/erosion/helpers/channel_spec.rb +173 -0
- data/spec/legion/extensions/agentic/defense/erosion/helpers/constants_spec.rb +137 -0
- data/spec/legion/extensions/agentic/defense/erosion/helpers/erosion_engine_spec.rb +263 -0
- data/spec/legion/extensions/agentic/defense/erosion/helpers/formation_spec.rb +206 -0
- data/spec/legion/extensions/agentic/defense/erosion/runners/cognitive_erosion_spec.rb +153 -0
- data/spec/legion/extensions/agentic/defense/error_monitoring/client_spec.rb +40 -0
- data/spec/legion/extensions/agentic/defense/error_monitoring/helpers/error_monitor_spec.rb +178 -0
- data/spec/legion/extensions/agentic/defense/error_monitoring/helpers/error_signal_spec.rb +76 -0
- data/spec/legion/extensions/agentic/defense/error_monitoring/runners/error_monitoring_spec.rb +87 -0
- data/spec/legion/extensions/agentic/defense/extinction/actors/protocol_monitor_spec.rb +45 -0
- data/spec/legion/extensions/agentic/defense/extinction/client_spec.rb +13 -0
- data/spec/legion/extensions/agentic/defense/extinction/helpers/levels_spec.rb +180 -0
- data/spec/legion/extensions/agentic/defense/extinction/helpers/protocol_state_spec.rb +291 -0
- data/spec/legion/extensions/agentic/defense/extinction/local_persistence_spec.rb +188 -0
- data/spec/legion/extensions/agentic/defense/extinction/runners/extinction_spec.rb +114 -0
- data/spec/legion/extensions/agentic/defense/friction/helpers/constants_spec.rb +46 -0
- data/spec/legion/extensions/agentic/defense/friction/helpers/friction_engine_spec.rb +175 -0
- data/spec/legion/extensions/agentic/defense/friction/helpers/state_transition_spec.rb +124 -0
- data/spec/legion/extensions/agentic/defense/friction/runners/cognitive_friction_spec.rb +89 -0
- data/spec/legion/extensions/agentic/defense/immune_response/client_spec.rb +32 -0
- data/spec/legion/extensions/agentic/defense/immune_response/cognitive_immune_response_spec.rb +7 -0
- data/spec/legion/extensions/agentic/defense/immune_response/helpers/antibody_spec.rb +117 -0
- data/spec/legion/extensions/agentic/defense/immune_response/helpers/antigen_spec.rb +125 -0
- data/spec/legion/extensions/agentic/defense/immune_response/helpers/constants_spec.rb +45 -0
- data/spec/legion/extensions/agentic/defense/immune_response/helpers/immune_engine_spec.rb +222 -0
- data/spec/legion/extensions/agentic/defense/immune_response/helpers/immune_response_spec.rb +84 -0
- data/spec/legion/extensions/agentic/defense/immune_response/runners_spec.rb +141 -0
- data/spec/legion/extensions/agentic/defense/immunology/client_spec.rb +61 -0
- data/spec/legion/extensions/agentic/defense/immunology/helpers/antibody_spec.rb +98 -0
- data/spec/legion/extensions/agentic/defense/immunology/helpers/constants_spec.rb +86 -0
- data/spec/legion/extensions/agentic/defense/immunology/helpers/immune_engine_spec.rb +275 -0
- data/spec/legion/extensions/agentic/defense/immunology/helpers/threat_spec.rb +133 -0
- data/spec/legion/extensions/agentic/defense/immunology/runners/cognitive_immunology_spec.rb +177 -0
- data/spec/legion/extensions/agentic/defense/phantom/client_spec.rb +53 -0
- data/spec/legion/extensions/agentic/defense/phantom/helpers/constants_spec.rb +87 -0
- data/spec/legion/extensions/agentic/defense/phantom/helpers/phantom_engine_spec.rb +222 -0
- data/spec/legion/extensions/agentic/defense/phantom/helpers/phantom_limb_spec.rb +180 -0
- data/spec/legion/extensions/agentic/defense/phantom/helpers/phantom_signal_spec.rb +59 -0
- data/spec/legion/extensions/agentic/defense/phantom/runners/cognitive_phantom_spec.rb +193 -0
- data/spec/legion/extensions/agentic/defense/quicksand/client_spec.rb +35 -0
- data/spec/legion/extensions/agentic/defense/quicksand/helpers/constants_spec.rb +58 -0
- data/spec/legion/extensions/agentic/defense/quicksand/helpers/pit_spec.rb +103 -0
- data/spec/legion/extensions/agentic/defense/quicksand/helpers/quicksand_engine_spec.rb +153 -0
- data/spec/legion/extensions/agentic/defense/quicksand/helpers/trap_spec.rb +166 -0
- data/spec/legion/extensions/agentic/defense/quicksand/runners/cognitive_quicksand_spec.rb +90 -0
- data/spec/legion/extensions/agentic/defense/quicksilver/client_spec.rb +72 -0
- data/spec/legion/extensions/agentic/defense/quicksilver/helpers/constants_spec.rb +105 -0
- data/spec/legion/extensions/agentic/defense/quicksilver/helpers/droplet_spec.rb +310 -0
- data/spec/legion/extensions/agentic/defense/quicksilver/helpers/pool_spec.rb +174 -0
- data/spec/legion/extensions/agentic/defense/quicksilver/helpers/quicksilver_engine_spec.rb +226 -0
- data/spec/legion/extensions/agentic/defense/quicksilver/runners/cognitive_quicksilver_spec.rb +227 -0
- data/spec/legion/extensions/agentic/defense/whirlpool/client_spec.rb +63 -0
- data/spec/legion/extensions/agentic/defense/whirlpool/helpers/captured_thought_spec.rb +171 -0
- data/spec/legion/extensions/agentic/defense/whirlpool/helpers/constants_spec.rb +65 -0
- data/spec/legion/extensions/agentic/defense/whirlpool/helpers/vortex_spec.rb +189 -0
- data/spec/legion/extensions/agentic/defense/whirlpool/helpers/whirlpool_engine_spec.rb +227 -0
- data/spec/legion/extensions/agentic/defense/whirlpool/runners/cognitive_whirlpool_spec.rb +226 -0
- data/spec/spec_helper.rb +46 -0
- metadata +303 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/agentic/defense/phantom/client'
|
|
4
|
+
|
|
5
|
+
RSpec.describe Legion::Extensions::Agentic::Defense::Phantom::Runners::CognitivePhantom do
|
|
6
|
+
let(:client) { Legion::Extensions::Agentic::Defense::Phantom::Client.new }
|
|
7
|
+
|
|
8
|
+
describe '#register_removal' do
|
|
9
|
+
it 'returns success: true with valid params' do
|
|
10
|
+
result = client.register_removal(capability_name: 'lex-http', capability_domain: :network)
|
|
11
|
+
expect(result[:success]).to be true
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'returns a phantom_id uuid' do
|
|
15
|
+
result = client.register_removal(capability_name: 'lex-http')
|
|
16
|
+
expect(result[:phantom_id]).to match(/\A[0-9a-f-]{36}\z/)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'returns capability_name in result' do
|
|
20
|
+
result = client.register_removal(capability_name: 'lex-redis')
|
|
21
|
+
expect(result[:capability_name]).to eq('lex-redis')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'returns state in result' do
|
|
25
|
+
result = client.register_removal(capability_name: 'lex-vault')
|
|
26
|
+
expect(Legion::Extensions::Agentic::Defense::Phantom::Helpers::Constants::PHANTOM_STATES).to include(result[:state])
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'returns intensity in result' do
|
|
30
|
+
result = client.register_removal(capability_name: 'lex-consul')
|
|
31
|
+
expect(result[:intensity]).to be_a(Float)
|
|
32
|
+
expect(result[:intensity]).to be_between(0.0, 1.0)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'returns success: false for empty capability_name' do
|
|
36
|
+
result = client.register_removal(capability_name: '')
|
|
37
|
+
expect(result[:success]).to be false
|
|
38
|
+
expect(result[:error]).to be_a(String)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'uses injected engine when provided' do
|
|
42
|
+
eng = Legion::Extensions::Agentic::Defense::Phantom::Helpers::PhantomEngine.new
|
|
43
|
+
result = client.register_removal(capability_name: 'lex-ssh', engine: eng)
|
|
44
|
+
expect(result[:success]).to be true
|
|
45
|
+
expect(eng.all_phantoms.size).to eq(1)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'defaults capability_domain to :general' do
|
|
49
|
+
result = client.register_removal(capability_name: 'lex-smtp')
|
|
50
|
+
expect(result[:success]).to be true
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
describe '#process_stimulus' do
|
|
55
|
+
before { client.register_removal(capability_name: 'lex-http', capability_domain: :network) }
|
|
56
|
+
|
|
57
|
+
it 'returns success: true' do
|
|
58
|
+
result = client.process_stimulus(stimulus: 'http.get fired')
|
|
59
|
+
expect(result[:success]).to be true
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it 'returns fired_count' do
|
|
63
|
+
result = client.process_stimulus(stimulus: 'event', domain: :any)
|
|
64
|
+
expect(result[:fired_count]).to be >= 1
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'returns signals array' do
|
|
68
|
+
result = client.process_stimulus(stimulus: 'event', domain: :any)
|
|
69
|
+
expect(result[:signals]).to be_an(Array)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'signals contain expected keys' do
|
|
73
|
+
result = client.process_stimulus(stimulus: 'event', domain: :any)
|
|
74
|
+
expect(result[:signals].first).to include(:id, :phantom_limb_id, :trigger_type, :intensity_at_trigger)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'returns domain in result' do
|
|
78
|
+
result = client.process_stimulus(stimulus: 'event', domain: :network)
|
|
79
|
+
expect(result[:domain]).to eq(:network)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'returns success: false when stimulus is nil' do
|
|
83
|
+
result = client.process_stimulus(stimulus: nil)
|
|
84
|
+
expect(result[:success]).to be false
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it 'uses injected engine' do
|
|
88
|
+
eng = Legion::Extensions::Agentic::Defense::Phantom::Helpers::PhantomEngine.new
|
|
89
|
+
eng.register_removal(capability_name: 'injected-cap')
|
|
90
|
+
result = client.process_stimulus(stimulus: 'event', domain: :any, engine: eng)
|
|
91
|
+
expect(result[:fired_count]).to eq(1)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
describe '#acknowledge_phantom' do
|
|
96
|
+
let(:phantom_id) do
|
|
97
|
+
client.register_removal(capability_name: 'lex-github')[:phantom_id]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it 'returns success: true for valid phantom_id' do
|
|
101
|
+
result = client.acknowledge_phantom(phantom_id: phantom_id)
|
|
102
|
+
expect(result[:success]).to be true
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it 'returns acknowledged: true for valid phantom_id' do
|
|
106
|
+
result = client.acknowledge_phantom(phantom_id: phantom_id)
|
|
107
|
+
expect(result[:acknowledged]).to be true
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it 'returns success: false for unknown phantom_id' do
|
|
111
|
+
result = client.acknowledge_phantom(phantom_id: SecureRandom.uuid)
|
|
112
|
+
expect(result[:success]).to be false
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it 'returns success: false for empty phantom_id' do
|
|
116
|
+
result = client.acknowledge_phantom(phantom_id: '')
|
|
117
|
+
expect(result[:success]).to be false
|
|
118
|
+
expect(result[:error]).to be_a(String)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it 'uses injected engine' do
|
|
122
|
+
eng = Legion::Extensions::Agentic::Defense::Phantom::Helpers::PhantomEngine.new
|
|
123
|
+
limb = eng.register_removal(capability_name: 'injected-cap')
|
|
124
|
+
result = client.acknowledge_phantom(phantom_id: limb.id, engine: eng)
|
|
125
|
+
expect(result[:acknowledged]).to be true
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
describe '#phantom_status' do
|
|
130
|
+
before do
|
|
131
|
+
client.register_removal(capability_name: 'lex-http')
|
|
132
|
+
client.register_removal(capability_name: 'lex-vault')
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it 'returns success: true' do
|
|
136
|
+
expect(client.phantom_status[:success]).to be true
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it 'returns total count' do
|
|
140
|
+
expect(client.phantom_status[:total]).to eq(2)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it 'returns active count' do
|
|
144
|
+
expect(client.phantom_status[:active]).to be >= 0
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
it 'returns by_state breakdown' do
|
|
148
|
+
expect(client.phantom_status[:by_state]).to be_a(Hash)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it 'returns total_activations' do
|
|
152
|
+
client.process_stimulus(stimulus: 'event', domain: :any)
|
|
153
|
+
expect(client.phantom_status[:total_activations]).to be >= 2
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it 'uses injected engine' do
|
|
157
|
+
eng = Legion::Extensions::Agentic::Defense::Phantom::Helpers::PhantomEngine.new
|
|
158
|
+
eng.register_removal(capability_name: 'cap')
|
|
159
|
+
result = client.phantom_status(engine: eng)
|
|
160
|
+
expect(result[:total]).to eq(1)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
describe '#decay_all' do
|
|
165
|
+
before { client.register_removal(capability_name: 'lex-redis') }
|
|
166
|
+
|
|
167
|
+
it 'returns success: true' do
|
|
168
|
+
expect(client.decay_all[:success]).to be true
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it 'returns resolved_this_cycle count' do
|
|
172
|
+
expect(client.decay_all[:resolved_this_cycle]).to be >= 0
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
it 'includes total in result' do
|
|
176
|
+
expect(client.decay_all[:total]).to be >= 0
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it 'decays phantom intensity' do
|
|
180
|
+
client.phantom_status[:by_state][:acute]
|
|
181
|
+
client.decay_all
|
|
182
|
+
after_report = client.phantom_status
|
|
183
|
+
expect(after_report[:total]).to be >= 0
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
it 'uses injected engine' do
|
|
187
|
+
eng = Legion::Extensions::Agentic::Defense::Phantom::Helpers::PhantomEngine.new
|
|
188
|
+
eng.register_removal(capability_name: 'cap')
|
|
189
|
+
result = client.decay_all(engine: eng)
|
|
190
|
+
expect(result[:success]).to be true
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Defense::Quicksand::Client do
|
|
4
|
+
subject(:client) { described_class.new }
|
|
5
|
+
|
|
6
|
+
it 'includes the runner module' do
|
|
7
|
+
expect(described_class.ancestors).to include(
|
|
8
|
+
Legion::Extensions::Agentic::Defense::Quicksand::Runners::CognitiveQuicksand
|
|
9
|
+
)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'responds to create_trap' do
|
|
13
|
+
expect(client).to respond_to(:create_trap)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'responds to struggle' do
|
|
17
|
+
expect(client).to respond_to(:struggle)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'responds to attempt_escape' do
|
|
21
|
+
expect(client).to respond_to(:attempt_escape)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'responds to quicksand_status' do
|
|
25
|
+
expect(client).to respond_to(:quicksand_status)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'can create and struggle through client' do
|
|
29
|
+
result = client.create_trap(trap_type: :overthinking, domain: :test, content: 'loop')
|
|
30
|
+
expect(result[:success]).to be true
|
|
31
|
+
trap_id = result[:trap][:id]
|
|
32
|
+
struggle_result = client.struggle(trap_id: trap_id)
|
|
33
|
+
expect(struggle_result[:success]).to be true
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Defense::Quicksand::Helpers::Constants do
|
|
4
|
+
described_class = Legion::Extensions::Agentic::Defense::Quicksand::Helpers::Constants
|
|
5
|
+
|
|
6
|
+
describe 'TRAP_TYPES' do
|
|
7
|
+
it 'contains expected types' do
|
|
8
|
+
expect(described_class::TRAP_TYPES).to eq(%i[overthinking rumination analysis_paralysis
|
|
9
|
+
perfectionism indecision])
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'is frozen' do
|
|
13
|
+
expect(described_class::TRAP_TYPES).to be_frozen
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe 'STRUGGLE_MODES' do
|
|
18
|
+
it 'contains expected modes' do
|
|
19
|
+
expect(described_class::STRUGGLE_MODES).to eq(%i[thrash freeze sink float escape])
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
describe 'numeric constants' do
|
|
24
|
+
it 'defines MAX_TRAPS' do
|
|
25
|
+
expect(described_class::MAX_TRAPS).to eq(200)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'defines SINK_RATE' do
|
|
29
|
+
expect(described_class::SINK_RATE).to eq(0.08)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'defines STRUGGLE_PENALTY' do
|
|
33
|
+
expect(described_class::STRUGGLE_PENALTY).to eq(0.12)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'defines ESCAPE_THRESHOLD' do
|
|
37
|
+
expect(described_class::ESCAPE_THRESHOLD).to eq(0.3)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe '.label_for' do
|
|
42
|
+
it 'returns :submerged for high depth' do
|
|
43
|
+
expect(described_class.label_for(described_class::DEPTH_LABELS, 0.9)).to eq(:submerged)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'returns :surface for low depth' do
|
|
47
|
+
expect(described_class.label_for(described_class::DEPTH_LABELS, 0.1)).to eq(:surface)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'returns :concrete for high viscosity' do
|
|
51
|
+
expect(described_class.label_for(described_class::VISCOSITY_LABELS, 0.9)).to eq(:concrete)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'returns :dry for low viscosity' do
|
|
55
|
+
expect(described_class.label_for(described_class::VISCOSITY_LABELS, 0.1)).to eq(:dry)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Defense::Quicksand::Helpers::Pit do
|
|
4
|
+
subject(:pit) { described_class.new }
|
|
5
|
+
|
|
6
|
+
describe '#initialize' do
|
|
7
|
+
it 'assigns a UUID id' do
|
|
8
|
+
expect(pit.id).to match(/\A[0-9a-f-]{36}\z/)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'defaults saturation to 0.5' do
|
|
12
|
+
expect(pit.saturation).to eq(0.5)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'defaults danger_level to 0.3' do
|
|
16
|
+
expect(pit.danger_level).to eq(0.3)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'starts with empty trap_ids' do
|
|
20
|
+
expect(pit.trap_ids).to be_empty
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe '#add_trap' do
|
|
25
|
+
it 'adds a trap id' do
|
|
26
|
+
expect(pit.add_trap('t-1')).to eq(:added)
|
|
27
|
+
expect(pit.trap_ids).to include('t-1')
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'returns :already_present for duplicates' do
|
|
31
|
+
pit.add_trap('t-1')
|
|
32
|
+
expect(pit.add_trap('t-1')).to eq(:already_present)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'recalculates danger level' do
|
|
36
|
+
initial = pit.danger_level
|
|
37
|
+
5.times { |i| pit.add_trap("t-#{i}") }
|
|
38
|
+
expect(pit.danger_level).not_to eq(initial)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe '#remove_trap' do
|
|
43
|
+
it 'removes a trap id' do
|
|
44
|
+
pit.add_trap('t-1')
|
|
45
|
+
expect(pit.remove_trap('t-1')).to eq(:removed)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'returns :not_found for missing' do
|
|
49
|
+
expect(pit.remove_trap('nope')).to eq(:not_found)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe '#saturate!' do
|
|
54
|
+
it 'increases saturation' do
|
|
55
|
+
initial = pit.saturation
|
|
56
|
+
pit.saturate!(rate: 0.1)
|
|
57
|
+
expect(pit.saturation).to eq((initial + 0.1).round(10))
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
describe '#drain!' do
|
|
62
|
+
it 'decreases saturation' do
|
|
63
|
+
initial = pit.saturation
|
|
64
|
+
pit.drain!(rate: 0.1)
|
|
65
|
+
expect(pit.saturation).to eq((initial - 0.1).round(10))
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
describe '#deadly?' do
|
|
70
|
+
it 'returns false at default' do
|
|
71
|
+
expect(pit).not_to be_deadly
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'returns true when danger_level >= 0.8' do
|
|
75
|
+
pit.danger_level = 0.9
|
|
76
|
+
expect(pit).to be_deadly
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
describe '#safe?' do
|
|
81
|
+
it 'returns false at default' do
|
|
82
|
+
expect(pit).not_to be_safe
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it 'returns true when danger_level < 0.2' do
|
|
86
|
+
pit.danger_level = 0.1
|
|
87
|
+
expect(pit).to be_safe
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe '#trap_count' do
|
|
92
|
+
it 'returns 0 initially' do
|
|
93
|
+
expect(pit.trap_count).to eq(0)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe '#to_h' do
|
|
98
|
+
it 'includes all expected keys' do
|
|
99
|
+
expected = %i[id saturation danger_level trap_count deadly safe created_at]
|
|
100
|
+
expect(pit.to_h.keys).to match_array(expected)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Defense::Quicksand::Helpers::QuicksandEngine do
|
|
4
|
+
subject(:engine) { described_class.new }
|
|
5
|
+
|
|
6
|
+
let(:default_attrs) { { trap_type: :overthinking, domain: :reasoning, content: 'loop' } }
|
|
7
|
+
|
|
8
|
+
describe '#create_trap' do
|
|
9
|
+
it 'creates and stores a trap' do
|
|
10
|
+
t = engine.create_trap(**default_attrs)
|
|
11
|
+
expect(t).to be_a(Legion::Extensions::Agentic::Defense::Quicksand::Helpers::Trap)
|
|
12
|
+
expect(engine.all_traps.size).to eq(1)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'raises when limit reached' do
|
|
16
|
+
stub_const('Legion::Extensions::Agentic::Defense::Quicksand::Helpers::Constants::MAX_TRAPS', 1)
|
|
17
|
+
engine.create_trap(**default_attrs)
|
|
18
|
+
expect do
|
|
19
|
+
engine.create_trap(trap_type: :rumination, domain: :t, content: 'x')
|
|
20
|
+
end.to raise_error(ArgumentError, /trap limit/)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe '#create_pit' do
|
|
25
|
+
it 'creates and stores a pit' do
|
|
26
|
+
p = engine.create_pit
|
|
27
|
+
expect(p).to be_a(Legion::Extensions::Agentic::Defense::Quicksand::Helpers::Pit)
|
|
28
|
+
expect(engine.all_pits.size).to eq(1)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe '#sink_trap' do
|
|
33
|
+
it 'increases trap depth' do
|
|
34
|
+
t = engine.create_trap(**default_attrs)
|
|
35
|
+
initial = t.depth
|
|
36
|
+
engine.sink_trap(trap_id: t.id)
|
|
37
|
+
expect(t.depth).to be > initial
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'raises for unknown trap' do
|
|
41
|
+
expect do
|
|
42
|
+
engine.sink_trap(trap_id: 'bad')
|
|
43
|
+
end.to raise_error(ArgumentError, /trap not found/)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe '#struggle' do
|
|
48
|
+
it 'returns trap data with depth and count' do
|
|
49
|
+
t = engine.create_trap(**default_attrs)
|
|
50
|
+
result = engine.struggle(trap_id: t.id)
|
|
51
|
+
expect(result[:struggle_count]).to eq(1)
|
|
52
|
+
expect(result[:depth]).to be > 0.3
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
describe '#calm' do
|
|
57
|
+
it 'decreases trap depth' do
|
|
58
|
+
t = engine.create_trap(**default_attrs)
|
|
59
|
+
initial = t.depth
|
|
60
|
+
engine.calm(trap_id: t.id)
|
|
61
|
+
expect(t.depth).to be < initial
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
describe '#attempt_escape' do
|
|
66
|
+
it 'escapes when shallow' do
|
|
67
|
+
t = engine.create_trap(**default_attrs, depth: 0.5)
|
|
68
|
+
result = engine.attempt_escape(trap_id: t.id)
|
|
69
|
+
expect(result[:result]).to eq(:escaped)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'fails when deep' do
|
|
73
|
+
t = engine.create_trap(**default_attrs, depth: 0.9)
|
|
74
|
+
result = engine.attempt_escape(trap_id: t.id)
|
|
75
|
+
expect(result[:result]).to eq(:too_deep)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
describe '#add_trap_to_pit' do
|
|
80
|
+
it 'links trap to pit' do
|
|
81
|
+
t = engine.create_trap(**default_attrs)
|
|
82
|
+
p = engine.create_pit
|
|
83
|
+
expect(engine.add_trap_to_pit(trap_id: t.id, pit_id: p.id)).to eq(:added)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
describe '#sink_all!' do
|
|
88
|
+
it 'sinks all traps' do
|
|
89
|
+
t1 = engine.create_trap(**default_attrs)
|
|
90
|
+
t2 = engine.create_trap(trap_type: :rumination, domain: :t, content: 'x')
|
|
91
|
+
engine.sink_all!
|
|
92
|
+
expect(t1.depth).to be > 0.3
|
|
93
|
+
expect(t2.depth).to be > 0.3
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe '#calm_all!' do
|
|
98
|
+
it 'calms all traps' do
|
|
99
|
+
t = engine.create_trap(**default_attrs, depth: 0.5)
|
|
100
|
+
engine.calm_all!
|
|
101
|
+
expect(t.depth).to be < 0.5
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe '#traps_by_type' do
|
|
106
|
+
it 'returns counts' do
|
|
107
|
+
engine.create_trap(**default_attrs)
|
|
108
|
+
engine.create_trap(trap_type: :rumination, domain: :t, content: 'x')
|
|
109
|
+
counts = engine.traps_by_type
|
|
110
|
+
expect(counts[:overthinking]).to eq(1)
|
|
111
|
+
expect(counts[:rumination]).to eq(1)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
describe '#deepest' do
|
|
116
|
+
it 'returns traps sorted by depth descending' do
|
|
117
|
+
engine.create_trap(**default_attrs, depth: 0.2)
|
|
118
|
+
t2 = engine.create_trap(trap_type: :rumination, domain: :t, content: 'x', depth: 0.9)
|
|
119
|
+
expect(engine.deepest(limit: 1).first).to eq(t2)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
describe '#submerged_traps' do
|
|
124
|
+
it 'returns only submerged' do
|
|
125
|
+
engine.create_trap(**default_attrs, depth: 0.9)
|
|
126
|
+
engine.create_trap(trap_type: :rumination, domain: :t, content: 'x', depth: 0.3)
|
|
127
|
+
expect(engine.submerged_traps.size).to eq(1)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
describe '#stuck_traps' do
|
|
132
|
+
it 'returns stuck traps' do
|
|
133
|
+
engine.create_trap(**default_attrs, depth: 0.6, viscosity: 0.7)
|
|
134
|
+
engine.create_trap(trap_type: :rumination, domain: :t, content: 'x', depth: 0.1)
|
|
135
|
+
expect(engine.stuck_traps.size).to eq(1)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
describe '#quicksand_report' do
|
|
140
|
+
it 'returns comprehensive hash' do
|
|
141
|
+
engine.create_trap(**default_attrs)
|
|
142
|
+
report = engine.quicksand_report
|
|
143
|
+
expect(report).to include(:total_traps, :total_pits, :by_type,
|
|
144
|
+
:submerged, :stuck, :avg_depth, :deadly_pits)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
it 'handles empty engine' do
|
|
148
|
+
report = engine.quicksand_report
|
|
149
|
+
expect(report[:total_traps]).to eq(0)
|
|
150
|
+
expect(report[:avg_depth]).to eq(0.0)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Defense::Quicksand::Helpers::Trap do
|
|
4
|
+
subject(:trap) do
|
|
5
|
+
described_class.new(trap_type: :overthinking, domain: :reasoning, content: 'recursive loop')
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
describe '#initialize' do
|
|
9
|
+
it 'assigns a UUID id' do
|
|
10
|
+
expect(trap.id).to match(/\A[0-9a-f-]{36}\z/)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'sets trap_type as symbol' do
|
|
14
|
+
expect(trap.trap_type).to eq(:overthinking)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'defaults depth to 0.3' do
|
|
18
|
+
expect(trap.depth).to eq(0.3)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'defaults viscosity to 0.5' do
|
|
22
|
+
expect(trap.viscosity).to eq(0.5)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'starts with 0 struggle_count' do
|
|
26
|
+
expect(trap.struggle_count).to eq(0)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'accepts custom depth' do
|
|
30
|
+
t = described_class.new(trap_type: :rumination, domain: :t, content: 'x', depth: 0.7)
|
|
31
|
+
expect(t.depth).to eq(0.7)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'clamps depth to 0..1' do
|
|
35
|
+
t = described_class.new(trap_type: :indecision, domain: :t, content: 'x', depth: 5.0)
|
|
36
|
+
expect(t.depth).to eq(1.0)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'raises on unknown trap type' do
|
|
40
|
+
expect do
|
|
41
|
+
described_class.new(trap_type: :happiness, domain: :t, content: 'x')
|
|
42
|
+
end.to raise_error(ArgumentError, /unknown trap type/)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe '#sink!' do
|
|
47
|
+
it 'increases depth' do
|
|
48
|
+
initial = trap.depth
|
|
49
|
+
trap.sink!
|
|
50
|
+
expect(trap.depth).to eq((initial + 0.08).round(10))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'accepts custom rate' do
|
|
54
|
+
trap.sink!(rate: 0.2)
|
|
55
|
+
expect(trap.depth).to eq(0.5)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'clamps at 1.0' do
|
|
59
|
+
trap.depth = 0.95
|
|
60
|
+
trap.sink!(rate: 0.2)
|
|
61
|
+
expect(trap.depth).to eq(1.0)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
describe '#struggle!' do
|
|
66
|
+
it 'increases depth based on viscosity' do
|
|
67
|
+
initial = trap.depth
|
|
68
|
+
trap.struggle!
|
|
69
|
+
penalty = 0.12 * 0.5
|
|
70
|
+
expect(trap.depth).to eq((initial + penalty).round(10))
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'increments struggle_count' do
|
|
74
|
+
trap.struggle!
|
|
75
|
+
expect(trap.struggle_count).to eq(1)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'sinks faster with high viscosity' do
|
|
79
|
+
trap.viscosity = 0.9
|
|
80
|
+
initial = trap.depth
|
|
81
|
+
trap.struggle!
|
|
82
|
+
expect(trap.depth).to be > (initial + 0.1)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
describe '#calm!' do
|
|
87
|
+
it 'decreases depth' do
|
|
88
|
+
initial = trap.depth
|
|
89
|
+
trap.calm!
|
|
90
|
+
expect(trap.depth).to eq((initial - 0.03).round(10))
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'clamps at 0' do
|
|
94
|
+
trap.depth = 0.01
|
|
95
|
+
trap.calm!(rate: 0.1)
|
|
96
|
+
expect(trap.depth).to eq(0.0)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
describe '#escape!' do
|
|
101
|
+
it 'returns :escaped when shallow enough' do
|
|
102
|
+
trap.depth = 0.5
|
|
103
|
+
expect(trap.escape!).to eq(:escaped)
|
|
104
|
+
expect(trap.depth).to eq(0.0)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it 'returns :too_deep when too deep' do
|
|
108
|
+
trap.depth = 0.8
|
|
109
|
+
expect(trap.escape!).to eq(:too_deep)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
describe '#submerged?' do
|
|
114
|
+
it 'returns false at default depth' do
|
|
115
|
+
expect(trap).not_to be_submerged
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'returns true at 0.8+' do
|
|
119
|
+
trap.depth = 0.85
|
|
120
|
+
expect(trap).to be_submerged
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
describe '#surface?' do
|
|
125
|
+
it 'returns false at default depth' do
|
|
126
|
+
expect(trap).not_to be_surface
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it 'returns true below 0.2' do
|
|
130
|
+
trap.depth = 0.1
|
|
131
|
+
expect(trap).to be_surface
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
describe '#stuck?' do
|
|
136
|
+
it 'returns false at default' do
|
|
137
|
+
expect(trap).not_to be_stuck
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it 'returns true when deep and viscous' do
|
|
141
|
+
trap.depth = 0.6
|
|
142
|
+
trap.viscosity = 0.7
|
|
143
|
+
expect(trap).to be_stuck
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
describe '#depth_label' do
|
|
148
|
+
it 'returns :ankle_deep at default' do
|
|
149
|
+
expect(trap.depth_label).to eq(:ankle_deep)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
describe '#viscosity_label' do
|
|
154
|
+
it 'returns :moderate at default' do
|
|
155
|
+
expect(trap.viscosity_label).to eq(:moderate)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
describe '#to_h' do
|
|
160
|
+
it 'includes all expected keys' do
|
|
161
|
+
expected = %i[id trap_type domain content depth viscosity depth_label
|
|
162
|
+
viscosity_label struggle_count submerged surface stuck created_at]
|
|
163
|
+
expect(trap.to_h.keys).to match_array(expected)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|