lex-reality-testing 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: c9a93d20a2e4a53f6396b812010686a6441cb0f12ed5ca532779115b61b66b00
4
+ data.tar.gz: e04d90a78cf5f1a6f8fb9f37ed5356873d7ca6fc6ee4bc7f680be0656bec0ed2
5
+ SHA512:
6
+ metadata.gz: 7dd6321e7864703bdb3c72e9bd933f4f7cddd82468c35b7c1c964d32209dc78ad4ad6aff726c5259ebde4b6cb5c3ed9328cac8558ed2cfa588022c5bec1961aa
7
+ data.tar.gz: 0de4693b35dbb1ccfefce3a08433bb5d03aa145162d932e16d0679d3b674f8c1b45d141ffe57deac53a93e67370ca1315d87483428580b383bb045d93d65c44c
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', require: false
10
+
11
+ gem 'legion-gaia', path: '../../legion-gaia'
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/legion/extensions/reality_testing/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'lex-reality-testing'
7
+ spec.version = Legion::Extensions::RealityTesting::VERSION
8
+ spec.authors = ['Esity']
9
+ spec.email = ['matthewdiverson@gmail.com']
10
+
11
+ spec.summary = 'LEX Reality Testing'
12
+ spec.description = 'Belief validation, evidence accumulation, and reality coherence for brain-modeled agentic AI'
13
+ spec.homepage = 'https://github.com/LegionIO/lex-reality-testing'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 3.4'
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-reality-testing'
19
+ spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-reality-testing'
20
+ spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-reality-testing'
21
+ spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-reality-testing/issues'
22
+ spec.metadata['rubygems_mfa_required'] = 'true'
23
+
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ Dir.glob('{lib,spec}/**/*') + %w[lex-reality-testing.gemspec Gemfile]
26
+ end
27
+ spec.require_paths = ['lib']
28
+ spec.add_development_dependency 'legion-gaia'
29
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/reality_testing/helpers/constants'
4
+ require 'legion/extensions/reality_testing/helpers/belief'
5
+ require 'legion/extensions/reality_testing/helpers/reality_engine'
6
+ require 'legion/extensions/reality_testing/runners/reality_testing'
7
+
8
+ module Legion
9
+ module Extensions
10
+ module RealityTesting
11
+ class Client
12
+ include Runners::RealityTesting
13
+
14
+ def initialize(**)
15
+ @reality_engine = Helpers::RealityEngine.new
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :reality_engine
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module RealityTesting
6
+ module Helpers
7
+ class Belief
8
+ include Constants
9
+
10
+ attr_reader :id, :claim, :domain, :confidence, :evidence_count,
11
+ :confirming_count, :disconfirming_count, :created_at, :last_tested_at
12
+
13
+ def initialize(id:, claim:, domain: :general, confidence: Constants::DEFAULT_CONFIDENCE)
14
+ @id = id
15
+ @claim = claim
16
+ @domain = domain
17
+ @confidence = confidence.clamp(0.0, 1.0)
18
+ @evidence_count = 0
19
+ @confirming_count = 0
20
+ @disconfirming_count = 0
21
+ @created_at = Time.now.utc
22
+ @last_tested_at = nil
23
+ end
24
+
25
+ def test_with_evidence!(evidence_type:, weight: 0.1)
26
+ raise ArgumentError, "unknown evidence_type: #{evidence_type}" unless Constants::EVIDENCE_TYPES.include?(evidence_type)
27
+
28
+ @evidence_count += 1
29
+ @last_tested_at = Time.now.utc
30
+
31
+ case evidence_type
32
+ when :confirming
33
+ @confirming_count += 1
34
+ @confidence = (@confidence + (Constants::CONFIDENCE_BOOST * weight * 10)).clamp(0.0, 1.0)
35
+ when :disconfirming
36
+ @disconfirming_count += 1
37
+ @confidence = (@confidence - (Constants::CONFIDENCE_PENALTY * weight * 10)).clamp(0.0, 1.0)
38
+ when :neutral
39
+ # no confidence change
40
+ when :ambiguous
41
+ delta = @confidence - 0.5
42
+ @confidence = (@confidence - (delta * 0.1)).clamp(0.0, 1.0)
43
+ end
44
+
45
+ self
46
+ end
47
+
48
+ def confidence_label
49
+ Constants::CONFIDENCE_LABELS.find { |entry| entry[:range].cover?(@confidence) }&.fetch(:label) || :rejected
50
+ end
51
+
52
+ def validity
53
+ total = @confirming_count + @disconfirming_count
54
+ return 0.5 if total.zero?
55
+
56
+ @confirming_count.to_f / total
57
+ end
58
+
59
+ def validity_label
60
+ v = validity
61
+ Constants::VALIDITY_LABELS.find { |entry| entry[:range].cover?(v) }&.fetch(:label) || :refuted
62
+ end
63
+
64
+ def needs_testing?
65
+ @confidence.between?(0.3, 0.7)
66
+ end
67
+
68
+ def decay!
69
+ @confidence = (@confidence - Constants::CONFIDENCE_DECAY).clamp(0.0, 1.0)
70
+ self
71
+ end
72
+
73
+ def to_h
74
+ {
75
+ id: @id,
76
+ claim: @claim,
77
+ domain: @domain,
78
+ confidence: @confidence,
79
+ confidence_label: confidence_label,
80
+ evidence_count: @evidence_count,
81
+ confirming_count: @confirming_count,
82
+ disconfirming_count: @disconfirming_count,
83
+ validity: validity.round(3),
84
+ validity_label: validity_label,
85
+ needs_testing: needs_testing?,
86
+ created_at: @created_at,
87
+ last_tested_at: @last_tested_at
88
+ }
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module RealityTesting
6
+ module Helpers
7
+ module Constants
8
+ MAX_BELIEFS = 300
9
+ MAX_EVIDENCE = 500
10
+
11
+ DEFAULT_CONFIDENCE = 0.5
12
+ CONFIDENCE_BOOST = 0.1
13
+ CONFIDENCE_PENALTY = 0.15
14
+ CONFIDENCE_DECAY = 0.02
15
+
16
+ CONFIDENCE_LABELS = [
17
+ { range: (0.85..1.0), label: :certain },
18
+ { range: (0.65...0.85), label: :confident },
19
+ { range: (0.35...0.65), label: :tentative },
20
+ { range: (0.15...0.35), label: :doubtful },
21
+ { range: (0.0...0.15), label: :rejected }
22
+ ].freeze
23
+
24
+ EVIDENCE_TYPES = %i[confirming disconfirming neutral ambiguous].freeze
25
+
26
+ VALIDITY_LABELS = [
27
+ { range: (0.75..1.0), label: :validated },
28
+ { range: (0.55...0.75), label: :supported },
29
+ { range: (0.35...0.55), label: :uncertain },
30
+ { range: (0.15...0.35), label: :questionable },
31
+ { range: (0.0...0.15), label: :refuted }
32
+ ].freeze
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module RealityTesting
6
+ module Helpers
7
+ class RealityEngine
8
+ def initialize
9
+ @beliefs = {}
10
+ @next_id = 1
11
+ end
12
+
13
+ def create_belief(claim:, domain: :general, confidence: Constants::DEFAULT_CONFIDENCE)
14
+ return { created: false, reason: :at_capacity } if @beliefs.size >= Constants::MAX_BELIEFS
15
+
16
+ id = "belief_#{@next_id}"
17
+ @next_id += 1
18
+ belief = Belief.new(id: id, claim: claim, domain: domain, confidence: confidence)
19
+ @beliefs[id] = belief
20
+ Legion::Logging.debug "[reality_testing] create_belief id=#{id} domain=#{domain} confidence=#{confidence.round(2)}"
21
+ { created: true, belief: belief.to_h }
22
+ end
23
+
24
+ def test_belief(belief_id:, evidence_type:, weight: 0.1)
25
+ belief = @beliefs[belief_id]
26
+ return { tested: false, reason: :not_found } unless belief
27
+
28
+ belief.test_with_evidence!(evidence_type: evidence_type, weight: weight)
29
+ Legion::Logging.debug "[reality_testing] test_belief id=#{belief_id} evidence=#{evidence_type} confidence=#{belief.confidence.round(2)}"
30
+ { tested: true, belief: belief.to_h }
31
+ end
32
+
33
+ def beliefs_needing_testing
34
+ @beliefs.values.select(&:needs_testing?)
35
+ end
36
+
37
+ def strongest_beliefs(limit: 10)
38
+ @beliefs.values
39
+ .sort_by { |b| -b.confidence }
40
+ .first(limit)
41
+ end
42
+
43
+ def weakest_beliefs(limit: 10)
44
+ @beliefs.values
45
+ .sort_by(&:confidence)
46
+ .first(limit)
47
+ end
48
+
49
+ def beliefs_by_domain(domain:)
50
+ @beliefs.values.select { |b| b.domain == domain }
51
+ end
52
+
53
+ def overall_reality_coherence
54
+ return 0.0 if @beliefs.empty?
55
+
56
+ total = @beliefs.values.sum(&:validity)
57
+ total / @beliefs.size
58
+ end
59
+
60
+ def decay_all
61
+ @beliefs.each_value(&:decay!)
62
+ @beliefs.size
63
+ end
64
+
65
+ def prune_rejected
66
+ before = @beliefs.size
67
+ @beliefs.delete_if { |_id, b| b.confidence < 0.1 }
68
+ pruned = before - @beliefs.size
69
+ Legion::Logging.debug "[reality_testing] prune_rejected pruned=#{pruned} remaining=#{@beliefs.size}"
70
+ pruned
71
+ end
72
+
73
+ def reality_report
74
+ domains = @beliefs.values.group_by(&:domain)
75
+ {
76
+ total_beliefs: @beliefs.size,
77
+ coherence: overall_reality_coherence.round(3),
78
+ needing_testing: beliefs_needing_testing.size,
79
+ domains: domains.transform_values(&:size),
80
+ strongest_claim: @beliefs.values.max_by(&:confidence)&.claim,
81
+ weakest_claim: @beliefs.values.min_by(&:confidence)&.claim
82
+ }
83
+ end
84
+
85
+ def to_h
86
+ {
87
+ belief_count: @beliefs.size,
88
+ coherence: overall_reality_coherence.round(3),
89
+ beliefs: @beliefs.values.map(&:to_h)
90
+ }
91
+ end
92
+
93
+ def size
94
+ @beliefs.size
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module RealityTesting
6
+ module Runners
7
+ module RealityTesting
8
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
9
+ Legion::Extensions::Helpers.const_defined?(:Lex)
10
+
11
+ def create_belief(claim:, domain: :general, confidence: nil, **)
12
+ conf = confidence || Helpers::Constants::DEFAULT_CONFIDENCE
13
+ result = reality_engine.create_belief(claim: claim, domain: domain, confidence: conf)
14
+ Legion::Logging.info "[reality_testing] create_belief claim=#{claim[0, 60]} domain=#{domain}"
15
+ result
16
+ end
17
+
18
+ def test_belief(belief_id:, evidence_type:, weight: 0.1, **)
19
+ etype = evidence_type.to_sym
20
+ reality_engine.test_belief(belief_id: belief_id, evidence_type: etype, weight: weight)
21
+ end
22
+
23
+ def get_belief(belief_id:, **)
24
+ engine = reality_engine
25
+ belief = engine.instance_variable_get(:@beliefs)[belief_id]
26
+ return { found: false, belief_id: belief_id } unless belief
27
+
28
+ { found: true, belief: belief.to_h }
29
+ end
30
+
31
+ def beliefs_needing_testing(**)
32
+ beliefs = reality_engine.beliefs_needing_testing
33
+ Legion::Logging.debug "[reality_testing] needs_testing count=#{beliefs.size}"
34
+ { count: beliefs.size, beliefs: beliefs.map(&:to_h) }
35
+ end
36
+
37
+ def strongest_beliefs(limit: 10, **)
38
+ beliefs = reality_engine.strongest_beliefs(limit: limit)
39
+ { count: beliefs.size, beliefs: beliefs.map(&:to_h) }
40
+ end
41
+
42
+ def weakest_beliefs(limit: 10, **)
43
+ beliefs = reality_engine.weakest_beliefs(limit: limit)
44
+ { count: beliefs.size, beliefs: beliefs.map(&:to_h) }
45
+ end
46
+
47
+ def beliefs_by_domain(domain:, **)
48
+ beliefs = reality_engine.beliefs_by_domain(domain: domain.to_sym)
49
+ { domain: domain, count: beliefs.size, beliefs: beliefs.map(&:to_h) }
50
+ end
51
+
52
+ def overall_coherence(**)
53
+ coherence = reality_engine.overall_reality_coherence
54
+ Legion::Logging.debug "[reality_testing] coherence=#{coherence.round(3)}"
55
+ { coherence: coherence.round(3) }
56
+ end
57
+
58
+ def decay_beliefs(**)
59
+ count = reality_engine.decay_all
60
+ Legion::Logging.debug "[reality_testing] decay_all count=#{count}"
61
+ { decayed: count }
62
+ end
63
+
64
+ def prune_rejected_beliefs(**)
65
+ pruned = reality_engine.prune_rejected
66
+ { pruned: pruned }
67
+ end
68
+
69
+ def reality_report(**)
70
+ reality_engine.reality_report
71
+ end
72
+
73
+ def reality_status(**)
74
+ {
75
+ total_beliefs: reality_engine.size,
76
+ coherence: reality_engine.overall_reality_coherence.round(3),
77
+ needing_testing: reality_engine.beliefs_needing_testing.size
78
+ }
79
+ end
80
+
81
+ private
82
+
83
+ def reality_engine
84
+ @reality_engine ||= Helpers::RealityEngine.new
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module RealityTesting
6
+ VERSION = '0.1.0'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/reality_testing/version'
4
+ require 'legion/extensions/reality_testing/helpers/constants'
5
+ require 'legion/extensions/reality_testing/helpers/belief'
6
+ require 'legion/extensions/reality_testing/helpers/reality_engine'
7
+ require 'legion/extensions/reality_testing/runners/reality_testing'
8
+
9
+ module Legion
10
+ module Extensions
11
+ module RealityTesting
12
+ extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/reality_testing/client'
4
+
5
+ RSpec.describe Legion::Extensions::RealityTesting::Client do
6
+ let(:client) { described_class.new }
7
+
8
+ it 'responds to all runner methods' do
9
+ expect(client).to respond_to(:create_belief)
10
+ expect(client).to respond_to(:test_belief)
11
+ expect(client).to respond_to(:get_belief)
12
+ expect(client).to respond_to(:beliefs_needing_testing)
13
+ expect(client).to respond_to(:strongest_beliefs)
14
+ expect(client).to respond_to(:weakest_beliefs)
15
+ expect(client).to respond_to(:beliefs_by_domain)
16
+ expect(client).to respond_to(:overall_coherence)
17
+ expect(client).to respond_to(:decay_beliefs)
18
+ expect(client).to respond_to(:prune_rejected_beliefs)
19
+ expect(client).to respond_to(:reality_report)
20
+ expect(client).to respond_to(:reality_status)
21
+ end
22
+
23
+ it 'maintains isolated state per instance' do
24
+ client_a = described_class.new
25
+ client_b = described_class.new
26
+ client_a.create_belief(claim: 'only in A')
27
+ expect(client_b.reality_status[:total_beliefs]).to eq(0)
28
+ end
29
+ end
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::RealityTesting::Helpers::Belief do
4
+ subject(:belief) { described_class.new(id: 'b1', claim: 'The sky is blue', domain: :perception) }
5
+
6
+ describe '#initialize' do
7
+ it 'sets id' do
8
+ expect(belief.id).to eq('b1')
9
+ end
10
+
11
+ it 'sets claim' do
12
+ expect(belief.claim).to eq('The sky is blue')
13
+ end
14
+
15
+ it 'sets domain' do
16
+ expect(belief.domain).to eq(:perception)
17
+ end
18
+
19
+ it 'sets default confidence' do
20
+ expect(belief.confidence).to eq(0.5)
21
+ end
22
+
23
+ it 'clamps confidence above 1.0' do
24
+ b = described_class.new(id: 'x', claim: 'test', confidence: 1.5)
25
+ expect(b.confidence).to eq(1.0)
26
+ end
27
+
28
+ it 'clamps confidence below 0.0' do
29
+ b = described_class.new(id: 'x', claim: 'test', confidence: -0.5)
30
+ expect(b.confidence).to eq(0.0)
31
+ end
32
+
33
+ it 'initializes evidence counts to zero' do
34
+ expect(belief.evidence_count).to eq(0)
35
+ expect(belief.confirming_count).to eq(0)
36
+ expect(belief.disconfirming_count).to eq(0)
37
+ end
38
+ end
39
+
40
+ describe '#test_with_evidence!' do
41
+ it 'increments evidence_count for any type' do
42
+ belief.test_with_evidence!(evidence_type: :confirming)
43
+ expect(belief.evidence_count).to eq(1)
44
+ end
45
+
46
+ it 'increases confidence for confirming evidence' do
47
+ before = belief.confidence
48
+ belief.test_with_evidence!(evidence_type: :confirming)
49
+ expect(belief.confidence).to be > before
50
+ end
51
+
52
+ it 'decreases confidence for disconfirming evidence' do
53
+ before = belief.confidence
54
+ belief.test_with_evidence!(evidence_type: :disconfirming)
55
+ expect(belief.confidence).to be < before
56
+ end
57
+
58
+ it 'does not change confidence for neutral evidence' do
59
+ before = belief.confidence
60
+ belief.test_with_evidence!(evidence_type: :neutral)
61
+ expect(belief.confidence).to eq(before)
62
+ end
63
+
64
+ it 'nudges confidence toward 0.5 for ambiguous evidence when above' do
65
+ b = described_class.new(id: 'x', claim: 'test', confidence: 0.8)
66
+ before = b.confidence
67
+ b.test_with_evidence!(evidence_type: :ambiguous)
68
+ expect(b.confidence).to be < before
69
+ end
70
+
71
+ it 'nudges confidence toward 0.5 for ambiguous evidence when below' do
72
+ b = described_class.new(id: 'x', claim: 'test', confidence: 0.2)
73
+ before = b.confidence
74
+ b.test_with_evidence!(evidence_type: :ambiguous)
75
+ expect(b.confidence).to be > before
76
+ end
77
+
78
+ it 'tracks confirming_count' do
79
+ belief.test_with_evidence!(evidence_type: :confirming)
80
+ expect(belief.confirming_count).to eq(1)
81
+ end
82
+
83
+ it 'tracks disconfirming_count' do
84
+ belief.test_with_evidence!(evidence_type: :disconfirming)
85
+ expect(belief.disconfirming_count).to eq(1)
86
+ end
87
+
88
+ it 'raises for unknown evidence_type' do
89
+ expect { belief.test_with_evidence!(evidence_type: :unknown) }.to raise_error(ArgumentError)
90
+ end
91
+
92
+ it 'returns self for chaining' do
93
+ result = belief.test_with_evidence!(evidence_type: :neutral)
94
+ expect(result).to be(belief)
95
+ end
96
+ end
97
+
98
+ describe '#confidence_label' do
99
+ it 'returns :certain at 1.0' do
100
+ b = described_class.new(id: 'x', claim: 'test', confidence: 1.0)
101
+ expect(b.confidence_label).to eq(:certain)
102
+ end
103
+
104
+ it 'returns :tentative at 0.5' do
105
+ expect(belief.confidence_label).to eq(:tentative)
106
+ end
107
+
108
+ it 'returns :rejected near 0.0' do
109
+ b = described_class.new(id: 'x', claim: 'test', confidence: 0.05)
110
+ expect(b.confidence_label).to eq(:rejected)
111
+ end
112
+ end
113
+
114
+ describe '#validity' do
115
+ it 'returns 0.5 when no evidence' do
116
+ expect(belief.validity).to eq(0.5)
117
+ end
118
+
119
+ it 'returns 1.0 with only confirming evidence' do
120
+ 3.times { belief.test_with_evidence!(evidence_type: :confirming) }
121
+ expect(belief.validity).to eq(1.0)
122
+ end
123
+
124
+ it 'returns 0.0 with only disconfirming evidence' do
125
+ 3.times { belief.test_with_evidence!(evidence_type: :disconfirming) }
126
+ expect(belief.validity).to eq(0.0)
127
+ end
128
+
129
+ it 'returns 0.5 with equal confirming and disconfirming' do
130
+ belief.test_with_evidence!(evidence_type: :confirming)
131
+ belief.test_with_evidence!(evidence_type: :disconfirming)
132
+ expect(belief.validity).to eq(0.5)
133
+ end
134
+ end
135
+
136
+ describe '#validity_label' do
137
+ it 'returns :validated with all confirming' do
138
+ 3.times { belief.test_with_evidence!(evidence_type: :confirming) }
139
+ expect(belief.validity_label).to eq(:validated)
140
+ end
141
+
142
+ it 'returns :refuted with all disconfirming' do
143
+ 3.times { belief.test_with_evidence!(evidence_type: :disconfirming) }
144
+ expect(belief.validity_label).to eq(:refuted)
145
+ end
146
+ end
147
+
148
+ describe '#needs_testing?' do
149
+ it 'returns true at default confidence 0.5' do
150
+ expect(belief.needs_testing?).to be true
151
+ end
152
+
153
+ it 'returns false at high confidence' do
154
+ b = described_class.new(id: 'x', claim: 'test', confidence: 0.9)
155
+ expect(b.needs_testing?).to be false
156
+ end
157
+
158
+ it 'returns false at low confidence' do
159
+ b = described_class.new(id: 'x', claim: 'test', confidence: 0.1)
160
+ expect(b.needs_testing?).to be false
161
+ end
162
+ end
163
+
164
+ describe '#decay!' do
165
+ it 'reduces confidence by CONFIDENCE_DECAY' do
166
+ before = belief.confidence
167
+ belief.decay!
168
+ expect(belief.confidence).to be_within(0.001).of(before - 0.02)
169
+ end
170
+
171
+ it 'floors at 0.0' do
172
+ b = described_class.new(id: 'x', claim: 'test', confidence: 0.01)
173
+ b.decay!
174
+ expect(b.confidence).to eq(0.0)
175
+ end
176
+
177
+ it 'returns self' do
178
+ expect(belief.decay!).to be(belief)
179
+ end
180
+ end
181
+
182
+ describe '#to_h' do
183
+ it 'includes all expected keys' do
184
+ h = belief.to_h
185
+ expect(h).to include(
186
+ :id, :claim, :domain, :confidence, :confidence_label,
187
+ :evidence_count, :confirming_count, :disconfirming_count,
188
+ :validity, :validity_label, :needs_testing, :created_at, :last_tested_at
189
+ )
190
+ end
191
+
192
+ it 'sets last_tested_at after testing' do
193
+ belief.test_with_evidence!(evidence_type: :confirming)
194
+ expect(belief.to_h[:last_tested_at]).not_to be_nil
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::RealityTesting::Helpers::Constants do
4
+ subject(:mod) { described_class }
5
+
6
+ describe 'capacity constants' do
7
+ it 'defines MAX_BELIEFS as 300' do
8
+ expect(mod::MAX_BELIEFS).to eq(300)
9
+ end
10
+
11
+ it 'defines MAX_EVIDENCE as 500' do
12
+ expect(mod::MAX_EVIDENCE).to eq(500)
13
+ end
14
+ end
15
+
16
+ describe 'confidence constants' do
17
+ it 'defines DEFAULT_CONFIDENCE as 0.5' do
18
+ expect(mod::DEFAULT_CONFIDENCE).to eq(0.5)
19
+ end
20
+
21
+ it 'defines CONFIDENCE_BOOST as 0.1' do
22
+ expect(mod::CONFIDENCE_BOOST).to eq(0.1)
23
+ end
24
+
25
+ it 'defines CONFIDENCE_PENALTY as 0.15' do
26
+ expect(mod::CONFIDENCE_PENALTY).to eq(0.15)
27
+ end
28
+
29
+ it 'defines CONFIDENCE_DECAY as 0.02' do
30
+ expect(mod::CONFIDENCE_DECAY).to eq(0.02)
31
+ end
32
+ end
33
+
34
+ describe 'CONFIDENCE_LABELS' do
35
+ it 'returns :certain for 1.0' do
36
+ label = mod::CONFIDENCE_LABELS.find { |e| e[:range].cover?(1.0) }[:label]
37
+ expect(label).to eq(:certain)
38
+ end
39
+
40
+ it 'returns :confident for 0.75' do
41
+ label = mod::CONFIDENCE_LABELS.find { |e| e[:range].cover?(0.75) }[:label]
42
+ expect(label).to eq(:confident)
43
+ end
44
+
45
+ it 'returns :tentative for 0.5' do
46
+ label = mod::CONFIDENCE_LABELS.find { |e| e[:range].cover?(0.5) }[:label]
47
+ expect(label).to eq(:tentative)
48
+ end
49
+
50
+ it 'returns :doubtful for 0.2' do
51
+ label = mod::CONFIDENCE_LABELS.find { |e| e[:range].cover?(0.2) }[:label]
52
+ expect(label).to eq(:doubtful)
53
+ end
54
+
55
+ it 'returns :rejected for 0.05' do
56
+ label = mod::CONFIDENCE_LABELS.find { |e| e[:range].cover?(0.05) }[:label]
57
+ expect(label).to eq(:rejected)
58
+ end
59
+ end
60
+
61
+ describe 'EVIDENCE_TYPES' do
62
+ it 'includes all four types' do
63
+ expect(mod::EVIDENCE_TYPES).to eq(%i[confirming disconfirming neutral ambiguous])
64
+ end
65
+ end
66
+
67
+ describe 'VALIDITY_LABELS' do
68
+ it 'returns :validated for 0.9' do
69
+ label = mod::VALIDITY_LABELS.find { |e| e[:range].cover?(0.9) }[:label]
70
+ expect(label).to eq(:validated)
71
+ end
72
+
73
+ it 'returns :refuted for 0.05' do
74
+ label = mod::VALIDITY_LABELS.find { |e| e[:range].cover?(0.05) }[:label]
75
+ expect(label).to eq(:refuted)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,191 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::RealityTesting::Helpers::RealityEngine do
4
+ subject(:engine) { described_class.new }
5
+
6
+ describe '#create_belief' do
7
+ it 'creates a belief and returns created: true' do
8
+ result = engine.create_belief(claim: 'All humans are mortal')
9
+ expect(result[:created]).to be true
10
+ end
11
+
12
+ it 'includes the belief hash in the result' do
13
+ result = engine.create_belief(claim: 'All humans are mortal')
14
+ expect(result[:belief]).to include(:id, :claim, :confidence)
15
+ end
16
+
17
+ it 'assigns sequential IDs' do
18
+ r1 = engine.create_belief(claim: 'claim 1')
19
+ r2 = engine.create_belief(claim: 'claim 2')
20
+ expect(r1[:belief][:id]).to eq('belief_1')
21
+ expect(r2[:belief][:id]).to eq('belief_2')
22
+ end
23
+
24
+ it 'respects custom domain' do
25
+ result = engine.create_belief(claim: 'test', domain: :science)
26
+ expect(result[:belief][:domain]).to eq(:science)
27
+ end
28
+
29
+ it 'respects custom confidence' do
30
+ result = engine.create_belief(claim: 'test', confidence: 0.8)
31
+ expect(result[:belief][:confidence]).to eq(0.8)
32
+ end
33
+
34
+ it 'returns at_capacity when MAX_BELIEFS reached' do
35
+ stub_const('Legion::Extensions::RealityTesting::Helpers::Constants::MAX_BELIEFS', 2)
36
+ engine.create_belief(claim: 'one')
37
+ engine.create_belief(claim: 'two')
38
+ result = engine.create_belief(claim: 'three')
39
+ expect(result[:created]).to be false
40
+ expect(result[:reason]).to eq(:at_capacity)
41
+ end
42
+ end
43
+
44
+ describe '#test_belief' do
45
+ let(:belief_id) do
46
+ engine.create_belief(claim: 'test claim')[:belief][:id]
47
+ end
48
+
49
+ it 'returns tested: true for known belief' do
50
+ result = engine.test_belief(belief_id: belief_id, evidence_type: :confirming)
51
+ expect(result[:tested]).to be true
52
+ end
53
+
54
+ it 'returns tested: false for unknown belief' do
55
+ result = engine.test_belief(belief_id: 'nope', evidence_type: :confirming)
56
+ expect(result[:tested]).to be false
57
+ expect(result[:reason]).to eq(:not_found)
58
+ end
59
+
60
+ it 'updates confidence after confirming evidence' do
61
+ before = engine.test_belief(belief_id: belief_id, evidence_type: :confirming)
62
+ expect(before[:belief][:confidence]).to be > 0.5
63
+ end
64
+
65
+ it 'updates confidence after disconfirming evidence' do
66
+ result = engine.test_belief(belief_id: belief_id, evidence_type: :disconfirming)
67
+ expect(result[:belief][:confidence]).to be < 0.5
68
+ end
69
+ end
70
+
71
+ describe '#beliefs_needing_testing' do
72
+ it 'returns beliefs with confidence between 0.3 and 0.7' do
73
+ engine.create_belief(claim: 'uncertain', confidence: 0.5)
74
+ engine.create_belief(claim: 'strong', confidence: 0.95)
75
+ engine.create_belief(claim: 'weak', confidence: 0.05)
76
+ result = engine.beliefs_needing_testing
77
+ expect(result.size).to eq(1)
78
+ expect(result.first.claim).to eq('uncertain')
79
+ end
80
+ end
81
+
82
+ describe '#strongest_beliefs' do
83
+ it 'returns beliefs sorted by descending confidence' do
84
+ engine.create_belief(claim: 'low', confidence: 0.2)
85
+ engine.create_belief(claim: 'high', confidence: 0.9)
86
+ engine.create_belief(claim: 'mid', confidence: 0.5)
87
+ result = engine.strongest_beliefs(limit: 2)
88
+ expect(result.first.claim).to eq('high')
89
+ expect(result.size).to eq(2)
90
+ end
91
+ end
92
+
93
+ describe '#weakest_beliefs' do
94
+ it 'returns beliefs sorted by ascending confidence' do
95
+ engine.create_belief(claim: 'low', confidence: 0.2)
96
+ engine.create_belief(claim: 'high', confidence: 0.9)
97
+ result = engine.weakest_beliefs(limit: 1)
98
+ expect(result.first.claim).to eq('low')
99
+ end
100
+ end
101
+
102
+ describe '#beliefs_by_domain' do
103
+ it 'filters beliefs by domain' do
104
+ engine.create_belief(claim: 'science fact', domain: :science)
105
+ engine.create_belief(claim: 'social fact', domain: :social)
106
+ result = engine.beliefs_by_domain(domain: :science)
107
+ expect(result.size).to eq(1)
108
+ expect(result.first.domain).to eq(:science)
109
+ end
110
+ end
111
+
112
+ describe '#overall_reality_coherence' do
113
+ it 'returns 0.0 with no beliefs' do
114
+ expect(engine.overall_reality_coherence).to eq(0.0)
115
+ end
116
+
117
+ it 'returns 0.5 with one belief and no evidence' do
118
+ engine.create_belief(claim: 'test')
119
+ expect(engine.overall_reality_coherence).to eq(0.5)
120
+ end
121
+
122
+ it 'increases toward 1.0 with confirming evidence' do
123
+ id = engine.create_belief(claim: 'test')[:belief][:id]
124
+ 5.times { engine.test_belief(belief_id: id, evidence_type: :confirming) }
125
+ expect(engine.overall_reality_coherence).to be > 0.5
126
+ end
127
+ end
128
+
129
+ describe '#decay_all' do
130
+ it 'decays all beliefs and returns count' do
131
+ engine.create_belief(claim: 'b1')
132
+ engine.create_belief(claim: 'b2')
133
+ count = engine.decay_all
134
+ expect(count).to eq(2)
135
+ end
136
+
137
+ it 'reduces confidence of each belief' do
138
+ id = engine.create_belief(claim: 'test')[:belief][:id]
139
+ belief_before = engine.instance_variable_get(:@beliefs)[id].confidence
140
+ engine.decay_all
141
+ belief_after = engine.instance_variable_get(:@beliefs)[id].confidence
142
+ expect(belief_after).to be < belief_before
143
+ end
144
+ end
145
+
146
+ describe '#prune_rejected' do
147
+ it 'removes beliefs with confidence below 0.1' do
148
+ id = engine.create_belief(claim: 'very weak', confidence: 0.05)[:belief][:id]
149
+ engine.create_belief(claim: 'strong', confidence: 0.8)
150
+ pruned = engine.prune_rejected
151
+ expect(pruned).to eq(1)
152
+ expect(engine.instance_variable_get(:@beliefs)).not_to have_key(id)
153
+ end
154
+
155
+ it 'returns 0 when nothing to prune' do
156
+ engine.create_belief(claim: 'ok', confidence: 0.5)
157
+ expect(engine.prune_rejected).to eq(0)
158
+ end
159
+ end
160
+
161
+ describe '#reality_report' do
162
+ it 'returns a report hash with required keys' do
163
+ engine.create_belief(claim: 'test')
164
+ report = engine.reality_report
165
+ expect(report).to include(:total_beliefs, :coherence, :needing_testing, :domains)
166
+ end
167
+
168
+ it 'reports total_beliefs correctly' do
169
+ engine.create_belief(claim: 'a')
170
+ engine.create_belief(claim: 'b')
171
+ expect(engine.reality_report[:total_beliefs]).to eq(2)
172
+ end
173
+ end
174
+
175
+ describe '#to_h' do
176
+ it 'returns belief_count and beliefs array' do
177
+ engine.create_belief(claim: 'one')
178
+ h = engine.to_h
179
+ expect(h[:belief_count]).to eq(1)
180
+ expect(h[:beliefs]).to be_an(Array)
181
+ end
182
+ end
183
+
184
+ describe '#size' do
185
+ it 'returns the number of stored beliefs' do
186
+ expect(engine.size).to eq(0)
187
+ engine.create_belief(claim: 'one')
188
+ expect(engine.size).to eq(1)
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/reality_testing/client'
4
+
5
+ RSpec.describe Legion::Extensions::RealityTesting::Runners::RealityTesting do
6
+ let(:client) { Legion::Extensions::RealityTesting::Client.new }
7
+
8
+ describe '#create_belief' do
9
+ it 'creates a belief and returns created: true' do
10
+ result = client.create_belief(claim: 'Water boils at 100C')
11
+ expect(result[:created]).to be true
12
+ end
13
+
14
+ it 'uses default confidence when not specified' do
15
+ result = client.create_belief(claim: 'default conf')
16
+ expect(result[:belief][:confidence]).to eq(0.5)
17
+ end
18
+
19
+ it 'accepts custom confidence' do
20
+ result = client.create_belief(claim: 'high conf', confidence: 0.9)
21
+ expect(result[:belief][:confidence]).to eq(0.9)
22
+ end
23
+
24
+ it 'accepts custom domain' do
25
+ result = client.create_belief(claim: 'domain test', domain: :physics)
26
+ expect(result[:belief][:domain]).to eq(:physics)
27
+ end
28
+ end
29
+
30
+ describe '#test_belief' do
31
+ let(:belief_id) { client.create_belief(claim: 'testable')[:belief][:id] }
32
+
33
+ it 'returns tested: true for known belief' do
34
+ expect(client.test_belief(belief_id: belief_id, evidence_type: :confirming)[:tested]).to be true
35
+ end
36
+
37
+ it 'returns tested: false for unknown belief' do
38
+ result = client.test_belief(belief_id: 'ghost', evidence_type: :confirming)
39
+ expect(result[:tested]).to be false
40
+ end
41
+
42
+ it 'accepts string evidence_type' do
43
+ result = client.test_belief(belief_id: belief_id, evidence_type: 'confirming')
44
+ expect(result[:tested]).to be true
45
+ end
46
+
47
+ it 'increases confidence with confirming evidence' do
48
+ result = client.test_belief(belief_id: belief_id, evidence_type: :confirming)
49
+ expect(result[:belief][:confidence]).to be > 0.5
50
+ end
51
+
52
+ it 'decreases confidence with disconfirming evidence' do
53
+ result = client.test_belief(belief_id: belief_id, evidence_type: :disconfirming)
54
+ expect(result[:belief][:confidence]).to be < 0.5
55
+ end
56
+ end
57
+
58
+ describe '#get_belief' do
59
+ it 'returns found: true for existing belief' do
60
+ id = client.create_belief(claim: 'fetch me')[:belief][:id]
61
+ result = client.get_belief(belief_id: id)
62
+ expect(result[:found]).to be true
63
+ expect(result[:belief][:claim]).to eq('fetch me')
64
+ end
65
+
66
+ it 'returns found: false for missing belief' do
67
+ result = client.get_belief(belief_id: 'missing')
68
+ expect(result[:found]).to be false
69
+ end
70
+ end
71
+
72
+ describe '#beliefs_needing_testing' do
73
+ it 'returns count and beliefs array' do
74
+ client.create_belief(claim: 'uncertain', confidence: 0.5)
75
+ result = client.beliefs_needing_testing
76
+ expect(result).to include(:count, :beliefs)
77
+ expect(result[:count]).to be >= 1
78
+ end
79
+ end
80
+
81
+ describe '#strongest_beliefs' do
82
+ it 'returns highest-confidence beliefs' do
83
+ client.create_belief(claim: 'weak', confidence: 0.2)
84
+ client.create_belief(claim: 'strong', confidence: 0.9)
85
+ result = client.strongest_beliefs(limit: 1)
86
+ expect(result[:beliefs].first[:claim]).to eq('strong')
87
+ end
88
+ end
89
+
90
+ describe '#weakest_beliefs' do
91
+ it 'returns lowest-confidence beliefs' do
92
+ client.create_belief(claim: 'weak', confidence: 0.2)
93
+ client.create_belief(claim: 'strong', confidence: 0.9)
94
+ result = client.weakest_beliefs(limit: 1)
95
+ expect(result[:beliefs].first[:claim]).to eq('weak')
96
+ end
97
+ end
98
+
99
+ describe '#beliefs_by_domain' do
100
+ it 'filters beliefs by domain' do
101
+ client.create_belief(claim: 'logic rule', domain: :logic)
102
+ client.create_belief(claim: 'social norm', domain: :social)
103
+ result = client.beliefs_by_domain(domain: :logic)
104
+ expect(result[:domain]).to eq(:logic)
105
+ expect(result[:count]).to eq(1)
106
+ end
107
+ end
108
+
109
+ describe '#overall_coherence' do
110
+ it 'returns a coherence value between 0 and 1' do
111
+ client.create_belief(claim: 'test')
112
+ result = client.overall_coherence
113
+ expect(result[:coherence]).to be_between(0.0, 1.0)
114
+ end
115
+ end
116
+
117
+ describe '#decay_beliefs' do
118
+ it 'returns the number of decayed beliefs' do
119
+ client.create_belief(claim: 'one')
120
+ result = client.decay_beliefs
121
+ expect(result[:decayed]).to eq(1)
122
+ end
123
+ end
124
+
125
+ describe '#prune_rejected_beliefs' do
126
+ it 'returns count of pruned beliefs' do
127
+ client.create_belief(claim: 'dead', confidence: 0.05)
128
+ result = client.prune_rejected_beliefs
129
+ expect(result[:pruned]).to eq(1)
130
+ end
131
+
132
+ it 'returns 0 when nothing pruned' do
133
+ client.create_belief(claim: 'alive', confidence: 0.8)
134
+ expect(client.prune_rejected_beliefs[:pruned]).to eq(0)
135
+ end
136
+ end
137
+
138
+ describe '#reality_report' do
139
+ it 'returns a full report hash' do
140
+ client.create_belief(claim: 'fact')
141
+ report = client.reality_report
142
+ expect(report).to include(:total_beliefs, :coherence, :needing_testing, :domains)
143
+ end
144
+ end
145
+
146
+ describe '#reality_status' do
147
+ it 'returns total_beliefs, coherence, needing_testing' do
148
+ client.create_belief(claim: 'status test')
149
+ result = client.reality_status
150
+ expect(result).to include(:total_beliefs, :coherence, :needing_testing)
151
+ expect(result[:total_beliefs]).to eq(1)
152
+ end
153
+ end
154
+ end
@@ -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/reality_testing'
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,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lex-reality-testing
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: Belief validation, evidence accumulation, and reality coherence 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-reality-testing.gemspec
36
+ - lib/legion/extensions/reality_testing.rb
37
+ - lib/legion/extensions/reality_testing/client.rb
38
+ - lib/legion/extensions/reality_testing/helpers/belief.rb
39
+ - lib/legion/extensions/reality_testing/helpers/constants.rb
40
+ - lib/legion/extensions/reality_testing/helpers/reality_engine.rb
41
+ - lib/legion/extensions/reality_testing/runners/reality_testing.rb
42
+ - lib/legion/extensions/reality_testing/version.rb
43
+ - spec/legion/extensions/reality_testing/client_spec.rb
44
+ - spec/legion/extensions/reality_testing/helpers/belief_spec.rb
45
+ - spec/legion/extensions/reality_testing/helpers/constants_spec.rb
46
+ - spec/legion/extensions/reality_testing/helpers/reality_engine_spec.rb
47
+ - spec/legion/extensions/reality_testing/runners/reality_testing_spec.rb
48
+ - spec/spec_helper.rb
49
+ homepage: https://github.com/LegionIO/lex-reality-testing
50
+ licenses:
51
+ - MIT
52
+ metadata:
53
+ homepage_uri: https://github.com/LegionIO/lex-reality-testing
54
+ source_code_uri: https://github.com/LegionIO/lex-reality-testing
55
+ documentation_uri: https://github.com/LegionIO/lex-reality-testing
56
+ changelog_uri: https://github.com/LegionIO/lex-reality-testing
57
+ bug_tracker_uri: https://github.com/LegionIO/lex-reality-testing/issues
58
+ rubygems_mfa_required: 'true'
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '3.4'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubygems_version: 3.6.9
74
+ specification_version: 4
75
+ summary: LEX Reality Testing
76
+ test_files: []