lex-dissonance 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.
@@ -0,0 +1,323 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/dissonance/client'
4
+
5
+ RSpec.describe Legion::Extensions::Dissonance::Runners::Dissonance do
6
+ let(:client) { Legion::Extensions::Dissonance::Client.new }
7
+
8
+ describe '#add_belief' do
9
+ it 'returns success true with a belief_id' do
10
+ result = client.add_belief(domain: 'ethics', content: 'honesty is required')
11
+ expect(result[:success]).to be true
12
+ expect(result[:belief_id]).to match(/\A[0-9a-f-]{36}\z/)
13
+ end
14
+
15
+ it 'returns the domain' do
16
+ result = client.add_belief(domain: 'ethics', content: 'test')
17
+ expect(result[:domain]).to eq('ethics')
18
+ end
19
+
20
+ it 'returns empty new_dissonance_events for first belief in domain' do
21
+ result = client.add_belief(domain: 'ethics', content: 'honesty is required')
22
+ expect(result[:new_dissonance_events]).to be_empty
23
+ end
24
+
25
+ it 'returns dissonance events when contradiction detected' do
26
+ client.add_belief(domain: 'safety', content: 'always ask permission')
27
+ result = client.add_belief(domain: 'safety', content: 'act without asking')
28
+ expect(result[:new_dissonance_events].size).to eq(1)
29
+ end
30
+
31
+ it 'sets dissonance_triggered when magnitude >= threshold' do
32
+ client.add_belief(domain: 'ethics', content: 'tell the truth', confidence: 1.0, importance: :core)
33
+ result = client.add_belief(domain: 'ethics', content: 'lie when convenient', confidence: 1.0, importance: :core)
34
+ expect(result[:dissonance_triggered]).to be true
35
+ end
36
+
37
+ it 'returns success false for invalid importance' do
38
+ result = client.add_belief(domain: 'ethics', content: 'test', importance: :invalid)
39
+ expect(result[:success]).to be false
40
+ expect(result[:error]).to eq(:invalid_importance)
41
+ end
42
+
43
+ it 'returns valid importance levels on error' do
44
+ result = client.add_belief(domain: 'ethics', content: 'test', importance: :invalid)
45
+ expect(result[:valid]).to contain_exactly(:core, :significant, :moderate, :peripheral)
46
+ end
47
+
48
+ it 'accepts all valid importance levels' do
49
+ %i[core significant moderate peripheral].each do |imp|
50
+ result = client.add_belief(domain: 'test', content: "content_#{imp}", importance: imp)
51
+ expect(result[:success]).to be true
52
+ end
53
+ end
54
+ end
55
+
56
+ describe '#update_dissonance' do
57
+ it 'returns success true' do
58
+ result = client.update_dissonance
59
+ expect(result[:success]).to be true
60
+ end
61
+
62
+ it 'returns current stress' do
63
+ result = client.update_dissonance
64
+ expect(result[:stress]).to be_a(Float)
65
+ expect(result[:stress]).to be_between(0.0, 1.0)
66
+ end
67
+
68
+ it 'returns unresolved_count' do
69
+ result = client.update_dissonance
70
+ expect(result[:unresolved_count]).to eq(0)
71
+ end
72
+
73
+ it 'above_threshold is false when no unresolved events' do
74
+ result = client.update_dissonance
75
+ expect(result[:above_threshold]).to be false
76
+ end
77
+
78
+ it 'above_threshold is true when high-magnitude unresolved events exist' do
79
+ client.add_belief(domain: 'x', content: 'a', confidence: 1.0, importance: :core)
80
+ client.add_belief(domain: 'x', content: 'b', confidence: 1.0, importance: :core)
81
+ result = client.update_dissonance
82
+ expect(result[:above_threshold]).to be true
83
+ end
84
+
85
+ it 'increases unresolved_count after adding contradictions' do
86
+ client.add_belief(domain: 'y', content: 'claim1')
87
+ client.add_belief(domain: 'y', content: 'claim2')
88
+ result = client.update_dissonance
89
+ expect(result[:unresolved_count]).to eq(1)
90
+ end
91
+ end
92
+
93
+ describe '#resolve_dissonance' do
94
+ let(:event_id) do
95
+ client.add_belief(domain: 'ethics', content: 'be honest')
96
+ result = client.add_belief(domain: 'ethics', content: 'deceive when convenient')
97
+ result[:new_dissonance_events].first[:id]
98
+ end
99
+
100
+ it 'returns success true on valid resolution' do
101
+ result = client.resolve_dissonance(event_id: event_id, strategy: :belief_revision)
102
+ expect(result[:success]).to be true
103
+ expect(result[:resolved]).to be true
104
+ end
105
+
106
+ it 'returns the strategy used' do
107
+ result = client.resolve_dissonance(event_id: event_id, strategy: :rationalization)
108
+ expect(result[:strategy]).to eq(:rationalization)
109
+ end
110
+
111
+ it 'returns the event hash' do
112
+ result = client.resolve_dissonance(event_id: event_id, strategy: :belief_revision)
113
+ expect(result[:event]).to be_a(Hash)
114
+ expect(result[:event][:resolved]).to be true
115
+ end
116
+
117
+ it 'returns success false for invalid strategy' do
118
+ result = client.resolve_dissonance(event_id: event_id, strategy: :magic)
119
+ expect(result[:success]).to be false
120
+ expect(result[:error]).to eq(:invalid_strategy)
121
+ end
122
+
123
+ it 'returns valid strategies on invalid strategy error' do
124
+ result = client.resolve_dissonance(event_id: event_id, strategy: :magic)
125
+ expect(result[:valid]).to eq(Legion::Extensions::Dissonance::Helpers::Constants::RESOLUTION_STRATEGIES)
126
+ end
127
+
128
+ it 'returns success false for unknown event_id' do
129
+ result = client.resolve_dissonance(event_id: 'no-such-id', strategy: :belief_revision)
130
+ expect(result[:success]).to be false
131
+ expect(result[:error]).to eq(:not_found_or_already_resolved)
132
+ end
133
+
134
+ it 'returns success false when event already resolved' do
135
+ client.resolve_dissonance(event_id: event_id, strategy: :belief_revision)
136
+ result = client.resolve_dissonance(event_id: event_id, strategy: :rationalization)
137
+ expect(result[:success]).to be false
138
+ end
139
+
140
+ it 'defaults strategy to belief_revision' do
141
+ result = client.resolve_dissonance(event_id: event_id)
142
+ expect(result[:strategy]).to eq(:belief_revision)
143
+ end
144
+
145
+ it 'works with all three resolution strategies' do
146
+ %i[belief_revision rationalization avoidance].each_with_index do |strategy, i|
147
+ c = Legion::Extensions::Dissonance::Client.new
148
+ c.add_belief(domain: 'domain', content: "a#{i}")
149
+ inner_result = c.add_belief(domain: 'domain', content: "b#{i}")
150
+ ev_id = inner_result[:new_dissonance_events].first[:id]
151
+ result = c.resolve_dissonance(event_id: ev_id, strategy: strategy)
152
+ expect(result[:success]).to be true
153
+ end
154
+ end
155
+ end
156
+
157
+ describe '#dissonance_status' do
158
+ it 'returns success true' do
159
+ result = client.dissonance_status
160
+ expect(result[:success]).to be true
161
+ end
162
+
163
+ it 'returns stress value' do
164
+ result = client.dissonance_status
165
+ expect(result[:stress]).to be_a(Float)
166
+ end
167
+
168
+ it 'returns total_beliefs count' do
169
+ client.add_belief(domain: 'x', content: 'test')
170
+ result = client.dissonance_status
171
+ expect(result[:total_beliefs]).to eq(1)
172
+ end
173
+
174
+ it 'returns total_events count' do
175
+ client.add_belief(domain: 'x', content: 'a')
176
+ client.add_belief(domain: 'x', content: 'b')
177
+ result = client.dissonance_status
178
+ expect(result[:total_events]).to eq(1)
179
+ end
180
+
181
+ it 'returns unresolved_count' do
182
+ result = client.dissonance_status
183
+ expect(result[:unresolved_count]).to eq(0)
184
+ end
185
+ end
186
+
187
+ describe '#domain_dissonance' do
188
+ it 'returns success true' do
189
+ result = client.domain_dissonance(domain: 'ethics')
190
+ expect(result[:success]).to be true
191
+ end
192
+
193
+ it 'returns the domain' do
194
+ result = client.domain_dissonance(domain: 'ethics')
195
+ expect(result[:domain]).to eq('ethics')
196
+ end
197
+
198
+ it 'returns 0.0 stress for domain with no beliefs' do
199
+ result = client.domain_dissonance(domain: 'ethics')
200
+ expect(result[:stress]).to eq(0.0)
201
+ end
202
+
203
+ it 'returns positive stress for domain with contradictions' do
204
+ client.add_belief(domain: 'ethics', content: 'be honest')
205
+ client.add_belief(domain: 'ethics', content: 'hide truth')
206
+ result = client.domain_dissonance(domain: 'ethics')
207
+ expect(result[:stress]).to be > 0.0
208
+ end
209
+
210
+ it 'returns unresolved events for the domain' do
211
+ client.add_belief(domain: 'ethics', content: 'be honest')
212
+ client.add_belief(domain: 'ethics', content: 'hide truth')
213
+ result = client.domain_dissonance(domain: 'ethics')
214
+ expect(result[:events].size).to eq(1)
215
+ expect(result[:events].first[:domain]).to eq('ethics')
216
+ end
217
+
218
+ it 'does not include events from other domains' do
219
+ client.add_belief(domain: 'ethics', content: 'be honest')
220
+ client.add_belief(domain: 'ethics', content: 'hide truth')
221
+ client.add_belief(domain: 'safety', content: 'claim x')
222
+ client.add_belief(domain: 'safety', content: 'claim y')
223
+ result = client.domain_dissonance(domain: 'ethics')
224
+ expect(result[:events].all? { |ev| ev[:domain] == 'ethics' }).to be true
225
+ end
226
+ end
227
+
228
+ describe '#beliefs_for' do
229
+ it 'returns success true' do
230
+ result = client.beliefs_for(domain: 'ethics')
231
+ expect(result[:success]).to be true
232
+ end
233
+
234
+ it 'returns empty beliefs for unknown domain' do
235
+ result = client.beliefs_for(domain: 'unknown')
236
+ expect(result[:beliefs]).to be_empty
237
+ expect(result[:count]).to eq(0)
238
+ end
239
+
240
+ it 'returns beliefs for a domain' do
241
+ client.add_belief(domain: 'ethics', content: 'test belief')
242
+ result = client.beliefs_for(domain: 'ethics')
243
+ expect(result[:count]).to eq(1)
244
+ expect(result[:beliefs].first[:content]).to eq('test belief')
245
+ end
246
+
247
+ it 'does not include beliefs from other domains' do
248
+ client.add_belief(domain: 'ethics', content: 'ethics belief')
249
+ client.add_belief(domain: 'safety', content: 'safety belief')
250
+ result = client.beliefs_for(domain: 'ethics')
251
+ expect(result[:count]).to eq(1)
252
+ end
253
+
254
+ it 'returns the domain in the result' do
255
+ result = client.beliefs_for(domain: 'ethics')
256
+ expect(result[:domain]).to eq('ethics')
257
+ end
258
+ end
259
+
260
+ describe '#unresolved' do
261
+ it 'returns success true' do
262
+ result = client.unresolved
263
+ expect(result[:success]).to be true
264
+ end
265
+
266
+ it 'returns empty events initially' do
267
+ result = client.unresolved
268
+ expect(result[:events]).to be_empty
269
+ expect(result[:count]).to eq(0)
270
+ end
271
+
272
+ it 'returns unresolved events after contradiction' do
273
+ client.add_belief(domain: 'x', content: 'claim a')
274
+ client.add_belief(domain: 'x', content: 'claim b')
275
+ result = client.unresolved
276
+ expect(result[:count]).to eq(1)
277
+ end
278
+
279
+ it 'does not include resolved events' do
280
+ client.add_belief(domain: 'x', content: 'claim a')
281
+ inner_result = client.add_belief(domain: 'x', content: 'claim b')
282
+ ev_id = inner_result[:new_dissonance_events].first[:id]
283
+ client.resolve_dissonance(event_id: ev_id, strategy: :belief_revision)
284
+ result = client.unresolved
285
+ expect(result[:count]).to eq(0)
286
+ end
287
+ end
288
+
289
+ describe '#dissonance_stats' do
290
+ it 'returns success true' do
291
+ result = client.dissonance_stats
292
+ expect(result[:success]).to be true
293
+ end
294
+
295
+ it 'returns comprehensive stats hash' do
296
+ result = client.dissonance_stats
297
+ expect(result).to include(:stress, :total_beliefs, :total_events, :unresolved_count,
298
+ :resolved_count, :domain_stresses, :resolution_breakdown, :above_threshold)
299
+ end
300
+
301
+ it 'resolution_breakdown contains all three strategies' do
302
+ result = client.dissonance_stats
303
+ expect(result[:resolution_breakdown].keys).to contain_exactly(:belief_revision, :rationalization, :avoidance)
304
+ end
305
+
306
+ it 'counts resolved events by strategy' do
307
+ client.add_belief(domain: 'x', content: 'a')
308
+ inner_result = client.add_belief(domain: 'x', content: 'b')
309
+ ev_id = inner_result[:new_dissonance_events].first[:id]
310
+ client.resolve_dissonance(event_id: ev_id, strategy: :rationalization)
311
+ result = client.dissonance_stats
312
+ expect(result[:resolution_breakdown][:rationalization]).to eq(1)
313
+ expect(result[:resolved_count]).to eq(1)
314
+ end
315
+
316
+ it 'domain_stresses includes all domains with beliefs' do
317
+ client.add_belief(domain: 'domain_a', content: 'alpha')
318
+ client.add_belief(domain: 'domain_b', content: 'beta')
319
+ result = client.dissonance_stats
320
+ expect(result[:domain_stresses].keys).to include('domain_a', 'domain_b')
321
+ end
322
+ end
323
+ end
@@ -0,0 +1,28 @@
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
+
13
+ module Extensions
14
+ module Helpers
15
+ module Lex; end
16
+ end
17
+
18
+ module Core; end
19
+ end
20
+ end
21
+
22
+ require 'legion/extensions/dissonance'
23
+
24
+ RSpec.configure do |config|
25
+ config.example_status_persistence_file_path = '.rspec_status'
26
+ config.disable_monkey_patching!
27
+ config.expect_with(:rspec) { |c| c.syntax = :expect }
28
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lex-dissonance
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
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: Cognitive dissonance modeling — contradiction detection, belief tracking,
27
+ and resolution strategies for brain-modeled agentic AI
28
+ email:
29
+ - matthewdiverson@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - Gemfile
35
+ - lex-dissonance.gemspec
36
+ - lib/legion/extensions/dissonance.rb
37
+ - lib/legion/extensions/dissonance/client.rb
38
+ - lib/legion/extensions/dissonance/helpers/belief.rb
39
+ - lib/legion/extensions/dissonance/helpers/constants.rb
40
+ - lib/legion/extensions/dissonance/helpers/dissonance_event.rb
41
+ - lib/legion/extensions/dissonance/helpers/dissonance_model.rb
42
+ - lib/legion/extensions/dissonance/runners/dissonance.rb
43
+ - lib/legion/extensions/dissonance/version.rb
44
+ - spec/legion/extensions/dissonance/client_spec.rb
45
+ - spec/legion/extensions/dissonance/helpers/belief_spec.rb
46
+ - spec/legion/extensions/dissonance/helpers/constants_spec.rb
47
+ - spec/legion/extensions/dissonance/helpers/dissonance_event_spec.rb
48
+ - spec/legion/extensions/dissonance/helpers/dissonance_model_spec.rb
49
+ - spec/legion/extensions/dissonance/runners/dissonance_spec.rb
50
+ - spec/spec_helper.rb
51
+ homepage: https://github.com/LegionIO/lex-dissonance
52
+ licenses:
53
+ - MIT
54
+ metadata:
55
+ homepage_uri: https://github.com/LegionIO/lex-dissonance
56
+ source_code_uri: https://github.com/LegionIO/lex-dissonance
57
+ documentation_uri: https://github.com/LegionIO/lex-dissonance
58
+ changelog_uri: https://github.com/LegionIO/lex-dissonance
59
+ bug_tracker_uri: https://github.com/LegionIO/lex-dissonance/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 Dissonance
78
+ test_files: []