ace-herdr 0.5.1 → 0.6.0

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: 00422d9f6820e48b0ea45698e4663f7ecaeb9b5b01ddf1626ab86aed50adcbfd
4
- data.tar.gz: 2632e4056271d3c022056a70fa5f3b95e2f69ae08544bf70f2577bc11fbac4f7
3
+ metadata.gz: 5e3b1b413921a28b8ebd2437c424f885d11ec65bcf886fdb173101b8ce5ca8de
4
+ data.tar.gz: a4ebad7d9470e1f7f1e74e6b7a66d3ced20605f13bb323c67f5abc51993978ee
5
5
  SHA512:
6
- metadata.gz: c6626094540195dadf06116add8bd27708934b809347c12cb0bc355cb6c03d5ff32e30e3e63870e158316cf9c80de7e2bdd5cad5f731cea4fef11303c1c9bd8a
7
- data.tar.gz: 7b4e02b683be089a602d10c7fafcb09abc134a9f5b60e5a58a8d0ef42b35bb0ec18555700e0c316ec5de12aae7e5c01a4f0ffede12e26597f1a881fcbb5d50af
6
+ metadata.gz: b53397db1e608164a19390d073ae7420b602ec1a8152b975f11d6dd3443dda4da4ff5208644aecf7f2bb7bfc269034c6813eac283683eed4ea916b1b01c52795
7
+ data.tar.gz: e716fc0fe8329fffda5b2c18c81395432b3257b4fee3118207f21e016f69185454f4ea2661fda6f8ba3aa42eebaf0aaca773fed7e20d7899ff08e6c139f2cb18
data/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.0] - 2026-10-16
11
+
12
+ ### Added
13
+
14
+ - Expose bounded native observations and hold ordinary Inbox delivery settlement through canonical Assign acceptance. Preserve exact targets and unresolved outcomes without repeat sends or alternate execution records.
15
+
10
16
  ## [0.5.1] - 2026-10-10
11
17
 
12
18
  ### Fixed
@@ -79,6 +79,15 @@ module Ace
79
79
  state == "delivered"
80
80
  end
81
81
 
82
+ # Exact native submission receipt, not a business completion verdict.
83
+ def inbox_delivered?
84
+ return false unless delivered? && inbox.is_a?(Hash)
85
+ receipt = inbox["receipt"]
86
+ inbox["claim_generation"].is_a?(Integer) && inbox["claim_generation"].positive? &&
87
+ receipt.is_a?(Hash) && receipt.values_at("event_id", "attempt_id", "claim_generation", "payload_sha256", "binding") ==
88
+ [event_id, inbox["attempt_id"], inbox["claim_generation"], answer_digest, inbox["binding"]]
89
+ end
90
+
82
91
  # A "submitting" history entry without a matching outcome means the
83
92
  # previous run crashed between prompt submission and persistence;
84
93
  # resending could duplicate the answer, so the outcome is ambiguous.
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "herdr_executor"
4
+
5
+ module Ace
6
+ module Herdr
7
+ module Molecules
8
+ # Read-only native attribution under one absolute deadline, including all
9
+ # ancestry probes. Never renew the budget for another process in the chain.
10
+ class BoundedObservation < HerdrExecutor
11
+ def initialize(deadline:, binary: DEFAULT_BINARY, process: BoundedProcess)
12
+ super(binary: binary, process: process)
13
+ @deadline = deadline
14
+ end
15
+
16
+ def pane_get(pane) = observe!([@binary, "pane", "get", pane])
17
+ def pane_process_info(pane) = observe!([@binary, "pane", "process-info", "--pane", pane])
18
+
19
+ # ProcessIdentity's existing executor seam. Do not let PATH select ps.
20
+ def capture2(command, *args, stdin_data: "")
21
+ raise ArgumentError, "observation only permits ps" unless command == "ps" && stdin_data.empty?
22
+ result = bounded(["/bin/ps", *args])
23
+ [result.stdout, result.status]
24
+ end
25
+
26
+ private
27
+
28
+ def observe!(argv)
29
+ result = bounded(argv)
30
+ execution = ExecutionResult.new(stdout: result.stdout.strip, stderr: result.stderr.strip,
31
+ success: result.status.success?, exit_code: result.status.exitstatus || -1)
32
+ raise classify(execution, argv) unless execution.success?
33
+ execution
34
+ end
35
+
36
+ def bounded(argv)
37
+ remaining = @deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
38
+ raise AgentNotReadyError, "native observation deadline elapsed" unless remaining.positive?
39
+ result = @process.call(argv, stdin_data: "", timeout_s: remaining,
40
+ output_limit: WAKE_OUTPUT_LIMIT, cleanup_group: true)
41
+ raise AgentNotReadyError, "native observation output exceeded limit" if result.oversized
42
+ result
43
+ rescue Timeout::Error, BoundedProcess::PostLaunchError
44
+ raise AgentNotReadyError, "native observation did not complete within its bound"
45
+ rescue SystemCallError
46
+ raise ExecutorUnavailableError, "native observation executable unavailable"
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -172,6 +172,39 @@ module Ace
172
172
 
