lex-synapse 0.4.13 → 0.4.14

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 140dd32d856f6a661d423c72a6f7fcc2424a789342e72d656c25302903d7eda9
4
- data.tar.gz: 89651542649d74fc871576b608172a4d941cb6b25322a073a54a947458eb369e
3
+ metadata.gz: ac485ed070081893c396ca551525535a381c7dd906f481768697978644611c81
4
+ data.tar.gz: fa1d81ddeaf2d7193b5aee2f5145077c30c6bfb9f3be70b6b943e6605af70ea0
5
5
  SHA512:
6
- metadata.gz: c852c9b3dad49309789b63563244983b075a5d13711efd46d3c83118bd0ec49058d7b7a1a9e4279ea0a9445ec3c3184845701b3389e70676960f3fad7fb74f5a
7
- data.tar.gz: bd6602b930def1ccac2ef61933c659844f959bd219d06267c696143f04802058eda62ccebaa988a45707424b73fd4c2ccdace4207809dabe173c5e61150e11ad
6
+ metadata.gz: abbe160b11aac0d146b2fdfbddde252f9a74e85fccceba0ac9c09a155d2f181ad58e928e4fc0a423b2aaa01ec181b35721bc7b7ceb268f0d5297ba1f5ba0c776
7
+ data.tar.gz: c3859b52b5edb5a68ccb21fd5866acf7a6a4a2ded6dd0cbc40e37f2a0b0cf195e37c04115ae7b892bc2b2e027798c51a04b18bcddbde8c0ecdfd492f9269a955
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.14] - 2026-06-01
4
+ ### Fixed
5
+ - Homeostasis actor: convert raw signal counts to signals/minute for correct spike/drought comparison; batch all updates in a single DB transaction
6
+ - Evaluate runner: prevent double confidence adjustment when validation fails (`run_transform` already adjusts)
7
+ - Revert mutation: use unique version (`synapse.version + 1`) instead of `restored_version` to avoid version collision
8
+
3
9
  ## [0.4.13] - 2026-05-07
4
10
  ### Fixed
5
11
  - `Homeostasis` actor: replace per-synapse `signals_dataset.count` with a single batched `GROUP BY` query to eliminate N+1 pool contention that caused `Sequel::PoolTimeout` on Postgres
@@ -18,35 +18,49 @@ module Legion
18
18
  return results unless defined?(Legion::Extensions::Synapse::Data::Model::Synapse)
19
19
 
20
20
  cutoff = Time.now - 60
21
+ window_seconds = 60.0
22
+ signal_model = Legion::Extensions::Synapse::Data::Model::SynapseSignal
23
+ synapse_model = Legion::Extensions::Synapse::Data::Model::Synapse
21
24
 
22
- active_synapses = Legion::Extensions::Synapse::Data::Model::Synapse
23
- .where(status: 'active')
24
- .where { baseline_throughput > 0 } # rubocop:disable Style/NumericPredicate
25
- .all
25
+ # Single query: count signals per synapse in the last 60s window
26
+ signal_counts = signal_model.where { created_at > cutoff }
27
+ .group_and_count(:synapse_id)
28
+ .as_hash(:synapse_id, :count)
26
29
 
30
+ # Fetch only active synapses with a nonzero baseline — eager-load to avoid N+1
31
+ active_synapses = synapse_model.where(status: 'active')
32
+ .where { baseline_throughput > 0 } # rubocop:disable Style/NumericPredicate
33
+ .all
27
34
  return results if active_synapses.empty?
28
35
 
29
- signal_counts = Legion::Extensions::Synapse::Data::Model::SynapseSignal
30
- .where(synapse_id: active_synapses.map(&:id))
31
- .where { created_at > cutoff }
32
- .group_and_count(:synapse_id)
33
- .as_hash(:synapse_id, :count)
36
+ # Collect updates in memory, then apply in a single batch to avoid connection churn
37
+ updates = []
34
38
 
35
39
  active_synapses.each do |synapse|
36
40
  baseline = synapse.baseline_throughput
