lex-synapse 0.2.2

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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +16 -0
  3. data/.gitignore +11 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +53 -0
  6. data/CHANGELOG.md +36 -0
  7. data/CLAUDE.md +98 -0
  8. data/Gemfile +16 -0
  9. data/README.md +156 -0
  10. data/Rakefile +7 -0
  11. data/lex-synapse.gemspec +31 -0
  12. data/lib/legion/extensions/synapse/actors/crystallize.rb +31 -0
  13. data/lib/legion/extensions/synapse/actors/decay.rb +31 -0
  14. data/lib/legion/extensions/synapse/actors/evaluate.rb +23 -0
  15. data/lib/legion/extensions/synapse/actors/homeostasis.rb +31 -0
  16. data/lib/legion/extensions/synapse/actors/pain.rb +23 -0
  17. data/lib/legion/extensions/synapse/client.rb +62 -0
  18. data/lib/legion/extensions/synapse/data/migrations/001_create_synapses.rb +26 -0
  19. data/lib/legion/extensions/synapse/data/migrations/002_create_synapse_mutations.rb +21 -0
  20. data/lib/legion/extensions/synapse/data/migrations/003_create_synapse_signals.rb +20 -0
  21. data/lib/legion/extensions/synapse/data/models/synapse.rb +16 -0
  22. data/lib/legion/extensions/synapse/data/models/synapse_mutation.rb +15 -0
  23. data/lib/legion/extensions/synapse/data/models/synapse_signal.rb +15 -0
  24. data/lib/legion/extensions/synapse/helpers/confidence.rb +75 -0
  25. data/lib/legion/extensions/synapse/helpers/homeostasis.rb +43 -0
  26. data/lib/legion/extensions/synapse/helpers/relationship_wrapper.rb +42 -0
  27. data/lib/legion/extensions/synapse/runners/crystallize.rb +45 -0
  28. data/lib/legion/extensions/synapse/runners/dream.rb +106 -0
  29. data/lib/legion/extensions/synapse/runners/evaluate.rb +101 -0
  30. data/lib/legion/extensions/synapse/runners/gaia_report.rb +68 -0
  31. data/lib/legion/extensions/synapse/runners/mutate.rb +67 -0
  32. data/lib/legion/extensions/synapse/runners/pain.rb +78 -0
  33. data/lib/legion/extensions/synapse/runners/promote.rb +79 -0
  34. data/lib/legion/extensions/synapse/runners/report.rb +50 -0
  35. data/lib/legion/extensions/synapse/runners/retrieve.rb +61 -0
  36. data/lib/legion/extensions/synapse/runners/revert.rb +58 -0
  37. data/lib/legion/extensions/synapse/transport/exchanges/synapse.rb +16 -0
  38. data/lib/legion/extensions/synapse/transport/messages/pain.rb +23 -0
  39. data/lib/legion/extensions/synapse/transport/messages/signal.rb +23 -0
  40. data/lib/legion/extensions/synapse/transport/queues/evaluate.rb +19 -0
  41. data/lib/legion/extensions/synapse/transport/queues/pain.rb +19 -0
  42. data/lib/legion/extensions/synapse/transport.rb +18 -0
  43. data/lib/legion/extensions/synapse/version.rb +9 -0
  44. data/lib/legion/extensions/synapse.rb +20 -0
  45. metadata +118 -0
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ create_table(:synapse_mutations) do
6
+ primary_key :id
7
+ foreign_key :synapse_id, :synapses, null: false, index: true
8
+ Integer :version, null: false
9
+ String :mutation_type, null: false, size: 50
10
+ String :before_state, text: true
11
+ String :after_state, text: true
12
+ String :trigger, null: false, size: 50
13
+ String :outcome, size: 50
14
+ DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
15
+ end
16
+ end
17
+
18
+ down do
19
+ drop_table :synapse_mutations
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ create_table(:synapse_signals) do
6
+ primary_key :id
7
+ foreign_key :synapse_id, :synapses, null: false, index: true
8
+ Integer :task_id
9
+ TrueClass :passed_attention, default: false
10
+ TrueClass :transform_success, default: false
11
+ String :downstream_outcome, size: 50
12
+ Integer :latency_ms
13
+ DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
14
+ end
15
+ end
16
+
17
+ down do
18
+ drop_table :synapse_signals
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Synapse
6
+ module Data
7
+ module Model
8
+ class Synapse < Sequel::Model(:synapses)
9
+ one_to_many :mutations, class: 'Legion::Extensions::Synapse::Data::Model::SynapseMutation'
10
+ one_to_many :signals, class: 'Legion::Extensions::Synapse::Data::Model::SynapseSignal'
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Synapse
6
+ module Data
7
+ module Model
8
+ class SynapseMutation < Sequel::Model(:synapse_mutations)
9
+ many_to_one :synapse, class: 'Legion::Extensions::Synapse::Data::Model::Synapse'
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Synapse
6
+ module Data
7
+ module Model
8
+ class SynapseSignal < Sequel::Model(:synapse_signals)
9
+ many_to_one :synapse, class: 'Legion::Extensions::Synapse::Data::Model::Synapse'
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Synapse
6
+ module Helpers
7
+ module Confidence
8
+ VALID_STATUSES = %w[active observing dampened].freeze
9
+ EVALUABLE_STATUSES = %w[active observing].freeze
10
+ VALID_ORIGINS = %w[explicit emergent seeded].freeze
11
+ VALID_OUTCOMES = %w[success failed].freeze
12
+
13
+ STARTING_SCORES = { explicit: 0.7, emergent: 0.3, seeded: 0.5 }.freeze
14
+ ADJUSTMENTS = {
15
+ success: 0.02,
16
+ failure: -0.05,
17
+ validation_failure: -0.03,
18
+ consecutive_bonus: 0.05
19
+ }.freeze
20
+ DECAY_RATE = 0.998
21
+ CONSECUTIVE_BONUS_THRESHOLD = 50
22
+
23
+ AUTONOMY_RANGES = {
24
+ observe: 0.0..0.3,
25
+ filter: 0.3..0.6,
26
+ transform: 0.6..0.8,
27
+ autonomous: 0.8..1.0
28
+ }.freeze
29
+
30
+ class << self
31
+ def starting_score(origin)
32
+ STARTING_SCORES.fetch(origin.to_sym, 0.5)
33
+ end
34
+
35
+ def adjust(confidence, event, consecutive_successes: 0)
36
+ delta = ADJUSTMENTS.fetch(event, 0)
37
+ result = confidence + delta
38
+ result += ADJUSTMENTS[:consecutive_bonus] if event == :success && consecutive_successes > CONSECUTIVE_BONUS_THRESHOLD
39
+ clamp(result)
40
+ end
41
+
42
+ def decay(confidence, hours: 1)
43
+ clamp(confidence * (DECAY_RATE**hours))
44
+ end
45
+
46
+ def autonomy_mode(confidence)
47
+ AUTONOMY_RANGES.each do |mode, range|
48
+ return mode if range.include?(confidence)
49
+ end
50
+ :observe
51
+ end
52
+
53
+ def can_filter?(confidence)
54
+ confidence >= 0.3
55
+ end
56
+
57
+ def can_transform?(confidence)
58
+ confidence >= 0.6
59
+ end
60
+
61
+ def can_self_modify?(confidence)
62
+ confidence >= 0.8
63
+ end
64
+
65
+ private
66
+
67
+ def clamp(value)
68
+ value.clamp(0.0, 1.0)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Synapse
6
+ module Helpers
7
+ module Homeostasis
8
+ SPIKE_MULTIPLIER = 3.0
9
+ SPIKE_DURATION_SECONDS = 60
10
+ DROUGHT_MULTIPLIER = 10.0
11
+
12
+ class << self
13
+ def spike?(current_throughput, baseline_throughput, duration_seconds: 0)
14
+ return false if baseline_throughput <= 0
15
+
16
+ current_throughput > (baseline_throughput * SPIKE_MULTIPLIER) &&
17
+ duration_seconds >= SPIKE_DURATION_SECONDS
18
+ end
19
+
20
+ def drought?(current_throughput, baseline_throughput, silent_seconds: 0)
21
+ return false if baseline_throughput <= 0
22
+
23
+ avg_interval = 60.0 / baseline_throughput
24
+ current_throughput.zero? && silent_seconds >= (avg_interval * DROUGHT_MULTIPLIER)
25
+ end
26
+
27
+ def update_baseline(current_baseline, new_sample, alpha: 0.1)
28
+ ((1 - alpha) * current_baseline) + (alpha * new_sample)
29
+ end
30
+
31
+ def should_dampen?(current_throughput, baseline_throughput, duration_seconds: 0)
32
+ spike?(current_throughput, baseline_throughput, duration_seconds: duration_seconds)
33
+ end
34
+
35
+ def should_flag_for_review?(current_throughput, baseline_throughput, silent_seconds: 0)
36
+ drought?(current_throughput, baseline_throughput, silent_seconds: silent_seconds)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../data/models/synapse'
4
+ require_relative 'confidence'
5
+
6
+ module Legion
7
+ module Extensions
8
+ module Synapse
9
+ module Helpers
10
+ module RelationshipWrapper
11
+ class << self
12
+ def wrap(relationship)
13
+ existing = Data::Model::Synapse.where(relationship_id: relationship[:id]).first
14
+ return existing if existing
15
+
16
+ Data::Model::Synapse.create(
17
+ source_function_id: relationship[:trigger_function_id],
18
+ target_function_id: relationship[:function_id],
19
+ relationship_id: relationship[:id],
20
+ attention: relationship[:conditions],
21
+ transform: relationship[:transformation],
22
+ routing_strategy: 'direct',
23
+ origin: 'explicit',
24
+ confidence: Confidence.starting_score(:explicit),
25
+ status: 'active'
26
+ )
27
+ end
28
+
29
+ def unwrap(synapse_id)
30
+ synapse = Data::Model::Synapse[synapse_id]
31
+ return { success: false, error: 'synapse not found' } unless synapse
32
+ return { success: false, error: 'not a wrapped relationship' } unless synapse.relationship_id
33
+
34
+ synapse.destroy
35
+ { success: true, relationship_id: synapse.relationship_id }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../helpers/confidence'
4
+ require_relative '../data/models/synapse'
5
+
6
+ module Legion
7
+ module Extensions
8
+ module Synapse
9
+ module Runners
10
+ module Crystallize
11
+ EMERGENCE_THRESHOLD = 20
12
+
13
+ def crystallize(signal_pairs: [], threshold: EMERGENCE_THRESHOLD)
14
+ created = []
15
+
16
+ signal_pairs.each do |pair|
17
+ next if pair[:count] < threshold
18
+ next if synapse_exists?(pair[:source_function_id], pair[:target_function_id])
19
+
20
+ synapse = Data::Model::Synapse.create(
21
+ source_function_id: pair[:source_function_id],
22
+ target_function_id: pair[:target_function_id],
23
+ origin: 'emergent',
24
+ confidence: Helpers::Confidence.starting_score(:emergent),
25
+ status: 'observing'
26
+ )
27
+ created << { id: synapse.id, source: pair[:source_function_id], target: pair[:target_function_id] }
28
+ end
29
+
30
+ { success: true, created: created, count: created.size }
31
+ end
32
+
33
+ private
34
+
35
+ def synapse_exists?(source_id, target_id)
36
+ Data::Model::Synapse.where(
37
+ source_function_id: source_id,
38
+ target_function_id: target_id
39
+ ).any?
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../data/models/synapse'
4
+ require_relative '../data/models/synapse_mutation'
5
+ require_relative '../helpers/confidence'
6
+
7
+ module Legion
8
+ module Extensions
9
+ module Synapse
10
+ module Runners
11
+ module Dream
12
+ def dream_replay(synapse_id: nil, **)
13
+ synapses = if synapse_id
14
+ s = Data::Model::Synapse[synapse_id]
15
+ s ? [s] : []
16
+ else
17
+ Data::Model::Synapse.where { version > 1 }.all
18
+ end
19
+
20
+ replays = synapses.map { |s| replay_mutations(s) }
21
+
22
+ {
23
+ success: true,
24
+ replays: replays,
25
+ count: replays.size
26
+ }
27
+ end
28
+
29
+ def dream_simulate(synapse_id:, mutation_type:, changes:, **)
30
+ synapse = Data::Model::Synapse[synapse_id]
31
+ return { success: false, error: 'synapse not found' } unless synapse
32
+
33
+ before = snapshot_state(synapse)
34
+ simulated = apply_simulated_changes(before, mutation_type, changes)
35
+ simulated_confidence = Helpers::Confidence.adjust(
36
+ simulated[:confidence] || synapse.confidence,
37
+ :success
38
+ )
39
+ simulated_mode = Helpers::Confidence.autonomy_mode(simulated_confidence)
40
+
41
+ {
42
+ success: true,
43
+ synapse_id: synapse.id,
44
+ before: before,
45
+ simulated: simulated,
46
+ simulated_confidence: simulated_confidence,
47
+ simulated_mode: simulated_mode,
48
+ recommendation: simulated_confidence > synapse.confidence ? :apply : :skip
49
+ }
50
+ end
51
+
52
+ private
53
+
54
+ def replay_mutations(synapse)
55
+ mutations = synapse.mutations_dataset.order(:version).all
56
+ timeline = mutations.map do |m|
57
+ {
58
+ version: m.version,
59
+ mutation_type: m.mutation_type,
60
+ trigger: m.trigger,
61
+ outcome: m.outcome,
62
+ created_at: m.created_at
63
+ }
64
+ end
65
+
66
+ {
67
+ synapse_id: synapse.id,
68
+ current_version: synapse.version,
69
+ origin: synapse.origin,
70
+ confidence: synapse.confidence,
71
+ mutation_count: mutations.size,
72
+ timeline: timeline,
73
+ reverts: mutations.count { |m| m.outcome == 'reverted' },
74
+ net_trend: synapse.confidence >= 0.5 ? :improving : :declining
75
+ }
76
+ end
77
+
78
+ def snapshot_state(synapse)
79
+ {
80
+ attention: synapse.attention,
81
+ transform: synapse.transform,
82
+ routing_strategy: synapse.routing_strategy,
83
+ confidence: synapse.confidence,
84
+ status: synapse.status
85
+ }
86
+ end
87
+
88
+ def apply_simulated_changes(state, mutation_type, changes)
89
+ simulated = state.dup
90
+ case mutation_type
91
+ when 'attention_adjusted'
92
+ simulated[:attention] = changes[:attention] if changes[:attention]
93
+ when 'transform_adjusted'
94
+ simulated[:transform] = changes[:transform] if changes[:transform]
95
+ when 'route_changed'
96
+ simulated[:routing_strategy] = changes[:routing_strategy] if changes[:routing_strategy]
97
+ when 'confidence_changed'
98
+ simulated[:confidence] = changes[:confidence] if changes[:confidence]
99
+ end
100
+ simulated
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../helpers/confidence'
4
+ require_relative '../data/models/synapse'
5
+ require_relative '../data/models/synapse_signal'
6
+
7
+ module Legion
8
+ module Extensions
9
+ module Synapse
10
+ module Runners
11
+ module Evaluate
12
+ def evaluate(synapse_id:, payload: {}, conditioner_client: nil, transformer_client: nil)
13
+ synapse = Data::Model::Synapse[synapse_id]
14
+ return { success: false, error: 'synapse not found' } unless synapse
15
+ return { success: false, error: 'synapse not active' } unless Helpers::Confidence::EVALUABLE_STATUSES.include?(synapse.status)
16
+
17
+ mode = Helpers::Confidence.autonomy_mode(synapse.confidence)
18
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
19
+
20
+ # Step 1: Attention check
21
+ attention_result = check_attention(synapse, payload, mode, conditioner_client)
22
+
23
+ # Step 2: Transform (if attention passed and confidence allows)
24
+ transform_result = if attention_result[:passed] && Helpers::Confidence.can_transform?(synapse.confidence)
25
+ run_transform(synapse, payload, transformer_client)
26
+ else
27
+ { success: attention_result[:passed], result: payload }
28
+ end
29
+
30
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) - start_time
31
+
32
+ # Step 3: Record signal
33
+ record_signal(synapse, attention_result[:passed], transform_result[:success], elapsed)
34
+
35
+ # Step 4: Adjust confidence
36
+ event = transform_result[:success] ? :success : :failure
37
+ new_confidence = Helpers::Confidence.adjust(synapse.confidence, event)
38
+ synapse.update(confidence: new_confidence)
39
+
40
+ {
41
+ success: transform_result[:success],
42
+ mode: mode,
43
+ passed: attention_result[:passed],
44
+ transformed: transform_result[:success],
45
+ result: transform_result[:result],
46
+ latency_ms: elapsed
47
+ }
48
+ end
49
+
50
+ private
51
+
52
+ def check_attention(synapse, payload, mode, conditioner_client)
53
+ return { passed: true } if synapse.attention.nil? || synapse.attention.empty?
54
+
55
+ passed = if conditioner_client
56
+ result = conditioner_client.evaluate(conditions: Legion::JSON.load(synapse.attention), values: payload)
57
+ result[:valid]
58
+ else
59
+ true
60
+ end
61
+
62
+ # In OBSERVE mode, always pass through regardless of result
63
+ passed = true if mode == :observe
64
+
65
+ { passed: passed }
66
+ end
67
+
68
+ def run_transform(synapse, payload, transformer_client)
69
+ return { success: true, result: payload } if synapse.transform.nil? || synapse.transform.empty?
70
+ return { success: true, result: payload } unless transformer_client
71
+
72
+ transform_def = Legion::JSON.load(synapse.transform)
73
+ result = transformer_client.transform(
74
+ transformation: transform_def[:template] || transform_def[:transformation],
75
+ payload: payload,
76
+ engine: transform_def[:engine]&.to_sym,
77
+ schema: transform_def[:schema]
78
+ )
79
+
80
+ if result[:success]
81
+ { success: true, result: result[:result] }
82
+ else
83
+ new_conf = Helpers::Confidence.adjust(synapse.confidence, :validation_failure)
84
+ synapse.update(confidence: new_conf)
85
+ { success: false, result: payload, error: result[:errors] }
86
+ end
87
+ end
88
+
89
+ def record_signal(synapse, passed_attention, transform_success, latency_ms)
90
+ Data::Model::SynapseSignal.create(
91
+ synapse_id: synapse.id,
92
+ passed_attention: passed_attention,
93
+ transform_success: transform_success,
94
+ latency_ms: latency_ms.to_i
95
+ )
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../data/models/synapse'
4
+ require_relative '../data/models/synapse_signal'
5
+ require_relative '../helpers/confidence'
6
+
7
+ module Legion
8
+ module Extensions
9
+ module Synapse
10
+ module Runners
11
+ module GaiaReport
12
+ def gaia_summary(**)
13
+ synapses = Data::Model::Synapse.all
14
+ active = synapses.select { |s| s.status == 'active' }
15
+ dampened = synapses.select { |s| s.status == 'dampened' }
16
+ observing = synapses.select { |s| s.status == 'observing' }
17
+
18
+ pain_threshold = 0.3
19
+ elevated_pain = active.select { |s| s.confidence < pain_threshold }
20
+
21
+ {
22
+ success: true,
23
+ total_synapses: synapses.size,
24
+ active_count: active.size,
25
+ dampened_count: dampened.size,
26
+ observing_count: observing.size,
27
+ elevated_pain_count: elevated_pain.size,
28
+ avg_confidence: avg_confidence(active),
29
+ emergent_candidates: observing.size,
30
+ health_score: compute_health_score(active, dampened, elevated_pain)
31
+ }
32
+ end
33
+
34
+ def gaia_reflection(**)
35
+ summary = gaia_summary
36
+ recent_mutations = Data::Model::SynapseMutation
37
+ .where { created_at >= Time.now - 3600 }
38
+ .all
39
+
40
+ {
41
+ success: true,
42
+ summary: summary,
43
+ mutations_1h: recent_mutations.size,
44
+ mutation_types: recent_mutations.map(&:mutation_type).tally,
45
+ mutation_triggers: recent_mutations.map(&:trigger).tally
46
+ }
47
+ end
48
+
49
+ private
50
+
51
+ def avg_confidence(synapses)
52
+ return 0.0 if synapses.empty?
53
+
54
+ (synapses.sum(&:confidence) / synapses.size).round(4)
55
+ end
56
+
57
+ def compute_health_score(active, dampened, elevated_pain)
58
+ return 1.0 if active.empty? && dampened.empty?
59
+
60
+ total = active.size + dampened.size
61
+ healthy = active.size - elevated_pain.size
62
+ (healthy.to_f / total).round(4).clamp(0.0, 1.0)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../data/models/synapse'
4
+ require_relative '../data/models/synapse_mutation'
5
+
6
+ module Legion
7
+ module Extensions
8
+ module Synapse
9
+ module Runners
10
+ module Mutate
11
+ VALID_MUTATION_TYPES = %w[attention_adjusted transform_adjusted route_changed confidence_changed].freeze
12
+ VALID_TRIGGERS = %w[hebbian pain dream gaia manual].freeze
13
+
14
+ def mutate(synapse_id:, mutation_type:, changes:, trigger:)
15
+ synapse = Data::Model::Synapse[synapse_id]
16
+ return { success: false, error: 'synapse not found' } unless synapse
17
+ return { success: false, error: "invalid mutation_type: #{mutation_type}" } unless VALID_MUTATION_TYPES.include?(mutation_type)
18
+ return { success: false, error: "invalid trigger: #{trigger}" } unless VALID_TRIGGERS.include?(trigger)
19
+
20
+ before_state = snapshot(synapse)
21
+ apply_changes(synapse, mutation_type, changes)
22
+ after_state = snapshot(synapse)
23
+
24
+ new_version = synapse.version + 1
25
+ synapse.update(version: new_version)
26
+
27
+ Data::Model::SynapseMutation.create(
28
+ synapse_id: synapse.id,
29
+ version: new_version,
30
+ mutation_type: mutation_type,
31
+ before_state: Legion::JSON.dump(before_state),
32
+ after_state: Legion::JSON.dump(after_state),
33
+ trigger: trigger
34
+ )
35
+
36
+ { success: true, version: new_version, synapse_id: synapse.id }
37
+ end
38
+
39
+ private
40
+
41
+ def snapshot(synapse)
42
+ {
43
+ attention: synapse.attention,
44
+ transform: synapse.transform,
45
+ routing_strategy: synapse.routing_strategy,
46
+ confidence: synapse.confidence,
47
+ status: synapse.status
48
+ }
49
+ end
50
+
51
+ def apply_changes(synapse, mutation_type, changes)
52
+ case mutation_type
53
+ when 'attention_adjusted'
54
+ synapse.update(attention: changes[:attention]) if changes[:attention]
55
+ when 'transform_adjusted'
56
+ synapse.update(transform: changes[:transform]) if changes[:transform]
57
+ when 'route_changed'
58
+ synapse.update(routing_strategy: changes[:routing_strategy]) if changes[:routing_strategy]
59
+ when 'confidence_changed'
60
+ synapse.update(confidence: changes[:confidence]) if changes[:confidence]
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end