Module: Legion::Gaia::BondRegistry
- Extended by:
- Logging::Helper
- Defined in:
- lib/legion/gaia/bond_registry.rb
Overview
rubocop:disable Metrics/ModuleLength
Constant Summary collapse
- TO_APOLLO_TAGS =
%w[self-knowledge bond].freeze
Class Method Summary collapse
- .all_bonds ⇒ Object
-
.apply_decay ⇒ Object
Apply decay to all bond strengths (spec §12.3, IDENTITY class rate).
- .bond(identity) ⇒ Object
-
.channel_identity(identity) ⇒ Object
Returns the channel-native identity for the given principal identity.
-
.dirty? ⇒ Boolean
---- Persistence (TrackerPattern) ----.
- .from_apollo(store: nil) ⇒ Object
- .gaia_settings ⇒ Object
-
.hydrate_from_apollo(store: nil) ⇒ Object
Hydrate from Apollo — structured JSON first, legacy markdown fallback.
- .mark_clean! ⇒ Object
-
.partner?(identity) ⇒ Boolean
Partner check: must be :partner bond AND strength >= partner_threshold.
-
.partner_entry ⇒ Object
Returns the single best partner bond entry.
- .partner_threshold ⇒ Object
- .record_channel(identity, channel_id:, channel_identity: nil) ⇒ Object
-
.register(identity, bond: nil, role: nil, priority: :normal, channel_identity: nil, preferred_channel: nil, last_channel: nil, origin: nil, strength: nil, reinforcement_count: nil, last_reinforced: nil) ⇒ Object
Register identity in the bond catalog.
-
.reinforce(identity, direct_address: false, new_channel: false, multiplier: 1.0) ⇒ Object
§12.3 reinforcement formula with diminishing returns.
- .reset! ⇒ Object
- .role(identity) ⇒ Object
-
.to_apollo_entries ⇒ Object
Apollo-upsert shape for TrackerPersistence.
Class Method Details
.all_bonds ⇒ Object
105 106 107 |
# File 'lib/legion/gaia/bond_registry.rb', line 105 def all_bonds @bonds.values end |
.apply_decay ⇒ Object
Apply decay to all bond strengths (spec §12.3, IDENTITY class rate).
174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/legion/gaia/bond_registry.rb', line 174 def apply_decay rate = gaia_settings&.dig(:partner, :identity_decay_rate) || 0.002 @mutex.synchronize do @bonds.each_value do |entry| next unless entry.key?(:strength) && entry[:strength] > 0.0 entry[:strength] = (entry[:strength] * (1 - rate)).max(0.0) end @dirty = true end rescue StandardError => e handle_exception(e, level: :warn, operation: 'gaia.bond_registry.apply_decay') end |
.bond(identity) ⇒ Object
70 71 72 73 |
# File 'lib/legion/gaia/bond_registry.rb', line 70 def bond(identity) entry = @bonds[identity.to_s] entry ? entry[:bond] : :unknown end |
.channel_identity(identity) ⇒ Object
Returns the channel-native identity for the given principal identity.
80 81 82 83 84 85 |
# File 'lib/legion/gaia/bond_registry.rb', line 80 def channel_identity(identity) entry = @bonds[identity.to_s] return nil unless entry entry[:channel_identity] || entry[:identity] end |
.dirty? ⇒ Boolean
---- Persistence (TrackerPattern) ----
190 191 192 |
# File 'lib/legion/gaia/bond_registry.rb', line 190 def dirty? @dirty == true end |
.from_apollo(store: nil) ⇒ Object
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/legion/gaia/bond_registry.rb', line 210 def from_apollo(store: nil) return unless store result = store.query(text: 'bond', tags: TO_APOLLO_TAGS) return unless result.is_a?(Hash) && result[:success] result[:results]&.each do |entry| parsed = Legion::JSON.load(entry[:content]) next unless parsed[:identity] register( parsed[:identity], bond: parsed[:bond], priority: parsed[:priority] || :normal, channel_identity: parsed[:channel_identity], preferred_channel: parsed[:preferred_channel], last_channel: parsed[:last_channel], origin: :hydrated, strength: parsed[:strength] || 0.0, reinforcement_count: parsed[:reinforcement_count] || 0, last_reinforced: parsed[:last_reinforced] ) rescue StandardError => e log.debug("[gaia] BondRegistry skipped unparseable Apollo entry: #{e.}") end log.info("[gaia] BondRegistry hydrated from Apollo count=#{result[:results]&.size || 0}") rescue StandardError => e handle_exception(e, level: :warn, operation: 'gaia.bond_registry.from_apollo') end |
.gaia_settings ⇒ Object
99 100 101 102 103 |
# File 'lib/legion/gaia/bond_registry.rb', line 99 def gaia_settings Legion::Gaia.settings rescue StandardError nil end |
.hydrate_from_apollo(store: nil) ⇒ Object
Hydrate from Apollo — structured JSON first, legacy markdown fallback.
241 242 243 244 245 246 247 248 249 250 |
# File 'lib/legion/gaia/bond_registry.rb', line 241 def hydrate_from_apollo(store: nil) return unless store json_hydrated = try_json_hydration(store) return if json_hydrated try_legacy_hydration(store) rescue StandardError => e handle_exception(e, level: :warn, operation: 'gaia.bond_registry.hydrate_from_apollo') end |
.mark_clean! ⇒ Object
194 195 196 |
# File 'lib/legion/gaia/bond_registry.rb', line 194 def mark_clean! @dirty = false end |
.partner?(identity) ⇒ Boolean
Partner check: must be :partner bond AND strength >= partner_threshold.
88 89 90 91 92 93 |
# File 'lib/legion/gaia/bond_registry.rb', line 88 def partner?(identity) entry = @bonds[identity.to_s] return false unless entry entry[:bond] == :partner && (entry[:strength] || 0) >= partner_threshold end |
.partner_entry ⇒ Object
Returns the single best partner bond entry. Sorted by highest strength first, then channel_identity, priority, earliest.
128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/legion/gaia/bond_registry.rb', line 128 def partner_entry partners = @bonds.values.select do |b| b[:bond] == :partner && (b[:strength] || 0) >= partner_threshold end return nil if partners.empty? sorted = partners.sort_by { |b| [-b[:strength], b[:since], b[:identity]] } sorted.find { |b| b[:channel_identity] } || sorted.find { |b| b[:priority] == :primary } || sorted.first end |
.partner_threshold ⇒ Object
95 96 97 |
# File 'lib/legion/gaia/bond_registry.rb', line 95 def partner_threshold gaia_settings&.dig(:partner, :partner_threshold) || 0.6 end |
.record_channel(identity, channel_id:, channel_identity: nil) ⇒ Object
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/legion/gaia/bond_registry.rb', line 109 def record_channel(identity, channel_id:, channel_identity: nil) @mutex.synchronize do entry = @bonds[identity.to_s] return nil unless entry channel = channel_id&.to_sym updated = entry.merge( last_channel: channel || entry[:last_channel], preferred_channel: entry[:preferred_channel] || channel, channel_identity: entry[:channel_identity] || channel_identity&.to_s ) @bonds[identity.to_s] = updated @dirty = true updated end end |
.register(identity, bond: nil, role: nil, priority: :normal, channel_identity: nil, preferred_channel: nil, last_channel: nil, origin: nil, strength: nil, reinforcement_count: nil, last_reinforced: nil) ⇒ Object
Register identity in the bond catalog. Origin defaults to :provisional for partner bonds, nil for others. rubocop:disable Metrics/ParameterLists
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/legion/gaia/bond_registry.rb', line 23 def register(identity, bond: nil, role: nil, priority: :normal, channel_identity: nil, preferred_channel: nil, last_channel: nil, origin: nil, strength: nil, reinforcement_count: nil, last_reinforced: nil) # rubocop:enable Metrics/ParameterLists effective_bond = (bond || role || :unknown).to_sym origin ||= effective_bond == :partner ? :provisional : nil strength ||= 0.0 @mutex.synchronize do @bonds[identity.to_s] = build_entry( identity, bond: effective_bond, priority: priority, channel_identity: channel_identity, preferred_channel: preferred_channel, last_channel: last_channel, origin: origin, strength: strength, reinforcement_count: reinforcement_count || 0, last_reinforced: last_reinforced ) @dirty = true end log.info( "BondRegistry registered identity=#{identity} bond=#{effective_bond} " \ "origin=#{origin} strength=#{strength}" ) end |
.reinforce(identity, direct_address: false, new_channel: false, multiplier: 1.0) ⇒ Object
§12.3 reinforcement formula with diminishing returns.
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/legion/gaia/bond_registry.rb', line 141 def reinforce(identity, direct_address: false, new_channel: false, multiplier: 1.0) identity = identity.to_s @mutex.synchronize do entry = @bonds[identity] return entry unless entry r = gaia_settings&.dig(:partner, :r_amount) || 0.1 weight = 1.0 weight *= gaia_settings&.dig(:partner, :direct_address_weight) || 1.5 if direct_address weight *= gaia_settings&.dig(:partner, :corroboration_weight) || 1.3 if new_channel delta = r * (1 - (entry[:strength] || 0)) * weight * multiplier new_strength = [(entry[:strength] || 0) + delta, 1.0].min entry[:strength] = new_strength entry[:reinforcement_count] = (entry[:reinforcement_count] || 0) + 1 entry[:last_reinforced] = Time.now.utc @dirty = true # Provisional -> earned on threshold crossing if entry[:origin] == :provisional && new_strength >= partner_threshold entry[:origin] = :earned log.info( "[gaia] bond promoted identity=#{identity} strength=#{new_strength.round(3)} " \ 'origin=:earned' ) end entry end end |
.reset! ⇒ Object
252 253 254 255 256 257 258 |
# File 'lib/legion/gaia/bond_registry.rb', line 252 def reset! @mutex.synchronize do @bonds = Concurrent::Hash.new @dirty = false end log.debug('BondRegistry reset') end |
.role(identity) ⇒ Object
75 76 77 |
# File 'lib/legion/gaia/bond_registry.rb', line 75 def role(identity) bond(identity) end |
.to_apollo_entries ⇒ Object
Apollo-upsert shape for TrackerPersistence.
199 200 201 202 203 204 205 206 207 208 |
# File 'lib/legion/gaia/bond_registry.rb', line 199 def to_apollo_entries @bonds.values.map do |entry| { content: Legion::JSON.dump(entry), tags: TO_APOLLO_TAGS, confidence: entry[:strength] || 0.0, access_scope: 'local' } end end |