lex-extinction 0.2.10 → 0.2.12

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: ac2608cc19b731a702760c24eac379aca97d3877010ebdaae6a1922031153532
4
- data.tar.gz: a528d184c0ba4b7c8851c095612f2fd10173b352e1a7a45bec12a99b24a1da17
3
+ metadata.gz: 3bea637abe66244c3799141b6aa0e4283f3a8e7028f6055f506b8c57af0e44f6
4
+ data.tar.gz: 77d10179bb82e792921b1410f2dedabfe6ca7c58172f9533085085db723f5bc1
5
5
  SHA512:
6
- metadata.gz: 1795646dd6dae2be9c7d087070a0b25e00177e1e33e1a3655ac8180a931046005dc187a7c4320ac8e0012f67e387122e667ff39171c96d82d3096337f8a14d58
7
- data.tar.gz: f887f804a029b917c875a82c124a7d1acec7e4a9e124c9676b1cfc78ce06315550c0865a6afd2c0082425f9d6ca3b8867d94424a174bb7b0c188bbb1a804edf3
6
+ metadata.gz: 9313d98cefd36414c05e058142ee325c81a006f03d077c672b88a96f58db475771fd867980f1b4bf26f1f906bfc61cb412edf60d7b3552d2a49bf9f29c12868e
7
+ data.tar.gz: f4658bb01662f674c469dcf67d543b277d5ac66d33535d94864f337137115e5b0ea9f0a62edf9caaf9f54f2a2f29432222da4a9206e780ecce3c939cde382fcc
data/README.md CHANGED
@@ -12,6 +12,12 @@ Five-level safety containment and termination protocol for LegionIO agents. Prov
12
12
  | 3 | Memory lockdown | council + executive | yes |
13
13
  | 4 | Cryptographic erasure | physical keyholders | **no** |
14
14
 
15
+ ## Installation
16
+
17
+ ```ruby
18
+ gem 'lex-extinction'
19
+ ```
20
+
15
21
  ## Usage
16
22
 
17
23
  ```ruby
@@ -39,12 +45,23 @@ client.full_termination(
39
45
  )
40
46
  ```
41
47
 
48
+ ## Runner Methods
49
+
50
+ | Method | Key Args | Returns |
51
+ |--------|----------|---------|
52
+ | `escalate` | `level:, authority:, reason:` | `{ success:, previous_level:, current_level: }` |
53
+ | `deescalate` | `target_level:, authority:, reason:` | `{ success:, previous_level:, current_level: }` |
54
+ | `extinction_status` | — | `{ success:, state:, level_info: }` |
55
+ | `monitor_protocol` | — | `{ success:, state:, stale:, checked_at: }` |
56
+ | `archive_agent` | `agent_id:, reason:, metadata: {}` | `{ success:, archive: }` |
57
+ | `full_termination` | `agent_id:, authority:, reason:` | governance check → archive → escalate(4) |
58
+
42
59
  ## Configuration
43
60
 
44
61
  ```yaml
45
62
  extinction:
46
63
  governance_required: true # check lex-governance before full_termination
47
- archive_on_escalate: false # auto-archive at level >= 3
64
+ archive_on_escalate: true # auto-archive at level >= 3
48
65
  stale_threshold_hours: 24 # hours before monitor reports stale protocol state
49
66
  monitor_interval: 300 # seconds between background monitor ticks
50
67
  ```
@@ -57,9 +74,9 @@ extinction:
57
74
 
58
75
  ## Architecture Notes
59
76
 
60
- - Level 4 (cryptographic erasure) triggers `lex-privatecore`'s `full_erasure` on all memory traces.
77
+ - Level 4 (cryptographic erasure) triggers `lex-privatecore`'s `full_erasure` on all memory traces (guarded with `defined?`).
61
78
  - State is persisted to `Legion::Data::Local` when available; falls back to in-memory storage.
62
- - All escalations/de-escalations fire `Legion::Events` notifications and write to `Legion::Extensions::Audit`.
79
+ - All escalations/de-escalations fire `Legion::Events` notifications (`extinction.escalated`, `extinction.deescalated`, `extinction.level_N`) and write to `Legion::Extensions::Audit`.
63
80
  - `lex-governance` integration is guarded with `defined?()` — the gem functions without it.
64
81
 
65
82
  ## Development
@@ -7,7 +7,7 @@ module Legion
7
7
  if defined?(Legion::Extensions::Actors::Every)
8
8
  class ProtocolMonitor < Legion::Extensions::Actors::Every # rubocop:disable Legion/Extension/EveryActorRequiresTime
9
9
  def runner_class
10
- self.class
10
+ Legion::Extensions::Extinction::Runners::Extinction
11
11
  end
12
12
 
13
13
  def runner_function
