Class: Legion::Gaia::BondStore
- Inherits:
-
Object
- Object
- Legion::Gaia::BondStore
- Includes:
- Logging::Helper
- Defined in:
- lib/legion/gaia/bond_store.rb
Overview
Per-identity bond evidence accumulator. Tracks a 0..1 strength score that grows through observed interactions. Supports provisional bonds (short-lived, capped at 0.5) and imprint-window acceleration.
Constant Summary collapse
- DEFAULT_WEIGHTS =
Evidence weight categories — sum to 1.0
{ direct_address: 0.20, interaction_frequency: 0.15, response_latency: 0.10, content_depth: 0.15, sentiment: 0.15, consistency: 0.10, helpfulness: 0.10, shared_vulnerability: 0.05 }.freeze
- DEFAULT_BELL_THRESHOLDS =
Bell thresholds — score crosses upward boundary
[0.25, 0.50, 0.75, 0.90, 1.0].freeze
- MAX_EVIDENCE =
Max evidence entries retained per identity (ring buffer)
200- DEFAULT_PROVISIONAL_TTL =
Default provisional TTL — 24 hours
24 * 3600
- DECAY_FLOOR =
Maximum decay factor (floor) — score doesn't drop below 70% of raw
0.7- PROVISIONAL_CEILING =
Provisional strength ceiling until confirmed
0.5
Instance Attribute Summary collapse
-
#bond_role ⇒ Object
readonly
Returns the value of attribute bond_role.
-
#evidence_log ⇒ Object
readonly
Returns the value of attribute evidence_log.
-
#identity ⇒ Object
readonly
Returns the value of attribute identity.
-
#identity_canonical_name ⇒ Object
readonly
Returns the value of attribute identity_canonical_name.
-
#identity_id ⇒ Object
readonly
Returns the value of attribute identity_id.
-
#identity_principal_id ⇒ Object
readonly
Returns the value of attribute identity_principal_id.
-
#provisional_expires_at ⇒ Object
readonly
Returns the value of attribute provisional_expires_at.
-
#provisional_ttl ⇒ Object
readonly
Returns the value of attribute provisional_ttl.
-
#raw_score ⇒ Object
readonly
Returns the value of attribute raw_score.
-
#started_at ⇒ Object
readonly
Returns the value of attribute started_at.
Instance Method Summary collapse
-
#accumulate(observation, imprint_multiplier: 1.0) ⇒ Object
Accumulate evidence from an observation hash.
-
#bell?(new_strength) ⇒ Boolean
Check if a new strength value crosses any uncrossed bell threshold.
-
#confirm! ⇒ Object
Mark this provisional bond as confirmed (remove cap and expiry).
- #dirty? ⇒ Boolean
-
#from_apollo(store:) ⇒ Object
Reconstruct state from Apollo query results (hydration).
-
#initialize(identity:, bond_role:, started_at: Time.now.utc, weights: DEFAULT_WEIGHTS, bell_thresholds: DEFAULT_BELL_THRESHOLDS, provisional_ttl: DEFAULT_PROVISIONAL_TTL) ⇒ BondStore
constructor
A new instance of BondStore.
- #mark_clean! ⇒ Object
- #provisional? ⇒ Boolean
-
#strength ⇒ Object
Returns 0..1 strength, applying time decay and provisional cap.
-
#to_apollo_entries ⇒ Object
Convert to Apollo upsert entries for persistence.
Constructor Details
#initialize(identity:, bond_role:, started_at: Time.now.utc, weights: DEFAULT_WEIGHTS, bell_thresholds: DEFAULT_BELL_THRESHOLDS, provisional_ttl: DEFAULT_PROVISIONAL_TTL) ⇒ BondStore
Returns a new instance of BondStore.
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/legion/gaia/bond_store.rb', line 43 def initialize(identity:, bond_role:, started_at: Time.now.utc, weights: DEFAULT_WEIGHTS, bell_thresholds: DEFAULT_BELL_THRESHOLDS, provisional_ttl: DEFAULT_PROVISIONAL_TTL) @identity = identity.to_s @bond_role = (bond_role || :unknown).to_sym @started_at = started_at @weights = weights @bell_thresholds = bell_thresholds @provisional_ttl = provisional_ttl @raw_score = 0.0 @evidence_log = [] @dirty = false @last_evidence_at = nil @provisional_expires_at = set_provisional_expiry! @crossed_bells = [] @identity_principal_id = nil @identity_canonical_name = nil @identity_id = nil end |
Instance Attribute Details
#bond_role ⇒ Object (readonly)
Returns the value of attribute bond_role.
40 41 42 |
# File 'lib/legion/gaia/bond_store.rb', line 40 def bond_role @bond_role end |
#evidence_log ⇒ Object (readonly)
Returns the value of attribute evidence_log.
40 41 42 |
# File 'lib/legion/gaia/bond_store.rb', line 40 def evidence_log @evidence_log end |
#identity ⇒ Object (readonly)
Returns the value of attribute identity.
40 41 42 |
# File 'lib/legion/gaia/bond_store.rb', line 40 def identity @identity end |
#identity_canonical_name ⇒ Object (readonly)
Returns the value of attribute identity_canonical_name.
40 41 42 |
# File 'lib/legion/gaia/bond_store.rb', line 40 def identity_canonical_name @identity_canonical_name end |
#identity_id ⇒ Object (readonly)
Returns the value of attribute identity_id.
40 41 42 |
# File 'lib/legion/gaia/bond_store.rb', line 40 def identity_id @identity_id end |
#identity_principal_id ⇒ Object (readonly)
Returns the value of attribute identity_principal_id.
40 41 42 |
# File 'lib/legion/gaia/bond_store.rb', line 40 def identity_principal_id @identity_principal_id end |
#provisional_expires_at ⇒ Object (readonly)
Returns the value of attribute provisional_expires_at.
40 41 42 |
# File 'lib/legion/gaia/bond_store.rb', line 40 def provisional_expires_at @provisional_expires_at end |
#provisional_ttl ⇒ Object (readonly)
Returns the value of attribute provisional_ttl.
40 41 42 |
# File 'lib/legion/gaia/bond_store.rb', line 40 def provisional_ttl @provisional_ttl end |
#raw_score ⇒ Object (readonly)
Returns the value of attribute raw_score.
40 41 42 |
# File 'lib/legion/gaia/bond_store.rb', line 40 def raw_score @raw_score end |
#started_at ⇒ Object (readonly)
Returns the value of attribute started_at.
40 41 42 |
# File 'lib/legion/gaia/bond_store.rb', line 40 def started_at @started_at end |
Instance Method Details
#accumulate(observation, imprint_multiplier: 1.0) ⇒ Object
Accumulate evidence from an observation hash. Returns the delta added to raw_score.
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/legion/gaia/bond_store.rb', line 66 def accumulate(observation, imprint_multiplier: 1.0) base_delta = extract_evidence_delta(observation) imprint = imprint_multiplier || 1.0 delta = base_delta * imprint * diminishing_factor # Apply decay discount to incoming evidence (older = less impactful) return 0.0 if delta.zero? @raw_score = [@raw_score + delta, 1.0].min @last_evidence_at = Time.now.utc # Capture identity columns from observation @identity_principal_id ||= observation[:identity_principal_id] @identity_canonical_name ||= observation[:identity_canonical_name] @identity_id ||= observation[:identity_id] # Log evidence (ring buffer) log_entry = { timestamp: observation[:timestamp] || Time.now.utc, delta: delta.round(6), base_delta: base_delta.round(6), direct_address: observation[:direct_address] || false, content_length: observation[:content_length] || 0, channel: (observation[:channel] || :unknown).to_s } @evidence_log << log_entry @evidence_log.shift if @evidence_log.size > MAX_EVIDENCE @dirty = true log.debug("[bond] accumulate identity=#{@identity} delta=#{delta.round(6)} " \ "score=#{@raw_score.round(4)} multiplier=#{imprint}") delta rescue StandardError => e handle_exception(e, level: :warn, operation: 'gaia.bond_store.accumulate', identity: @identity) 0.0 end |
#bell?(new_strength) ⇒ Boolean
Check if a new strength value crosses any uncrossed bell threshold. Returns list of newly crossed thresholds.
123 124 125 126 127 128 129 |
# File 'lib/legion/gaia/bond_store.rb', line 123 def bell?(new_strength) new_bells = @bell_thresholds.select do |threshold| new_strength >= threshold && !@crossed_bells.include?(threshold) end @crossed_bells += new_bells if new_bells.any? new_bells end |
#confirm! ⇒ Object
Mark this provisional bond as confirmed (remove cap and expiry).
132 133 134 135 136 137 138 139 140 |
# File 'lib/legion/gaia/bond_store.rb', line 132 def confirm! return unless @bond_role == :provisional old_role = @bond_role @bond_role = :partner @provisional_expires_at = nil @dirty = true log.info("[bond] confirmed provisional identity=#{@identity} role=#{old_role}->#{@bond_role}") end |
#dirty? ⇒ Boolean
146 147 148 |
# File 'lib/legion/gaia/bond_store.rb', line 146 def dirty? @dirty end |
#from_apollo(store:) ⇒ Object
Reconstruct state from Apollo query results (hydration).
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/legion/gaia/bond_store.rb', line 176 def from_apollo(store:) result = store.query(text: "bond #{@identity}", tags: %w[bond gaia bond_evidence]) return unless result.is_a?(Hash) && result[:success] && result[:results] result[:results].each do |entry| # Extract strength from confidence field raw_confidence = entry[:confidence] || 0.0 @raw_score = [raw_confidence, 1.0].min if raw_confidence.is_a?(Numeric) # Extract identity columns @identity_principal_id ||= entry[:identity_principal_id] @identity_canonical_name ||= entry[:identity_canonical_name] @identity_id ||= entry[:identity_id] end @last_evidence_at = Time.now.utc rescue StandardError => e handle_exception(e, level: :warn, operation: 'gaia.bond_store.from_apollo', identity: @identity) end |
#mark_clean! ⇒ Object
150 151 152 |
# File 'lib/legion/gaia/bond_store.rb', line 150 def mark_clean! @dirty = false end |
#provisional? ⇒ Boolean
142 143 144 |
# File 'lib/legion/gaia/bond_store.rb', line 142 def provisional? @bond_role == :provisional end |
#strength ⇒ Object
Returns 0..1 strength, applying time decay and provisional cap.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/legion/gaia/bond_store.rb', line 104 def strength decayed = apply_decay(@raw_score) if @bond_role == :provisional && @provisional_expires_at return 0.0 if Time.now.utc > @provisional_expires_at # Expired provisional transitions to unknown if Time.now.utc > @provisional_expires_at @bond_role = :unknown log.debug("[bond] provisional expired identity=#{@identity}") end return [decayed, PROVISIONAL_CEILING].min end decayed end |
#to_apollo_entries ⇒ Object
Convert to Apollo upsert entries for persistence.
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/legion/gaia/bond_store.rb', line 155 def to_apollo_entries entries = [] # Summary entry — current strength snapshot entries << { content: content_from_values, tags: %w[bond gaia bond_evidence], confidence: strength, access_scope: 'local', identity_canonical_name: @identity_canonical_name, identity_principal_id: @identity_principal_id, identity_id: @identity_id } # Evidence batch entries (last N entries since clean) entries += evidence_entries entries end |