lex-trust 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,359 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'sequel'
5
+ require 'tmpdir'
6
+
7
+ RSpec.describe 'lex-trust local SQLite persistence' do
8
+ let(:db_path) { File.join(Dir.tmpdir, "trust_test_#{Process.pid}_#{rand(9999)}.db") }
9
+ let(:db) { Sequel.sqlite(db_path) }
10
+
11
+ before do
12
+ db.create_table(:trust_entries) do
13
+ primary_key :id
14
+ String :agent_id, null: false
15
+ String :domain, null: false
16
+ Float :reliability, default: 0.3
17
+ Float :competence, default: 0.3
18
+ Float :integrity, default: 0.3
19
+ Float :benevolence, default: 0.3
20
+ Float :composite, default: 0.3
21
+ Integer :interaction_count, default: 0
22
+ Integer :positive_count, default: 0
23
+ Integer :negative_count, default: 0
24
+ DateTime :last_interaction
25
+ DateTime :created_at, null: false
26
+ unique %i[agent_id domain]
27
+ index [:agent_id]
28
+ end
29
+
30
+ stub_const('Legion::Data::Local', Module.new do
31
+ def self.connected?
32
+ true
33
+ end
34
+
35
+ def self.connection
36
+ @_connection
37
+ end
38
+
39
+ def self._set_connection(conn)
40
+ @_connection = conn
41
+ end
42
+ end)
43
+
44
+ Legion::Data::Local._set_connection(db)
45
+ end
46
+
47
+ after do
48
+ db.disconnect
49
+ FileUtils.rm_f(db_path)
50
+ end
51
+
52
+ describe 'save_to_local' do
53
+ it 'writes an entry to the database' do
54
+ map = Legion::Extensions::Trust::Helpers::TrustMap.new
55
+ map.record_interaction('agent-001', positive: true)
56
+ map.save_to_local
57
+
58
+ row = db[:trust_entries].where(agent_id: 'agent-001', domain: 'general').first
59
+ expect(row).not_to be_nil
60
+ expect(row[:agent_id]).to eq('agent-001')
61
+ expect(row[:domain]).to eq('general')
62
+ expect(row[:positive_count]).to eq(1)
63
+ expect(row[:interaction_count]).to eq(1)
64
+ end
65
+
66
+ it 'updates an existing row on second save' do
67
+ map = Legion::Extensions::Trust::Helpers::TrustMap.new
68
+ map.record_interaction('agent-001', positive: true)
69
+ map.save_to_local
70
+
71
+ map.record_interaction('agent-001', positive: false)
72
+ map.save_to_local
73
+
74
+ rows = db[:trust_entries].where(agent_id: 'agent-001', domain: 'general').all
75
+ expect(rows.size).to eq(1)
76
+ expect(rows.first[:negative_count]).to eq(1)
77
+ expect(rows.first[:interaction_count]).to eq(2)
78
+ end
79
+
80
+ it 'persists composite score' do
81
+ map = Legion::Extensions::Trust::Helpers::TrustMap.new
82
+ map.record_interaction('agent-001', positive: true)
83
+ map.save_to_local
84
+
85
+ row = db[:trust_entries].where(agent_id: 'agent-001', domain: 'general').first
86
+ expect(row[:composite]).to be > 0.3
87
+ end
88
+
89
+ it 'persists all four dimension scores' do
90
+ map = Legion::Extensions::Trust::Helpers::TrustMap.new
91
+ map.record_interaction('agent-001', positive: true)
92
+ map.save_to_local
93
+
94
+ row = db[:trust_entries].where(agent_id: 'agent-001', domain: 'general').first
95
+ expect(row[:reliability]).to be > 0.3
96
+ expect(row[:competence]).to be > 0.3
97
+ expect(row[:integrity]).to be > 0.3
98
+ expect(row[:benevolence]).to be > 0.3
99
+ end
100
+
101
+ it 'persists entries for multiple agents' do
102
+ map = Legion::Extensions::Trust::Helpers::TrustMap.new
103
+ map.record_interaction('agent-a', positive: true)
104
+ map.record_interaction('agent-b', positive: false)
105
+ map.save_to_local
106
+
107
+ expect(db[:trust_entries].count).to eq(2)
108
+ end
109
+
110
+ it 'persists entries for multiple domains of the same agent' do
111
+ map = Legion::Extensions::Trust::Helpers::TrustMap.new
112
+ map.record_interaction('agent-001', positive: true, domain: :code)
113
+ map.record_interaction('agent-001', positive: false, domain: :ops)
114
+ map.save_to_local
115
+
116
+ expect(db[:trust_entries].where(agent_id: 'agent-001').count).to eq(2)
117
+ end
118
+
119
+ it 'removes DB rows for entries removed from memory' do
120
+ map = Legion::Extensions::Trust::Helpers::TrustMap.new
121
+ map.get_or_create('agent-a')
122
+ map.get_or_create('agent-b')
123
+ map.save_to_local
124
+
125
+ expect(db[:trust_entries].count).to eq(2)
126
+
127
+ # Simulate removal: build new map with only agent-a, then save
128
+ map2 = Legion::Extensions::Trust::Helpers::TrustMap.new
129
+ map2.save_to_local # loaded agent-a and agent-b from DB, both still in memory
130
+ expect(db[:trust_entries].count).to eq(2) # nothing deleted yet
131
+
132
+ # Remove agent-b from memory then save
133
+ map2.entries.delete_if { |k, _| k.start_with?('agent-b') }
134
+ map2.save_to_local
135
+
136
+ expect(db[:trust_entries].count).to eq(1)
137
+ expect(db[:trust_entries].first[:agent_id]).to eq('agent-a')
138
+ end
139
+ end
140
+
141
+ describe 'load_from_local' do
142
+ it 'restores entries from the database on initialize' do
143
+ db[:trust_entries].insert(
144
+ agent_id: 'agent-001',
145
+ domain: 'general',
146
+ reliability: 0.45,
147
+ competence: 0.45,
148
+ integrity: 0.45,
149
+ benevolence: 0.45,
150
+ composite: 0.45,
151
+ interaction_count: 3,
152
+ positive_count: 3,
153
+ negative_count: 0,
154
+ created_at: Time.now.utc
155
+ )
156
+
157
+ map = Legion::Extensions::Trust::Helpers::TrustMap.new
158
+ entry = map.get('agent-001')
159
+ expect(entry).not_to be_nil
160
+ expect(entry[:composite]).to be_within(0.0001).of(0.45)
161
+ expect(entry[:interaction_count]).to eq(3)
162
+ expect(entry[:positive_count]).to eq(3)
163
+ end
164
+
165
+ it 'restores domain as a symbol' do
166
+ db[:trust_entries].insert(
167
+ agent_id: 'agent-001',
168
+ domain: 'code',
169
+ reliability: 0.3,
170
+ competence: 0.3,
171
+ integrity: 0.3,
172
+ benevolence: 0.3,
173
+ composite: 0.3,
174
+ interaction_count: 0,
175
+ positive_count: 0,
176
+ negative_count: 0,
177
+ created_at: Time.now.utc
178
+ )
179
+
180
+ map = Legion::Extensions::Trust::Helpers::TrustMap.new
181
+ entry = map.get('agent-001', domain: :code)
182
+ expect(entry).not_to be_nil
183
+ expect(entry[:domain]).to eq(:code)
184
+ end
185
+
186
+ it 'restores all four dimension values' do
187
+ db[:trust_entries].insert(
188
+ agent_id: 'agent-001',
189
+ domain: 'general',
190
+ reliability: 0.6,
191
+ competence: 0.5,
192
+ integrity: 0.4,
193
+ benevolence: 0.3,
194
+ composite: 0.45,
195
+ interaction_count: 0,
196
+ positive_count: 0,
197
+ negative_count: 0,
198
+ created_at: Time.now.utc
199
+ )
200
+
201
+ map = Legion::Extensions::Trust::Helpers::TrustMap.new
202
+ entry = map.get('agent-001')
203
+ expect(entry[:dimensions][:reliability]).to be_within(0.0001).of(0.6)
204
+ expect(entry[:dimensions][:competence]).to be_within(0.0001).of(0.5)
205
+ expect(entry[:dimensions][:integrity]).to be_within(0.0001).of(0.4)
206
+ expect(entry[:dimensions][:benevolence]).to be_within(0.0001).of(0.3)
207
+ end
208
+
209
+ it 'restores multiple entries' do
210
+ db[:trust_entries].insert(
211
+ agent_id: 'agent-a', domain: 'general',
212
+ reliability: 0.3, competence: 0.3, integrity: 0.3, benevolence: 0.3,
213
+ composite: 0.3, interaction_count: 0, positive_count: 0, negative_count: 0,
214
+ created_at: Time.now.utc
215
+ )
216
+ db[:trust_entries].insert(
217
+ agent_id: 'agent-b', domain: 'ops',
218
+ reliability: 0.3, competence: 0.3, integrity: 0.3, benevolence: 0.3,
219
+ composite: 0.3, interaction_count: 0, positive_count: 0, negative_count: 0,
220
+ created_at: Time.now.utc
221
+ )
222
+
223
+ map = Legion::Extensions::Trust::Helpers::TrustMap.new
224
+ expect(map.count).to eq(2)
225
+ end
226
+
227
+ it 'allows normal operations on restored entries' do
228
+ db[:trust_entries].insert(
229
+ agent_id: 'agent-001',
230
+ domain: 'general',
231
+ reliability: 0.4,
232
+ competence: 0.4,
233
+ integrity: 0.4,
234
+ benevolence: 0.4,
235
+ composite: 0.4,
236
+ interaction_count: 2,
237
+ positive_count: 2,
238
+ negative_count: 0,
239
+ created_at: Time.now.utc
240
+ )
241
+
242
+ map = Legion::Extensions::Trust::Helpers::TrustMap.new
243
+ map.record_interaction('agent-001', positive: true)
244
+ entry = map.get('agent-001')
245
+ expect(entry[:interaction_count]).to eq(3)
246
+ expect(entry[:positive_count]).to eq(3)
247
+ end
248
+ end
249
+
250
+ describe 'round-trip persistence' do
251
+ it 'saves and restores trust score across instances' do
252
+ map1 = Legion::Extensions::Trust::Helpers::TrustMap.new
253
+ 5.times { map1.record_interaction('agent-001', positive: true) }
254
+ composite_before = map1.get('agent-001')[:composite]
255
+ map1.save_to_local
256
+
257
+ map2 = Legion::Extensions::Trust::Helpers::TrustMap.new
258
+ expect(map2.get('agent-001')[:composite]).to be_within(0.0001).of(composite_before)
259
+ end
260
+
261
+ it 'round-trips interaction counts accurately' do
262
+ map1 = Legion::Extensions::Trust::Helpers::TrustMap.new
263
+ 3.times { map1.record_interaction('agent-001', positive: true) }
264
+ 2.times { map1.record_interaction('agent-001', positive: false) }
265
+ map1.save_to_local
266
+
267
+ map2 = Legion::Extensions::Trust::Helpers::TrustMap.new
268
+ entry = map2.get('agent-001')
269
+ expect(entry[:interaction_count]).to eq(5)
270
+ expect(entry[:positive_count]).to eq(3)
271
+ expect(entry[:negative_count]).to eq(2)
272
+ end
273
+
274
+ it 'round-trips multiple agents independently' do
275
+ map1 = Legion::Extensions::Trust::Helpers::TrustMap.new
276
+ 5.times { map1.record_interaction('agent-high', positive: true) }
277
+ map1.record_interaction('agent-low', positive: false)
278
+ map1.save_to_local
279
+
280
+ map2 = Legion::Extensions::Trust::Helpers::TrustMap.new
281
+ expect(map2.get('agent-high')[:composite]).to be > map2.get('agent-low')[:composite]
282
+ end
283
+
284
+ it 'round-trips domain-scoped entries independently' do
285
+ map1 = Legion::Extensions::Trust::Helpers::TrustMap.new
286
+ 5.times { map1.record_interaction('agent-001', positive: true, domain: :code) }
287
+ map1.record_interaction('agent-001', positive: false, domain: :ops)
288
+ map1.save_to_local
289
+
290
+ map2 = Legion::Extensions::Trust::Helpers::TrustMap.new
291
+ code_entry = map2.get('agent-001', domain: :code)
292
+ ops_entry = map2.get('agent-001', domain: :ops)
293
+ expect(code_entry[:composite]).to be > ops_entry[:composite]
294
+ end
295
+
296
+ it 'trusted_agents works correctly after round-trip' do
297
+ map1 = Legion::Extensions::Trust::Helpers::TrustMap.new
298
+ 5.times { map1.record_interaction('agent-001', positive: true) }
299
+ map1.save_to_local
300
+
301
+ map2 = Legion::Extensions::Trust::Helpers::TrustMap.new
302
+ trusted = map2.trusted_agents
303
+ expect(trusted).not_to be_empty
304
+ expect(trusted.first[:agent_id]).to eq('agent-001')
305
+ end
306
+ end
307
+
308
+ describe 'graceful no-op when Legion::Data::Local is unavailable' do
309
+ before do
310
+ hide_const('Legion::Data::Local')
311
+ end
312
+
313
+ it 'initialize completes without raising' do
314
+ expect { Legion::Extensions::Trust::Helpers::TrustMap.new }.not_to raise_error
315
+ end
316
+
317
+ it 'save_to_local does nothing without raising' do
318
+ map = Legion::Extensions::Trust::Helpers::TrustMap.new
319
+ map.record_interaction('agent-001', positive: true)
320
+ expect { map.save_to_local }.not_to raise_error
321
+ end
322
+
323
+ it 'starts with empty in-memory state' do
324
+ map = Legion::Extensions::Trust::Helpers::TrustMap.new
325
+ expect(map.count).to eq(0)
326
+ end
327
+
328
+ it 'operates normally in memory when DB unavailable' do
329
+ map = Legion::Extensions::Trust::Helpers::TrustMap.new
330
+ map.record_interaction('agent-001', positive: true)
331
+ expect(map.get('agent-001')[:positive_count]).to eq(1)
332
+ end
333
+ end
334
+
335
+ describe 'graceful no-op when Legion::Data::Local is defined but not connected' do
336
+ before do
337
+ stub_const('Legion::Data::Local', Module.new do
338
+ def self.connected?
339
+ false
340
+ end
341
+ end)
342
+ end
343
+
344
+ it 'initialize completes without raising' do
345
+ expect { Legion::Extensions::Trust::Helpers::TrustMap.new }.not_to raise_error
346
+ end
347
+
348
+ it 'save_to_local does nothing without raising' do
349
+ map = Legion::Extensions::Trust::Helpers::TrustMap.new
350
+ map.record_interaction('agent-001', positive: true)
351
+ expect { map.save_to_local }.not_to raise_error
352
+ end
353
+
354
+ it 'starts with empty in-memory state' do
355
+ map = Legion::Extensions::Trust::Helpers::TrustMap.new
356
+ expect(map.count).to eq(0)
357
+ end
358
+ end
359
+ 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/trust'
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,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lex-trust
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: sequel
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '5.70'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '5.70'
26
+ - !ruby/object:Gem::Dependency
27
+ name: sqlite3
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ description: Emergent domain-specific trust modeling for brain-modeled agentic AI
41
+ email:
42
+ - matthewdiverson@gmail.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - Gemfile
48
+ - lex-trust.gemspec
49
+ - lib/legion/extensions/trust.rb
50
+ - lib/legion/extensions/trust/actors/decay.rb
51
+ - lib/legion/extensions/trust/client.rb
52
+ - lib/legion/extensions/trust/helpers/trust_map.rb
53
+ - lib/legion/extensions/trust/helpers/trust_model.rb
54
+ - lib/legion/extensions/trust/local_migrations/20260316000020_create_trust_entries.rb
55
+ - lib/legion/extensions/trust/runners/trust.rb
56
+ - lib/legion/extensions/trust/version.rb
57
+ - spec/legion/extensions/trust/actors/decay_spec.rb
58
+ - spec/legion/extensions/trust/client_spec.rb
59
+ - spec/legion/extensions/trust/helpers/trust_map_spec.rb
60
+ - spec/legion/extensions/trust/helpers/trust_model_spec.rb
61
+ - spec/legion/extensions/trust/runners/trust_spec.rb
62
+ - spec/local_persistence_spec.rb
63
+ - spec/spec_helper.rb
64
+ homepage: https://github.com/LegionIO/lex-trust
65
+ licenses:
66
+ - MIT
67
+ metadata:
68
+ homepage_uri: https://github.com/LegionIO/lex-trust
69
+ source_code_uri: https://github.com/LegionIO/lex-trust
70
+ documentation_uri: https://github.com/LegionIO/lex-trust
71
+ changelog_uri: https://github.com/LegionIO/lex-trust
72
+ bug_tracker_uri: https://github.com/LegionIO/lex-trust/issues
73
+ rubygems_mfa_required: 'true'
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '3.4'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubygems_version: 3.6.9
89
+ specification_version: 4
90
+ summary: LEX Trust
91
+ test_files: []