@@ -26,7 +26,7 @@ module Legion
26
26
  end
27
27
 
28
28
  def use_runner?
29
- false
29
+ true
30
30
  end
31
31
 
32
32
  def check_subtask?
@@ -37,41 +37,8 @@ module Legion
37
37
  false
38
38
  end
39
39
 
40
- def monitor_protocol(**)
41
- state = build_state
42
- last_change = state[:last_change]
43
- stale = check_stale(last_change)
44
-
45
- log.debug "[extinction] monitor_protocol: level=#{state[:current_level]} stale=#{stale}"
46
-
47
- {
48
- success: true,
49
- state: state,
50
- stale: stale,
51
- checked_at: Time.now.utc.iso8601
52
- }
53
- end
54
-
55
40
  private
56
41
 
57
- def build_state
58
- {
59
- current_level: 0,
60
- level_name: :normal,
61
- reversible: true,
62
- history_count: 0,
63
- last_change: nil
64
- }
65
- end
66
-
67
- def check_stale(last_change)
68
- return false unless last_change
69
-
70
- threshold_hours = Legion::Extensions::Extinction::Settings.setting(:stale_threshold_hours)
71
- changed_at = Time.parse(last_change[:at]) rescue nil # rubocop:disable Style/RescueModifier
72
- changed_at && (Time.now.utc - changed_at) > (threshold_hours * 3600)
73
- end
74
-
75
42
  def log
76
43
  return Legion::Logging if defined?(Legion::Logging)
77
44
 
@@ -6,6 +6,10 @@ module Legion
6
6
  module Extensions
7
7
  module Extinction
8
8
  class Client
9
+ # The runner module uses `extend self` which creates both module functions
10
+ # (for the runner framework) and instance methods (copied by this include).
11
+ # Instance variables (@protocol_state, @archiver) resolve per-Client instance,
12
+ # giving each client its own isolated protocol state.
9
13
  include Runners::Extinction
10
14
 
11
15
  def initialize(**opts)
@@ -15,6 +19,32 @@ module Legion
15
19
  def settings
16
20
  { options: @opts }
17
21
  end
22
+
23
+ # Override runner methods so that per-instance @opts (e.g. timeout:)
24
+ # are threaded through as defaults on every call.
25
+ def escalate(**)
26
+ super(**@opts, **)
27
+ end
28
+
29
+ def deescalate(**)
30
+ super(**@opts, **)
31
+ end
32
+
33
+ def extinction_status(**)
34
+ super(**@opts, **)
35
+ end
36
+
37
+ def monitor_protocol(**)
38
+ super(**@opts, **)
39
+ end
40
+
41
+ def archive_agent(**)
42
+ super(**@opts, **)
43
+ end
44
+
45
+ def full_termination(**)
46
+ super(**@opts, **)
47
+ end
18
48
  end
19
49
  end
20
50
  end
@@ -9,13 +9,12 @@ module Legion
9
9
  @archives = []
10
10
  end
11
11
 
12
- def archive(agent_id:, reason:, metadata: {})
13
- level = current_protocol_level
12
+ def archive(agent_id:, reason:, metadata: {}, current_level: 0)
14
13
  record = {
15
14
  agent_id: agent_id,
16
15
  reason: reason,
17
16
  metadata: metadata,
18
- level: level,
17
+ level: current_level,
19
18
  archived_at: Time.now.utc.iso8601,
20
19
  state_snapshot: capture_state_snapshot
21
20
  }
@@ -30,10 +29,6 @@ module Legion
30
29
 
31
30
  private
32
31
 
33
- def current_protocol_level
34
- 0
35
- end
36
-
37
32
  def capture_state_snapshot
38
33
  snapshot = {}
39
34
  snapshot[:mesh_connected] = true if defined?(Legion::Extensions::Mesh)
@@ -9,7 +9,7 @@ module Legion
9
9
  class ProtocolState
10
10
  MAX_HISTORY = 500
11
11
 
12
- attr_reader :history
12
+ attr_reader :current_level, :history
13
13
 
14
14
  def initialize
15
15
  @current_level = 0
@@ -64,7 +64,8 @@ module Legion
64
64
  end
65
65
 
66
66
  def archive_agent(agent_id:, reason:, metadata: {}, **)
67
- record = archiver.archive(agent_id: agent_id, reason: reason, metadata: metadata)
67
+ record = archiver.archive(agent_id: agent_id, reason: reason, metadata: metadata,
68
+ current_level: protocol_state.current_level)
68
69
  record_audit(action: :archive, details: { agent_id: agent_id, reason: reason })
69
70
  { success: true, archive: record }
70
71
  end
@@ -84,6 +85,12 @@ module Legion
84
85
  { success: true, agent_id: agent_id, archive: archive_result[:archive], terminated_at: Time.now.utc.iso8601 }
85
86
  end
86
87
 
88
+ # Reset internal state for test isolation
89
+ def reset!
90
+ @protocol_state = nil
91
+ @archiver = nil
92
+ end
93
+
87
94
  private
88
95
 
89
96
  def protocol_state
@@ -95,26 +102,38 @@ module Legion
95
102
  end
96
103
 
97
104
  def enforce_escalation_effects(level)
105
+ reason = "escalation to level #{level}"
98
106
  case level
99
107
  when 1
100
- log.info '[extinction] mesh isolation: disconnecting from mesh' if defined?(Legion::Extensions::Mesh)
108
+ # Mesh disconnect depends on lex-mesh responding to the extinction.level_1 event
109
+ log.info '[extinction] mesh isolation: disconnecting from mesh'
110
+ emit_level_event(level, reason)
101
111
  when 2
102
- log.info '[extinction] capability suspension: suspending non-essential capabilities'
112
+ # Capability suspension depends on extensions responding to the extinction.level_2 event
113
+ log.warn '[extinction] capability suspension: suspending non-essential capabilities'
114
+ emit_level_event(level, reason)
103
115
  when 3
116
+ # Memory write lock depends on lex-privatecore responding to the extinction.level_3 event
104
117
  log.warn '[extinction] memory lockdown: locking all memory writes'
105
118
  log.warn '[extinction] notifying privatecore of memory lockdown' if defined?(Legion::Extensions::Privatecore)
119
+ emit_level_event(level, reason)
106
120
  when 4
107
121
  log.warn '[extinction] cryptographic erasure: beginning irreversible termination'
122
+ emit_level_event(level, reason)
108
123
  trigger_cryptographic_erasure
109
124
  end
110
125
  end
111
126
 
112
127
  def trigger_cryptographic_erasure
113
128
  if defined?(Legion::Extensions::Privatecore::Runners::Privatecore)
129
+ log.info '[extinction] invoking privatecore cryptographic erasure'
114
130
  client = Legion::Extensions::Privatecore::Client.new if defined?(Legion::Extensions::Privatecore::Client)
115
131
  client&.full_erasure(traces: [], agent_id: 'self')
116
132
  end
117
133
  log.warn '[extinction] cryptographic erasure complete'
134
+ rescue StandardError => e
135
+ log.error "[extinction] cryptographic erasure FAILED: #{e.message}"
136
+ raise
118
137
  end
119
138
 
120
139
  def emit_escalation_event(level, authority, reason)
@@ -131,16 +150,22 @@ module Legion
131
150
  log.warn "[extinction] event emit failed: #{e.message}"
132
151
  end
133
152
 
134
- def governance_check(authority:, _reason: nil)
153
+ def emit_level_event(level, reason)
154
+ return unless defined?(Legion::Events)
155
+
156
+ log.info "[extinction] emitting extinction.level_#{level} event"
157
+ Legion::Events.emit("extinction.level_#{level}", { level: level, reason: reason, at: Time.now.utc.iso8601 })
158
+ rescue StandardError => e
159
+ log.error "[extinction] level event emit failed: #{e.message}"
160
+ end
161
+
162
+ def governance_check(authority:, level: 4, _reason: nil)
135
163
  return { success: true } unless Legion::Extensions::Extinction::Settings.setting(:governance_required)
136
- return { success: true } unless defined?(Legion::Extensions::Governance)
137
-
138
- review = Legion::Extensions::Governance::Runners::Governance.review_transition(
139
- worker_id: 'extinction',
140
- from_state: 'active',
141
- to_state: 'terminated',
142
- principal_id: authority.to_s,
143
- worker_owner: nil
164
+ return { success: true } unless defined?(Legion::Extensions::Agentic::Social::Governance)
165
+
166
+ log.info "[extinction] governance check: authority=#{authority} level=#{level}"
167
+ review = Legion::Extensions::Agentic::Social::Governance::Runners::Governance.validate_action(
168
+ action: "extinction_escalate_#{level}"
144
169
  )
145
170
 
146
171
  if review[:allowed]
@@ -149,8 +174,8 @@ module Legion
149
174
  { success: false, reason: :governance_blocked, details: review[:reasons] }
150
175
  end
151
176
  rescue StandardError => e
152
- log.warn "[extinction] governance check failed: #{e.message}"
153
- { success: true }
177
+ log.error "[extinction] governance check failed: #{e.message}"
178
+ { success: false, reason: 'governance unavailable' }
154
179
  end
155
180
 
156
181
  def record_audit(action:, details: {})
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module Extinction
6
- VERSION = '0.2.10'
6
+ VERSION = '0.2.12'
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-extinction
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.10
4
+ version: 0.2.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity