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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2dea7a09ee184044d9ec2bd1ab8b225072b166960290e2e97ca97e2740f75875
4
+ data.tar.gz: 4a00f3fa6f9cbf962ac790d5a75c5f99cc46eb9bc43154d40d249521f30a4065
5
+ SHA512:
6
+ metadata.gz: ec15e829ba922b803c848f85b0200feccc3526fef59afe1432de044e95a4f65761b76ae9e72950c254f3070962c17afbfde032c1dfa508c65ed9944cadfdf0b4
7
+ data.tar.gz: f1e966d6acad1e9777cfeaf4e9c9ffccfdce045768f49bf99dd808f6ea1cc6f80c2db998c7c5bbf559581c01a14499d9a603bf179ac4f910364b291155650344
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
6
+
7
+ gem 'rspec', '~> 3.13'
8
+ gem 'rubocop', '~> 1.75', require: false
9
+ gem 'rubocop-rspec'
10
+
11
+ gem 'legion-gaia', path: '../../legion-gaia'
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Esity
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # lex-cognitive-debugging
2
+
3
+ Self-debugging system for cognitive processes in LegionIO. Detects reasoning errors, traces their causal chain through cognitive phases, proposes corrective strategies, and tracks resolution effectiveness.
4
+
5
+ ## What It Does
6
+
7
+ Cognitive debugging models the metacognitive capacity to catch and fix reasoning failures before they propagate into actions. An error is detected with a type and severity, then traced through a causal investigation (steps + root cause). One or more corrections are proposed using defined strategies, applied, and measured for effectiveness. The system tracks per-type error frequencies, per-phase detection rates, and per-strategy effectiveness averages — enabling ongoing quality assessment of the agent's own reasoning.
8
+
9
+ Eight error types cover the common failure modes: inconsistency, circular logic, ungrounded claims, overconfidence, logical fallacies, missing evidence, false analogy, and confirmation bias. Seven correction strategies map to standard epistemic repair techniques: retrace, reframe, weaken_confidence, seek_evidence, decompose, analogize, and devil_advocate.
10
+
11
+ ## Usage
12
+
13
+ ```ruby
14
+ client = Legion::Extensions::CognitiveDebugging::Client.new
15
+
16
+ # Detect a reasoning error
17
+ result = client.detect_error(
18
+ error_type: :overconfidence,
19
+ description: 'Predicted outcome with 0.95 confidence after only two data points',
20
+ severity: 0.7,
21
+ source_phase: :prediction_engine,
22
+ confidence_at_detection: 0.8
23
+ )
24
+ error_id = result[:error_id]
25
+
26
+ # Trace the causal chain
27
+ client.trace_error(
28
+ error_id: error_id,
29
+ steps: ['checked prediction log', 'found insufficient evidence base'],
30
+ root_cause: 'recency bias from last two successful predictions',
31
+ confidence: 0.75
32
+ )
33
+
34
+ # Propose and apply a correction
35
+ correction = client.propose_correction(
36
+ error_id: error_id,
37
+ strategy: :weaken_confidence,
38
+ description: 'Reduce confidence floor until five+ data points available'
39
+ )
40
+ correction_id = correction[:correction_id]
41
+
42
+ client.apply_correction(correction_id: correction_id)
43
+ client.measure_correction(correction_id: correction_id, effectiveness: 0.8)
44
+ client.resolve_error(error_id: error_id)
45
+
46
+ # Analysis
47
+ client.active_errors
48
+ client.errors_by_type
49
+ client.most_common_error_type
50
+ client.most_effective_strategy
51
+ client.correction_success_rate
52
+ client.debugging_report
53
+ ```
54
+
55
+ ## Development
56
+
57
+ ```bash
58
+ bundle install
59
+ bundle exec rspec
60
+ bundle exec rubocop
61
+ ```
62
+
63
+ ## License
64
+
65
+ MIT
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/legion/extensions/cognitive_debugging/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'lex-cognitive-debugging'
7
+ spec.version = Legion::Extensions::CognitiveDebugging::VERSION
8
+ spec.authors = ['Esity']
9
+ spec.email = ['matthewdiverson@gmail.com']
10
+
11
+ spec.summary = 'LEX Cognitive Debugging'
12
+ spec.description = 'Self-debugging system for cognitive processes in LegionIO — detects reasoning errors, ' \
13
+ 'traces causal chains, and applies corrective strategies'
14
+ spec.homepage = 'https://github.com/LegionIO/lex-cognitive-debugging'
15
+ spec.license = 'MIT'
16
+ spec.required_ruby_version = '>= 3.4'
17
+
18
+ spec.metadata['homepage_uri'] = spec.homepage
19
+ spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-cognitive-debugging'
20
+ spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-cognitive-debugging'
21
+ spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-cognitive-debugging'
22
+ spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-cognitive-debugging/issues'
23
+ spec.metadata['rubygems_mfa_required'] = 'true'
24
+
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ Dir.glob('{lib,spec}/**/*') + %w[lex-cognitive-debugging.gemspec Gemfile LICENSE README.md]
27
+ end
28
+ spec.require_paths = ['lib']
29
+ spec.add_development_dependency 'legion-gaia'
30
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/cognitive_debugging/helpers/constants'
4
+ require 'legion/extensions/cognitive_debugging/helpers/reasoning_error'
5
+ require 'legion/extensions/cognitive_debugging/helpers/causal_trace'
6
+ require 'legion/extensions/cognitive_debugging/helpers/correction'
7
+ require 'legion/extensions/cognitive_debugging/helpers/debugging_engine'
8
+ require 'legion/extensions/cognitive_debugging/runners/cognitive_debugging'
9
+
10
+ module Legion
11
+ module Extensions
12
+ module CognitiveDebugging
13
+ class Client
14
+ include Runners::CognitiveDebugging
15
+
16
+ def initialize(engine: nil, **)
17
+ @engine = engine || Helpers::DebuggingEngine.new
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :engine
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module CognitiveDebugging
8
+ module Helpers
9
+ class CausalTrace
10
+ attr_reader :id, :error_id, :steps, :root_cause, :confidence
11
+
12
+ def initialize(error_id:, root_cause:, confidence:)
13
+ @id = SecureRandom.uuid
14
+ @error_id = error_id
15
+ @root_cause = root_cause
16
+ @confidence = confidence.clamp(0.0, 1.0).round(10)
17
+ @steps = []
18
+ end
19
+
20
+ def add_step!(phase:, description:)
21
+ @steps << {
22
+ phase: phase,
23
+ description: description,
24
+ timestamp: Time.now.utc
25
+ }
26
+ self
27
+ end
28
+
29
+ def depth
30
+ @steps.size
31
+ end
32
+
33
+ def to_h
34
+ {
35
+ id: @id,
36
+ error_id: @error_id,
37
+ steps: @steps.map(&:dup),
38
+ root_cause: @root_cause,
39
+ confidence: @confidence,
40
+ depth: depth
41
+ }
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveDebugging
6
+ module Helpers
7
+ module Constants
8
+ MAX_ERRORS = 300
9
+ MAX_TRACES = 500
10
+ MAX_CORRECTIONS = 200
11
+
12
+ ERROR_TYPES = %i[
13
+ inconsistency
14
+ circular_logic
15
+ ungrounded_claim
16
+ overconfidence
17
+ logical_fallacy
18
+ missing_evidence
19
+ false_analogy
20
+ confirmation_bias
21
+ ].freeze
22
+
23
+ CORRECTION_STRATEGIES = %i[
24
+ retrace
25
+ reframe
26
+ weaken_confidence
27
+ seek_evidence
28
+ decompose
29
+ analogize
30
+ devil_advocate
31
+ ].freeze
32
+
33
+ # Range-based severity labels: 0.0..1.0 -> label
34
+ SEVERITY_LABELS = [
35
+ { range: (0.0...0.2), label: :trivial },
36
+ { range: (0.2...0.4), label: :minor },
37
+ { range: (0.4...0.6), label: :moderate },
38
+ { range: (0.6...0.8), label: :major },
39
+ { range: (0.8..1.0), label: :critical }
40
+ ].freeze
41
+
42
+ STATUS_LABELS = %i[detected traced correcting resolved unresolvable].freeze
43
+
44
+ module_function
45
+
46
+ def severity_label(severity)
47
+ entry = SEVERITY_LABELS.find { |e| e[:range].cover?(severity) }
48
+ entry ? entry[:label] : :critical
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module CognitiveDebugging
8
+ module Helpers
9
+ class Correction
10
+ attr_reader :id, :error_id, :strategy, :description, :applied, :effectiveness
11
+
12
+ def initialize(error_id:, strategy:, description:)
13
+ @id = SecureRandom.uuid
14
+ @error_id = error_id
15
+ @strategy = strategy
16
+ @description = description
17
+ @applied = false
18
+ @effectiveness = nil
19
+ end
20
+
21
+ def apply!
22
+ @applied = true
23
+ self
24
+ end
25
+
26
+ def measure_effectiveness!(score)
27
+ @effectiveness = score.clamp(0.0, 1.0).round(10)
28
+ self
29
+ end
30
+
31
+ def effective?
32
+ return false if @effectiveness.nil?
33
+
34
+ @effectiveness >= 0.6
35
+ end
36
+
37
+ def to_h
38
+ {
39
+ id: @id,
40
+ error_id: @error_id,
41
+ strategy: @strategy,
42
+ description: @description,
43
+ applied: @applied,
44
+ effectiveness: @effectiveness,
45
+ effective: effective?
46
+ }
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveDebugging
6
+ module Helpers
7
+ class DebuggingEngine
8
+ attr_reader :errors, :traces, :corrections
9
+
10
+ def initialize
11
+ @errors = {}
12
+ @traces = {}
13
+ @corrections = {}
14
+ end
15
+
16
+ def detect_error(error_type:, description:, severity:, source_phase:, confidence_at_detection:)
17
+ return nil unless Constants::ERROR_TYPES.include?(error_type)
18
+ return nil if @errors.size >= Constants::MAX_ERRORS
19
+
20
+ err = ReasoningError.new(
21
+ error_type: error_type,
22
+ description: description,
23
+ severity: severity,
24
+ source_phase: source_phase,
25
+ confidence_at_detection: confidence_at_detection
26
+ )
27
+ @errors[err.id] = err
28
+ err
29
+ end
30
+
31
+ def trace_error(error_id:, steps:, root_cause:, confidence:)
32
+ err = @errors[error_id]
33
+ return nil unless err
34
+ return nil if @traces.size >= Constants::MAX_TRACES
35
+
36
+ trace = CausalTrace.new(error_id: error_id, root_cause: root_cause, confidence: confidence)
37
+ steps.each { |s| trace.add_step!(phase: s.fetch(:phase), description: s.fetch(:description)) }
38
+ @traces[trace.id] = trace
39
+ err.trace!(trace.id)
40
+ trace
41
+ end
42
+
43
+ def propose_correction(error_id:, strategy:, description:)
44
+ err = @errors[error_id]
45
+ return nil unless err
46
+ return nil unless Constants::CORRECTION_STRATEGIES.include?(strategy)
47
+ return nil if @corrections.size >= Constants::MAX_CORRECTIONS
48
+
49
+ correction = Correction.new(error_id: error_id, strategy: strategy, description: description)
50
+ @corrections[correction.id] = correction
51
+ err.correct!(correction.id)
52
+ correction
53
+ end
54
+
55
+ def apply_correction(correction_id:)
56
+ correction = @corrections[correction_id]
57
+ return nil unless correction
58
+
59
+ correction.apply!
60
+ end
61
+
62
+ def measure_correction(correction_id:, effectiveness:)
63
+ correction = @corrections[correction_id]
64
+ return nil unless correction
65
+
66
+ correction.measure_effectiveness!(effectiveness)
67
+ end
68
+
69
+ def resolve_error(error_id:)
70
+ err = @errors[error_id]
71
+ return nil unless err
72
+
73
+ err.resolve!
74
+ end
75
+
76
+ def active_errors
77
+ @errors.values.select(&:active?)
78
+ end
79
+
80
+ def resolved_errors
81
+ @errors.values.select(&:resolved?)
82
+ end
83
+
84
+ def errors_by_type
85
+ @errors.values.group_by(&:error_type).transform_values(&:length)
86
+ end
87
+
88
+ def errors_by_phase
89
+ @errors.values.group_by(&:source_phase).transform_values(&:length)
90
+ end
91
+
92
+ def most_common_error_type
93
+ tally = errors_by_type
94
+ return nil if tally.empty?
95
+
96
+ tally.max_by { |_, count| count }&.first
97
+ end
98
+
99
+ def most_effective_strategy
100
+ applied = @corrections.values.reject { |c| c.effectiveness.nil? }
101
+ return nil if applied.empty?
102
+
103
+ by_strategy = applied.group_by(&:strategy)
104
+ best = by_strategy.max_by do |_, list|
105
+ scores = list.map(&:effectiveness)
106
+ scores.sum.round(10) / scores.size
107
+ end
108
+ best&.first
109
+ end
110
+
111
+ def error_rate_by_phase
112
+ errors_by_phase
113
+ end
114
+
115
+ def correction_success_rate
116
+ applied = @corrections.values.select(&:applied)
117
+ return 0.0 if applied.empty?
118
+
119
+ measured = applied.reject { |c| c.effectiveness.nil? }
120
+ return 0.0 if measured.empty?
121
+
122
+ effective_count = measured.count(&:effective?)
123
+ (effective_count.to_f / measured.size).round(10)
124
+ end
125
+
126
+ def debugging_report
127
+ {
128
+ total_errors: @errors.size,
129
+ active_errors: active_errors.size,
130
+ resolved_errors: resolved_errors.size,
131
+ total_traces: @traces.size,
132
+ total_corrections: @corrections.size,
133
+ correction_success_rate: correction_success_rate,
134
+ most_common_error_type: most_common_error_type,
135
+ most_effective_strategy: most_effective_strategy,
136
+ errors_by_type: errors_by_type,
137
+ error_rate_by_phase: error_rate_by_phase
138
+ }
139
+ end
140
+
141
+ def to_h
142
+ {
143
+ errors: @errors.transform_values(&:to_h),
144
+ traces: @traces.transform_values(&:to_h),
145
+ corrections: @corrections.transform_values(&:to_h)
146
+ }
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module CognitiveDebugging
8
+ module Helpers
9
+ class ReasoningError
10
+ attr_reader :id, :error_type, :description, :severity, :source_phase,
11
+ :confidence_at_detection, :trace_ids, :correction_ids,
12
+ :created_at, :resolved_at, :status
13
+
14
+ def initialize(error_type:, description:, severity:, source_phase:, confidence_at_detection:)
15
+ @id = SecureRandom.uuid
16
+ @error_type = error_type
17
+ @description = description
18
+ @severity = severity.clamp(0.0, 1.0).round(10)
19
+ @source_phase = source_phase
20
+ @confidence_at_detection = confidence_at_detection.clamp(0.0, 1.0).round(10)
21
+ @status = :detected
22
+ @trace_ids = []
23
+ @correction_ids = []
24
+ @created_at = Time.now.utc
25
+ @resolved_at = nil
26
+ end
27
+
28
+ def detect!
29
+ @status = :detected
30
+ self
31
+ end
32
+
33
+ def trace!(trace_id)
34
+ @trace_ids << trace_id
35
+ @status = :traced
36
+ self
37
+ end
38
+
39
+ def correct!(correction_id)
40
+ @correction_ids << correction_id
41
+ @status = :correcting
42
+ self
43
+ end
44
+
45
+ def resolve!
46
+ @status = :resolved
47
+ @resolved_at = Time.now.utc
48
+ self
49
+ end
50
+
51
+ def mark_unresolvable!
52
+ @status = :unresolvable
53
+ @resolved_at = Time.now.utc
54
+ self
55
+ end
56
+
57
+ def severe?
58
+ @severity >= 0.7
59
+ end
60
+
61
+ def resolved?
62
+ @status == :resolved
63
+ end
64
+
65
+ def active?
66
+ !%i[resolved unresolvable].include?(@status)
67
+ end
68
+
69
+ def severity_label
70
+ Constants.severity_label(@severity)
71
+ end
72
+
73
+ def to_h
74
+ {
75
+ id: @id,
76
+ error_type: @error_type,
77
+ description: @description,
78
+ severity: @severity,
79
+ severity_label: severity_label,
80
+ source_phase: @source_phase,
81
+ confidence_at_detection: @confidence_at_detection,
82
+ status: @status,
83
+ trace_ids: @trace_ids.dup,
84
+ correction_ids: @correction_ids.dup,
85
+ created_at: @created_at,
86
+ resolved_at: @resolved_at
87
+ }
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end