lex-synapse 0.4.14 → 0.4.15
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 +4 -4
- data/CHANGELOG.md +7 -0
- data/lib/legion/extensions/synapse/actors/blast_radius.rb +1 -1
- data/lib/legion/extensions/synapse/actors/decay.rb +1 -1
- data/lib/legion/extensions/synapse/actors/homeostasis.rb +1 -1
- data/lib/legion/extensions/synapse/runners/blast_radius.rb +1 -1
- data/lib/legion/extensions/synapse/runners/evaluate.rb +23 -15
- data/lib/legion/extensions/synapse/runners/mutate.rb +19 -16
- data/lib/legion/extensions/synapse/runners/pain.rb +9 -0
- data/lib/legion/extensions/synapse/runners/revert.rb +39 -39
- data/lib/legion/extensions/synapse/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '037082609e6545e0a3581981a602d49afdd4a532cf4b15d166f0cf04fd3a746f'
|
|
4
|
+
data.tar.gz: 42c81c769ef9f4367dec25d4713d5ab4bc4eb1491b88478bb365d5cbd3e2521e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a5f60904a36dcf8fb276c75683485a7c4ef8207b9b1f1b6c03cae33bd392fa85d4fc44beb605aeae62f492be50be546b90b209021361d1105c9adc43306bfe21
|
|
7
|
+
data.tar.gz: '059f003e2a808fe705e315b8c7c75c01631ac97ff86c2f675467dd563f37106ea039fc4aaddf32ad89315d4290226e0308d209c0e3df8ebd89644584ab315de9'
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.15] - 2026-10-29
|
|
4
|
+
### Fixed
|
|
5
|
+
- Treat missing configured conditioner/transformer capabilities as unavailable rather than successful learning; retain legitimate observe/filter pass-through.
|
|
6
|
+
- Attribute reactive proposals to the signal created by that evaluation and report actual transformation execution.
|
|
7
|
+
- Restore standalone pain-triggered reversion, ignore pending evaluations in completed-outcome failure streaks, and return restored confidence.
|
|
8
|
+
- Make mutation/revert writes transactional and keep history versions unambiguous across repeated reversion, retaining actual before/after snapshots.
|
|
9
|
+
|
|
3
10
|
## [0.4.14] - 2026-06-01
|
|
4
11
|
### Fixed
|
|
5
12
|
- Homeostasis actor: convert raw signal counts to signals/minute for correct spike/drought comparison; batch all updates in a single DB transaction
|
|
@@ -27,6 +27,14 @@ module Legion
|
|
|
27
27
|
|
|
28
28
|
# Step 1: Attention check
|
|
29
29
|
attention_result = check_attention(synapse, payload, mode, conditioner_client)
|
|
30
|
+
# Unavailable execution is not evidence for confidence or proposal learning.
|
|
31
|
+
if attention_result[:unavailable]
|
|
32
|
+
return {
|
|
33
|
+
success: mode == :observe, mode: mode, passed: attention_result[:passed],
|
|
34
|
+
transformed: false, result: payload, error: attention_result[:error],
|
|
35
|
+
latency_ms: ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond) - start_time
|
|
36
|
+
}
|
|
37
|
+
end
|
|
30
38
|
|
|
31
39
|
# Step 2: Transform (if attention passed and confidence allows)
|
|
32
40
|
transform_result = if attention_result[:passed] && Helpers::Confidence.can_transform?(synapse.confidence)
|
|
@@ -36,15 +44,19 @@ module Legion
|
|
|
36
44
|
end
|
|
37
45
|
|
|
38
46
|
elapsed = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond) - start_time
|
|
47
|
+
if transform_result[:unavailable]
|
|
48
|
+
return {
|
|
49
|
+
success: false, mode: mode, passed: attention_result[:passed],
|
|
50
|
+
transformed: false, result: payload, error: transform_result[:error], latency_ms: elapsed
|
|
51
|
+
}
|
|
52
|
+
end
|
|
39
53
|
|
|
40
54
|
# Step 3: Record signal
|
|
41
|
-
record_signal(synapse, attention_result[:passed], transform_result[:success], elapsed)
|
|
55
|
+
signal_record = record_signal(synapse, attention_result[:passed], transform_result[:success], elapsed)
|
|
42
56
|
|
|
43
57
|
# Step 4: Adjust confidence (skip if run_transform already penalized for validation failure)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
new_confidence = synapse.confidence
|
|
47
|
-
else
|
|
58
|
+
new_confidence = synapse.confidence
|
|
59
|
+
unless transform_result[:validation_failure]
|
|
48
60
|
event = transform_result[:success] ? :success : :failure
|
|
49
61
|
new_confidence = Helpers::Confidence.adjust(synapse.confidence, event)
|
|
50
62
|
synapse.update(confidence: new_confidence)
|
|
@@ -52,7 +64,6 @@ module Legion
|
|
|
52
64
|
|
|
53
65
|
# Step 5: Generate proposals if autonomous
|
|
54
66
|
if Helpers::Confidence.can_self_modify?(new_confidence) && Helpers::Proposals.reactive?
|
|
55
|
-
signal_record = Data::Model::SynapseSignal.where(synapse_id: synapse.id).order(Sequel.desc(:id)).first
|
|
56
67
|
propose_reactive(
|
|
57
68
|
synapse: synapse, payload: payload, signal_id: signal_record&.id,
|
|
58
69
|
attention_result: attention_result, transform_result: transform_result,
|
|
@@ -64,7 +75,7 @@ module Legion
|
|
|
64
75
|
success: transform_result[:success],
|
|
65
76
|
mode: mode,
|
|
66
77
|
passed: attention_result[:passed],
|
|
67
|
-
transformed: transform_result[:
|
|
78
|
+
transformed: transform_result[:transformed] == true,
|
|
68
79
|
result: transform_result[:result],
|
|
69
80
|
latency_ms: elapsed
|
|
70
81
|
}
|
|
@@ -74,13 +85,10 @@ module Legion
|
|
|
74
85
|
|
|
75
86
|
def check_attention(synapse, payload, mode, conditioner_client)
|
|
76
87
|
return { passed: true } if synapse.attention.nil? || synapse.attention.empty?
|
|
88
|
+
return { passed: mode == :observe, unavailable: true, error: 'conditioner unavailable' } unless conditioner_client.respond_to?(:evaluate)
|
|
77
89
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
result[:valid]
|
|
81
|
-
else
|
|
82
|
-
true
|
|
83
|
-
end
|
|
90
|
+
result = conditioner_client.evaluate(conditions: Legion::JSON.load(synapse.attention), values: payload)
|
|
91
|
+
passed = result[:valid]
|
|
84
92
|
|
|
85
93
|
# In OBSERVE mode, always pass through regardless of result
|
|
86
94
|
passed = true if mode == :observe
|
|
@@ -90,7 +98,7 @@ module Legion
|
|
|
90
98
|
|
|
91
99
|
def run_transform(synapse, payload, transformer_client)
|
|
92
100
|
return { success: true, result: payload } if synapse.transform.nil? || synapse.transform.empty?
|
|
93
|
-
return { success:
|
|
101
|
+
return { success: false, result: payload, unavailable: true, error: 'transformer unavailable' } unless transformer_client.respond_to?(:transform)
|
|
94
102
|
|
|
95
103
|
transform_def = Legion::JSON.load(synapse.transform)
|
|
96
104
|
result = transformer_client.transform(
|
|
@@ -101,7 +109,7 @@ module Legion
|
|
|
101
109
|
)
|
|
102
110
|
|
|
103
111
|
if result[:success]
|
|
104
|
-
{ success: true, result: result[:result] }
|
|
112
|
+
{ success: true, transformed: true, result: result[:result] }
|
|
105
113
|
else
|
|
106
114
|
new_conf = Helpers::Confidence.adjust(synapse.confidence, :validation_failure)
|
|
107
115
|
synapse.update(confidence: new_conf)
|
|
@@ -19,23 +19,25 @@ module Legion
|
|
|
19
19
|
return { success: false, error: "invalid mutation_type: #{mutation_type}" } unless VALID_MUTATION_TYPES.include?(mutation_type)
|
|
20
20
|
return { success: false, error: "invalid trigger: #{trigger}" } unless VALID_TRIGGERS.include?(trigger)
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
synapse.db.transaction do
|
|
23
|
+
synapse.lock!
|
|
24
|
+
before_state = snapshot(synapse)
|
|
25
|
+
new_version = [synapse.version, synapse.mutations_dataset.max(:version) || 1].max + 1
|
|
26
|
+
apply_changes(synapse, mutation_type, changes)
|
|
27
|
+
synapse.update(version: new_version)
|
|
28
|
+
after_state = snapshot(synapse)
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
Data::Model::SynapseMutation.create(
|
|
31
|
+
synapse_id: synapse.id,
|
|
32
|
+
version: new_version,
|
|
33
|
+
mutation_type: mutation_type,
|
|
34
|
+
before_state: Legion::JSON.dump(before_state),
|
|
35
|
+
after_state: Legion::JSON.dump(after_state),
|
|
36
|
+
trigger: trigger
|
|
37
|
+
)
|
|
28
38
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
version: new_version,
|
|
32
|
-
mutation_type: mutation_type,
|
|
33
|
-
before_state: Legion::JSON.dump(before_state),
|
|
34
|
-
after_state: Legion::JSON.dump(after_state),
|
|
35
|
-
trigger: trigger
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
{ success: true, version: new_version, synapse_id: synapse.id }
|
|
39
|
+
{ success: true, version: new_version, synapse_id: synapse.id }
|
|
40
|
+
end
|
|
39
41
|
end
|
|
40
42
|
|
|
41
43
|
private
|
|
@@ -46,7 +48,8 @@ module Legion
|
|
|
46
48
|
transform: synapse.transform,
|
|
47
49
|
routing_strategy: synapse.routing_strategy,
|
|
48
50
|
confidence: synapse.confidence,
|
|
49
|
-
status: synapse.status
|
|
51
|
+
status: synapse.status,
|
|
52
|
+
version: synapse.version
|
|
50
53
|
}
|
|
51
54
|
end
|
|
52
55
|
|
|
@@ -3,12 +3,15 @@
|
|
|
3
3
|
require_relative '../helpers/confidence'
|
|
4
4
|
require_relative '../data/models/synapse'
|
|
5
5
|
require_relative '../data/models/synapse_signal'
|
|
6
|
+
require_relative 'revert'
|
|
6
7
|
|
|
7
8
|
module Legion
|
|
8
9
|
module Extensions
|
|
9
10
|
module Synapse
|
|
10
11
|
module Runners
|
|
11
12
|
module Pain
|
|
13
|
+
include Legion::Extensions::Synapse::Runners::Revert
|
|
14
|
+
|
|
12
15
|
CONSECUTIVE_FAILURE_THRESHOLD = 3
|
|
13
16
|
|
|
14
17
|
def handle_pain(synapse_id:, task_id: nil)
|
|
@@ -40,6 +43,11 @@ module Legion
|
|
|
40
43
|
result[:action] = :auto_revert
|
|
41
44
|
revert_result = revert(synapse_id: synapse_id, trigger: 'pain')
|
|
42
45
|
result[:reverted] = revert_result[:success] == true
|
|
46
|
+
if result[:reverted]
|
|
47
|
+
synapse.refresh
|
|
48
|
+
new_confidence = synapse.confidence
|
|
49
|
+
result[:confidence] = new_confidence
|
|
50
|
+
end
|
|
43
51
|
end
|
|
44
52
|
|
|
45
53
|
# Check if confidence dropped below autonomy threshold
|
|
@@ -59,6 +67,7 @@ module Legion
|
|
|
59
67
|
|
|
60
68
|
def count_consecutive_failures(synapse)
|
|
61
69
|
recent = synapse.signals_dataset
|
|
70
|
+
.exclude(downstream_outcome: nil)
|
|
62
71
|
.order(Sequel.desc(:id))
|
|
63
72
|
.limit(CONSECUTIVE_FAILURE_THRESHOLD)
|
|
64
73
|
.all
|
|
@@ -14,45 +14,45 @@ module Legion
|
|
|
14
14
|
synapse = Data::Model::Synapse[synapse_id]
|
|
15
15
|
return { success: false, error: 'synapse not found' } unless synapse
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
17
|
+
synapse.db.transaction do
|
|
18
|
+
synapse.lock!
|
|
19
|
+
mutation_version = to_version || synapse.version
|
|
20
|
+
next { success: false, error: 'no previous version' } if mutation_version < 2
|
|
21
|
+
|
|
22
|
+
mutation = Data::Model::SynapseMutation.where(
|
|
23
|
+
synapse_id: synapse.id,
|
|
24
|
+
version: mutation_version
|
|
25
|
+
).first
|
|
26
|
+
|
|
27
|
+
next { success: false, error: "mutation version #{mutation_version} not found" } unless mutation
|
|
28
|
+
|
|
29
|
+
before_state = Legion::JSON.load(mutation.before_state)
|
|
30
|
+
restored_version = before_state[:version] || (mutation_version - 1)
|
|
31
|
+
revert_version = [synapse.version, synapse.mutations_dataset.max(:version) || 1].max + 1
|
|
32
|
+
current_state = synapse.values.slice(:attention, :transform, :routing_strategy, :confidence, :status, :version)
|
|
33
|
+
synapse.update(
|
|
34
|
+
attention: before_state[:attention],
|
|
35
|
+
transform: before_state[:transform],
|
|
36
|
+
routing_strategy: before_state[:routing_strategy],
|
|
37
|
+
confidence: before_state[:confidence] || synapse.confidence,
|
|
38
|
+
status: before_state[:status] || synapse.status,
|
|
39
|
+
version: restored_version
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
mutation.update(outcome: 'reverted')
|
|
43
|
+
restored_state = synapse.values.slice(:attention, :transform, :routing_strategy, :confidence, :status, :version)
|
|
44
|
+
Data::Model::SynapseMutation.create(
|
|
45
|
+
synapse_id: synapse.id,
|
|
46
|
+
version: revert_version,
|
|
47
|
+
mutation_type: 'confidence_changed',
|
|
48
|
+
before_state: Legion::JSON.dump(current_state),
|
|
49
|
+
after_state: Legion::JSON.dump(restored_state),
|
|
50
|
+
trigger: trigger,
|
|
51
|
+
outcome: 'reverted'
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
{ success: true, reverted_to: restored_version, synapse_id: synapse.id }
|
|
55
|
+
end
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
end
|