lex-agentic-defense 0.1.0 → 0.1.4
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 +4 -4
- data/CHANGELOG.md +23 -0
- data/README.md +45 -3
- data/lib/legion/extensions/agentic/defense/epistemic_vigilance/helpers/vigilance_engine.rb +2 -0
- data/lib/legion/extensions/agentic/defense/epistemic_vigilance/version.rb +1 -1
- data/lib/legion/extensions/agentic/defense/extinction/runners/extinction.rb +17 -7
- data/lib/legion/extensions/agentic/defense/friction/helpers/friction_engine.rb +14 -2
- data/lib/legion/extensions/agentic/defense/friction/version.rb +1 -1
- data/lib/legion/extensions/agentic/defense/immune_response/helpers/immune_engine.rb +4 -0
- data/lib/legion/extensions/agentic/defense/immune_response/version.rb +1 -1
- data/lib/legion/extensions/agentic/defense/immunology/helpers/immune_engine.rb +4 -0
- data/lib/legion/extensions/agentic/defense/immunology/version.rb +1 -1
- data/lib/legion/extensions/agentic/defense/version.rb +1 -1
- data/spec/legion/extensions/agentic/defense/epistemic_vigilance/helpers/vigilance_engine_spec.rb +14 -0
- data/spec/legion/extensions/agentic/defense/extinction/runners/extinction_spec.rb +20 -0
- data/spec/legion/extensions/agentic/defense/friction/helpers/friction_engine_spec.rb +82 -44
- data/spec/legion/extensions/agentic/defense/friction/runners/cognitive_friction_spec.rb +9 -9
- data/spec/legion/extensions/agentic/defense/immune_response/cognitive_immune_response_spec.rb +1 -1
- data/spec/legion/extensions/agentic/defense/immune_response/helpers/immune_engine_spec.rb +26 -0
- data/spec/legion/extensions/agentic/defense/immunology/helpers/immune_engine_spec.rb +26 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3dabe10b53cfee2ee630d2f7094a78a0731f7a0f4038f218bf2ac177d409352b
|
|
4
|
+
data.tar.gz: 78a58c65ef5d6465e03322e6fe9031c333078c65b983bf0be11b07b2f27b3fd7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 671ce401a9d8ba558d5ee77e8223b716115543d892944542155be97748052c0240830c535995c60251bd2d8fcb40abf16eeffe15f84bce38eae6bcd9eaabb493
|
|
7
|
+
data.tar.gz: 5c91978e36743338e7fa07bc290d8c26650016a9d6523ea1ffc7532a241a6a83617dc4a1b9f5483d0c8abc26111f02bfc19051fb954b79b639a9dc0bf26cb139
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.1.3] - 2026-03-21
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Right-to-erasure propagation from extinction level 4 to Apollo knowledge store
|
|
9
|
+
- Apollo erasure wired in enforce_escalation_effects (delete non-confirmed, redact confirmed)
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- enforce_escalation_effects restructured: DigitalWorker termination uses if-block instead of early return
|
|
13
|
+
|
|
14
|
+
## [0.1.2] - 2026-03-18
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Enforce ANTIGEN_TYPES at ImmuneResponse::ImmuneEngine method boundaries: `register_antigen` and `create_antibody` return nil for invalid antigen_type values (v0.1.1)
|
|
18
|
+
- Enforce MANIPULATION_TACTICS at Immunology::ImmuneEngine method boundaries: `detect_threat` and `create_antibody` return nil for invalid tactic values (v0.1.1)
|
|
19
|
+
- Added 8 new enum validation specs (2 per enforced constant)
|
|
20
|
+
|
|
21
|
+
## [0.1.1] - 2026-03-18
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
- Enforce STATE_TYPES at FrictionEngine method boundaries: `set_current_state`, `set_friction`, `get_friction`, `attempt_transition`, `force_transition` return nil for invalid state values
|
|
25
|
+
- Enforce CLAIM_VERDICTS at VigilanceEngine#adjudicate_claim: returns nil for invalid verdict values
|
|
26
|
+
- Updated specs to use valid STATE_TYPES values; added 8 new enum validation specs
|
|
27
|
+
|
|
5
28
|
## [0.1.0] - 2026-03-18
|
|
6
29
|
|
|
7
30
|
### Added
|
data/README.md
CHANGED
|
@@ -1,13 +1,55 @@
|
|
|
1
1
|
# lex-agentic-defense
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Domain consolidation gem for cognitive defense, immunity, and error management. Bundles 15 source extensions into one loadable unit under `Legion::Extensions::Agentic::Defense`.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Overview
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**Gem**: `lex-agentic-defense`
|
|
8
|
+
**Version**: 0.1.2
|
|
9
|
+
**Namespace**: `Legion::Extensions::Agentic::Defense`
|
|
10
|
+
|
|
11
|
+
## Sub-Modules
|
|
12
|
+
|
|
13
|
+
| Sub-Module | Source Gem | Purpose |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| `Defense::ImmuneResponse` | `lex-cognitive-immune-response` | Active defense responses to cognitive threats |
|
|
16
|
+
| `Defense::Immunology` | `lex-cognitive-immunology` | Immune system modeling for belief protection |
|
|
17
|
+
| `Defense::Erosion` | `lex-cognitive-erosion` | Gradual degradation of outdated beliefs |
|
|
18
|
+
| `Defense::Friction` | `lex-cognitive-friction` | Resistance to undesired belief or behavior change |
|
|
19
|
+
| `Defense::Quicksand` | `lex-cognitive-quicksand` | Entrapment patterns — stuck states |
|
|
20
|
+
| `Defense::Quicksilver` | `lex-cognitive-quicksilver` | Rapid adaptive response to threats |
|
|
21
|
+
| `Defense::Phantom` | `lex-cognitive-phantom` | Phantom cognitive states — residual patterns after removal |
|
|
22
|
+
| `Defense::EpistemicVigilance` | `lex-epistemic-vigilance` | Critical evaluation of incoming information, deception detection |
|
|
23
|
+
| `Defense::Bias` | `lex-bias` | Cognitive bias catalog and de-biasing strategies |
|
|
24
|
+
| `Defense::Confabulation` | `lex-confabulation` | False memory generation detection |
|
|
25
|
+
| `Defense::Dissonance` | `lex-dissonance` | Cognitive dissonance detection and reduction |
|
|
26
|
+
| `Defense::ErrorMonitoring` | `lex-error-monitoring` | ACC error monitoring — anterior cingulate analog |
|
|
27
|
+
| `Defense::Extinction` | `lex-extinction` | Four-level containment ladder with authority-gated escalation |
|
|
28
|
+
| `Defense::Avalanche` | `lex-cognitive-avalanche` | Cascading cognitive failure detection |
|
|
29
|
+
| `Defense::Whirlpool` | `lex-cognitive-whirlpool` | Circular/recursive thought pattern detection |
|
|
30
|
+
|
|
31
|
+
## Actors
|
|
32
|
+
|
|
33
|
+
- `Defense::Bias::Actors::Update` — interval actor, updates bias calibration state
|
|
34
|
+
- `Defense::Confabulation::Actors::Decay` — interval actor, decays confabulation detections
|
|
35
|
+
- `Defense::EpistemicVigilance::Actors::Update` — interval actor, updates vigilance baseline
|
|
36
|
+
- `Defense::ErrorMonitoring::Actors::Tick` — interval actor, runs error monitoring tick
|
|
37
|
+
- `Defense::Extinction::Actors::ProtocolMonitor` — runs every 300s, monitors containment protocol state
|
|
8
38
|
|
|
9
39
|
## Installation
|
|
10
40
|
|
|
11
41
|
```ruby
|
|
12
42
|
gem 'lex-agentic-defense'
|
|
13
43
|
```
|
|
44
|
+
|
|
45
|
+
## Development
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
bundle install
|
|
49
|
+
bundle exec rspec # 1706 examples, 0 failures
|
|
50
|
+
bundle exec rubocop # 0 offenses
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## License
|
|
54
|
+
|
|
55
|
+
MIT
|
|
@@ -84,16 +84,26 @@ module Legion
|
|
|
84
84
|
Legion::Logging.warn '[extinction] cryptographic erasure triggered'
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
if defined?(Legion::Data::Model::DigitalWorker)
|
|
88
|
+
begin
|
|
89
|
+
Legion::Data::Model::DigitalWorker.where(lifecycle_state: 'active').update(
|
|
90
|
+
lifecycle_state: 'terminated', updated_at: Time.now.utc
|
|
91
|
+
)
|
|
92
|
+
rescue StandardError
|
|
93
|
+
nil
|
|
94
|
+
end
|
|
95
|
+
Legion::Logging.warn '[extinction] all active workers terminated'
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
return unless defined?(Legion::Extensions::Apollo::Runners::Knowledge)
|
|
88
99
|
|
|
89
100
|
begin
|
|
90
|
-
Legion::
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
rescue StandardError
|
|
94
|
-
|
|
101
|
+
obj = Object.new.extend(Legion::Extensions::Apollo::Runners::Knowledge)
|
|
102
|
+
obj.handle_erasure_request(agent_id: 'system:extinction')
|
|
103
|
+
Legion::Logging.warn '[extinction] apollo erasure propagated'
|
|
104
|
+
rescue StandardError => e
|
|
105
|
+
Legion::Logging.error "[extinction] apollo erasure failed: #{e.message}"
|
|
95
106
|
end
|
|
96
|
-
Legion::Logging.warn '[extinction] all active workers terminated'
|
|
97
107
|
end
|
|
98
108
|
|
|
99
109
|
def emit_escalation_event(level, authority, reason)
|
|
@@ -16,24 +16,34 @@ module Legion
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def set_current_state(state:)
|
|
19
|
+
return nil unless STATE_TYPES.include?(state.to_sym)
|
|
20
|
+
|
|
19
21
|
@current_state = state.to_sym
|
|
20
22
|
end
|
|
21
23
|
|
|
22
24
|
attr_reader :current_state
|
|
23
25
|
|
|
24
26
|
def set_friction(from_state:, to_state:, friction:)
|
|
27
|
+
return nil unless STATE_TYPES.include?(from_state.to_sym)
|
|
28
|
+
return nil unless STATE_TYPES.include?(to_state.to_sym)
|
|
29
|
+
|
|
25
30
|
key = :"#{from_state}_to_#{to_state}"
|
|
26
31
|
@friction_map[key] = friction.to_f.clamp(0.0, 1.0)
|
|
27
32
|
end
|
|
28
33
|
|
|
29
34
|
def get_friction(from_state:, to_state:)
|
|
35
|
+
return nil unless STATE_TYPES.include?(from_state.to_sym)
|
|
36
|
+
return nil unless STATE_TYPES.include?(to_state.to_sym)
|
|
37
|
+
|
|
30
38
|
key = :"#{from_state}_to_#{to_state}"
|
|
31
39
|
@friction_map.fetch(key, DEFAULT_FRICTION)
|
|
32
40
|
end
|
|
33
41
|
|
|
34
42
|
def attempt_transition(to_state:, force: 0.5)
|
|
43
|
+
return nil unless STATE_TYPES.include?(to_state.to_sym)
|
|
44
|
+
|
|
35
45
|
prune_if_needed
|
|
36
|
-
friction = get_friction(from_state: @current_state, to_state: to_state)
|
|
46
|
+
friction = get_friction(from_state: @current_state, to_state: to_state) || DEFAULT_FRICTION
|
|
37
47
|
transition = StateTransition.new(
|
|
38
48
|
from_state: @current_state,
|
|
39
49
|
to_state: to_state,
|
|
@@ -46,8 +56,10 @@ module Legion
|
|
|
46
56
|
end
|
|
47
57
|
|
|
48
58
|
def force_transition(to_state:)
|
|
59
|
+
return nil unless STATE_TYPES.include?(to_state.to_sym)
|
|
60
|
+
|
|
49
61
|
prune_if_needed
|
|
50
|
-
friction = get_friction(from_state: @current_state, to_state: to_state)
|
|
62
|
+
friction = get_friction(from_state: @current_state, to_state: to_state) || DEFAULT_FRICTION
|
|
51
63
|
transition = StateTransition.new(
|
|
52
64
|
from_state: @current_state,
|
|
53
65
|
to_state: to_state,
|
|
@@ -16,6 +16,8 @@ module Legion
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def register_antigen(pattern:, antigen_type:, threat_level: DEFAULT_THREAT_LEVEL)
|
|
19
|
+
return nil unless ANTIGEN_TYPES.include?(antigen_type.to_sym)
|
|
20
|
+
|
|
19
21
|
prune_antigens
|
|
20
22
|
antigen = Antigen.new(
|
|
21
23
|
pattern: pattern, antigen_type: antigen_type, threat_level: threat_level
|
|
@@ -38,6 +40,8 @@ module Legion
|
|
|
38
40
|
end
|
|
39
41
|
|
|
40
42
|
def create_antibody(antigen_type:, signature:, immunity_level: 0.3)
|
|
43
|
+
return nil unless ANTIGEN_TYPES.include?(antigen_type.to_sym)
|
|
44
|
+
|
|
41
45
|
prune_antibodies
|
|
42
46
|
antibody = Antibody.new(
|
|
43
47
|
antigen_type: antigen_type, signature: signature, immunity_level: immunity_level
|
|
@@ -17,6 +17,8 @@ module Legion
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def detect_threat(source:, tactic:, content_hash:, threat_level: 0.5)
|
|
20
|
+
return nil unless Constants::MANIPULATION_TACTICS.include?(tactic.to_sym)
|
|
21
|
+
|
|
20
22
|
prune_threats_if_full
|
|
21
23
|
|
|
22
24
|
threat = Threat.new(
|
|
@@ -68,6 +70,8 @@ module Legion
|
|
|
68
70
|
end
|
|
69
71
|
|
|
70
72
|
def create_antibody(tactic:, pattern:, strength: 0.5)
|
|
73
|
+
return nil unless Constants::MANIPULATION_TACTICS.include?(tactic.to_sym)
|
|
74
|
+
|
|
71
75
|
prune_antibodies_if_full
|
|
72
76
|
|
|
73
77
|
ab = Antibody.new(tactic: tactic, pattern: pattern, strength: strength)
|
data/spec/legion/extensions/agentic/defense/epistemic_vigilance/helpers/vigilance_engine_spec.rb
CHANGED
|
@@ -149,6 +149,20 @@ RSpec.describe Legion::Extensions::Agentic::Defense::EpistemicVigilance::Helpers
|
|
|
149
149
|
after = engine.source_reliability(source_id: source_id)[:reliability]
|
|
150
150
|
expect(after).to be < before
|
|
151
151
|
end
|
|
152
|
+
|
|
153
|
+
it 'returns nil for an invalid verdict' do
|
|
154
|
+
result = engine.adjudicate_claim(claim_id: claim_id, verdict: :bogus_verdict)
|
|
155
|
+
expect(result).to be_nil
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
it 'accepts all valid CLAIM_VERDICTS' do
|
|
159
|
+
verdicts = Legion::Extensions::Agentic::Defense::EpistemicVigilance::Helpers::Constants::CLAIM_VERDICTS
|
|
160
|
+
verdicts.each do |v|
|
|
161
|
+
cid = engine.submit_claim(content: "claim_#{v}", source_id: source_id, domain: :science)[:claim][:id]
|
|
162
|
+
result = engine.adjudicate_claim(claim_id: cid, verdict: v)
|
|
163
|
+
expect(result[:verdict]).to eq(v)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
152
166
|
end
|
|
153
167
|
|
|
154
168
|
describe '#source_reliability' do
|
|
@@ -80,6 +80,26 @@ RSpec.describe Legion::Extensions::Agentic::Defense::Extinction::Runners::Extinc
|
|
|
80
80
|
expect(pc_mod).to receive(:erase_all)
|
|
81
81
|
client.escalate(level: 4, authority: :physical_keyholders, reason: 'final')
|
|
82
82
|
end
|
|
83
|
+
|
|
84
|
+
it 'propagates Apollo erasure on level 4' do
|
|
85
|
+
events = Module.new { def self.emit(*, **); end }
|
|
86
|
+
stub_const('Legion::Events', events)
|
|
87
|
+
pc_mod = Module.new { def self.erase_all; end }
|
|
88
|
+
stub_const('Legion::Extensions::Privatecore::Runners::Privatecore', pc_mod)
|
|
89
|
+
|
|
90
|
+
apollo_runner = Module.new do
|
|
91
|
+
def handle_erasure_request(agent_id:, **)
|
|
92
|
+
{ deleted: 1, redacted: 0, agent_id: agent_id }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
stub_const('Legion::Extensions::Apollo::Runners::Knowledge', apollo_runner)
|
|
96
|
+
|
|
97
|
+
client.escalate(level: 1, authority: :governance_council, reason: 's1')
|
|
98
|
+
client.escalate(level: 2, authority: :governance_council, reason: 's2')
|
|
99
|
+
client.escalate(level: 3, authority: :council_plus_executive, reason: 's3')
|
|
100
|
+
result = client.escalate(level: 4, authority: :physical_keyholders, reason: 'final')
|
|
101
|
+
expect(result[:escalated]).to be true
|
|
102
|
+
end
|
|
83
103
|
end
|
|
84
104
|
|
|
85
105
|
describe '#monitor_protocol' do
|
|
@@ -10,92 +10,126 @@ RSpec.describe Legion::Extensions::Agentic::Defense::Friction::Helpers::Friction
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
describe '#set_current_state' do
|
|
13
|
-
it 'changes the current state' do
|
|
14
|
-
engine.set_current_state(state: :
|
|
15
|
-
expect(engine.current_state).to eq(:
|
|
13
|
+
it 'changes the current state to a valid STATE_TYPE' do
|
|
14
|
+
engine.set_current_state(state: :focus_mode)
|
|
15
|
+
expect(engine.current_state).to eq(:focus_mode)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
it '
|
|
19
|
-
engine.set_current_state(state:
|
|
20
|
-
expect(
|
|
18
|
+
it 'returns nil for an invalid state' do
|
|
19
|
+
result = engine.set_current_state(state: :invalid_state)
|
|
20
|
+
expect(result).to be_nil
|
|
21
|
+
expect(engine.current_state).to eq(:rest_mode)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'accepts all valid STATE_TYPES' do
|
|
25
|
+
described_class.include(Legion::Extensions::Agentic::Defense::Friction::Helpers::Constants)
|
|
26
|
+
Legion::Extensions::Agentic::Defense::Friction::Helpers::Constants::STATE_TYPES.each do |state|
|
|
27
|
+
eng = described_class.new
|
|
28
|
+
eng.set_current_state(state: state)
|
|
29
|
+
expect(eng.current_state).to eq(state)
|
|
30
|
+
end
|
|
21
31
|
end
|
|
22
32
|
end
|
|
23
33
|
|
|
24
34
|
describe '#set_friction / #get_friction' do
|
|
25
|
-
it 'stores and retrieves friction for a path' do
|
|
26
|
-
engine.set_friction(from_state: :
|
|
27
|
-
expect(engine.get_friction(from_state: :
|
|
35
|
+
it 'stores and retrieves friction for a valid path' do
|
|
36
|
+
engine.set_friction(from_state: :rest_mode, to_state: :focus_mode, friction: 0.7)
|
|
37
|
+
expect(engine.get_friction(from_state: :rest_mode, to_state: :focus_mode)).to eq(0.7)
|
|
28
38
|
end
|
|
29
39
|
|
|
30
40
|
it 'clamps friction to 0..1' do
|
|
31
|
-
engine.set_friction(from_state: :
|
|
32
|
-
expect(engine.get_friction(from_state: :
|
|
41
|
+
engine.set_friction(from_state: :rest_mode, to_state: :focus_mode, friction: 5.0)
|
|
42
|
+
expect(engine.get_friction(from_state: :rest_mode, to_state: :focus_mode)).to eq(1.0)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'returns nil when from_state is invalid' do
|
|
46
|
+
expect(engine.set_friction(from_state: :invalid, to_state: :focus_mode, friction: 0.5)).to be_nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'returns nil when to_state is invalid' do
|
|
50
|
+
expect(engine.set_friction(from_state: :rest_mode, to_state: :invalid, friction: 0.5)).to be_nil
|
|
33
51
|
end
|
|
34
52
|
|
|
35
|
-
it 'returns
|
|
53
|
+
it 'returns nil from get_friction when from_state is invalid' do
|
|
54
|
+
expect(engine.get_friction(from_state: :invalid, to_state: :focus_mode)).to be_nil
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'returns default friction for unknown valid paths' do
|
|
36
58
|
default = Legion::Extensions::Agentic::Defense::Friction::Helpers::Constants::DEFAULT_FRICTION
|
|
37
|
-
expect(engine.get_friction(from_state: :
|
|
59
|
+
expect(engine.get_friction(from_state: :rest_mode, to_state: :focus_mode)).to eq(default)
|
|
38
60
|
end
|
|
39
61
|
end
|
|
40
62
|
|
|
41
63
|
describe '#attempt_transition' do
|
|
42
64
|
it 'moves to new state on success' do
|
|
43
|
-
engine.set_friction(from_state: :rest_mode, to_state: :
|
|
44
|
-
transition = engine.attempt_transition(to_state: :
|
|
65
|
+
engine.set_friction(from_state: :rest_mode, to_state: :focus_mode, friction: 0.3)
|
|
66
|
+
transition = engine.attempt_transition(to_state: :focus_mode, force: 0.8)
|
|
45
67
|
expect(transition.completed?).to be true
|
|
46
|
-
expect(engine.current_state).to eq(:
|
|
68
|
+
expect(engine.current_state).to eq(:focus_mode)
|
|
47
69
|
end
|
|
48
70
|
|
|
49
71
|
it 'stays in current state on resistance' do
|
|
50
|
-
engine.set_friction(from_state: :rest_mode, to_state: :
|
|
51
|
-
transition = engine.attempt_transition(to_state: :
|
|
72
|
+
engine.set_friction(from_state: :rest_mode, to_state: :focus_mode, friction: 0.9)
|
|
73
|
+
transition = engine.attempt_transition(to_state: :focus_mode, force: 0.1)
|
|
52
74
|
expect(transition.completed?).to be false
|
|
53
75
|
expect(engine.current_state).to eq(:rest_mode)
|
|
54
76
|
end
|
|
55
77
|
|
|
56
78
|
it 'records the transition in history' do
|
|
57
|
-
engine.attempt_transition(to_state: :
|
|
79
|
+
engine.attempt_transition(to_state: :focus_mode, force: 0.8)
|
|
58
80
|
expect(engine.transition_history.size).to eq(1)
|
|
59
81
|
end
|
|
82
|
+
|
|
83
|
+
it 'returns nil for invalid to_state' do
|
|
84
|
+
result = engine.attempt_transition(to_state: :invalid_state, force: 0.9)
|
|
85
|
+
expect(result).to be_nil
|
|
86
|
+
end
|
|
60
87
|
end
|
|
61
88
|
|
|
62
89
|
describe '#force_transition' do
|
|
63
90
|
it 'always moves to new state regardless of friction' do
|
|
64
|
-
engine.set_friction(from_state: :rest_mode, to_state: :
|
|
65
|
-
transition = engine.force_transition(to_state: :
|
|
91
|
+
engine.set_friction(from_state: :rest_mode, to_state: :vigilant_mode, friction: 1.0)
|
|
92
|
+
transition = engine.force_transition(to_state: :vigilant_mode)
|
|
66
93
|
expect(transition.completed?).to be true
|
|
67
|
-
expect(engine.current_state).to eq(:
|
|
94
|
+
expect(engine.current_state).to eq(:vigilant_mode)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it 'returns nil for invalid to_state' do
|
|
98
|
+
result = engine.force_transition(to_state: :bogus_mode)
|
|
99
|
+
expect(result).to be_nil
|
|
68
100
|
end
|
|
69
101
|
end
|
|
70
102
|
|
|
71
103
|
describe '#transition_history' do
|
|
72
104
|
it 'returns transitions in chronological order' do
|
|
73
|
-
engine.attempt_transition(to_state: :
|
|
74
|
-
engine.attempt_transition(to_state: :
|
|
105
|
+
engine.attempt_transition(to_state: :focus_mode, force: 0.9)
|
|
106
|
+
engine.attempt_transition(to_state: :analytical_mode, force: 0.9)
|
|
75
107
|
history = engine.transition_history
|
|
76
108
|
expect(history.first.created_at).to be <= history.last.created_at
|
|
77
109
|
end
|
|
78
110
|
|
|
79
111
|
it 'respects the limit' do
|
|
80
|
-
|
|
112
|
+
states = %i[focus_mode social_mode analytical_mode creative_mode vigilant_mode]
|
|
113
|
+
states.each { |s| engine.attempt_transition(to_state: s, force: 0.9) }
|
|
81
114
|
expect(engine.transition_history(limit: 3).size).to eq(3)
|
|
82
115
|
end
|
|
83
116
|
end
|
|
84
117
|
|
|
85
118
|
describe '#successful_transitions' do
|
|
86
119
|
it 'returns only completed transitions' do
|
|
87
|
-
engine.set_friction(from_state: :rest_mode, to_state: :
|
|
88
|
-
engine.attempt_transition(to_state: :
|
|
89
|
-
engine.
|
|
90
|
-
engine.
|
|
120
|
+
engine.set_friction(from_state: :rest_mode, to_state: :focus_mode, friction: 0.3)
|
|
121
|
+
engine.attempt_transition(to_state: :focus_mode, force: 0.8)
|
|
122
|
+
engine.set_current_state(state: :focus_mode)
|
|
123
|
+
engine.set_friction(from_state: :focus_mode, to_state: :analytical_mode, friction: 0.9)
|
|
124
|
+
engine.attempt_transition(to_state: :analytical_mode, force: 0.1)
|
|
91
125
|
expect(engine.successful_transitions.size).to eq(1)
|
|
92
126
|
end
|
|
93
127
|
end
|
|
94
128
|
|
|
95
129
|
describe '#resisted_transitions' do
|
|
96
130
|
it 'returns only resisted transitions' do
|
|
97
|
-
engine.set_friction(from_state: :rest_mode, to_state: :
|
|
98
|
-
engine.attempt_transition(to_state: :
|
|
131
|
+
engine.set_friction(from_state: :rest_mode, to_state: :vigilant_mode, friction: 0.9)
|
|
132
|
+
engine.attempt_transition(to_state: :vigilant_mode, force: 0.1)
|
|
99
133
|
expect(engine.resisted_transitions.size).to eq(1)
|
|
100
134
|
end
|
|
101
135
|
end
|
|
@@ -106,10 +140,11 @@ RSpec.describe Legion::Extensions::Agentic::Defense::Friction::Helpers::Friction
|
|
|
106
140
|
end
|
|
107
141
|
|
|
108
142
|
it 'calculates ratio of successful to total' do
|
|
109
|
-
engine.set_friction(from_state: :rest_mode, to_state: :
|
|
110
|
-
engine.attempt_transition(to_state: :
|
|
111
|
-
engine.
|
|
112
|
-
engine.
|
|
143
|
+
engine.set_friction(from_state: :rest_mode, to_state: :focus_mode, friction: 0.3)
|
|
144
|
+
engine.attempt_transition(to_state: :focus_mode, force: 0.8)
|
|
145
|
+
engine.set_current_state(state: :focus_mode)
|
|
146
|
+
engine.set_friction(from_state: :focus_mode, to_state: :social_mode, friction: 0.9)
|
|
147
|
+
engine.attempt_transition(to_state: :social_mode, force: 0.1)
|
|
113
148
|
expect(engine.success_rate).to eq(0.5)
|
|
114
149
|
end
|
|
115
150
|
end
|
|
@@ -120,26 +155,28 @@ RSpec.describe Legion::Extensions::Agentic::Defense::Friction::Helpers::Friction
|
|
|
120
155
|
end
|
|
121
156
|
|
|
122
157
|
it 'calculates average friction across transitions' do
|
|
123
|
-
engine.set_friction(from_state: :rest_mode, to_state: :
|
|
124
|
-
engine.attempt_transition(to_state: :
|
|
125
|
-
engine.
|
|
126
|
-
engine.
|
|
158
|
+
engine.set_friction(from_state: :rest_mode, to_state: :focus_mode, friction: 0.2)
|
|
159
|
+
engine.attempt_transition(to_state: :focus_mode, force: 0.9)
|
|
160
|
+
engine.set_current_state(state: :focus_mode)
|
|
161
|
+
engine.set_friction(from_state: :focus_mode, to_state: :social_mode, friction: 0.8)
|
|
162
|
+
engine.attempt_transition(to_state: :social_mode, force: 0.9)
|
|
127
163
|
expect(engine.average_friction).to eq(0.5)
|
|
128
164
|
end
|
|
129
165
|
end
|
|
130
166
|
|
|
131
167
|
describe '#highest_friction_paths' do
|
|
132
168
|
it 'returns paths sorted by friction descending' do
|
|
133
|
-
engine.set_friction(from_state: :
|
|
134
|
-
engine.set_friction(from_state: :
|
|
135
|
-
engine.set_friction(from_state: :
|
|
169
|
+
engine.set_friction(from_state: :rest_mode, to_state: :focus_mode, friction: 0.3)
|
|
170
|
+
engine.set_friction(from_state: :rest_mode, to_state: :social_mode, friction: 0.9)
|
|
171
|
+
engine.set_friction(from_state: :rest_mode, to_state: :analytical_mode, friction: 0.6)
|
|
136
172
|
paths = engine.highest_friction_paths(limit: 3)
|
|
137
173
|
expect(paths.first[:friction]).to eq(0.9)
|
|
138
174
|
expect(paths.last[:friction]).to eq(0.3)
|
|
139
175
|
end
|
|
140
176
|
|
|
141
177
|
it 'respects the limit' do
|
|
142
|
-
|
|
178
|
+
valid_states = %i[focus_mode social_mode analytical_mode creative_mode vigilant_mode]
|
|
179
|
+
valid_states.each { |s| engine.set_friction(from_state: :rest_mode, to_state: s, friction: 0.5) }
|
|
143
180
|
expect(engine.highest_friction_paths(limit: 2).size).to eq(2)
|
|
144
181
|
end
|
|
145
182
|
end
|
|
@@ -168,7 +205,8 @@ RSpec.describe Legion::Extensions::Agentic::Defense::Friction::Helpers::Friction
|
|
|
168
205
|
it 'prunes oldest transition when limit reached' do
|
|
169
206
|
stub_const('Legion::Extensions::Agentic::Defense::Friction::Helpers::Constants::MAX_TRANSITIONS', 3)
|
|
170
207
|
eng = described_class.new
|
|
171
|
-
|
|
208
|
+
states = %i[focus_mode social_mode analytical_mode creative_mode]
|
|
209
|
+
states.each { |s| eng.attempt_transition(to_state: s, force: 0.9) }
|
|
172
210
|
expect(eng.transition_history.size).to eq(3)
|
|
173
211
|
end
|
|
174
212
|
end
|
|
@@ -5,15 +5,15 @@ RSpec.describe Legion::Extensions::Agentic::Defense::Friction::Runners::Cognitiv
|
|
|
5
5
|
|
|
6
6
|
describe '#set_current_state' do
|
|
7
7
|
it 'returns success with new state' do
|
|
8
|
-
result = client.set_current_state(state: :
|
|
8
|
+
result = client.set_current_state(state: :focus_mode)
|
|
9
9
|
expect(result[:success]).to be true
|
|
10
|
-
expect(result[:state]).to eq(:
|
|
10
|
+
expect(result[:state]).to eq(:focus_mode)
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
describe '#set_friction' do
|
|
15
15
|
it 'returns success with friction details' do
|
|
16
|
-
result = client.set_friction(from_state: :
|
|
16
|
+
result = client.set_friction(from_state: :rest_mode, to_state: :focus_mode, friction: 0.7)
|
|
17
17
|
expect(result[:success]).to be true
|
|
18
18
|
expect(result[:friction]).to eq(0.7)
|
|
19
19
|
end
|
|
@@ -21,8 +21,8 @@ RSpec.describe Legion::Extensions::Agentic::Defense::Friction::Runners::Cognitiv
|
|
|
21
21
|
|
|
22
22
|
describe '#get_friction' do
|
|
23
23
|
it 'retrieves stored friction' do
|
|
24
|
-
client.set_friction(from_state: :
|
|
25
|
-
result = client.get_friction(from_state: :
|
|
24
|
+
client.set_friction(from_state: :rest_mode, to_state: :focus_mode, friction: 0.6)
|
|
25
|
+
result = client.get_friction(from_state: :rest_mode, to_state: :focus_mode)
|
|
26
26
|
expect(result[:success]).to be true
|
|
27
27
|
expect(result[:friction]).to eq(0.6)
|
|
28
28
|
end
|
|
@@ -30,7 +30,7 @@ RSpec.describe Legion::Extensions::Agentic::Defense::Friction::Runners::Cognitiv
|
|
|
30
30
|
|
|
31
31
|
describe '#attempt_transition' do
|
|
32
32
|
it 'returns transition details' do
|
|
33
|
-
result = client.attempt_transition(to_state: :
|
|
33
|
+
result = client.attempt_transition(to_state: :focus_mode, force: 0.9)
|
|
34
34
|
expect(result[:success]).to be true
|
|
35
35
|
expect(result[:transition]).to be_a(Hash)
|
|
36
36
|
expect(result[:current_state]).to be_a(Symbol)
|
|
@@ -39,7 +39,7 @@ RSpec.describe Legion::Extensions::Agentic::Defense::Friction::Runners::Cognitiv
|
|
|
39
39
|
|
|
40
40
|
describe '#force_transition' do
|
|
41
41
|
it 'always completes' do
|
|
42
|
-
result = client.force_transition(to_state: :
|
|
42
|
+
result = client.force_transition(to_state: :vigilant_mode)
|
|
43
43
|
expect(result[:success]).to be true
|
|
44
44
|
expect(result[:transition][:completed]).to be true
|
|
45
45
|
end
|
|
@@ -47,7 +47,7 @@ RSpec.describe Legion::Extensions::Agentic::Defense::Friction::Runners::Cognitiv
|
|
|
47
47
|
|
|
48
48
|
describe '#transition_history' do
|
|
49
49
|
it 'returns history array' do
|
|
50
|
-
client.attempt_transition(to_state: :
|
|
50
|
+
client.attempt_transition(to_state: :focus_mode, force: 0.9)
|
|
51
51
|
result = client.transition_history
|
|
52
52
|
expect(result[:success]).to be true
|
|
53
53
|
expect(result[:count]).to eq(1)
|
|
@@ -72,7 +72,7 @@ RSpec.describe Legion::Extensions::Agentic::Defense::Friction::Runners::Cognitiv
|
|
|
72
72
|
|
|
73
73
|
describe '#highest_friction_paths' do
|
|
74
74
|
it 'returns paths array' do
|
|
75
|
-
client.set_friction(from_state: :
|
|
75
|
+
client.set_friction(from_state: :rest_mode, to_state: :focus_mode, friction: 0.8)
|
|
76
76
|
result = client.highest_friction_paths
|
|
77
77
|
expect(result[:success]).to be true
|
|
78
78
|
expect(result[:paths]).to be_a(Array)
|
data/spec/legion/extensions/agentic/defense/immune_response/cognitive_immune_response_spec.rb
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
RSpec.describe Legion::Extensions::Agentic::Defense::ImmuneResponse do
|
|
4
4
|
it 'has a version number' do
|
|
5
|
-
expect(Legion::Extensions::Agentic::Defense::ImmuneResponse::VERSION).to eq('0.1.
|
|
5
|
+
expect(Legion::Extensions::Agentic::Defense::ImmuneResponse::VERSION).to eq('0.1.1')
|
|
6
6
|
end
|
|
7
7
|
end
|
|
@@ -15,6 +15,19 @@ RSpec.describe Legion::Extensions::Agentic::Defense::ImmuneResponse::Helpers::Im
|
|
|
15
15
|
ag = engine.register_antigen(pattern: 'test', antigen_type: :data_poisoning)
|
|
16
16
|
expect(engine.most_threatening.map(&:id)).to include(ag.id)
|
|
17
17
|
end
|
|
18
|
+
|
|
19
|
+
it 'rejects invalid antigen_type' do
|
|
20
|
+
result = engine.register_antigen(pattern: 'test', antigen_type: :nonexistent_threat)
|
|
21
|
+
expect(result).to be_nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'accepts all ANTIGEN_TYPES' do
|
|
25
|
+
constants = Legion::Extensions::Agentic::Defense::ImmuneResponse::Helpers::Constants::ANTIGEN_TYPES
|
|
26
|
+
constants.each do |val|
|
|
27
|
+
result = engine.register_antigen(pattern: 'test', antigen_type: val)
|
|
28
|
+
expect(result).not_to be_nil, "Expected #{val.inspect} to be accepted"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
18
31
|
end
|
|
19
32
|
|
|
20
33
|
describe '#encounter' do
|
|
@@ -52,6 +65,19 @@ RSpec.describe Legion::Extensions::Agentic::Defense::ImmuneResponse::Helpers::Im
|
|
|
52
65
|
ab = engine.create_antibody(antigen_type: :prompt_injection, signature: 'test')
|
|
53
66
|
expect(ab).to be_a(Legion::Extensions::Agentic::Defense::ImmuneResponse::Helpers::Antibody)
|
|
54
67
|
end
|
|
68
|
+
|
|
69
|
+
it 'rejects invalid antigen_type' do
|
|
70
|
+
result = engine.create_antibody(antigen_type: :nonexistent_threat, signature: 'test')
|
|
71
|
+
expect(result).to be_nil
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'accepts all ANTIGEN_TYPES' do
|
|
75
|
+
constants = Legion::Extensions::Agentic::Defense::ImmuneResponse::Helpers::Constants::ANTIGEN_TYPES
|
|
76
|
+
constants.each do |val|
|
|
77
|
+
result = engine.create_antibody(antigen_type: val, signature: 'test')
|
|
78
|
+
expect(result).not_to be_nil, "Expected #{val.inspect} to be accepted"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
55
81
|
end
|
|
56
82
|
|
|
57
83
|
describe '#vaccinate' do
|
|
@@ -36,6 +36,19 @@ RSpec.describe Legion::Extensions::Agentic::Defense::Immunology::Helpers::Immune
|
|
|
36
36
|
engine.detect_threat(source: 's2', tactic: :strawman, content_hash: 'b')
|
|
37
37
|
expect(engine.to_h[:threat_count]).to eq(2)
|
|
38
38
|
end
|
|
39
|
+
|
|
40
|
+
it 'rejects invalid tactic' do
|
|
41
|
+
result = engine.detect_threat(source: 'user', tactic: :nonexistent_tactic, content_hash: 'h1')
|
|
42
|
+
expect(result).to be_nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'accepts all MANIPULATION_TACTICS' do
|
|
46
|
+
constants = Legion::Extensions::Agentic::Defense::Immunology::Helpers::Constants::MANIPULATION_TACTICS
|
|
47
|
+
constants.each do |val|
|
|
48
|
+
result = engine.detect_threat(source: 'user', tactic: val, content_hash: "hash_#{val}")
|
|
49
|
+
expect(result).not_to be_nil, "Expected #{val.inspect} to be accepted"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
39
52
|
end
|
|
40
53
|
|
|
41
54
|
describe '#quarantine_threat' do
|
|
@@ -114,6 +127,19 @@ RSpec.describe Legion::Extensions::Agentic::Defense::Immunology::Helpers::Immune
|
|
|
114
127
|
ab = engine.create_antibody(tactic: :gaslighting, pattern: 'test', strength: 0.8)
|
|
115
128
|
expect(ab.strength).to eq(0.8)
|
|
116
129
|
end
|
|
130
|
+
|
|
131
|
+
it 'rejects invalid tactic' do
|
|
132
|
+
result = engine.create_antibody(tactic: :nonexistent_tactic, pattern: 'test')
|
|
133
|
+
expect(result).to be_nil
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it 'accepts all MANIPULATION_TACTICS' do
|
|
137
|
+
constants = Legion::Extensions::Agentic::Defense::Immunology::Helpers::Constants::MANIPULATION_TACTICS
|
|
138
|
+
constants.each do |val|
|
|
139
|
+
result = engine.create_antibody(tactic: val, pattern: "pattern_#{val}")
|
|
140
|
+
expect(result).not_to be_nil, "Expected #{val.inspect} to be accepted"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
117
143
|
end
|
|
118
144
|
|
|
119
145
|
describe '#scan_for_tactic' do
|