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.
- checksums.yaml +7 -0
- data/Gemfile +10 -0
- data/lex-dissonance.gemspec +29 -0
- data/lib/legion/extensions/dissonance/client.rb +28 -0
- data/lib/legion/extensions/dissonance/helpers/belief.rb +42 -0
- data/lib/legion/extensions/dissonance/helpers/constants.rb +23 -0
- data/lib/legion/extensions/dissonance/helpers/dissonance_event.rb +48 -0
- data/lib/legion/extensions/dissonance/helpers/dissonance_model.rb +155 -0
- data/lib/legion/extensions/dissonance/runners/dissonance.rb +159 -0
- data/lib/legion/extensions/dissonance/version.rb +9 -0
- data/lib/legion/extensions/dissonance.rb +16 -0
- data/spec/legion/extensions/dissonance/client_spec.rb +51 -0
- data/spec/legion/extensions/dissonance/helpers/belief_spec.rb +103 -0
- data/spec/legion/extensions/dissonance/helpers/constants_spec.rb +60 -0
- data/spec/legion/extensions/dissonance/helpers/dissonance_event_spec.rb +113 -0
- data/spec/legion/extensions/dissonance/helpers/dissonance_model_spec.rb +252 -0
- data/spec/legion/extensions/dissonance/runners/dissonance_spec.rb +323 -0
- data/spec/spec_helper.rb +28 -0
- metadata +78 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Dissonance::Helpers::Constants do
|
|
4
|
+
it 'defines DISSONANCE_THRESHOLD' do
|
|
5
|
+
expect(described_class::DISSONANCE_THRESHOLD).to eq(0.4)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it 'defines MAX_BELIEFS' do
|
|
9
|
+
expect(described_class::MAX_BELIEFS).to eq(200)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'defines MAX_DISSONANCE_EVENTS' do
|
|
13
|
+
expect(described_class::MAX_DISSONANCE_EVENTS).to eq(100)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'defines DECAY_RATE' do
|
|
17
|
+
expect(described_class::DECAY_RATE).to eq(0.03)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'defines RESOLUTION_RELIEF' do
|
|
21
|
+
expect(described_class::RESOLUTION_RELIEF).to eq(0.3)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'defines RATIONALIZATION_FACTOR' do
|
|
25
|
+
expect(described_class::RATIONALIZATION_FACTOR).to eq(0.5)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'defines IMPORTANCE_WEIGHTS with four levels' do
|
|
29
|
+
weights = described_class::IMPORTANCE_WEIGHTS
|
|
30
|
+
expect(weights[:core]).to eq(1.0)
|
|
31
|
+
expect(weights[:significant]).to eq(0.7)
|
|
32
|
+
expect(weights[:moderate]).to eq(0.5)
|
|
33
|
+
expect(weights[:peripheral]).to eq(0.25)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'defines RESOLUTION_STRATEGIES' do
|
|
37
|
+
expect(described_class::RESOLUTION_STRATEGIES).to contain_exactly(:belief_revision, :rationalization, :avoidance)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'defines STRESS_CEILING and STRESS_FLOOR' do
|
|
41
|
+
expect(described_class::STRESS_CEILING).to eq(1.0)
|
|
42
|
+
expect(described_class::STRESS_FLOOR).to eq(0.0)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'defines CONTRADICTION_TYPES' do
|
|
46
|
+
expect(described_class::CONTRADICTION_TYPES).to contain_exactly(:direct, :inverse, :conditional, :temporal)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'freezes IMPORTANCE_WEIGHTS' do
|
|
50
|
+
expect(described_class::IMPORTANCE_WEIGHTS).to be_frozen
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'freezes RESOLUTION_STRATEGIES' do
|
|
54
|
+
expect(described_class::RESOLUTION_STRATEGIES).to be_frozen
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'freezes CONTRADICTION_TYPES' do
|
|
58
|
+
expect(described_class::CONTRADICTION_TYPES).to be_frozen
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Dissonance::Helpers::DissonanceEvent do
|
|
4
|
+
let(:event) do
|
|
5
|
+
described_class.new(
|
|
6
|
+
belief_a_id: 'uuid-a',
|
|
7
|
+
belief_b_id: 'uuid-b',
|
|
8
|
+
domain: 'ethics',
|
|
9
|
+
magnitude: 0.6,
|
|
10
|
+
contradiction_type: :direct
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe '#initialize' do
|
|
15
|
+
it 'assigns a uuid id' do
|
|
16
|
+
expect(event.id).to match(/\A[0-9a-f-]{36}\z/)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'assigns belief_a_id' do
|
|
20
|
+
expect(event.belief_a_id).to eq('uuid-a')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'assigns belief_b_id' do
|
|
24
|
+
expect(event.belief_b_id).to eq('uuid-b')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'assigns domain' do
|
|
28
|
+
expect(event.domain).to eq('ethics')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'assigns magnitude' do
|
|
32
|
+
expect(event.magnitude).to eq(0.6)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'assigns contradiction_type' do
|
|
36
|
+
expect(event.contradiction_type).to eq(:direct)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'starts as unresolved' do
|
|
40
|
+
expect(event.resolved).to be false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'starts with nil resolution_strategy' do
|
|
44
|
+
expect(event.resolution_strategy).to be_nil
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'assigns timestamp as UTC Time' do
|
|
48
|
+
expect(event.timestamp).to be_a(Time)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'clamps magnitude above 1.0' do
|
|
52
|
+
ev = described_class.new(belief_a_id: 'a', belief_b_id: 'b', domain: 'd', magnitude: 1.5)
|
|
53
|
+
expect(ev.magnitude).to eq(1.0)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'clamps magnitude below 0.0' do
|
|
57
|
+
ev = described_class.new(belief_a_id: 'a', belief_b_id: 'b', domain: 'd', magnitude: -0.5)
|
|
58
|
+
expect(ev.magnitude).to eq(0.0)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'defaults contradiction_type to :direct' do
|
|
62
|
+
ev = described_class.new(belief_a_id: 'a', belief_b_id: 'b', domain: 'd', magnitude: 0.5)
|
|
63
|
+
expect(ev.contradiction_type).to eq(:direct)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe '#resolve!' do
|
|
68
|
+
it 'marks the event as resolved' do
|
|
69
|
+
event.resolve!(:belief_revision)
|
|
70
|
+
expect(event.resolved).to be true
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'records the resolution strategy' do
|
|
74
|
+
event.resolve!(:rationalization)
|
|
75
|
+
expect(event.resolution_strategy).to eq(:rationalization)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'returns self for chaining' do
|
|
79
|
+
result = event.resolve!(:avoidance)
|
|
80
|
+
expect(result).to be(event)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'can be resolved with any valid strategy' do
|
|
84
|
+
Legion::Extensions::Dissonance::Helpers::Constants::RESOLUTION_STRATEGIES.each do |strategy|
|
|
85
|
+
ev = described_class.new(belief_a_id: 'a', belief_b_id: 'b', domain: 'd', magnitude: 0.5)
|
|
86
|
+
ev.resolve!(strategy)
|
|
87
|
+
expect(ev.resolution_strategy).to eq(strategy)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
describe '#to_h' do
|
|
93
|
+
it 'returns hash with all fields when unresolved' do
|
|
94
|
+
h = event.to_h
|
|
95
|
+
expect(h[:id]).to eq(event.id)
|
|
96
|
+
expect(h[:belief_a_id]).to eq('uuid-a')
|
|
97
|
+
expect(h[:belief_b_id]).to eq('uuid-b')
|
|
98
|
+
expect(h[:domain]).to eq('ethics')
|
|
99
|
+
expect(h[:magnitude]).to eq(0.6)
|
|
100
|
+
expect(h[:contradiction_type]).to eq(:direct)
|
|
101
|
+
expect(h[:resolved]).to be false
|
|
102
|
+
expect(h[:resolution_strategy]).to be_nil
|
|
103
|
+
expect(h[:timestamp]).to be_a(Time)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it 'reflects resolved state in to_h' do
|
|
107
|
+
event.resolve!(:belief_revision)
|
|
108
|
+
h = event.to_h
|
|
109
|
+
expect(h[:resolved]).to be true
|
|
110
|
+
expect(h[:resolution_strategy]).to eq(:belief_revision)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Dissonance::Helpers::DissonanceModel do
|
|
4
|
+
subject(:model) { described_class.new }
|
|
5
|
+
|
|
6
|
+
describe '#initialize' do
|
|
7
|
+
it 'starts with empty beliefs' do
|
|
8
|
+
expect(model.beliefs).to be_empty
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'starts with empty events' do
|
|
12
|
+
expect(model.events).to be_empty
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'starts with zero stress' do
|
|
16
|
+
expect(model.stress).to eq(0.0)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe '#add_belief' do
|
|
21
|
+
it 'adds a belief to the model' do
|
|
22
|
+
model.add_belief(domain: 'ethics', content: 'honesty matters', confidence: 0.8, importance: :core)
|
|
23
|
+
expect(model.beliefs.size).to eq(1)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'returns the new belief and empty events for first belief in domain' do
|
|
27
|
+
result = model.add_belief(domain: 'ethics', content: 'honesty matters')
|
|
28
|
+
expect(result[:belief]).to be_a(Legion::Extensions::Dissonance::Helpers::Belief)
|
|
29
|
+
expect(result[:new_dissonance_events]).to be_empty
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'detects contradiction between beliefs in the same domain' do
|
|
33
|
+
model.add_belief(domain: 'safety', content: 'always ask permission')
|
|
34
|
+
result = model.add_belief(domain: 'safety', content: 'act autonomously')
|
|
35
|
+
expect(result[:new_dissonance_events].size).to eq(1)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'does not detect contradiction for same content' do
|
|
39
|
+
model.add_belief(domain: 'safety', content: 'always ask permission')
|
|
40
|
+
result = model.add_belief(domain: 'safety', content: 'always ask permission')
|
|
41
|
+
expect(result[:new_dissonance_events]).to be_empty
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'does not create duplicate contradiction events for the same pair' do
|
|
45
|
+
model.add_belief(domain: 'safety', content: 'always ask permission')
|
|
46
|
+
result1 = model.add_belief(domain: 'safety', content: 'act autonomously')
|
|
47
|
+
ev_id = result1[:new_dissonance_events].first.id
|
|
48
|
+
# Artificially call detect_contradictions again — no new events since pair is tracked
|
|
49
|
+
new_evs = model.detect_contradictions
|
|
50
|
+
expect(new_evs).to be_empty
|
|
51
|
+
expect(model.events.size).to eq(1)
|
|
52
|
+
expect(model.events.key?(ev_id)).to be true
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'does not create contradiction across different domains' do
|
|
56
|
+
model.add_belief(domain: 'ethics', content: 'be transparent')
|
|
57
|
+
result = model.add_belief(domain: 'safety', content: 'act differently')
|
|
58
|
+
expect(result[:new_dissonance_events]).to be_empty
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'stores the belief with the correct importance weight' do
|
|
62
|
+
result = model.add_belief(domain: 'ethics', content: 'test', importance: :significant)
|
|
63
|
+
expect(result[:belief].importance).to eq(:significant)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe '#detect_contradictions' do
|
|
68
|
+
it 'returns empty array when no contradictions exist' do
|
|
69
|
+
model.add_belief(domain: 'ethics', content: 'honesty matters')
|
|
70
|
+
expect(model.detect_contradictions).to be_empty
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'finds untracked contradictions among existing beliefs' do
|
|
74
|
+
b1 = Legion::Extensions::Dissonance::Helpers::Belief.new(domain: 'ethics', content: 'be honest')
|
|
75
|
+
b2 = Legion::Extensions::Dissonance::Helpers::Belief.new(domain: 'ethics', content: 'deceive when needed')
|
|
76
|
+
model.beliefs[b1.id] = b1
|
|
77
|
+
model.beliefs[b2.id] = b2
|
|
78
|
+
new_events = model.detect_contradictions
|
|
79
|
+
expect(new_events.size).to eq(1)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'does not re-detect already tracked contradictions' do
|
|
83
|
+
model.add_belief(domain: 'ethics', content: 'tell truth')
|
|
84
|
+
model.add_belief(domain: 'ethics', content: 'lie sometimes')
|
|
85
|
+
model.detect_contradictions
|
|
86
|
+
second_run = model.detect_contradictions
|
|
87
|
+
expect(second_run).to be_empty
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe '#resolve' do
|
|
92
|
+
let(:event_id) do
|
|
93
|
+
model.add_belief(domain: 'ethics', content: 'be honest')
|
|
94
|
+
result = model.add_belief(domain: 'ethics', content: 'deceive when useful')
|
|
95
|
+
result[:new_dissonance_events].first.id
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'returns the resolved event' do
|
|
99
|
+
event = model.resolve(event_id, strategy: :belief_revision)
|
|
100
|
+
expect(event).to be_a(Legion::Extensions::Dissonance::Helpers::DissonanceEvent)
|
|
101
|
+
expect(event.resolved).to be true
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it 'reduces stress after belief_revision resolution' do
|
|
105
|
+
model.add_belief(domain: 'ethics', content: 'be honest')
|
|
106
|
+
result = model.add_belief(domain: 'ethics', content: 'deceive when useful')
|
|
107
|
+
ev_id = result[:new_dissonance_events].first.id
|
|
108
|
+
model.decay
|
|
109
|
+
stress_before = model.stress
|
|
110
|
+
model.resolve(ev_id, strategy: :belief_revision)
|
|
111
|
+
expect(model.stress).to be < stress_before
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it 'reduces stress less with rationalization than belief_revision' do
|
|
115
|
+
model2 = described_class.new
|
|
116
|
+
model.add_belief(domain: 'domain', content: 'claim a')
|
|
117
|
+
ev_id_m1 = model.add_belief(domain: 'domain', content: 'claim b')[:new_dissonance_events].first.id
|
|
118
|
+
model2.add_belief(domain: 'domain', content: 'claim a')
|
|
119
|
+
ev_id_m2 = model2.add_belief(domain: 'domain', content: 'claim b')[:new_dissonance_events].first.id
|
|
120
|
+
# Build up enough stress so that relief amounts differ meaningfully after clamping
|
|
121
|
+
15.times { model.decay }
|
|
122
|
+
15.times { model2.decay }
|
|
123
|
+
model.resolve(ev_id_m1, strategy: :belief_revision)
|
|
124
|
+
model2.resolve(ev_id_m2, strategy: :rationalization)
|
|
125
|
+
expect(model.stress).to be < model2.stress
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it 'returns nil for unknown event_id' do
|
|
129
|
+
expect(model.resolve('non-existent-id', strategy: :belief_revision)).to be_nil
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
it 'returns nil if already resolved' do
|
|
133
|
+
model.add_belief(domain: 'ethics', content: 'be honest')
|
|
134
|
+
result = model.add_belief(domain: 'ethics', content: 'deceive when useful')
|
|
135
|
+
ev_id = result[:new_dissonance_events].first.id
|
|
136
|
+
model.resolve(ev_id, strategy: :belief_revision)
|
|
137
|
+
expect(model.resolve(ev_id, strategy: :rationalization)).to be_nil
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it 'returns nil for invalid strategy' do
|
|
141
|
+
model.add_belief(domain: 'x', content: 'a')
|
|
142
|
+
result = model.add_belief(domain: 'x', content: 'b')
|
|
143
|
+
ev_id = result[:new_dissonance_events].first.id
|
|
144
|
+
expect(model.resolve(ev_id, strategy: :invalid_strategy)).to be_nil
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
describe '#stress_level' do
|
|
149
|
+
it 'returns 0.0 initially' do
|
|
150
|
+
expect(model.stress_level).to eq(0.0)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it 'increases after decay with unresolved events' do
|
|
154
|
+
model.add_belief(domain: 'x', content: 'a')
|
|
155
|
+
model.add_belief(domain: 'x', content: 'b')
|
|
156
|
+
model.decay
|
|
157
|
+
expect(model.stress_level).to be > 0.0
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
describe '#domain_stress' do
|
|
162
|
+
it 'returns 0.0 for a domain with no unresolved events' do
|
|
163
|
+
expect(model.domain_stress('ethics')).to eq(0.0)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it 'returns positive stress for domain with unresolved events' do
|
|
167
|
+
model.add_belief(domain: 'ethics', content: 'be honest')
|
|
168
|
+
model.add_belief(domain: 'ethics', content: 'hide truth')
|
|
169
|
+
expect(model.domain_stress('ethics')).to be > 0.0
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it 'returns 0.0 for a domain after all events resolved' do
|
|
173
|
+
model.add_belief(domain: 'ethics', content: 'be honest')
|
|
174
|
+
result = model.add_belief(domain: 'ethics', content: 'hide truth')
|
|
175
|
+
ev_id = result[:new_dissonance_events].first.id
|
|
176
|
+
model.resolve(ev_id, strategy: :belief_revision)
|
|
177
|
+
expect(model.domain_stress('ethics')).to eq(0.0)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
describe '#unresolved_events' do
|
|
182
|
+
it 'returns empty list when no events' do
|
|
183
|
+
expect(model.unresolved_events).to be_empty
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
it 'returns only unresolved events' do
|
|
187
|
+
model.add_belief(domain: 'x', content: 'a')
|
|
188
|
+
result = model.add_belief(domain: 'x', content: 'b')
|
|
189
|
+
ev_id = result[:new_dissonance_events].first.id
|
|
190
|
+
model.resolve(ev_id, strategy: :belief_revision)
|
|
191
|
+
|
|
192
|
+
model.add_belief(domain: 'y', content: 'p')
|
|
193
|
+
model.add_belief(domain: 'y', content: 'q')
|
|
194
|
+
|
|
195
|
+
unresolved = model.unresolved_events
|
|
196
|
+
expect(unresolved.all? { |ev| !ev.resolved }).to be true
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
describe '#decay' do
|
|
201
|
+
it 'increases stress when there are unresolved events' do
|
|
202
|
+
model.add_belief(domain: 'x', content: 'a')
|
|
203
|
+
model.add_belief(domain: 'x', content: 'b')
|
|
204
|
+
initial_stress = model.stress
|
|
205
|
+
model.decay
|
|
206
|
+
expect(model.stress).to be > initial_stress
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
it 'decreases stress when there are no unresolved events' do
|
|
210
|
+
model.instance_variable_set(:@stress, 0.5)
|
|
211
|
+
model.decay
|
|
212
|
+
expect(model.stress).to be < 0.5
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
it 'does not exceed STRESS_CEILING' do
|
|
216
|
+
model.instance_variable_set(:@stress, 0.99)
|
|
217
|
+
100.times { model.add_belief(domain: 'x', content: "belief_#{rand}") }
|
|
218
|
+
model.decay
|
|
219
|
+
expect(model.stress).to be <= 1.0
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
it 'does not go below STRESS_FLOOR' do
|
|
223
|
+
model.decay
|
|
224
|
+
expect(model.stress).to be >= 0.0
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
it 'returns the current stress value' do
|
|
228
|
+
result = model.decay
|
|
229
|
+
expect(result).to eq(model.stress)
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
describe '#to_h' do
|
|
234
|
+
it 'returns a snapshot hash' do
|
|
235
|
+
model.add_belief(domain: 'ethics', content: 'test')
|
|
236
|
+
h = model.to_h
|
|
237
|
+
expect(h[:beliefs]).to be_an(Array)
|
|
238
|
+
expect(h[:events]).to be_an(Array)
|
|
239
|
+
expect(h[:stress]).to eq(model.stress)
|
|
240
|
+
expect(h[:total_beliefs]).to eq(1)
|
|
241
|
+
expect(h[:total_events]).to eq(0)
|
|
242
|
+
expect(h[:unresolved_count]).to eq(0)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
it 'counts unresolved events correctly' do
|
|
246
|
+
model.add_belief(domain: 'x', content: 'a')
|
|
247
|
+
model.add_belief(domain: 'x', content: 'b')
|
|
248
|
+
h = model.to_h
|
|
249
|
+
expect(h[:unresolved_count]).to eq(1)
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|