37
- current = signal_counts.fetch(synapse.id, 0).to_f
41
+ # Convert raw count in the window to signals/minute for apples-to-apples comparison
42
+ current = (signal_counts.fetch(synapse.id, 0).to_f / window_seconds) * 60.0
38
43
 
39
- if Helpers::Homeostasis.spike?(current, baseline, duration_seconds: 60)
44
+ if Helpers::Homeostasis.spike?(current, baseline, duration_seconds: window_seconds)
40
45
  results[:spikes] += 1
41
- elsif Helpers::Homeostasis.drought?(current, baseline, silent_seconds: 60)
46
+ elsif Helpers::Homeostasis.drought?(current, baseline, silent_seconds: window_seconds)
42
47
  results[:droughts] += 1
43
48
  end
44
49
 
45
50
  new_baseline = Helpers::Homeostasis.update_baseline(baseline, current)
46
- synapse.update(baseline_throughput: new_baseline)
51
+ updates << { synapse_id: synapse.id, baseline_throughput: new_baseline }
47
52
  results[:updated] += 1
48
53
  end
49
54
 
55
+ # Batch-update all baselines in a single transaction
56
+ unless updates.empty?
57
+ synapse_model.db.transaction do
58
+ updates.each do |u|
59
+ synapse_model.where(id: u[:synapse_id]).update(baseline_throughput: u[:baseline_throughput])
60
+ end
61
+ end
62
+ end
63
+
50
64
  results
51
65
  end
52
66
  end
@@ -40,10 +40,15 @@ module Legion
40
40
  # Step 3: Record signal
41
41
  record_signal(synapse, attention_result[:passed], transform_result[:success], elapsed)
42
42
 
43
- # Step 4: Adjust confidence
44
- event = transform_result[:success] ? :success : :failure
45
- new_confidence = Helpers::Confidence.adjust(synapse.confidence, event)
46
- synapse.update(confidence: new_confidence)
43
+ # Step 4: Adjust confidence (skip if run_transform already penalized for validation failure)
44
+ if transform_result[:validation_failure]
45
+ # run_transform already adjusted confidence; reload to get the updated value
46
+ new_confidence = synapse.confidence
47
+ else
48
+ event = transform_result[:success] ? :success : :failure
49
+ new_confidence = Helpers::Confidence.adjust(synapse.confidence, event)
50
+ synapse.update(confidence: new_confidence)
51
+ end
47
52
 
48
53
  # Step 5: Generate proposals if autonomous
49
54
  if Helpers::Confidence.can_self_modify?(new_confidence) && Helpers::Proposals.reactive?
@@ -100,7 +105,7 @@ module Legion
100
105
  else
101
106
  new_conf = Helpers::Confidence.adjust(synapse.confidence, :validation_failure)
102
107
  synapse.update(confidence: new_conf)
103
- { success: false, result: payload, error: result[:errors] }
108
+ { success: false, result: payload, error: result[:errors], validation_failure: true }
104
109
  end
105
110
  end
106
111
 
@@ -28,6 +28,7 @@ module Legion
28
28
 
29
29
  restored_version = mutation_version - 1
30
30
  before_state = Legion::JSON.load(mutation.before_state)
31
+ revert_version = synapse.version + 1
31
32
  synapse.update(
32
33
  attention: before_state[:attention],
33
34
  transform: before_state[:transform],
@@ -40,10 +41,10 @@ module Legion
40
41
  # Mark the reverted mutation
41
42
  mutation.update(outcome: 'reverted')
42
43
 
43
- # Record the revert as a new mutation
44
+ # Record the revert as a new mutation (uses a unique version to avoid collision)
44
45
  Data::Model::SynapseMutation.create(
45
46
  synapse_id: synapse.id,
46
- version: restored_version,
47
+ version: revert_version,
47
48
  mutation_type: 'confidence_changed',
48
49
  before_state: mutation.after_state,
49
50
  after_state: mutation.before_state,
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module Synapse
6
- VERSION = '0.4.13'
6
+ VERSION = '0.4.14'
7
7
  end
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-synapse
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.13
4
+ version: 0.4.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity