lex-cognitive-debugging 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,290 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::CognitiveDebugging::Helpers::DebuggingEngine do
4
+ subject(:engine) { described_class.new }
5
+
6
+ let(:base_error_args) do
7
+ {
8
+ error_type: :inconsistency,
9
+ description: 'Claim A contradicts Claim B',
10
+ severity: 0.6,
11
+ source_phase: :prediction_engine,
12
+ confidence_at_detection: 0.8
13
+ }
14
+ end
15
+
16
+ def add_error(overrides = {})
17
+ engine.detect_error(**base_error_args, **overrides)
18
+ end
19
+
20
+ describe '#initialize' do
21
+ it 'starts with empty errors' do
22
+ expect(engine.errors).to be_empty
23
+ end
24
+
25
+ it 'starts with empty traces' do
26
+ expect(engine.traces).to be_empty
27
+ end
28
+
29
+ it 'starts with empty corrections' do
30
+ expect(engine.corrections).to be_empty
31
+ end
32
+ end
33
+
34
+ describe '#detect_error' do
35
+ it 'creates and stores a ReasoningError' do
36
+ err = add_error
37
+ expect(err).to be_a(Legion::Extensions::CognitiveDebugging::Helpers::ReasoningError)
38
+ expect(engine.errors[err.id]).to be(err)
39
+ end
40
+
41
+ it 'returns nil for invalid error_type' do
42
+ result = engine.detect_error(**base_error_args, error_type: :not_real)
43
+ expect(result).to be_nil
44
+ end
45
+
46
+ it 'assigns unique ids to multiple errors' do
47
+ e1 = add_error
48
+ e2 = add_error(error_type: :circular_logic)
49
+ expect(e1.id).not_to eq(e2.id)
50
+ end
51
+
52
+ it 'stores all valid error types without error' do
53
+ Legion::Extensions::CognitiveDebugging::Helpers::Constants::ERROR_TYPES.each do |type|
54
+ result = engine.detect_error(**base_error_args, error_type: type)
55
+ expect(result).not_to be_nil
56
+ end
57
+ end
58
+ end
59
+
60
+ describe '#trace_error' do
61
+ let(:err) { add_error }
62
+
63
+ it 'creates a CausalTrace and links it to the error' do
64
+ steps = [{ phase: :prediction_engine, description: 'Overconfident prior' }]
65
+ trace = engine.trace_error(error_id: err.id, steps: steps, root_cause: :bad_prior, confidence: 0.7)
66
+ expect(trace).to be_a(Legion::Extensions::CognitiveDebugging::Helpers::CausalTrace)
67
+ expect(err.trace_ids).to include(trace.id)
68
+ expect(err.status).to eq(:traced)
69
+ end
70
+
71
+ it 'returns nil for unknown error_id' do
72
+ result = engine.trace_error(error_id: 'nope', steps: [], root_cause: :unknown, confidence: 0.5)
73
+ expect(result).to be_nil
74
+ end
75
+
76
+ it 'stores the trace in the engine' do
77
+ steps = [{ phase: :tick, description: 'step 1' }]
78
+ trace = engine.trace_error(error_id: err.id, steps: steps, root_cause: :loop, confidence: 0.5)
79
+ expect(engine.traces[trace.id]).to be(trace)
80
+ end
81
+
82
+ it 'builds steps correctly' do
83
+ steps = [
84
+ { phase: :emotional_evaluation, description: 'First step' },
85
+ { phase: :memory_retrieval, description: 'Second step' }
86
+ ]
87
+ trace = engine.trace_error(error_id: err.id, steps: steps, root_cause: :bias, confidence: 0.8)
88
+ expect(trace.depth).to eq(2)
89
+ end
90
+ end
91
+
92
+ describe '#propose_correction' do
93
+ let(:err) { add_error }
94
+
95
+ it 'creates a Correction and links it to the error' do
96
+ correction = engine.propose_correction(error_id: err.id, strategy: :retrace,
97
+ description: 'Retrace reasoning path')
98
+ expect(correction).to be_a(Legion::Extensions::CognitiveDebugging::Helpers::Correction)
99
+ expect(err.correction_ids).to include(correction.id)
100
+ expect(err.status).to eq(:correcting)
101
+ end
102
+
103
+ it 'returns nil for invalid strategy' do
104
+ result = engine.propose_correction(error_id: err.id, strategy: :not_a_strategy, description: 'x')
105
+ expect(result).to be_nil
106
+ end
107
+
108
+ it 'returns nil for unknown error_id' do
109
+ result = engine.propose_correction(error_id: 'nope', strategy: :retrace, description: 'x')
110
+ expect(result).to be_nil
111
+ end
112
+
113
+ it 'stores correction in engine' do
114
+ correction = engine.propose_correction(error_id: err.id, strategy: :reframe, description: 'reframe it')
115
+ expect(engine.corrections[correction.id]).to be(correction)
116
+ end
117
+ end
118
+
119
+ describe '#apply_correction' do
120
+ it 'marks correction as applied' do
121
+ err = add_error
122
+ correction = engine.propose_correction(error_id: err.id, strategy: :retrace, description: 'x')
123
+ engine.apply_correction(correction_id: correction.id)
124
+ expect(correction.applied).to be true
125
+ end
126
+
127
+ it 'returns nil for unknown correction_id' do
128
+ expect(engine.apply_correction(correction_id: 'nope')).to be_nil
129
+ end
130
+ end
131
+
132
+ describe '#measure_correction' do
133
+ it 'sets effectiveness on the correction' do
134
+ err = add_error
135
+ correction = engine.propose_correction(error_id: err.id, strategy: :retrace, description: 'x')
136
+ engine.measure_correction(correction_id: correction.id, effectiveness: 0.85)
137
+ expect(correction.effectiveness).to eq(0.85)
138
+ end
139
+
140
+ it 'returns nil for unknown correction_id' do
141
+ expect(engine.measure_correction(correction_id: 'nope', effectiveness: 0.5)).to be_nil
142
+ end
143
+ end
144
+
145
+ describe '#resolve_error' do
146
+ it 'resolves the error' do
147
+ err = add_error
148
+ engine.resolve_error(error_id: err.id)
149
+ expect(err.status).to eq(:resolved)
150
+ end
151
+
152
+ it 'returns nil for unknown error_id' do
153
+ expect(engine.resolve_error(error_id: 'nope')).to be_nil
154
+ end
155
+ end
156
+
157
+ describe '#active_errors' do
158
+ it 'returns errors that are not resolved' do
159
+ e1 = add_error
160
+ e2 = add_error(error_type: :circular_logic)
161
+ engine.resolve_error(error_id: e1.id)
162
+ active = engine.active_errors
163
+ expect(active).to include(e2)
164
+ expect(active).not_to include(e1)
165
+ end
166
+ end
167
+
168
+ describe '#resolved_errors' do
169
+ it 'returns only resolved errors' do
170
+ e1 = add_error
171
+ add_error(error_type: :circular_logic)
172
+ engine.resolve_error(error_id: e1.id)
173
+ expect(engine.resolved_errors).to contain_exactly(e1)
174
+ end
175
+ end
176
+
177
+ describe '#errors_by_type' do
178
+ it 'returns a tally hash by error_type' do
179
+ add_error(error_type: :inconsistency)
180
+ add_error(error_type: :inconsistency)
181
+ add_error(error_type: :overconfidence)
182
+ tally = engine.errors_by_type
183
+ expect(tally[:inconsistency]).to eq(2)
184
+ expect(tally[:overconfidence]).to eq(1)
185
+ end
186
+ end
187
+
188
+ describe '#errors_by_phase' do
189
+ it 'returns a tally hash by source_phase' do
190
+ add_error(source_phase: :prediction_engine)
191
+ add_error(source_phase: :prediction_engine)
192
+ add_error(source_phase: :emotional_evaluation)
193
+ tally = engine.errors_by_phase
194
+ expect(tally[:prediction_engine]).to eq(2)
195
+ expect(tally[:emotional_evaluation]).to eq(1)
196
+ end
197
+ end
198
+
199
+ describe '#most_common_error_type' do
200
+ it 'returns nil when no errors' do
201
+ expect(engine.most_common_error_type).to be_nil
202
+ end
203
+
204
+ it 'returns the most frequent error type' do
205
+ add_error(error_type: :overconfidence)
206
+ add_error(error_type: :overconfidence)
207
+ add_error(error_type: :inconsistency)
208
+ expect(engine.most_common_error_type).to eq(:overconfidence)
209
+ end
210
+ end
211
+
212
+ describe '#most_effective_strategy' do
213
+ it 'returns nil when no measured corrections' do
214
+ expect(engine.most_effective_strategy).to be_nil
215
+ end
216
+
217
+ it 'returns the strategy with highest average effectiveness' do
218
+ err = add_error
219
+ c1 = engine.propose_correction(error_id: err.id, strategy: :retrace, description: 'x')
220
+ c2 = engine.propose_correction(error_id: err.id, strategy: :reframe, description: 'y')
221
+ engine.measure_correction(correction_id: c1.id, effectiveness: 0.4)
222
+ engine.measure_correction(correction_id: c2.id, effectiveness: 0.9)
223
+ expect(engine.most_effective_strategy).to eq(:reframe)
224
+ end
225
+ end
226
+
227
+ describe '#error_rate_by_phase' do
228
+ it 'is an alias for errors_by_phase' do
229
+ add_error(source_phase: :tick)
230
+ expect(engine.error_rate_by_phase).to eq(engine.errors_by_phase)
231
+ end
232
+ end
233
+
234
+ describe '#correction_success_rate' do
235
+ it 'returns 0.0 when no corrections applied' do
236
+ expect(engine.correction_success_rate).to eq(0.0)
237
+ end
238
+
239
+ it 'returns 0.0 when applied but none measured' do
240
+ err = add_error
241
+ c = engine.propose_correction(error_id: err.id, strategy: :retrace, description: 'x')
242
+ engine.apply_correction(correction_id: c.id)
243
+ expect(engine.correction_success_rate).to eq(0.0)
244
+ end
245
+
246
+ it 'calculates the ratio of effective corrections' do
247
+ err = add_error
248
+ c1 = engine.propose_correction(error_id: err.id, strategy: :retrace, description: 'a')
249
+ c2 = engine.propose_correction(error_id: err.id, strategy: :reframe, description: 'b')
250
+ engine.apply_correction(correction_id: c1.id)
251
+ engine.apply_correction(correction_id: c2.id)
252
+ engine.measure_correction(correction_id: c1.id, effectiveness: 0.8)
253
+ engine.measure_correction(correction_id: c2.id, effectiveness: 0.3)
254
+ expect(engine.correction_success_rate).to eq(0.5)
255
+ end
256
+ end
257
+
258
+ describe '#debugging_report' do
259
+ it 'returns a hash with expected keys' do
260
+ report = engine.debugging_report
261
+ expect(report).to include(
262
+ :total_errors, :active_errors, :resolved_errors,
263
+ :total_traces, :total_corrections,
264
+ :correction_success_rate, :most_common_error_type,
265
+ :most_effective_strategy, :errors_by_type, :error_rate_by_phase
266
+ )
267
+ end
268
+
269
+ it 'reflects current state' do
270
+ add_error
271
+ report = engine.debugging_report
272
+ expect(report[:total_errors]).to eq(1)
273
+ expect(report[:active_errors]).to eq(1)
274
+ end
275
+ end
276
+
277
+ describe '#to_h' do
278
+ it 'returns nested hashes for errors, traces, corrections' do
279
+ err = add_error
280
+ steps = [{ phase: :tick, description: 'step' }]
281
+ engine.trace_error(error_id: err.id, steps: steps, root_cause: :bias, confidence: 0.6)
282
+ engine.propose_correction(error_id: err.id, strategy: :retrace, description: 'x')
283
+ h = engine.to_h
284
+ expect(h[:errors]).to be_a(Hash)
285
+ expect(h[:traces]).to be_a(Hash)
286
+ expect(h[:corrections]).to be_a(Hash)
287
+ expect(h[:errors][err.id]).to include(:error_type)
288
+ end
289
+ end
290
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::CognitiveDebugging::Helpers::ReasoningError do
4
+ let(:error) do
5
+ described_class.new(
6
+ error_type: :inconsistency,
7
+ description: 'Contradicting earlier claim',
8
+ severity: 0.75,
9
+ source_phase: :prediction_engine,
10
+ confidence_at_detection: 0.9
11
+ )
12
+ end
13
+
14
+ describe '#initialize' do
15
+ it 'assigns a UUID id' do
16
+ expect(error.id).to match(/\A[0-9a-f-]{36}\z/)
17
+ end
18
+
19
+ it 'sets error_type' do
20
+ expect(error.error_type).to eq(:inconsistency)
21
+ end
22
+
23
+ it 'sets description' do
24
+ expect(error.description).to eq('Contradicting earlier claim')
25
+ end
26
+
27
+ it 'clamps severity to [0, 1]' do
28
+ e = described_class.new(error_type: :overconfidence, description: 'x',
29
+ severity: 1.5, source_phase: :tick, confidence_at_detection: 0.5)
30
+ expect(e.severity).to eq(1.0)
31
+ end
32
+
33
+ it 'clamps confidence_at_detection to [0, 1]' do
34
+ e = described_class.new(error_type: :overconfidence, description: 'x',
35
+ severity: 0.5, source_phase: :tick, confidence_at_detection: -0.1)
36
+ expect(e.confidence_at_detection).to eq(0.0)
37
+ end
38
+
39
+ it 'initializes status as :detected' do
40
+ expect(error.status).to eq(:detected)
41
+ end
42
+
43
+ it 'initializes empty trace_ids' do
44
+ expect(error.trace_ids).to be_empty
45
+ end
46
+
47
+ it 'initializes empty correction_ids' do
48
+ expect(error.correction_ids).to be_empty
49
+ end
50
+
51
+ it 'sets created_at' do
52
+ expect(error.created_at).to be_a(Time)
53
+ end
54
+
55
+ it 'initializes resolved_at as nil' do
56
+ expect(error.resolved_at).to be_nil
57
+ end
58
+ end
59
+
60
+ describe '#detect!' do
61
+ it 'sets status to :detected' do
62
+ error.trace!('trace-1')
63
+ error.detect!
64
+ expect(error.status).to eq(:detected)
65
+ end
66
+ end
67
+
68
+ describe '#trace!' do
69
+ it 'adds trace_id and sets status to :traced' do
70
+ error.trace!('some-trace-uuid')
71
+ expect(error.trace_ids).to include('some-trace-uuid')
72
+ expect(error.status).to eq(:traced)
73
+ end
74
+
75
+ it 'accumulates multiple trace_ids' do
76
+ error.trace!('trace-1')
77
+ error.trace!('trace-2')
78
+ expect(error.trace_ids.size).to eq(2)
79
+ end
80
+ end
81
+
82
+ describe '#correct!' do
83
+ it 'adds correction_id and sets status to :correcting' do
84
+ error.correct!('correction-uuid')
85
+ expect(error.correction_ids).to include('correction-uuid')
86
+ expect(error.status).to eq(:correcting)
87
+ end
88
+ end
89
+
90
+ describe '#resolve!' do
91
+ it 'sets status to :resolved and resolved_at' do
92
+ error.resolve!
93
+ expect(error.status).to eq(:resolved)
94
+ expect(error.resolved_at).to be_a(Time)
95
+ end
96
+ end
97
+
98
+ describe '#mark_unresolvable!' do
99
+ it 'sets status to :unresolvable and resolved_at' do
100
+ error.mark_unresolvable!
101
+ expect(error.status).to eq(:unresolvable)
102
+ expect(error.resolved_at).to be_a(Time)
103
+ end
104
+ end
105
+
106
+ describe '#severe?' do
107
+ it 'returns true when severity >= 0.7' do
108
+ expect(error.severe?).to be true
109
+ end
110
+
111
+ it 'returns false when severity < 0.7' do
112
+ e = described_class.new(error_type: :minor_type, description: 'x',
113
+ severity: 0.5, source_phase: :tick, confidence_at_detection: 0.5)
114
+ expect(e.severe?).to be false
115
+ end
116
+ end
117
+
118
+ describe '#resolved?' do
119
+ it 'returns false for a new error' do
120
+ expect(error.resolved?).to be false
121
+ end
122
+
123
+ it 'returns true after resolve!' do
124
+ error.resolve!
125
+ expect(error.resolved?).to be true
126
+ end
127
+ end
128
+
129
+ describe '#active?' do
130
+ it 'returns true for :detected status' do
131
+ expect(error.active?).to be true
132
+ end
133
+
134
+ it 'returns false after resolved' do
135
+ error.resolve!
136
+ expect(error.active?).to be false
137
+ end
138
+
139
+ it 'returns false after mark_unresolvable!' do
140
+ error.mark_unresolvable!
141
+ expect(error.active?).to be false
142
+ end
143
+ end
144
+
145
+ describe '#severity_label' do
146
+ it 'returns :major for severity 0.75' do
147
+ expect(error.severity_label).to eq(:major)
148
+ end
149
+ end
150
+
151
+ describe '#to_h' do
152
+ it 'returns a hash with expected keys' do
153
+ h = error.to_h
154
+ expect(h).to include(:id, :error_type, :description, :severity, :severity_label,
155
+ :source_phase, :confidence_at_detection, :status,
156
+ :trace_ids, :correction_ids, :created_at, :resolved_at)
157
+ end
158
+
159
+ it 'returns dup of trace_ids array' do
160
+ h = error.to_h
161
+ expect(h[:trace_ids]).not_to be(error.trace_ids)
162
+ end
163
+ end
164
+ end