173
173
  def context_root = @deliveries_dir.dup.freeze
174
174
 
175
+ # The installed consumer supplies canonical references and native
176
+ # owner, while this owner holds every existing record lock through the
177
+ # consumer's acceptance CAS. No delivery/retry/wake is performed here.
178
+ def with_delivery_settlement(deadline: nil)
179
+ self.class.with_retained_records(deliveries_dir: @deliveries_dir, deadline: deadline) do |records|
180
+ verify = lambda do |references:, attempt_id:, owner:|
181
+ unless references.is_a?(Array) && references.all? { |reference|
182
+ reference.is_a?(Hash) && reference.keys.sort == %w[attempt_id event_id payload_sha256] &&
183
+ reference["attempt_id"] == attempt_id }
184
+ raise ValidationError, "canonical Inbox references differ"
185
+ end
186
+ selected = records.select { |record| record.inbox.fetch("attempt_id") == attempt_id }
187
+ ids = references.map { |reference| reference.fetch("event_id") }
188
+ unless ids.uniq == ids && selected.map(&:event_id).sort == ids.sort
189
+ raise ValidationError, "canonical Inbox reference inventory differs"
190
+ end
191
+ next [] if selected.empty?
192
+ target = target_from_owner(owner)
193
+ selected.map do |record|
194
+ reference = references.find { |item| item.fetch("event_id") == record.event_id }
195
+ binding = record.inbox.fetch("binding", {})
196
+ unless !record.inbox["original_context"] && record.inbox_delivered? && record.answer_digest == reference.fetch("payload_sha256") &&
197
+ record.inbox["origin_target"] == target && binding.slice(*target.keys) == target &&
198
+ binding["payload_sha256"] == record.answer_digest && %w[none sent].include?(record.inbox.dig("wake", "status"))
199
+ raise ValidationError, "original Inbox delivery or wake remains unsettled"
200
+ end
201
+ reference.merge("claim_generation" => record.inbox.fetch("claim_generation"))
202
+ end
203
+ end
204
+ yield verify
205
+ end
206
+ end
207
+
175
208
 
176
209
  def enqueue(event:, attempt:, ref:, payload:, managed_envelope: nil, expected_target: nil, original_context: nil, original_binding_digest: nil)
177
210
  validate_id!(event, "event")
@@ -378,12 +378,7 @@ module Ace
378
378
  end
379
379
 
380
380
  def completed_record?(record)
381
- return false unless record.state == "delivered" && record.inbox.is_a?(Hash)
382
- inbox = record.inbox
383
- receipt = inbox["receipt"]
384
- inbox["claim_generation"].is_a?(Integer) && inbox["claim_generation"].positive? &&
385
- receipt.is_a?(Hash) && receipt.values_at("event_id", "attempt_id", "claim_generation", "payload_sha256", "binding") ==
386
- [record.event_id, inbox["attempt_id"], inbox["claim_generation"], record.answer_digest, inbox["binding"]]
381
+ record.inbox_delivered?
387
382
  end
388
383
 
389
384
  def expected!(state, expected)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ace
4
4
  module Herdr
5
- VERSION = "0.5.1"
5
+ VERSION = "0.6.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ace-herdr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Czyz
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-10-10 00:00:00.000000000 Z
10
+ date: 2026-10-16 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: websocket-driver
@@ -237,6 +237,7 @@ files:
237
237
  - lib/ace/herdr/errors.rb
238
238
  - lib/ace/herdr/models/delivery_record.rb
239
239
  - lib/ace/herdr/models/dispatch_outcome.rb
240
+ - lib/ace/herdr/molecules/bounded_observation.rb
240
241
  - lib/ace/herdr/molecules/bounded_process.rb
241
242
  - lib/ace/herdr/molecules/delivery_record_store.rb
242
243
  - lib/ace/herdr/molecules/guarded_native_origin.rb