lex-subliminal 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/.github/workflows/ci.yml +16 -0
- data/.gitignore +10 -0
- data/.rspec +3 -0
- data/.rubocop.yml +37 -0
- data/CLAUDE.md +120 -0
- data/Gemfile +11 -0
- data/README.md +66 -0
- data/lex-subliminal.gemspec +29 -0
- data/lib/legion/extensions/subliminal/client.rb +15 -0
- data/lib/legion/extensions/subliminal/helpers/constants.rb +70 -0
- data/lib/legion/extensions/subliminal/helpers/influence_event.rb +40 -0
- data/lib/legion/extensions/subliminal/helpers/subliminal_engine.rb +145 -0
- data/lib/legion/extensions/subliminal/helpers/subliminal_trace.rb +97 -0
- data/lib/legion/extensions/subliminal/runners/subliminal.rb +67 -0
- data/lib/legion/extensions/subliminal/version.rb +9 -0
- data/lib/legion/extensions/subliminal.rb +16 -0
- data/spec/legion/extensions/subliminal/client_spec.rb +21 -0
- data/spec/legion/extensions/subliminal/helpers/influence_event_spec.rb +58 -0
- data/spec/legion/extensions/subliminal/helpers/subliminal_engine_spec.rb +161 -0
- data/spec/legion/extensions/subliminal/helpers/subliminal_trace_spec.rb +168 -0
- data/spec/legion/extensions/subliminal/runners_spec.rb +78 -0
- data/spec/legion/extensions/subliminal_spec.rb +7 -0
- data/spec/spec_helper.rb +28 -0
- metadata +84 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Subliminal
|
|
6
|
+
module Runners
|
|
7
|
+
module Subliminal
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
|
|
9
|
+
|
|
10
|
+
def create_subliminal_trace(content:, trace_type: :associative, domain: :general, activation: nil,
|
|
11
|
+
influence_target: :attention, engine: nil, **)
|
|
12
|
+
eng = engine || @default_engine
|
|
13
|
+
trace = eng.create_trace(content: content, trace_type: trace_type, domain: domain,
|
|
14
|
+
activation: activation || Helpers::Constants::DEFAULT_ACTIVATION,
|
|
15
|
+
influence_target: influence_target)
|
|
16
|
+
{ success: true, trace: trace.to_h }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def boost_trace(trace_id:, amount: nil, engine: nil, **)
|
|
20
|
+
eng = engine || @default_engine
|
|
21
|
+
trace = eng.boost_trace(trace_id: trace_id, amount: amount || Helpers::Constants::ACTIVATION_BOOST)
|
|
22
|
+
return { success: false, error: 'trace not found' } unless trace
|
|
23
|
+
|
|
24
|
+
{ success: true, trace: trace.to_h }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def process_influences(engine: nil, **)
|
|
28
|
+
eng = engine || @default_engine
|
|
29
|
+
result = eng.process_influences!
|
|
30
|
+
{ success: true, **result }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def decay_all(engine: nil, **)
|
|
34
|
+
eng = engine || @default_engine
|
|
35
|
+
result = eng.decay_all!
|
|
36
|
+
{ success: true, **result }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def active_traces(engine: nil, **)
|
|
40
|
+
eng = engine || @default_engine
|
|
41
|
+
traces = eng.active_traces
|
|
42
|
+
{ success: true, count: traces.size, traces: traces.map(&:to_h) }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def near_threshold(engine: nil, **)
|
|
46
|
+
eng = engine || @default_engine
|
|
47
|
+
traces = eng.near_threshold_traces
|
|
48
|
+
{ success: true, count: traces.size, traces: traces.map(&:to_h) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def influence_on(target:, engine: nil, **)
|
|
52
|
+
eng = engine || @default_engine
|
|
53
|
+
level = eng.influence_on(target: target)
|
|
54
|
+
label = Helpers::Constants.label_for(Helpers::Constants::INFLUENCE_LABELS, level)
|
|
55
|
+
{ success: true, target: target, influence: level, label: label }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def subliminal_status(engine: nil, **)
|
|
59
|
+
eng = engine || @default_engine
|
|
60
|
+
report = eng.subliminal_report
|
|
61
|
+
{ success: true, **report }
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'subliminal/version'
|
|
4
|
+
require_relative 'subliminal/helpers/constants'
|
|
5
|
+
require_relative 'subliminal/helpers/subliminal_trace'
|
|
6
|
+
require_relative 'subliminal/helpers/influence_event'
|
|
7
|
+
require_relative 'subliminal/helpers/subliminal_engine'
|
|
8
|
+
require_relative 'subliminal/runners/subliminal'
|
|
9
|
+
require_relative 'subliminal/client'
|
|
10
|
+
|
|
11
|
+
module Legion
|
|
12
|
+
module Extensions
|
|
13
|
+
module Subliminal
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Subliminal::Client do
|
|
4
|
+
subject(:client) { described_class.new }
|
|
5
|
+
|
|
6
|
+
it 'responds to runner methods' do
|
|
7
|
+
expect(client).to respond_to(:create_subliminal_trace, :process_influences, :subliminal_status)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'runs a full subliminal lifecycle' do
|
|
11
|
+
result = client.create_subliminal_trace(content: 'danger signal', domain: :security)
|
|
12
|
+
trace_id = result[:trace][:id]
|
|
13
|
+
|
|
14
|
+
client.boost_trace(trace_id: trace_id)
|
|
15
|
+
client.process_influences
|
|
16
|
+
client.decay_all
|
|
17
|
+
|
|
18
|
+
status = client.subliminal_status
|
|
19
|
+
expect(status[:total_traces]).to be >= 1
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Subliminal::Helpers::InfluenceEvent do
|
|
4
|
+
subject(:event) do
|
|
5
|
+
described_class.new(trace_id: 'abc-123', target: :attention, magnitude: 0.08, domain: :security)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
describe '#initialize' do
|
|
9
|
+
it 'assigns a UUID id' do
|
|
10
|
+
expect(event.id).to match(/\A[0-9a-f-]{36}\z/)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'stores trace_id' do
|
|
14
|
+
expect(event.trace_id).to eq('abc-123')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'stores target' do
|
|
18
|
+
expect(event.target).to eq(:attention)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'stores magnitude' do
|
|
22
|
+
expect(event.magnitude).to eq(0.08)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'stores domain' do
|
|
26
|
+
expect(event.domain).to eq(:security)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe '#significant?' do
|
|
31
|
+
it 'is true for magnitude >= 0.05' do
|
|
32
|
+
expect(event.significant?).to be true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'is false for small magnitude' do
|
|
36
|
+
small = described_class.new(trace_id: 'x', target: :emotion, magnitude: 0.03)
|
|
37
|
+
expect(small.significant?).to be false
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe '#subtle?' do
|
|
42
|
+
it 'is true for small positive magnitude' do
|
|
43
|
+
small = described_class.new(trace_id: 'x', target: :emotion, magnitude: 0.03)
|
|
44
|
+
expect(small.subtle?).to be true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'is false for significant magnitude' do
|
|
48
|
+
expect(event.subtle?).to be false
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe '#to_h' do
|
|
53
|
+
it 'includes all fields' do
|
|
54
|
+
hash = event.to_h
|
|
55
|
+
expect(hash).to include(:id, :trace_id, :target, :magnitude, :domain, :created_at)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Subliminal::Helpers::SubliminalEngine do
|
|
4
|
+
subject(:engine) { described_class.new }
|
|
5
|
+
|
|
6
|
+
describe '#create_trace' do
|
|
7
|
+
it 'creates and stores a trace' do
|
|
8
|
+
trace = engine.create_trace(content: 'faint memory')
|
|
9
|
+
expect(trace.content).to eq('faint memory')
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe '#boost_trace' do
|
|
14
|
+
it 'boosts activation' do
|
|
15
|
+
trace = engine.create_trace(content: 'test')
|
|
16
|
+
original = trace.activation
|
|
17
|
+
engine.boost_trace(trace_id: trace.id)
|
|
18
|
+
expect(trace.activation).to be > original
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'returns nil for unknown trace' do
|
|
22
|
+
expect(engine.boost_trace(trace_id: 'bad')).to be_nil
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe '#process_influences!' do
|
|
27
|
+
it 'generates influence events from active traces' do
|
|
28
|
+
engine.create_trace(content: 'a', activation: 0.3)
|
|
29
|
+
result = engine.process_influences!
|
|
30
|
+
expect(result[:influences_generated]).to be >= 1
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'returns empty when no active traces' do
|
|
34
|
+
result = engine.process_influences!
|
|
35
|
+
expect(result[:influences_generated]).to eq(0)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe '#decay_all!' do
|
|
40
|
+
it 'decays all traces' do
|
|
41
|
+
trace = engine.create_trace(content: 'test')
|
|
42
|
+
original = trace.activation
|
|
43
|
+
engine.decay_all!
|
|
44
|
+
expect(trace.activation).to be < original
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
describe '#active_traces' do
|
|
49
|
+
it 'returns non-extinct traces' do
|
|
50
|
+
engine.create_trace(content: 'active', activation: 0.2)
|
|
51
|
+
expect(engine.active_traces.size).to eq(1)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe '#near_threshold_traces' do
|
|
56
|
+
it 'returns traces near conscious threshold' do
|
|
57
|
+
engine.create_trace(content: 'near', activation: 0.35)
|
|
58
|
+
engine.create_trace(content: 'far', activation: 0.1)
|
|
59
|
+
expect(engine.near_threshold_traces.size).to eq(1)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
describe '#potent_traces' do
|
|
64
|
+
it 'returns traces with activation >= 0.2' do
|
|
65
|
+
engine.create_trace(content: 'potent', activation: 0.25)
|
|
66
|
+
engine.create_trace(content: 'weak', activation: 0.05)
|
|
67
|
+
expect(engine.potent_traces.size).to eq(1)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
describe '#traces_by_domain' do
|
|
72
|
+
it 'filters by domain' do
|
|
73
|
+
engine.create_trace(content: 'a', domain: :security)
|
|
74
|
+
engine.create_trace(content: 'b', domain: :memory)
|
|
75
|
+
expect(engine.traces_by_domain(domain: :security).size).to eq(1)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
describe '#traces_by_type' do
|
|
80
|
+
it 'filters by trace type' do
|
|
81
|
+
engine.create_trace(content: 'a', trace_type: :emotional)
|
|
82
|
+
engine.create_trace(content: 'b', trace_type: :procedural)
|
|
83
|
+
expect(engine.traces_by_type(trace_type: :emotional).size).to eq(1)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
describe '#traces_by_target' do
|
|
88
|
+
it 'filters by influence target' do
|
|
89
|
+
engine.create_trace(content: 'a', influence_target: :emotion)
|
|
90
|
+
engine.create_trace(content: 'b', influence_target: :decision)
|
|
91
|
+
expect(engine.traces_by_target(target: :emotion).size).to eq(1)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
describe '#influence_on' do
|
|
96
|
+
it 'returns 0.0 with no influences' do
|
|
97
|
+
expect(engine.influence_on(target: :attention)).to eq(0.0)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it 'returns accumulated influence after processing' do
|
|
101
|
+
engine.create_trace(content: 'a', activation: 0.3, influence_target: :attention)
|
|
102
|
+
engine.process_influences!
|
|
103
|
+
expect(engine.influence_on(target: :attention)).to be > 0.0
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
describe '#domain_saturation' do
|
|
108
|
+
it 'returns 0.0 for empty domain' do
|
|
109
|
+
expect(engine.domain_saturation(domain: :security)).to eq(0.0)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it 'returns positive for populated domain' do
|
|
113
|
+
engine.create_trace(content: 'a', domain: :security, activation: 0.3)
|
|
114
|
+
expect(engine.domain_saturation(domain: :security)).to be > 0.0
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
describe '#overall_subliminal_load' do
|
|
119
|
+
it 'returns 0.0 with no traces' do
|
|
120
|
+
expect(engine.overall_subliminal_load).to eq(0.0)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it 'returns positive with active traces' do
|
|
124
|
+
engine.create_trace(content: 'a', activation: 0.3)
|
|
125
|
+
expect(engine.overall_subliminal_load).to be > 0.0
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
describe '#strongest_traces' do
|
|
130
|
+
it 'returns sorted by activation descending' do
|
|
131
|
+
engine.create_trace(content: 'weak', activation: 0.1)
|
|
132
|
+
strong = engine.create_trace(content: 'strong', activation: 0.35)
|
|
133
|
+
expect(engine.strongest_traces(limit: 1).first.id).to eq(strong.id)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
describe '#breached_traces' do
|
|
138
|
+
it 'returns empty when all below threshold' do
|
|
139
|
+
engine.create_trace(content: 'below', activation: 0.3)
|
|
140
|
+
expect(engine.breached_traces).to be_empty
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
describe '#subliminal_report' do
|
|
145
|
+
it 'includes all report fields' do
|
|
146
|
+
report = engine.subliminal_report
|
|
147
|
+
expect(report).to include(
|
|
148
|
+
:total_traces, :active_count, :near_threshold, :potent_count,
|
|
149
|
+
:total_influences, :subliminal_load, :saturation_label,
|
|
150
|
+
:breached_count, :strongest
|
|
151
|
+
)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
describe '#to_h' do
|
|
156
|
+
it 'includes summary fields' do
|
|
157
|
+
hash = engine.to_h
|
|
158
|
+
expect(hash).to include(:total_traces, :active, :subliminal_load, :total_influences)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Subliminal::Helpers::SubliminalTrace do
|
|
4
|
+
subject(:trace) { described_class.new(content: 'faint memory of danger') }
|
|
5
|
+
|
|
6
|
+
describe '#initialize' do
|
|
7
|
+
it 'assigns a UUID id' do
|
|
8
|
+
expect(trace.id).to match(/\A[0-9a-f-]{36}\z/)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'stores content' do
|
|
12
|
+
expect(trace.content).to eq('faint memory of danger')
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'defaults trace_type to :associative' do
|
|
16
|
+
expect(trace.trace_type).to eq(:associative)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'defaults domain to :general' do
|
|
20
|
+
expect(trace.domain).to eq(:general)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'defaults activation to DEFAULT_ACTIVATION' do
|
|
24
|
+
expect(trace.activation).to eq(0.2)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'clamps activation to subliminal ceiling' do
|
|
28
|
+
high = described_class.new(content: 'x', activation: 0.9)
|
|
29
|
+
expect(high.activation).to eq(0.39)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'clamps activation to subliminal floor' do
|
|
33
|
+
low = described_class.new(content: 'x', activation: 0.001)
|
|
34
|
+
expect(low.activation).to eq(0.02)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'defaults influence_target to :attention' do
|
|
38
|
+
expect(trace.influence_target).to eq(:attention)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'validates trace_type' do
|
|
42
|
+
bad = described_class.new(content: 'x', trace_type: :nonexistent)
|
|
43
|
+
expect(bad.trace_type).to eq(:associative)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'validates influence_target' do
|
|
47
|
+
bad = described_class.new(content: 'x', influence_target: :nonexistent)
|
|
48
|
+
expect(bad.influence_target).to eq(:attention)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe '#boost!' do
|
|
53
|
+
it 'increases activation' do
|
|
54
|
+
original = trace.activation
|
|
55
|
+
trace.boost!
|
|
56
|
+
expect(trace.activation).to be > original
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'clamps at subliminal ceiling' do
|
|
60
|
+
10.times { trace.boost! }
|
|
61
|
+
expect(trace.activation).to eq(0.39)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
describe '#decay!' do
|
|
66
|
+
it 'decreases activation' do
|
|
67
|
+
original = trace.activation
|
|
68
|
+
trace.decay!
|
|
69
|
+
expect(trace.activation).to be < original
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'clamps at 0.0' do
|
|
73
|
+
50.times { trace.decay! }
|
|
74
|
+
expect(trace.activation).to eq(0.0)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
describe '#exert_influence!' do
|
|
79
|
+
it 'returns influence magnitude' do
|
|
80
|
+
mag = trace.exert_influence!
|
|
81
|
+
expect(mag).to be > 0.0
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it 'increments influence_count' do
|
|
85
|
+
trace.exert_influence!
|
|
86
|
+
expect(trace.influence_count).to eq(1)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'slightly decreases activation' do
|
|
90
|
+
original = trace.activation
|
|
91
|
+
trace.exert_influence!
|
|
92
|
+
expect(trace.activation).to be < original
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
describe '#near_threshold?' do
|
|
97
|
+
it 'is false at default activation' do
|
|
98
|
+
expect(trace.near_threshold?).to be false
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'is true when near conscious threshold' do
|
|
102
|
+
near = described_class.new(content: 'x', activation: 0.35)
|
|
103
|
+
expect(near.near_threshold?).to be true
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
describe '#active?' do
|
|
108
|
+
it 'is true at default activation' do
|
|
109
|
+
expect(trace.active?).to be true
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it 'is false when fully decayed' do
|
|
113
|
+
50.times { trace.decay! }
|
|
114
|
+
expect(trace.active?).to be false
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
describe '#extinct?' do
|
|
119
|
+
it 'is false at default activation' do
|
|
120
|
+
expect(trace.extinct?).to be false
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it 'is true when nearly zero' do
|
|
124
|
+
50.times { trace.decay! }
|
|
125
|
+
expect(trace.extinct?).to be true
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
describe '#potent?' do
|
|
130
|
+
it 'is true at default activation' do
|
|
131
|
+
expect(trace.potent?).to be true
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it 'is false when low' do
|
|
135
|
+
faint = described_class.new(content: 'x', activation: 0.1)
|
|
136
|
+
expect(faint.potent?).to be false
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
describe '#breached_threshold?' do
|
|
141
|
+
it 'is false by default' do
|
|
142
|
+
expect(trace.breached_threshold?).to be false
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
describe '#persistence' do
|
|
147
|
+
it 'is 1.0 initially' do
|
|
148
|
+
expect(trace.persistence).to eq(1.0)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it 'decreases after decay' do
|
|
152
|
+
trace.decay!
|
|
153
|
+
expect(trace.persistence).to be < 1.0
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
describe '#to_h' do
|
|
158
|
+
it 'includes all fields' do
|
|
159
|
+
hash = trace.to_h
|
|
160
|
+
expect(hash).to include(
|
|
161
|
+
:id, :content, :trace_type, :domain, :activation,
|
|
162
|
+
:original_activation, :influence_target, :influence_count,
|
|
163
|
+
:influence_magnitude, :near_threshold, :active, :extinct,
|
|
164
|
+
:activation_label, :persistence, :created_at
|
|
165
|
+
)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Subliminal::Runners::Subliminal do
|
|
4
|
+
let(:engine) { Legion::Extensions::Subliminal::Helpers::SubliminalEngine.new }
|
|
5
|
+
let(:runner) do
|
|
6
|
+
obj = Object.new
|
|
7
|
+
obj.extend(described_class)
|
|
8
|
+
obj.instance_variable_set(:@default_engine, engine)
|
|
9
|
+
obj
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe '#create_subliminal_trace' do
|
|
13
|
+
it 'returns success with trace hash' do
|
|
14
|
+
result = runner.create_subliminal_trace(content: 'test', engine: engine)
|
|
15
|
+
expect(result[:success]).to be true
|
|
16
|
+
expect(result[:trace][:content]).to eq('test')
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe '#boost_trace' do
|
|
21
|
+
it 'returns success for known trace' do
|
|
22
|
+
trace = engine.create_trace(content: 'test')
|
|
23
|
+
result = runner.boost_trace(trace_id: trace.id, engine: engine)
|
|
24
|
+
expect(result[:success]).to be true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'returns failure for unknown trace' do
|
|
28
|
+
result = runner.boost_trace(trace_id: 'bad', engine: engine)
|
|
29
|
+
expect(result[:success]).to be false
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe '#process_influences' do
|
|
34
|
+
it 'returns success' do
|
|
35
|
+
result = runner.process_influences(engine: engine)
|
|
36
|
+
expect(result[:success]).to be true
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe '#decay_all' do
|
|
41
|
+
it 'returns success' do
|
|
42
|
+
result = runner.decay_all(engine: engine)
|
|
43
|
+
expect(result[:success]).to be true
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe '#active_traces' do
|
|
48
|
+
it 'returns active list' do
|
|
49
|
+
engine.create_trace(content: 'test')
|
|
50
|
+
result = runner.active_traces(engine: engine)
|
|
51
|
+
expect(result[:count]).to eq(1)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe '#near_threshold' do
|
|
56
|
+
it 'returns near-threshold list' do
|
|
57
|
+
engine.create_trace(content: 'near', activation: 0.35)
|
|
58
|
+
result = runner.near_threshold(engine: engine)
|
|
59
|
+
expect(result[:count]).to eq(1)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
describe '#influence_on' do
|
|
64
|
+
it 'returns influence level' do
|
|
65
|
+
result = runner.influence_on(target: :attention, engine: engine)
|
|
66
|
+
expect(result[:success]).to be true
|
|
67
|
+
expect(result[:influence]).to eq(0.0)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
describe '#subliminal_status' do
|
|
72
|
+
it 'returns comprehensive status' do
|
|
73
|
+
result = runner.subliminal_status(engine: engine)
|
|
74
|
+
expect(result[:success]).to be true
|
|
75
|
+
expect(result).to include(:total_traces, :subliminal_load)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/subliminal'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module Helpers
|
|
8
|
+
module Lex; end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
module Logging
|
|
13
|
+
def self.method_missing(_, *) = nil
|
|
14
|
+
def self.respond_to_missing?(_, _ = false) = true
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
RSpec.configure do |config|
|
|
19
|
+
config.expect_with :rspec do |expectations|
|
|
20
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
|
21
|
+
end
|
|
22
|
+
config.mock_with :rspec do |mocks|
|
|
23
|
+
mocks.verify_partial_doubles = true
|
|
24
|
+
end
|
|
25
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
|
26
|
+
config.order = :random
|
|
27
|
+
Kernel.srand config.seed
|
|
28
|
+
end
|