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 +7 -0
- data/Gemfile +11 -0
- data/lex-reality-testing.gemspec +29 -0
- data/lib/legion/extensions/reality_testing/client.rb +24 -0
- data/lib/legion/extensions/reality_testing/helpers/belief.rb +94 -0
- data/lib/legion/extensions/reality_testing/helpers/constants.rb +37 -0
- data/lib/legion/extensions/reality_testing/helpers/reality_engine.rb +100 -0
- data/lib/legion/extensions/reality_testing/runners/reality_testing.rb +90 -0
- data/lib/legion/extensions/reality_testing/version.rb +9 -0
- data/lib/legion/extensions/reality_testing.rb +15 -0
- data/spec/legion/extensions/reality_testing/client_spec.rb +29 -0
- data/spec/legion/extensions/reality_testing/helpers/belief_spec.rb +197 -0
- data/spec/legion/extensions/reality_testing/helpers/constants_spec.rb +78 -0
- data/spec/legion/extensions/reality_testing/helpers/reality_engine_spec.rb +191 -0
- data/spec/legion/extensions/reality_testing/runners/reality_testing_spec.rb +154 -0
- data/spec/spec_helper.rb +20 -0
- metadata +76 -0
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,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,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
|
data/spec/spec_helper.rb
ADDED
|
@@ -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: []
|