ace-assign 0.67.0 → 0.68.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 +4 -4
- data/CHANGELOG.md +7 -0
- data/handbook/workflow-instructions/assign/drive.wf.md +39 -0
- data/lib/ace/assign/atoms/fixed_merge_schemas.rb +398 -0
- data/lib/ace/assign/errors.rb +5 -1
- data/lib/ace/assign/models/evidence_event.rb +1 -0
- data/lib/ace/assign/molecules/evidence_journal.rb +281 -10
- data/lib/ace/assign/molecules/fixed_effect_reference.rb +31 -0
- data/lib/ace/assign/molecules/ordinary_merge_cli.rb +68 -0
- data/lib/ace/assign/molecules/ordinary_merge_contract.rb +410 -0
- data/lib/ace/assign/organisms/attempt_coordinator.rb +90 -7
- data/lib/ace/assign/organisms/ordinary_merge_consumer.rb +365 -0
- data/lib/ace/assign/version.rb +1 -1
- data/lib/ace/assign.rb +3 -0
- metadata +7 -2
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tempfile"
|
|
4
|
+
require_relative "../errors"
|
|
5
|
+
require_relative "../molecules/ordinary_merge_contract"
|
|
6
|
+
require_relative "../molecules/ordinary_merge_cli"
|
|
7
|
+
|
|
8
|
+
module Ace
|
|
9
|
+
module Assign
|
|
10
|
+
module Organisms
|
|
11
|
+
# Ordinary project-overseer merge consumer (qkb.1).
|
|
12
|
+
#
|
|
13
|
+
# Records the exact immutable integration request as attempt evidence
|
|
14
|
+
# before dispatch, invokes the fixed producer through `ace-lab service
|
|
15
|
+
# request/status --mode ordinary-merge` with the closed selectors,
|
|
16
|
+
# persists the receiver-owned opaque reference, restores it after a
|
|
17
|
+
# queue-owner restart, and accepts only the exact terminal receipt.
|
|
18
|
+
#
|
|
19
|
+
# ace-assign attempt/evidence state is the sole queue/effect record:
|
|
20
|
+
# no caller-local journal, receipt ledger or second engine exists here.
|
|
21
|
+
# Lost acknowledgments, requester/receiver restarts, unavailable
|
|
22
|
+
# receivers and uncertain original results reconcile only by the
|
|
23
|
+
# recorded binding and reference, performing at most one merge. An
|
|
24
|
+
# uncertain original result is never resubmitted; only a proven
|
|
25
|
+
# pre-effect refusal closes an intent for a new reviewed request.
|
|
26
|
+
# Read-only status polls never dispatch, repair or refresh anything,
|
|
27
|
+
# and no `refused` state exists as status — refusal is request-time
|
|
28
|
+
# only.
|
|
29
|
+
class OrdinaryMergeConsumer
|
|
30
|
+
INTENT_STAGE = "ordinary-merge-intent"
|
|
31
|
+
ACK_STAGE = "ordinary-merge-ack"
|
|
32
|
+
RESULT_STAGE = "ordinary-merge-result"
|
|
33
|
+
|
|
34
|
+
def initialize(repo_root:, journal: nil, coordinator: nil, identity_resolver: nil, producer: nil)
|
|
35
|
+
@repo_root = repo_root
|
|
36
|
+
@journal = journal || Molecules::EvidenceJournal.new(repo_root: repo_root)
|
|
37
|
+
@coordinator = coordinator || AttemptCoordinator.new(repo_root: repo_root, journal: @journal)
|
|
38
|
+
@identity_resolver = identity_resolver || Molecules::ExecutionIdentityResolver.new
|
|
39
|
+
@producer = producer || Molecules::OrdinaryMergeCli.new
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Records the immutable binding and dispatches one admission request.
|
|
43
|
+
# Re-invocation with the identical recorded binding reconciles by the
|
|
44
|
+
# same request ID (an identical digest returns the original
|
|
45
|
+
# acknowledgment); any different binding while an effect is unresolved
|
|
46
|
+
# is forbidden resubmission.
|
|
47
|
+
def dispatch(assignment_id:, attempt_id:, project:, queue_entry:, input:, authorization:, request_id:)
|
|
48
|
+
identity = trusted_identity!
|
|
49
|
+
binding = identity_binding!(assignment_id, attempt_id)
|
|
50
|
+
attempt = authoritative_attempt!(binding)
|
|
51
|
+
raise AttemptErrors::ReceiptRejected, "Ordinary merge attempt is no longer active" unless attempt.active?
|
|
52
|
+
input_document = validate_input!(input)
|
|
53
|
+
selectors = validated_selectors(project: project, queue_entry: queue_entry,
|
|
54
|
+
authorization: authorization, request_id: request_id)
|
|
55
|
+
digest = Atoms::FixedMergeSchemas.canonical_request_digest(
|
|
56
|
+
project: selectors[:project], assignment: binding.fetch("assignment_id"),
|
|
57
|
+
attempt: binding.fetch("attempt_id"), queue_entry: selectors[:queue_entry],
|
|
58
|
+
authorization: selectors[:authorization], request_id: selectors[:request_id],
|
|
59
|
+
input: input_document
|
|
60
|
+
)
|
|
61
|
+
recorded = merge_effects(binding)
|
|
62
|
+
if (intent = recorded[:intent])
|
|
63
|
+
unless identical_binding?(intent.fetch("payload"), selectors, digest)
|
|
64
|
+
raise AttemptErrors::Conflict,
|
|
65
|
+
"Unresolved ordinary merge binding requires read-only status reconciliation; resubmission is forbidden"
|
|
66
|
+
end
|
|
67
|
+
return admit(binding, identity, selectors, input_document, digest, record_intent: false)
|
|
68
|
+
end
|
|
69
|
+
raise AttemptErrors::InvalidState, "Ordinary merge is already terminal for this attempt" if recorded[:receipt]
|
|
70
|
+
admit(binding, identity, selectors, input_document, digest, record_intent: true)
|
|
71
|
+
rescue Molecules::OrdinaryMergeCli::UnavailableError
|
|
72
|
+
raise AttemptErrors::EvidenceUnavailable,
|
|
73
|
+
"ordinary merge receiver is unavailable before admission; reconciliation by the recorded binding is required, resubmission is forbidden"
|
|
74
|
+
rescue ArgumentError
|
|
75
|
+
raise AttemptErrors::ReceiptRejected, "ordinary merge request violates the closed contract"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Read-only observation. Recovers the binding and opaque reference
|
|
79
|
+
# from canonical attempt evidence, polls the producer status command,
|
|
80
|
+
# and accepts only the exact terminal receipt. Once a terminal
|
|
81
|
+
# receipt is accepted it is returned without further producer calls.
|
|
82
|
+
def poll(assignment_id:, attempt_id:)
|
|
83
|
+
trusted_identity!
|
|
84
|
+
binding = identity_binding!(assignment_id, attempt_id)
|
|
85
|
+
authoritative_attempt!(binding)
|
|
86
|
+
recorded = merge_effects(binding)
|
|
87
|
+
return terminal_result(recorded[:receipt].fetch("payload")) if recorded[:receipt]
|
|
88
|
+
unless recorded[:intent]
|
|
89
|
+
raise AttemptErrors::NotFound,
|
|
90
|
+
"No ordinary merge request is recorded or unresolved for this attempt"
|
|
91
|
+
end
|
|
92
|
+
intent = recorded[:intent].fetch("payload")
|
|
93
|
+
selectors = intent_selectors(intent)
|
|
94
|
+
input_document = intent.fetch("input")
|
|
95
|
+
digest = rederive_recorded_digest(binding, intent)
|
|
96
|
+
reference = recorded[:ack]&.fetch("payload")&.fetch("reference")
|
|
97
|
+
return recover_admission(binding, selectors, input_document, digest) unless reference
|
|
98
|
+
observe(binding, selectors, reference, input_document, digest)
|
|
99
|
+
rescue Molecules::OrdinaryMergeCli::UnavailableError
|
|
100
|
+
raise AttemptErrors::EvidenceUnavailable,
|
|
101
|
+
"ordinary merge status is unavailable; read-only reconciliation by the recorded reference is required"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
private
|
|
105
|
+
|
|
106
|
+
def trusted_identity!
|
|
107
|
+
identity = @identity_resolver.resolve
|
|
108
|
+
raise AttemptErrors::UnauthorizedIdentity, "Only a trusted coordinator may consume ordinary merge" unless
|
|
109
|
+
@identity_resolver.trusted?(identity)
|
|
110
|
+
identity
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def identity_binding!(assignment_id, attempt_id)
|
|
114
|
+
[assignment_id, attempt_id].each do |value|
|
|
115
|
+
raise ArgumentError, "ordinary merge assignment/attempt must match the closed token grammar" unless
|
|
116
|
+
value.is_a?(String) && value.match?(Molecules::JournalMutation::ID)
|
|
117
|
+
end
|
|
118
|
+
{"assignment_id" => assignment_id, "attempt_id" => attempt_id}
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def authoritative_attempt!(binding)
|
|
122
|
+
attempt = @coordinator.authoritative_attempt(binding)
|
|
123
|
+
raise AttemptErrors::NotFound, "Ordinary merge attempt not found" unless attempt
|
|
124
|
+
raise AttemptErrors::ReceiptRejected, "Ordinary merge requires a managed attempt" unless attempt.managed?
|
|
125
|
+
attempt
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def validate_input!(input)
|
|
129
|
+
Molecules::OrdinaryMergeContract.validate_request_input!(input)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def validated_selectors(project:, queue_entry:, authorization:, request_id:)
|
|
133
|
+
{"project" => project, "queue_entry" => queue_entry, "request_id" => request_id}.each do |label, value|
|
|
134
|
+
raise ArgumentError, "ordinary merge #{label} must match the closed token grammar" unless
|
|
135
|
+
value.is_a?(String) && value.match?(Molecules::JournalMutation::ID)
|
|
136
|
+
end
|
|
137
|
+
unless authorization.is_a?(String) && authorization.length.between?(1, 256)
|
|
138
|
+
raise ArgumentError, "ordinary merge authorization reference must be a non-empty string of at most 256 characters"
|
|
139
|
+
end
|
|
140
|
+
{project: project, queue_entry: queue_entry, authorization: authorization, request_id: request_id}
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def intent_selectors(intent)
|
|
144
|
+
{project: intent.fetch("project"), queue_entry: intent.fetch("queue_entry"),
|
|
145
|
+
authorization: intent.fetch("authorization"), request_id: intent.fetch("request_id")}
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def identical_binding?(intent, selectors, digest)
|
|
149
|
+
intent_selectors(intent) == selectors && intent.fetch("input_digest") == digest
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# The canonical request digest (spec ruling r4) is the SHARED
|
|
153
|
+
# Atoms::FixedMergeSchemas.canonical_request_digest implementation —
|
|
154
|
+
# this consumer deliberately keeps no local digest computation.
|
|
155
|
+
|
|
156
|
+
# Re-derives the identical digest from the recorded binding and
|
|
157
|
+
# verifies it against the stored value before any echo validation.
|
|
158
|
+
def rederive_recorded_digest(binding, intent)
|
|
159
|
+
selectors = intent_selectors(intent)
|
|
160
|
+
derived = Atoms::FixedMergeSchemas.canonical_request_digest(
|
|
161
|
+
project: selectors[:project], assignment: binding.fetch("assignment_id"),
|
|
162
|
+
attempt: binding.fetch("attempt_id"), queue_entry: selectors[:queue_entry],
|
|
163
|
+
authorization: selectors[:authorization], request_id: selectors[:request_id],
|
|
164
|
+
input: intent.fetch("input")
|
|
165
|
+
)
|
|
166
|
+
unless derived == intent.fetch("input_digest")
|
|
167
|
+
raise AttemptErrors::ReceiptRejected,
|
|
168
|
+
"recorded ordinary merge binding digest does not re-derive from the recorded binding"
|
|
169
|
+
end
|
|
170
|
+
derived
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# The recorded ordinary-merge effects for one attempt: the open
|
|
174
|
+
# intent (if any), its acknowledgment reference and an accepted
|
|
175
|
+
# terminal receipt. Intents settle only through a result event for
|
|
176
|
+
# the same request ID.
|
|
177
|
+
def merge_effects(binding)
|
|
178
|
+
events = @journal.read_events(binding.fetch("assignment_id")).select do |event|
|
|
179
|
+
event["type"] == "delivery" && event["attempt_id"] == binding.fetch("attempt_id") &&
|
|
180
|
+
event.dig("payload", "stage").to_s.start_with?("ordinary-merge-")
|
|
181
|
+
end
|
|
182
|
+
intents = events.select { |event| event.dig("payload", "stage") == INTENT_STAGE }
|
|
183
|
+
settled = events.select { |event| event.dig("payload", "stage") == RESULT_STAGE }
|
|
184
|
+
.map { |event| event.dig("payload", "request_id") }
|
|
185
|
+
open = intents.reject { |event| settled.include?(event.dig("payload", "request_id")) }
|
|
186
|
+
raise AttemptErrors::Conflict, "Multiple unresolved ordinary merge bindings require reconciliation" if open.length > 1
|
|
187
|
+
intent = open.last
|
|
188
|
+
ack = intent && events.reverse.find do |event|
|
|
189
|
+
event.dig("payload", "stage") == ACK_STAGE &&
|
|
190
|
+
event.dig("payload", "request_id") == intent.dig("payload", "request_id")
|
|
191
|
+
end
|
|
192
|
+
receipt = events.reverse.find do |event|
|
|
193
|
+
event.dig("payload", "stage") == RESULT_STAGE &&
|
|
194
|
+
%w[succeeded failed].include?(event.dig("payload", "terminal_status"))
|
|
195
|
+
end
|
|
196
|
+
{intent: intent, ack: ack, receipt: receipt}
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def admit(binding, identity, selectors, input_document, digest, record_intent:)
|
|
200
|
+
record_event(binding, identity, intent_payload(selectors, input_document, digest)) if record_intent
|
|
201
|
+
branch = admission_branch(request_admission(binding, selectors, input_document))
|
|
202
|
+
if branch[:status] == "ok"
|
|
203
|
+
ack = begin
|
|
204
|
+
Molecules::OrdinaryMergeContract.validate_ack!(branch[:payload],
|
|
205
|
+
request_id: selectors[:request_id], input_digest: digest)
|
|
206
|
+
rescue ArgumentError
|
|
207
|
+
raise AttemptErrors::EvidenceUnavailable,
|
|
208
|
+
"ordinary merge acknowledgment is not the maintained contract; admission stays unconfirmed"
|
|
209
|
+
end
|
|
210
|
+
record_event(binding, identity, {"stage" => ACK_STAGE, "reference" => ack.fetch("reference"),
|
|
211
|
+
"request_id" => ack.fetch("request_id"), "input_digest" => ack.fetch("input_digest")})
|
|
212
|
+
return {state: "accepted", reference: ack.fetch("reference"),
|
|
213
|
+
request_id: ack.fetch("request_id"), input_digest: ack.fetch("input_digest")}
|
|
214
|
+
end
|
|
215
|
+
handle_refusal(binding, identity, branch[:payload], expected_digest: digest)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Closed outer envelope for admission responses: exactly {status,
|
|
219
|
+
# data} for ok and exactly {status, error} for error, both objects
|
|
220
|
+
# rejecting unknown fields. Any other outer status or shape —
|
|
221
|
+
# including a refusal attached to a non-error envelope such as
|
|
222
|
+
# pending — is malformed, leaves admission unconfirmed, and is never
|
|
223
|
+
# persisted as a proven refusal.
|
|
224
|
+
def admission_branch(envelope)
|
|
225
|
+
Molecules::OrdinaryMergeContract.validate_envelope_branch!(envelope)
|
|
226
|
+
rescue ArgumentError
|
|
227
|
+
raise AttemptErrors::EvidenceUnavailable,
|
|
228
|
+
"ordinary merge response is not the maintained envelope; admission stays unconfirmed"
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def request_admission(binding, selectors, input_document)
|
|
232
|
+
input_path = nil
|
|
233
|
+
Tempfile.create("ace-assign-ordinary-merge-input") do |file|
|
|
234
|
+
file.write(Atoms::EvidenceDigest.canonical_json(input_document))
|
|
235
|
+
file.flush
|
|
236
|
+
input_path = file.path
|
|
237
|
+
return @producer.request(project: selectors[:project], assignment: binding.fetch("assignment_id"),
|
|
238
|
+
attempt: binding.fetch("attempt_id"), queue_entry: selectors[:queue_entry],
|
|
239
|
+
input_path: input_path, authorization: selectors[:authorization],
|
|
240
|
+
request_id: selectors[:request_id])
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def handle_refusal(binding, identity, error, expected_digest:)
|
|
245
|
+
refusal = begin
|
|
246
|
+
Molecules::OrdinaryMergeContract.validate_refusal!(error)
|
|
247
|
+
rescue ArgumentError
|
|
248
|
+
raise AttemptErrors::EvidenceUnavailable,
|
|
249
|
+
"ordinary merge refusal is not the maintained contract; admission stays unconfirmed"
|
|
250
|
+
end
|
|
251
|
+
echo = refusal.fetch("input_digest")
|
|
252
|
+
if echo && echo != expected_digest
|
|
253
|
+
raise AttemptErrors::EvidenceUnavailable,
|
|
254
|
+
"ordinary merge refusal echoes a different input digest; admission stays unconfirmed"
|
|
255
|
+
end
|
|
256
|
+
if refusal.fetch("code") == Molecules::OrdinaryMergeContract::UNAVAILABLE_CODE
|
|
257
|
+
raise AttemptErrors::EvidenceUnavailable,
|
|
258
|
+
"ordinary merge receiver refused as unavailable before admission; this is a blocker, never a proven refusal"
|
|
259
|
+
end
|
|
260
|
+
record_event(binding, identity, {"stage" => RESULT_STAGE, "outcome" => "refused_no_effect",
|
|
261
|
+
"code" => refusal.fetch("code"), "message" => refusal.fetch("message"),
|
|
262
|
+
"request_id" => refusal.fetch("request_id"), "input_digest" => refusal.fetch("input_digest")})
|
|
263
|
+
{state: "refused", code: refusal.fetch("code"), message: refusal.fetch("message")}
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Lost acknowledgment after durable admission: reconcile by the
|
|
267
|
+
# identical recorded binding. A request ID bound to the identical
|
|
268
|
+
# digest returns the original acknowledgment and reference.
|
|
269
|
+
def recover_admission(binding, selectors, input_document, digest)
|
|
270
|
+
identity = trusted_identity!
|
|
271
|
+
result = admit(binding, identity, selectors, input_document, digest, record_intent: false)
|
|
272
|
+
return result if result.is_a?(Hash) && result[:state] == "refused"
|
|
273
|
+
unless result.is_a?(Hash) && result[:state] == "accepted"
|
|
274
|
+
raise AttemptErrors::EvidenceUnavailable, "ordinary merge acknowledgment is still unavailable"
|
|
275
|
+
end
|
|
276
|
+
observe(binding, selectors, result.fetch(:reference), input_document, digest)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def observe(binding, selectors, reference, input_document, digest)
|
|
280
|
+
envelope = @producer.status(reference: reference, request_id: selectors[:request_id],
|
|
281
|
+
project: selectors[:project], assignment: binding.fetch("assignment_id"),
|
|
282
|
+
attempt: binding.fetch("attempt_id"), queue_entry: selectors[:queue_entry],
|
|
283
|
+
input_digest: digest)
|
|
284
|
+
branch = status_branch(envelope)
|
|
285
|
+
if branch[:status] == "error"
|
|
286
|
+
begin
|
|
287
|
+
validated = Molecules::OrdinaryMergeContract.validate_refusal!(branch[:payload],
|
|
288
|
+
schema: Molecules::OrdinaryMergeContract::STATUS_ERROR_SCHEMA)
|
|
289
|
+
rescue ArgumentError
|
|
290
|
+
raise AttemptErrors::EvidenceUnavailable, "ordinary merge status error is not the maintained contract"
|
|
291
|
+
end
|
|
292
|
+
echo = validated.fetch("input_digest")
|
|
293
|
+
if echo && echo != digest
|
|
294
|
+
raise AttemptErrors::EvidenceUnavailable,
|
|
295
|
+
"ordinary merge status error echoes a different input digest; never a durable state"
|
|
296
|
+
end
|
|
297
|
+
raise AttemptErrors::EvidenceUnavailable,
|
|
298
|
+
"ordinary merge status command error (#{validated.fetch("code")}); a command error is never a durable state or a refusal"
|
|
299
|
+
end
|
|
300
|
+
observed = begin
|
|
301
|
+
Molecules::OrdinaryMergeContract.validate_status!(envelope, reference: reference,
|
|
302
|
+
request_id: selectors[:request_id], input_digest: digest)
|
|
303
|
+
rescue ArgumentError
|
|
304
|
+
raise AttemptErrors::ReceiptRejected, "ordinary merge status or receipt differs from the closed contract"
|
|
305
|
+
end
|
|
306
|
+
return {state: observed[:state]} unless observed[:receipt]
|
|
307
|
+
accept_receipt(binding, selectors, reference, input_document, digest, observed[:receipt])
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# Closed outer envelope for read-only status responses. A malformed
|
|
311
|
+
# shape — including a missing branch object — is a classified caller-
|
|
312
|
+
# side blocker, never a raw internal error and never a durable state.
|
|
313
|
+
def status_branch(envelope)
|
|
314
|
+
Molecules::OrdinaryMergeContract.validate_envelope_branch!(envelope)
|
|
315
|
+
rescue ArgumentError
|
|
316
|
+
raise AttemptErrors::EvidenceUnavailable,
|
|
317
|
+
"ordinary merge status response is not the maintained envelope"
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def accept_receipt(binding, selectors, reference, input_document, digest, receipt)
|
|
321
|
+
receipt_binding = {reference: reference, request_id: selectors[:request_id], input_digest: digest,
|
|
322
|
+
request: {project: selectors[:project], assignment: binding.fetch("assignment_id"),
|
|
323
|
+
attempt: binding.fetch("attempt_id"), queue_entry: selectors[:queue_entry],
|
|
324
|
+
authorization: selectors[:authorization], input: input_document}}
|
|
325
|
+
accepted = begin
|
|
326
|
+
Molecules::OrdinaryMergeContract.validate_receipt!(receipt, binding: receipt_binding)
|
|
327
|
+
rescue ArgumentError
|
|
328
|
+
raise AttemptErrors::ReceiptRejected, "terminal receipt differs from the recorded binding or the closed schema"
|
|
329
|
+
end
|
|
330
|
+
payload = {"stage" => RESULT_STAGE, "terminal_status" => accepted.fetch("terminal_status"),
|
|
331
|
+
"outcome" => accepted.fetch("effect").fetch("outcome"), "reference" => reference,
|
|
332
|
+
"request_id" => selectors[:request_id], "input_digest" => digest, "receipt" => accepted,
|
|
333
|
+
"synchronization_required" => accepted.fetch("effect").fetch("outcome") == "already_merged"}
|
|
334
|
+
record_event(binding, trusted_identity!, payload)
|
|
335
|
+
terminal_result(payload)
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def terminal_result(payload)
|
|
339
|
+
{state: payload.fetch("terminal_status"), receipt: payload.fetch("receipt"),
|
|
340
|
+
synchronization_required: payload.fetch("synchronization_required")}
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def intent_payload(selectors, input_document, digest)
|
|
344
|
+
{"stage" => INTENT_STAGE, "project" => selectors[:project], "queue_entry" => selectors[:queue_entry],
|
|
345
|
+
"operation" => "merge", "authorization" => selectors[:authorization],
|
|
346
|
+
"request_id" => selectors[:request_id], "input" => input_document, "input_digest" => digest}
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def record_event(binding, identity, payload)
|
|
350
|
+
producer = {"actor" => identity.actor, "role" => identity.role, "runtime" => identity.runtime}
|
|
351
|
+
@journal.record(**binding.transform_keys(&:to_sym), type: "delivery",
|
|
352
|
+
payload: payload.merge("producer" => producer),
|
|
353
|
+
guard: -> {
|
|
354
|
+
live = @coordinator.authoritative_attempt(binding)
|
|
355
|
+
raise AttemptErrors::ReceiptRejected, "Ordinary merge attempt is no longer active" unless
|
|
356
|
+
live&.active? && live.managed?
|
|
357
|
+
})
|
|
358
|
+
rescue SystemCallError
|
|
359
|
+
raise AttemptErrors::EvidenceUnavailable,
|
|
360
|
+
"ordinary merge evidence record is unavailable; reconcile by the recorded binding"
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
end
|
data/lib/ace/assign/version.rb
CHANGED
data/lib/ace/assign.rb
CHANGED
|
@@ -98,4 +98,7 @@ module Ace
|
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
require_relative "assign/atoms/delivery_parameters"
|
|
101
|
+
require_relative "assign/molecules/ordinary_merge_contract"
|
|
102
|
+
require_relative "assign/molecules/ordinary_merge_cli"
|
|
103
|
+
require_relative "assign/organisms/ordinary_merge_consumer"
|
|
101
104
|
require_relative "assign/organisms/delivery_coordinator"
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ace-assign
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.68.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
|
+
date: 2026-10-14 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: ace-llm-providers-cli
|
|
@@ -409,6 +409,7 @@ files:
|
|
|
409
409
|
- lib/ace/assign/atoms/composition_rules.rb
|
|
410
410
|
- lib/ace/assign/atoms/delivery_parameters.rb
|
|
411
411
|
- lib/ace/assign/atoms/evidence_digest.rb
|
|
412
|
+
- lib/ace/assign/atoms/fixed_merge_schemas.rb
|
|
412
413
|
- lib/ace/assign/atoms/number_generator.rb
|
|
413
414
|
- lib/ace/assign/atoms/preset_expander.rb
|
|
414
415
|
- lib/ace/assign/atoms/preset_loader.rb
|
|
@@ -537,11 +538,14 @@ files:
|
|
|
537
538
|
- lib/ace/assign/molecules/evidence_journal.rb
|
|
538
539
|
- lib/ace/assign/molecules/execution_identity_resolver.rb
|
|
539
540
|
- lib/ace/assign/molecules/execution_scope_lineage.rb
|
|
541
|
+
- lib/ace/assign/molecules/fixed_effect_reference.rb
|
|
540
542
|
- lib/ace/assign/molecules/fork_session_launcher.rb
|
|
541
543
|
- lib/ace/assign/molecules/journal_input_inhibition.rb
|
|
542
544
|
- lib/ace/assign/molecules/journal_mutation.rb
|
|
543
545
|
- lib/ace/assign/molecules/journal_prompt_mutation.rb
|
|
544
546
|
- lib/ace/assign/molecules/lifecycle_exclusion.rb
|
|
547
|
+
- lib/ace/assign/molecules/ordinary_merge_cli.rb
|
|
548
|
+
- lib/ace/assign/molecules/ordinary_merge_contract.rb
|
|
545
549
|
- lib/ace/assign/molecules/original_launch_binding.rb
|
|
546
550
|
- lib/ace/assign/molecules/preset_inferrer.rb
|
|
547
551
|
- lib/ace/assign/molecules/proposal_journal.rb
|
|
@@ -558,6 +562,7 @@ files:
|
|
|
558
562
|
- lib/ace/assign/organisms/assignment_executor.rb
|
|
559
563
|
- lib/ace/assign/organisms/attempt_coordinator.rb
|
|
560
564
|
- lib/ace/assign/organisms/delivery_coordinator.rb
|
|
565
|
+
- lib/ace/assign/organisms/ordinary_merge_consumer.rb
|
|
561
566
|
- lib/ace/assign/organisms/prepared_work_builder.rb
|
|
562
567
|
- lib/ace/assign/organisms/protected_delivery_coordinator.rb
|
|
563
568
|
- lib/ace/assign/organisms/task_assignment_creator.rb
|