lex-conflict 0.1.1
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/Gemfile +10 -0
- data/lex-conflict.gemspec +29 -0
- data/lib/legion/extensions/conflict/actors/stale_check.rb +41 -0
- data/lib/legion/extensions/conflict/client.rb +23 -0
- data/lib/legion/extensions/conflict/helpers/conflict_log.rb +66 -0
- data/lib/legion/extensions/conflict/helpers/llm_enhancer.rb +126 -0
- data/lib/legion/extensions/conflict/helpers/severity.rb +43 -0
- data/lib/legion/extensions/conflict/runners/conflict.rb +108 -0
- data/lib/legion/extensions/conflict/version.rb +9 -0
- data/lib/legion/extensions/conflict.rb +15 -0
- data/spec/legion/extensions/conflict/actors/stale_check_spec.rb +45 -0
- data/spec/legion/extensions/conflict/client_spec.rb +15 -0
- data/spec/legion/extensions/conflict/helpers/conflict_log_spec.rb +232 -0
- data/spec/legion/extensions/conflict/helpers/llm_enhancer_spec.rb +189 -0
- data/spec/legion/extensions/conflict/helpers/severity_spec.rb +215 -0
- data/spec/legion/extensions/conflict/runners/conflict_spec.rb +151 -0
- data/spec/spec_helper.rb +20 -0
- metadata +78 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/conflict/client'
|
|
4
|
+
|
|
5
|
+
RSpec.describe Legion::Extensions::Conflict::Runners::Conflict do
|
|
6
|
+
let(:client) { Legion::Extensions::Conflict::Client.new }
|
|
7
|
+
|
|
8
|
+
describe '#register_conflict' do
|
|
9
|
+
it 'creates a conflict with recommended posture' do
|
|
10
|
+
result = client.register_conflict(parties: %w[agent human], severity: :high, description: 'disagreement')
|
|
11
|
+
expect(result[:conflict_id]).to match(/\A[0-9a-f-]{36}\z/)
|
|
12
|
+
expect(result[:posture]).to eq(:persistent_engagement)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'recommends stubborn_presence for critical' do
|
|
16
|
+
result = client.register_conflict(parties: %w[agent human], severity: :critical, description: 'safety issue')
|
|
17
|
+
expect(result[:posture]).to eq(:stubborn_presence)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'rejects invalid severity' do
|
|
21
|
+
result = client.register_conflict(parties: [], severity: :invalid, description: 'test')
|
|
22
|
+
expect(result[:error]).to eq(:invalid_severity)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe '#add_exchange' do
|
|
27
|
+
it 'records an exchange' do
|
|
28
|
+
c = client.register_conflict(parties: %w[a b], severity: :medium, description: 'test')
|
|
29
|
+
result = client.add_exchange(conflict_id: c[:conflict_id], speaker: 'a', message: 'I disagree')
|
|
30
|
+
expect(result[:recorded]).to be true
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe '#resolve_conflict' do
|
|
35
|
+
it 'resolves a conflict' do
|
|
36
|
+
c = client.register_conflict(parties: %w[a b], severity: :low, description: 'test')
|
|
37
|
+
result = client.resolve_conflict(conflict_id: c[:conflict_id], outcome: :compromise)
|
|
38
|
+
expect(result[:resolved]).to be true
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe '#active_conflicts' do
|
|
43
|
+
it 'lists active conflicts' do
|
|
44
|
+
client.register_conflict(parties: %w[a b], severity: :low, description: 'test')
|
|
45
|
+
result = client.active_conflicts
|
|
46
|
+
expect(result[:count]).to eq(1)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe '#recommended_posture' do
|
|
51
|
+
it 'returns posture for severity' do
|
|
52
|
+
result = client.recommended_posture(severity: :critical)
|
|
53
|
+
expect(result[:posture]).to eq(:stubborn_presence)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe '#check_stale_conflicts' do
|
|
58
|
+
it 'returns zero stale when no conflicts exist' do
|
|
59
|
+
result = client.check_stale_conflicts
|
|
60
|
+
expect(result[:checked]).to eq(0)
|
|
61
|
+
expect(result[:stale_count]).to eq(0)
|
|
62
|
+
expect(result[:stale_ids]).to eq([])
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'returns zero stale for recently created conflicts' do
|
|
66
|
+
client.register_conflict(parties: %w[a b], severity: :low, description: 'fresh')
|
|
67
|
+
result = client.check_stale_conflicts
|
|
68
|
+
expect(result[:stale_count]).to eq(0)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'detects stale conflicts older than STALE_CONFLICT_TIMEOUT' do
|
|
72
|
+
c = client.register_conflict(parties: %w[a b], severity: :medium, description: 'old')
|
|
73
|
+
# Backdate the created_at timestamp
|
|
74
|
+
conflict = client.instance_variable_get(:@conflict_log).conflicts[c[:conflict_id]]
|
|
75
|
+
conflict[:created_at] = Time.now.utc - (Legion::Extensions::Conflict::Helpers::Severity::STALE_CONFLICT_TIMEOUT + 1)
|
|
76
|
+
result = client.check_stale_conflicts
|
|
77
|
+
expect(result[:stale_count]).to eq(1)
|
|
78
|
+
expect(result[:stale_ids]).to include(c[:conflict_id])
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'does not include resolved conflicts in stale check' do
|
|
82
|
+
c = client.register_conflict(parties: %w[a b], severity: :low, description: 'resolved')
|
|
83
|
+
conflict = client.instance_variable_get(:@conflict_log).conflicts[c[:conflict_id]]
|
|
84
|
+
conflict[:created_at] = Time.now.utc - (Legion::Extensions::Conflict::Helpers::Severity::STALE_CONFLICT_TIMEOUT + 1)
|
|
85
|
+
client.resolve_conflict(conflict_id: c[:conflict_id], outcome: :closed)
|
|
86
|
+
result = client.check_stale_conflicts
|
|
87
|
+
expect(result[:stale_count]).to eq(0)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
context 'with LLM available' do
|
|
91
|
+
let(:fake_chat) { double }
|
|
92
|
+
let(:fake_analysis_response) do
|
|
93
|
+
double(content: "RECOMMENDATION: escalate\nANALYSIS: The conflict has stalled and needs governance intervention.")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
before do
|
|
97
|
+
stub_const('Legion::LLM', double(respond_to?: true, started?: true))
|
|
98
|
+
allow(Legion::LLM).to receive(:chat).and_return(fake_chat)
|
|
99
|
+
allow(fake_chat).to receive(:with_instructions)
|
|
100
|
+
allow(fake_chat).to receive(:ask).and_return(fake_analysis_response)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'includes LLM analysis in the stale system exchange message' do
|
|
104
|
+
c = client.register_conflict(parties: %w[a b], severity: :high, description: 'ongoing issue')
|
|
105
|
+
conflict = client.instance_variable_get(:@conflict_log).conflicts[c[:conflict_id]]
|
|
106
|
+
conflict[:created_at] = Time.now.utc - (Legion::Extensions::Conflict::Helpers::Severity::STALE_CONFLICT_TIMEOUT + 1)
|
|
107
|
+
|
|
108
|
+
client.check_stale_conflicts
|
|
109
|
+
|
|
110
|
+
exchanges = client.instance_variable_get(:@conflict_log).conflicts[c[:conflict_id]][:exchanges]
|
|
111
|
+
expect(exchanges).not_to be_empty
|
|
112
|
+
system_msg = exchanges.find { |e| e[:speaker] == :system }
|
|
113
|
+
expect(system_msg).not_to be_nil
|
|
114
|
+
expect(system_msg[:message]).to include('governance intervention')
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
describe '#resolve_conflict with LLM' do
|
|
120
|
+
let(:fake_chat) { double }
|
|
121
|
+
let(:fake_resolution_response) do
|
|
122
|
+
double(content: <<~TEXT)
|
|
123
|
+
OUTCOME: resolved
|
|
124
|
+
NOTES: Both parties reached a compromise after reviewing the evidence. Next steps include documenting the outcome and monitoring for recurrence.
|
|
125
|
+
TEXT
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
before do
|
|
129
|
+
stub_const('Legion::LLM', double(respond_to?: true, started?: true))
|
|
130
|
+
allow(Legion::LLM).to receive(:chat).and_return(fake_chat)
|
|
131
|
+
allow(fake_chat).to receive(:with_instructions)
|
|
132
|
+
allow(fake_chat).to receive(:ask).and_return(fake_resolution_response)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it 'uses LLM-generated notes when no resolution_notes provided' do
|
|
136
|
+
c = client.register_conflict(parties: %w[a b], severity: :medium, description: 'test conflict')
|
|
137
|
+
client.resolve_conflict(conflict_id: c[:conflict_id], outcome: :compromise)
|
|
138
|
+
|
|
139
|
+
conflict = client.instance_variable_get(:@conflict_log).conflicts[c[:conflict_id]]
|
|
140
|
+
expect(conflict[:resolution_notes]).to include('compromise')
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it 'preserves caller-provided resolution_notes over LLM' do
|
|
144
|
+
c = client.register_conflict(parties: %w[a b], severity: :medium, description: 'test conflict')
|
|
145
|
+
client.resolve_conflict(conflict_id: c[:conflict_id], outcome: :compromise, resolution_notes: 'manual notes')
|
|
146
|
+
|
|
147
|
+
conflict = client.instance_variable_get(:@conflict_log).conflicts[c[:conflict_id]]
|
|
148
|
+
expect(conflict[:resolution_notes]).to eq('manual notes')
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Logging
|
|
7
|
+
def self.debug(_msg); end
|
|
8
|
+
def self.info(_msg); end
|
|
9
|
+
def self.warn(_msg); end
|
|
10
|
+
def self.error(_msg); end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
require 'legion/extensions/conflict'
|
|
15
|
+
|
|
16
|
+
RSpec.configure do |config|
|
|
17
|
+
config.example_status_persistence_file_path = '.rspec_status'
|
|
18
|
+
config.disable_monkey_patching!
|
|
19
|
+
config.expect_with(:rspec) { |c| c.syntax = :expect }
|
|
20
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-conflict
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Esity
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: legion-gaia
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
description: Conflict resolution with severity levels and postures for brain-modeled
|
|
27
|
+
agentic AI
|
|
28
|
+
email:
|
|
29
|
+
- matthewdiverson@gmail.com
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- Gemfile
|
|
35
|
+
- lex-conflict.gemspec
|
|
36
|
+
- lib/legion/extensions/conflict.rb
|
|
37
|
+
- lib/legion/extensions/conflict/actors/stale_check.rb
|
|
38
|
+
- lib/legion/extensions/conflict/client.rb
|
|
39
|
+
- lib/legion/extensions/conflict/helpers/conflict_log.rb
|
|
40
|
+
- lib/legion/extensions/conflict/helpers/llm_enhancer.rb
|
|
41
|
+
- lib/legion/extensions/conflict/helpers/severity.rb
|
|
42
|
+
- lib/legion/extensions/conflict/runners/conflict.rb
|
|
43
|
+
- lib/legion/extensions/conflict/version.rb
|
|
44
|
+
- spec/legion/extensions/conflict/actors/stale_check_spec.rb
|
|
45
|
+
- spec/legion/extensions/conflict/client_spec.rb
|
|
46
|
+
- spec/legion/extensions/conflict/helpers/conflict_log_spec.rb
|
|
47
|
+
- spec/legion/extensions/conflict/helpers/llm_enhancer_spec.rb
|
|
48
|
+
- spec/legion/extensions/conflict/helpers/severity_spec.rb
|
|
49
|
+
- spec/legion/extensions/conflict/runners/conflict_spec.rb
|
|
50
|
+
- spec/spec_helper.rb
|
|
51
|
+
homepage: https://github.com/LegionIO/lex-conflict
|
|
52
|
+
licenses:
|
|
53
|
+
- MIT
|
|
54
|
+
metadata:
|
|
55
|
+
homepage_uri: https://github.com/LegionIO/lex-conflict
|
|
56
|
+
source_code_uri: https://github.com/LegionIO/lex-conflict
|
|
57
|
+
documentation_uri: https://github.com/LegionIO/lex-conflict
|
|
58
|
+
changelog_uri: https://github.com/LegionIO/lex-conflict
|
|
59
|
+
bug_tracker_uri: https://github.com/LegionIO/lex-conflict/issues
|
|
60
|
+
rubygems_mfa_required: 'true'
|
|
61
|
+
rdoc_options: []
|
|
62
|
+
require_paths:
|
|
63
|
+
- lib
|
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '3.4'
|
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '0'
|
|
74
|
+
requirements: []
|
|
75
|
+
rubygems_version: 3.6.9
|
|
76
|
+
specification_version: 4
|
|
77
|
+
summary: LEX Conflict
|
|
78
|
+
test_files: []
|