ace-assign 0.70.0 → 0.73.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/.ace-defaults/assign/catalog/steps/auto-merge.step.yml +8 -8
- data/.ace-defaults/assign/catalog/steps/create-pr.step.yml +1 -1
- data/.ace-defaults/assign/catalog/steps/mark-pr-ready.step.yml +1 -1
- data/.ace-defaults/assign/config.yml +0 -13
- data/.ace-defaults/assign/presets/fix-bug.yml +1 -1
- data/.ace-defaults/assign/presets/work-on-task-ace-development.yml +1 -1
- data/.ace-defaults/assign/presets/work-on-task-auto-merge.yml +1 -1
- data/.ace-defaults/assign/presets/work-on-task.yml +1 -1
- data/CHANGELOG.md +3 -2099
- data/README.md +58 -31
- data/docs/exit-codes.md +36 -44
- data/docs/getting-started.md +9 -4
- data/docs/usage.md +129 -522
- data/handbook/skills/as-assign-drive/SKILL.md +1 -1
- data/handbook/workflow-instructions/assign/drive.wf.md +47 -118
- data/handbook/workflow-instructions/assign/prepare.wf.md +1 -1
- data/handbook/workflow-instructions/assign/verify-test-suite.wf.md +8 -2
- data/lib/ace/assign/cli/commands/assignment_target.rb +2 -15
- data/lib/ace/assign/cli/commands/create.rb +1 -8
- data/lib/ace/assign/cli/commands/fail.rb +0 -6
- data/lib/ace/assign/cli/commands/finish.rb +2 -23
- data/lib/ace/assign/cli/commands/fork_session.rb +0 -1
- data/lib/ace/assign/cli/commands/select.rb +0 -1
- data/lib/ace/assign/cli/commands/start.rb +0 -2
- data/lib/ace/assign/cli/commands/status.rb +1 -31
- data/lib/ace/assign/cli/commands/step.rb +1 -3
- data/lib/ace/assign/cli.rb +1 -85
- data/lib/ace/assign/errors.rb +8 -80
- data/lib/ace/assign/models/assignment.rb +5 -28
- data/lib/ace/assign/molecules/assignment_manager.rb +0 -26
- data/lib/ace/assign/molecules/fork_session_launcher.rb +4 -12
- data/lib/ace/assign/molecules/lifecycle_exclusion.rb +15 -112
- data/lib/ace/assign/molecules/runtime_control_surface_runner.rb +2 -1
- data/lib/ace/assign/organisms/assignment_executor.rb +0 -6
- data/lib/ace/assign/organisms/task_assignment_creator.rb +3 -8
- data/lib/ace/assign/version.rb +1 -1
- data/lib/ace/assign.rb +0 -6
- metadata +4 -161
- data/.ace-defaults/assign/presets/close-delivery.yml +0 -57
- data/.ace-defaults/assign/presets/deliver-release.yml +0 -60
- data/.ace-defaults/assign/presets/integrate-batch.yml +0 -72
- data/.ace-defaults/assign/presets/supervise-delivery.yml +0 -69
- data/docs/protected-authority.md +0 -282
- data/exe/ace-scope-ready +0 -55
- data/lib/ace/assign/atoms/assignment_scope.rb +0 -58
- data/lib/ace/assign/atoms/attempt_state_machine.rb +0 -84
- data/lib/ace/assign/atoms/delivery_parameters.rb +0 -19
- data/lib/ace/assign/atoms/evidence_digest.rb +0 -78
- data/lib/ace/assign/atoms/fixed_merge_schemas.rb +0 -398
- data/lib/ace/assign/authority/assignment_definition.rb +0 -35
- data/lib/ace/assign/authority/assignment_inventory.rb +0 -290
- data/lib/ace/assign/authority/campaign_consumer_policy.rb +0 -100
- data/lib/ace/assign/authority/campaign_execution.rb +0 -119
- data/lib/ace/assign/authority/campaign_round_transfer.rb +0 -82
- data/lib/ace/assign/authority/candidate_transfer.rb +0 -327
- data/lib/ace/assign/authority/cleanup_result_protection.rb +0 -53
- data/lib/ace/assign/authority/client.rb +0 -257
- data/lib/ace/assign/authority/deployment.rb +0 -627
- data/lib/ace/assign/authority/deployment_history.rb +0 -146
- data/lib/ace/assign/authority/endcap.rb +0 -455
- data/lib/ace/assign/authority/endcap_attempts.rb +0 -274
- data/lib/ace/assign/authority/endcap_campaign_results.rb +0 -232
- data/lib/ace/assign/authority/endcap_campaign_rounds.rb +0 -266
- data/lib/ace/assign/authority/endcap_contexts.rb +0 -23
- data/lib/ace/assign/authority/endcap_inboxes.rb +0 -37
- data/lib/ace/assign/authority/endcap_prepared_work.rb +0 -80
- data/lib/ace/assign/authority/endcap_publication.rb +0 -207
- data/lib/ace/assign/authority/endcap_results.rb +0 -398
- data/lib/ace/assign/authority/endcap_review_requests.rb +0 -183
- data/lib/ace/assign/authority/endcap_reviews.rb +0 -130
- data/lib/ace/assign/authority/endcap_service_settlement.rb +0 -152
- data/lib/ace/assign/authority/endcap_services.rb +0 -598
- data/lib/ace/assign/authority/evidence_transfer.rb +0 -67
- data/lib/ace/assign/authority/execution_scope_observation.rb +0 -903
- data/lib/ace/assign/authority/inbox_context_original.rb +0 -113
- data/lib/ace/assign/authority/launch_attempt_consumers.rb +0 -51
- data/lib/ace/assign/authority/launch_campaign_registration.rb +0 -214
- data/lib/ace/assign/authority/launch_control_channel.rb +0 -313
- data/lib/ace/assign/authority/launch_control_exclusions.rb +0 -127
- data/lib/ace/assign/authority/launch_driver.rb +0 -393
- data/lib/ace/assign/authority/launch_input_inhibition.rb +0 -132
- data/lib/ace/assign/authority/launch_lifecycle.rb +0 -997
- data/lib/ace/assign/authority/launch_prepared_work.rb +0 -122
- data/lib/ace/assign/authority/launch_review.rb +0 -91
- data/lib/ace/assign/authority/launch_scope_abort.rb +0 -81
- data/lib/ace/assign/authority/launch_scope_admission.rb +0 -215
- data/lib/ace/assign/authority/launch_scope_close.rb +0 -146
- data/lib/ace/assign/authority/launch_scope_parent.rb +0 -44
- data/lib/ace/assign/authority/launch_scope_release.rb +0 -859
- data/lib/ace/assign/authority/launch_steering.rb +0 -353
- data/lib/ace/assign/authority/launch_stop.rb +0 -254
- data/lib/ace/assign/authority/maintenance_workspace_retirement.rb +0 -229
- data/lib/ace/assign/authority/ordinary_attempt_start.rb +0 -137
- data/lib/ace/assign/authority/ordinary_client.rb +0 -70
- data/lib/ace/assign/authority/ordinary_completion.rb +0 -94
- data/lib/ace/assign/authority/ordinary_effect_settlement.rb +0 -118
- data/lib/ace/assign/authority/ordinary_lifecycle.rb +0 -191
- data/lib/ace/assign/authority/ordinary_peer.rb +0 -54
- data/lib/ace/assign/authority/ordinary_progress.rb +0 -162
- data/lib/ace/assign/authority/ordinary_queue.rb +0 -115
- data/lib/ace/assign/authority/ordinary_results.rb +0 -196
- data/lib/ace/assign/authority/ordinary_server.rb +0 -75
- data/lib/ace/assign/authority/original_inbox_selection.rb +0 -41
- data/lib/ace/assign/authority/posix_acl.rb +0 -59
- data/lib/ace/assign/authority/prepared_input.rb +0 -130
- data/lib/ace/assign/authority/prepared_queue.rb +0 -185
- data/lib/ace/assign/authority/prepared_task_context.rb +0 -116
- data/lib/ace/assign/authority/prepared_work.rb +0 -304
- data/lib/ace/assign/authority/prepared_worker.rb +0 -69
- data/lib/ace/assign/authority/private_directory.rb +0 -33
- data/lib/ace/assign/authority/protected_assignment_context.rb +0 -226
- data/lib/ace/assign/authority/readiness_hook.rb +0 -120
- data/lib/ace/assign/authority/receipt_transfer.rb +0 -48
- data/lib/ace/assign/authority/router.rb +0 -81
- data/lib/ace/assign/authority/server.rb +0 -131
- data/lib/ace/assign/authority/service_cleanup_dispatch.rb +0 -88
- data/lib/ace/assign/authority/service_cleanup_evidence.rb +0 -180
- data/lib/ace/assign/authority/service_delivery_evidence.rb +0 -101
- data/lib/ace/assign/authority/service_evidence.rb +0 -286
- data/lib/ace/assign/authority/service_publication_evidence.rb +0 -110
- data/lib/ace/assign/authority/socket_server.rb +0 -255
- data/lib/ace/assign/authority/task_context_entry.rb +0 -32
- data/lib/ace/assign/authority/transfer_codec.rb +0 -187
- data/lib/ace/assign/cli/commands/attempt/base.rb +0 -84
- data/lib/ace/assign/cli/commands/attempt/evidence.rb +0 -28
- data/lib/ace/assign/cli/commands/attempt/finish.rb +0 -51
- data/lib/ace/assign/cli/commands/attempt/reconcile.rb +0 -49
- data/lib/ace/assign/cli/commands/attempt/start.rb +0 -45
- data/lib/ace/assign/cli/commands/attempt/status.rb +0 -54
- data/lib/ace/assign/cli/commands/authority/inbox_context_principal.rb +0 -25
- data/lib/ace/assign/cli/commands/authority/inbox_context_selection.rb +0 -28
- data/lib/ace/assign/cli/commands/authority/launch.rb +0 -97
- data/lib/ace/assign/cli/commands/authority/serve.rb +0 -31
- data/lib/ace/assign/cli/commands/authority/status.rb +0 -26
- data/lib/ace/assign/cli/commands/authority/task_context.rb +0 -31
- data/lib/ace/assign/cli/commands/authority/task_context_principal.rb +0 -25
- data/lib/ace/assign/cli/commands/authority/task_context_selection.rb +0 -30
- data/lib/ace/assign/cli/commands/authority/terminate.rb +0 -29
- data/lib/ace/assign/cli/commands/authority/worker.rb +0 -23
- data/lib/ace/assign/cli/commands/campaign_export_result.rb +0 -55
- data/lib/ace/assign/cli/commands/campaign_record_round.rb +0 -57
- data/lib/ace/assign/cli/commands/delivery.rb +0 -107
- data/lib/ace/assign/cli/commands/inbox_bind.rb +0 -34
- data/lib/ace/assign/cli/commands/protected_submission.rb +0 -61
- data/lib/ace/assign/cli/commands/resume.rb +0 -38
- data/lib/ace/assign/cli/commands/submit_candidate.rb +0 -34
- data/lib/ace/assign/cli/commands/submit_result.rb +0 -61
- data/lib/ace/assign/models/attempt.rb +0 -178
- data/lib/ace/assign/models/attempt_binding.rb +0 -116
- data/lib/ace/assign/models/evidence_event.rb +0 -86
- data/lib/ace/assign/models/execution_receipt.rb +0 -139
- data/lib/ace/assign/molecules/attempt_reconciler.rb +0 -127
- data/lib/ace/assign/molecules/attempt_store.rb +0 -201
- data/lib/ace/assign/molecules/canonical_attempt_state.rb +0 -151
- data/lib/ace/assign/molecules/canonical_evidence.rb +0 -153
- data/lib/ace/assign/molecules/canonical_read_commands.rb +0 -84
- data/lib/ace/assign/molecules/canonical_read_snapshot.rb +0 -313
- data/lib/ace/assign/molecules/evidence_calculator.rb +0 -237
- data/lib/ace/assign/molecules/evidence_journal.rb +0 -1912
- data/lib/ace/assign/molecules/execution_identity_resolver.rb +0 -137
- data/lib/ace/assign/molecules/execution_scope_lineage.rb +0 -345
- data/lib/ace/assign/molecules/fixed_effect_reference.rb +0 -31
- data/lib/ace/assign/molecules/installed_journal_read.rb +0 -143
- data/lib/ace/assign/molecules/journal_input_inhibition.rb +0 -78
- data/lib/ace/assign/molecules/journal_mutation.rb +0 -273
- data/lib/ace/assign/molecules/journal_prompt_mutation.rb +0 -241
- data/lib/ace/assign/molecules/ordinary_merge_cli.rb +0 -68
- data/lib/ace/assign/molecules/ordinary_merge_contract.rb +0 -410
- data/lib/ace/assign/molecules/original_launch_binding.rb +0 -37
- data/lib/ace/assign/molecules/proposal_journal.rb +0 -179
- data/lib/ace/assign/molecules/protected_control_exclusion.rb +0 -303
- data/lib/ace/assign/molecules/protected_workspace_exclusion.rb +0 -330
- data/lib/ace/assign/molecules/protected_workspace_provisioner.rb +0 -164
- data/lib/ace/assign/molecules/receipt_verifier.rb +0 -314
- data/lib/ace/assign/molecules/terminal_scope_receipt.rb +0 -88
- data/lib/ace/assign/organisms/attempt_coordinator.rb +0 -1337
- data/lib/ace/assign/organisms/delivery_coordinator.rb +0 -342
- data/lib/ace/assign/organisms/ordinary_merge_consumer.rb +0 -365
- data/lib/ace/assign/organisms/prepared_work_builder.rb +0 -194
- data/lib/ace/assign/organisms/protected_delivery_coordinator.rb +0 -100
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "json"
|
|
4
|
-
require "open3"
|
|
5
|
-
require_relative "../errors"
|
|
6
|
-
|
|
7
|
-
module Ace
|
|
8
|
-
module Assign
|
|
9
|
-
module Molecules
|
|
10
|
-
# Invokes the maintained ordinary fixed merge producer through the
|
|
11
|
-
# installed `ace-lab service request/status --mode ordinary-merge`
|
|
12
|
-
# CLI. The argv is closed: no shell, no caller-selected executable,
|
|
13
|
-
# socket, evidence path or token selector beyond the published request
|
|
14
|
-
# form. Output is bounded and parsed as one strict JSON envelope.
|
|
15
|
-
#
|
|
16
|
-
# Any local execution, framing or parse failure raises
|
|
17
|
-
# UnavailableError: a caller-side blocker that never proves refusal
|
|
18
|
-
# and never authorizes resubmission.
|
|
19
|
-
class OrdinaryMergeCli
|
|
20
|
-
MAX_OUTPUT_BYTES = 16 * 1024
|
|
21
|
-
|
|
22
|
-
# Raised when no maintained producer response is obtainable before
|
|
23
|
-
# the envelope could be observed. Maps to the closed
|
|
24
|
-
# `ordinary_merge_unavailable` caller-side blocker.
|
|
25
|
-
class UnavailableError < Ace::Assign::AttemptErrors::EvidenceUnavailable
|
|
26
|
-
def initialize(message = "ordinary merge producer response unavailable")
|
|
27
|
-
super
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def initialize(binary: "ace-lab")
|
|
32
|
-
@binary = binary
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def request(project:, assignment:, attempt:, queue_entry:, input_path:, authorization:, request_id:)
|
|
36
|
-
run(["service", "request", "--mode", "ordinary-merge",
|
|
37
|
-
"--project", project, "--assignment", assignment, "--attempt", attempt,
|
|
38
|
-
"--queue-entry", queue_entry, "--operation", "merge", "--input", input_path,
|
|
39
|
-
"--authorization", authorization, "--request-id", request_id])
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def status(reference:, request_id:, project:, assignment:, attempt:, queue_entry:, input_digest:)
|
|
43
|
-
run(["service", "status", "--mode", "ordinary-merge", "--reference", reference,
|
|
44
|
-
"--request", request_id, "--project", project, "--assignment", assignment,
|
|
45
|
-
"--attempt", attempt, "--queue-entry", queue_entry,
|
|
46
|
-
"--input-digest", input_digest, "--format", "json"])
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
private
|
|
50
|
-
|
|
51
|
-
def run(arguments)
|
|
52
|
-
output, = Open3.capture3(@binary, *arguments)
|
|
53
|
-
raise UnavailableError, "ordinary merge producer output is missing or exceeds its bound" unless
|
|
54
|
-
output.is_a?(String) && !output.empty? && output.bytesize <= MAX_OUTPUT_BYTES
|
|
55
|
-
envelope = JSON.parse(output, max_nesting: 16, create_additions: false,
|
|
56
|
-
allow_duplicate_key: false, allow_comments: false, allow_nan: false)
|
|
57
|
-
unless envelope.is_a?(Hash) && envelope["status"].is_a?(String) &&
|
|
58
|
-
(envelope.keys - %w[status data error]).empty?
|
|
59
|
-
raise UnavailableError, "ordinary merge producer output is not a maintained envelope"
|
|
60
|
-
end
|
|
61
|
-
envelope
|
|
62
|
-
rescue JSON::ParserError, SystemCallError
|
|
63
|
-
raise UnavailableError, "ordinary merge producer response unavailable"
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
@@ -1,410 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "time"
|
|
4
|
-
require "uri"
|
|
5
|
-
require "ace/git/atoms/delivery_parameters"
|
|
6
|
-
require_relative "../atoms/evidence_digest"
|
|
7
|
-
require_relative "../atoms/fixed_merge_schemas"
|
|
8
|
-
|
|
9
|
-
module Ace
|
|
10
|
-
module Assign
|
|
11
|
-
module Molecules
|
|
12
|
-
# Closed wire contract for the ordinary fixed merge producer
|
|
13
|
-
# (`8x3.t.xz9.4`) consumed by the qkb.1 ordinary merge consumer through
|
|
14
|
-
# `ace-lab service request/status --mode ordinary-merge`.
|
|
15
|
-
#
|
|
16
|
-
# The consumer invokes and validates the maintained envelopes without
|
|
17
|
-
# translation or flattening. Every validator rejects unknown fields at
|
|
18
|
-
# every nesting level and enforces the published patterns, formats and
|
|
19
|
-
# semantic joins. The terminal receipt stays embedded: request
|
|
20
|
-
# selectors live only under the single `request` object, and succeeded
|
|
21
|
-
# (merged/already_merged) versus proven-no-effect failed are mutually
|
|
22
|
-
# exclusive.
|
|
23
|
-
module OrdinaryMergeContract
|
|
24
|
-
REQUEST_SCHEMA = "ace.lab.service.ordinary-merge-request/v1"
|
|
25
|
-
ACK_SCHEMA = "ace.lab.service.ordinary-merge-ack/v1"
|
|
26
|
-
REFUSAL_SCHEMA = "ace.lab.service.ordinary-merge-refusal/v1"
|
|
27
|
-
STATUS_SCHEMA = "ace.lab.service.ordinary-merge-status/v1"
|
|
28
|
-
STATUS_ERROR_SCHEMA = "ace.lab.service.ordinary-merge-status-error/v1"
|
|
29
|
-
RECEIPT_SCHEMA = "ace.assign.fixed-effect-receipt/v1"
|
|
30
|
-
REVIEW_SCHEMA = "ace.review.acceptance-reference/v1"
|
|
31
|
-
TEST_SCHEMA = "ace.test.execution-reference/v1"
|
|
32
|
-
|
|
33
|
-
REFUSAL_CODES = %w[
|
|
34
|
-
ordinary_merge_invalid_request
|
|
35
|
-
ordinary_merge_unauthenticated
|
|
36
|
-
ordinary_merge_forbidden
|
|
37
|
-
ordinary_merge_selection_mismatch
|
|
38
|
-
ordinary_merge_authorization_invalid
|
|
39
|
-
ordinary_merge_provenance_mismatch
|
|
40
|
-
ordinary_merge_target_changed
|
|
41
|
-
ordinary_merge_evidence_invalid
|
|
42
|
-
ordinary_merge_request_conflict
|
|
43
|
-
ordinary_merge_unavailable
|
|
44
|
-
].freeze
|
|
45
|
-
|
|
46
|
-
# A caller-side blocker: never a proven refusal, never authorization
|
|
47
|
-
# for resubmission. Every other code is an authenticated receiver
|
|
48
|
-
# refusal that proves no effect began.
|
|
49
|
-
UNAVAILABLE_CODE = "ordinary_merge_unavailable"
|
|
50
|
-
PROVEN_NO_EFFECT_CODES = (REFUSAL_CODES - [UNAVAILABLE_CODE]).freeze
|
|
51
|
-
|
|
52
|
-
STATES = %w[pending uncertain succeeded failed].freeze
|
|
53
|
-
TERMINAL_STATES = %w[succeeded failed].freeze
|
|
54
|
-
OUTCOMES = %w[merged already_merged failed_no_effect].freeze
|
|
55
|
-
|
|
56
|
-
HEX64 = /\A[0-9a-f]{64}\z/
|
|
57
|
-
SHA40 = /\A[0-9a-f]{40}\z/
|
|
58
|
-
FORGE_NAME = /\A[A-Za-z0-9][A-Za-z0-9._:-]{0,127}\z/
|
|
59
|
-
DATETIME = /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})\z/
|
|
60
|
-
|
|
61
|
-
REQUEST_KEYS = %w[schema forge pr_url head_repository head_ref candidate_head
|
|
62
|
-
base_repository base_ref expected_base_head method review_evidence test_evidence].freeze
|
|
63
|
-
REVIEW_KEYS = %w[schema reference reviewer verdict reviewed_head recorded_at record_sha256].freeze
|
|
64
|
-
TEST_KEYS = %w[schema reference candidate_head command result recorded_at receipt_sha256].freeze
|
|
65
|
-
ACK_KEYS = %w[schema type request_id input_digest reference].freeze
|
|
66
|
-
REFUSAL_KEYS = %w[schema code message request_id input_digest].freeze
|
|
67
|
-
STATUS_KEYS = %w[schema state reference request_id input_digest receipt].freeze
|
|
68
|
-
RECEIPT_KEYS = %w[schema reference request_id input_digest terminal_status request
|
|
69
|
-
producer effect remote_observation].freeze
|
|
70
|
-
RECEIPT_REQUEST_KEYS = %w[project assignment attempt queue_entry operation authorization input].freeze
|
|
71
|
-
PRODUCER_KEYS = %w[receiver_id principal configuration_sha256].freeze
|
|
72
|
-
EFFECT_KEYS = %w[started_at finished_at outcome failure_code no_effect_evidence].freeze
|
|
73
|
-
OBSERVATION_KEYS = %w[forge_instance pr_identity_url target_repository_identity target_ref_observed
|
|
74
|
-
merged_candidate_head admission_target_head resulting_target_head merge_commit
|
|
75
|
-
checkout_id worktree_id branch].freeze
|
|
76
|
-
|
|
77
|
-
class << self
|
|
78
|
-
# Validates and returns the closed ordinary-merge request input.
|
|
79
|
-
# Both packages enforce `format` and reject unknown fields at every
|
|
80
|
-
# nesting level; evidence heads must equal the candidate head.
|
|
81
|
-
def validate_request_input!(document)
|
|
82
|
-
exact_object!(document, REQUEST_KEYS, "request input")
|
|
83
|
-
const!(document.fetch("schema"), REQUEST_SCHEMA, "request input schema")
|
|
84
|
-
pattern!(document.fetch("forge"), FORGE_NAME, "forge")
|
|
85
|
-
url!(document.fetch("pr_url"))
|
|
86
|
-
bounded!(document.fetch("head_repository"), 1024, "head_repository")
|
|
87
|
-
bounded!(document.fetch("head_ref"), 255, "head_ref")
|
|
88
|
-
pattern!(document.fetch("candidate_head"), SHA40, "candidate_head")
|
|
89
|
-
bounded!(document.fetch("base_repository"), 1024, "base_repository")
|
|
90
|
-
bounded!(document.fetch("base_ref"), 255, "base_ref")
|
|
91
|
-
pattern!(document.fetch("expected_base_head"), SHA40, "expected_base_head")
|
|
92
|
-
const!(document.fetch("method"), "merge", "method")
|
|
93
|
-
review = document.fetch("review_evidence")
|
|
94
|
-
exact_object!(review, REVIEW_KEYS, "review_evidence")
|
|
95
|
-
const!(review.fetch("schema"), REVIEW_SCHEMA, "review_evidence schema")
|
|
96
|
-
bounded!(review.fetch("reference"), 256, "review_evidence.reference")
|
|
97
|
-
bounded!(review.fetch("reviewer"), 128, "review_evidence.reviewer")
|
|
98
|
-
const!(review.fetch("verdict"), "approved", "review_evidence.verdict")
|
|
99
|
-
pattern!(review.fetch("reviewed_head"), SHA40, "review_evidence.reviewed_head")
|
|
100
|
-
datetime!(review.fetch("recorded_at"), "review_evidence.recorded_at")
|
|
101
|
-
pattern!(review.fetch("record_sha256"), HEX64, "review_evidence.record_sha256")
|
|
102
|
-
tests = document.fetch("test_evidence")
|
|
103
|
-
raise ArgumentError, "request input test_evidence must be a non-empty array" unless tests.is_a?(Array) && !tests.empty?
|
|
104
|
-
seen = []
|
|
105
|
-
tests.each_with_index do |item, index|
|
|
106
|
-
exact_object!(item, TEST_KEYS, "test_evidence[#{index}]")
|
|
107
|
-
const!(item.fetch("schema"), TEST_SCHEMA, "test_evidence[#{index}] schema")
|
|
108
|
-
bounded!(item.fetch("reference"), 256, "test_evidence[#{index}].reference")
|
|
109
|
-
pattern!(item.fetch("candidate_head"), SHA40, "test_evidence[#{index}].candidate_head")
|
|
110
|
-
bounded!(item.fetch("command"), 2048, "test_evidence[#{index}].command")
|
|
111
|
-
const!(item.fetch("result"), "passed", "test_evidence[#{index}].result")
|
|
112
|
-
datetime!(item.fetch("recorded_at"), "test_evidence[#{index}].recorded_at")
|
|
113
|
-
pattern!(item.fetch("receipt_sha256"), HEX64, "test_evidence[#{index}].receipt_sha256")
|
|
114
|
-
entry = Atoms::EvidenceDigest.digest(item)
|
|
115
|
-
raise ArgumentError, "request input test_evidence entries must be unique" if seen.include?(entry)
|
|
116
|
-
seen << entry
|
|
117
|
-
end
|
|
118
|
-
unless review.fetch("reviewed_head") == document.fetch("candidate_head") &&
|
|
119
|
-
tests.all? { |item| item.fetch("candidate_head") == document.fetch("candidate_head") }
|
|
120
|
-
raise ArgumentError, "request input evidence heads must equal the candidate head"
|
|
121
|
-
end
|
|
122
|
-
document
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
# Canonical lowercase SHA-256 input digest of a validated document.
|
|
126
|
-
# Canonical request digest — spec ruling r4: the shared producer/
|
|
127
|
-
# consumer implementation lives in Atoms::FixedMergeSchemas
|
|
128
|
-
# (canonical_request_digest); this contract deliberately keeps no
|
|
129
|
-
# local digest computation so both packages can never diverge.
|
|
130
|
-
|
|
131
|
-
# Admission acknowledgment: exact keys, exact selector echo, opaque
|
|
132
|
-
# reference with no parseable structure implied.
|
|
133
|
-
def validate_ack!(data, request_id:, input_digest:)
|
|
134
|
-
exact_object!(data, ACK_KEYS, "acknowledgment")
|
|
135
|
-
const!(data.fetch("schema"), ACK_SCHEMA, "acknowledgment schema")
|
|
136
|
-
const!(data.fetch("type"), "accepted", "acknowledgment type")
|
|
137
|
-
selector!(data.fetch("request_id"), "acknowledgment request_id")
|
|
138
|
-
pattern!(data.fetch("input_digest"), HEX64, "acknowledgment input_digest")
|
|
139
|
-
selector!(data.fetch("reference"), "acknowledgment reference")
|
|
140
|
-
unless data.fetch("request_id") == request_id && data.fetch("input_digest") == input_digest
|
|
141
|
-
raise ArgumentError, "acknowledgment selectors differ from the recorded binding"
|
|
142
|
-
end
|
|
143
|
-
data
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
# Closed outer response envelope: status "ok" carries exactly the
|
|
147
|
-
# data object; status "error" carries exactly the error object. The
|
|
148
|
-
# outer objects reject unknown fields; the returned branch payload
|
|
149
|
-
# is validated by the inner contract validators. Any other outer
|
|
150
|
-
# status or shape is malformed. Returns {status:, payload:}.
|
|
151
|
-
def validate_envelope_branch!(envelope)
|
|
152
|
-
unless envelope.is_a?(Hash) && %w[ok error].include?(envelope["status"])
|
|
153
|
-
raise ArgumentError, "response envelope status must be ok or error"
|
|
154
|
-
end
|
|
155
|
-
if envelope["status"] == "ok"
|
|
156
|
-
exact_object!(envelope, %w[status data], "response envelope")
|
|
157
|
-
payload = envelope.fetch("data")
|
|
158
|
-
else
|
|
159
|
-
exact_object!(envelope, %w[status error], "response envelope")
|
|
160
|
-
payload = envelope.fetch("error")
|
|
161
|
-
end
|
|
162
|
-
raise ArgumentError, "response envelope branch must be a JSON object" unless payload.is_a?(Hash)
|
|
163
|
-
{status: envelope["status"], payload: payload}
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
# Pre-admission refusal envelope. Refusal is request-time only; it
|
|
167
|
-
# never carries a reference and is never a read-only status value.
|
|
168
|
-
def validate_refusal!(error, schema: REFUSAL_SCHEMA)
|
|
169
|
-
exact_object!(error, REFUSAL_KEYS, "refusal")
|
|
170
|
-
const!(error.fetch("schema"), schema, "refusal schema")
|
|
171
|
-
unless REFUSAL_CODES.include?(error.fetch("code"))
|
|
172
|
-
raise ArgumentError, "refusal code is outside the closed set"
|
|
173
|
-
end
|
|
174
|
-
message = error.fetch("message")
|
|
175
|
-
unless message.is_a?(String) && !message.empty? && message.length <= 512
|
|
176
|
-
raise ArgumentError, "refusal message must be a sanitized non-empty string of at most 512 characters"
|
|
177
|
-
end
|
|
178
|
-
selector!(error.fetch("request_id"), "refusal request_id")
|
|
179
|
-
digest = error.fetch("input_digest")
|
|
180
|
-
valid_digest = digest.nil? || (digest.is_a?(String) && digest.match?(HEX64))
|
|
181
|
-
unless valid_digest
|
|
182
|
-
raise ArgumentError, "refusal input_digest must be a lowercase SHA-256 string or null"
|
|
183
|
-
end
|
|
184
|
-
error
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
# Read-only status success envelope. Reports only pending,
|
|
188
|
-
# uncertain, succeeded or failed; succeeded/failed carry the one
|
|
189
|
-
# embedded terminal receipt, pending/uncertain a null receipt.
|
|
190
|
-
def validate_status!(envelope, reference:, request_id:, input_digest:)
|
|
191
|
-
exact_object!(envelope, %w[status data], "status envelope")
|
|
192
|
-
const!(envelope.fetch("status"), "ok", "status envelope")
|
|
193
|
-
data = envelope.fetch("data")
|
|
194
|
-
exact_object!(data, STATUS_KEYS, "status data")
|
|
195
|
-
const!(data.fetch("schema"), STATUS_SCHEMA, "status schema")
|
|
196
|
-
unless STATES.include?(data.fetch("state"))
|
|
197
|
-
raise ArgumentError, "status state is outside the closed model"
|
|
198
|
-
end
|
|
199
|
-
selector!(data.fetch("reference"), "status reference")
|
|
200
|
-
selector!(data.fetch("request_id"), "status request_id")
|
|
201
|
-
pattern!(data.fetch("input_digest"), HEX64, "status input_digest")
|
|
202
|
-
unless data.fetch("reference") == reference && data.fetch("request_id") == request_id &&
|
|
203
|
-
data.fetch("input_digest") == input_digest
|
|
204
|
-
raise ArgumentError, "status identifiers differ from the recorded binding"
|
|
205
|
-
end
|
|
206
|
-
receipt = data.fetch("receipt")
|
|
207
|
-
if TERMINAL_STATES.include?(data.fetch("state"))
|
|
208
|
-
raise ArgumentError, "terminal status requires the embedded receipt" unless receipt.is_a?(Hash)
|
|
209
|
-
validated = validate_receipt!(receipt)
|
|
210
|
-
raise ArgumentError, "receipt terminal_status differs from the status state" unless validated.fetch("terminal_status") == data.fetch("state")
|
|
211
|
-
else
|
|
212
|
-
raise ArgumentError, "pending/uncertain status must carry a null receipt" unless receipt.nil?
|
|
213
|
-
validated = nil
|
|
214
|
-
end
|
|
215
|
-
{state: data.fetch("state"), receipt: validated}
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
# Terminal receipt: embedded request, never flattened. When
|
|
219
|
-
# `binding` is given, every command selector and the complete input
|
|
220
|
-
# document must equal the recorded binding; success additionally
|
|
221
|
-
# joins the remote observation back to the embedded request.
|
|
222
|
-
def validate_receipt!(receipt, binding: nil)
|
|
223
|
-
exact_object!(receipt, RECEIPT_KEYS, "terminal receipt")
|
|
224
|
-
const!(receipt.fetch("schema"), RECEIPT_SCHEMA, "receipt schema")
|
|
225
|
-
selector!(receipt.fetch("reference"), "receipt reference")
|
|
226
|
-
selector!(receipt.fetch("request_id"), "receipt request_id")
|
|
227
|
-
pattern!(receipt.fetch("input_digest"), HEX64, "receipt input_digest")
|
|
228
|
-
unless %w[succeeded failed].include?(receipt.fetch("terminal_status"))
|
|
229
|
-
raise ArgumentError, "receipt terminal_status is outside the closed set"
|
|
230
|
-
end
|
|
231
|
-
request = receipt.fetch("request")
|
|
232
|
-
exact_object!(request, RECEIPT_REQUEST_KEYS, "receipt request")
|
|
233
|
-
bounded!(request.fetch("project"), 128, "receipt request.project")
|
|
234
|
-
bounded!(request.fetch("assignment"), 128, "receipt request.assignment")
|
|
235
|
-
bounded!(request.fetch("attempt"), 128, "receipt request.attempt")
|
|
236
|
-
bounded!(request.fetch("queue_entry"), 128, "receipt request.queue_entry")
|
|
237
|
-
const!(request.fetch("operation"), "merge", "receipt request.operation")
|
|
238
|
-
selector!(request.fetch("authorization"), "receipt request.authorization")
|
|
239
|
-
input = validate_request_input!(request.fetch("input"))
|
|
240
|
-
producer = receipt.fetch("producer")
|
|
241
|
-
exact_object!(producer, PRODUCER_KEYS, "receipt producer")
|
|
242
|
-
bounded!(producer.fetch("receiver_id"), 128, "receipt producer.receiver_id")
|
|
243
|
-
bounded!(producer.fetch("principal"), 256, "receipt producer.principal")
|
|
244
|
-
pattern!(producer.fetch("configuration_sha256"), HEX64, "receipt producer.configuration_sha256")
|
|
245
|
-
unless Atoms::FixedMergeSchemas.canonical_request_digest(
|
|
246
|
-
project: request.fetch("project"), assignment: request.fetch("assignment"),
|
|
247
|
-
attempt: request.fetch("attempt"), queue_entry: request.fetch("queue_entry"),
|
|
248
|
-
authorization: request.fetch("authorization"), request_id: receipt.fetch("request_id"),
|
|
249
|
-
input: input
|
|
250
|
-
) == receipt.fetch("input_digest")
|
|
251
|
-
raise ArgumentError, "receipt input_digest is not the canonical digest of the embedded binding"
|
|
252
|
-
end
|
|
253
|
-
validate_effect!(receipt.fetch("effect"))
|
|
254
|
-
validate_observation!(receipt.fetch("remote_observation"), input)
|
|
255
|
-
enforce_exclusivity!(receipt)
|
|
256
|
-
join_observation!(receipt, input)
|
|
257
|
-
join_binding!(receipt, binding, input) if binding
|
|
258
|
-
receipt
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
private
|
|
262
|
-
|
|
263
|
-
def validate_effect!(effect)
|
|
264
|
-
exact_object!(effect, EFFECT_KEYS, "receipt effect")
|
|
265
|
-
started_at = effect.fetch("started_at")
|
|
266
|
-
datetime!(started_at, "receipt effect.started_at") unless started_at.nil?
|
|
267
|
-
datetime!(effect.fetch("finished_at"), "receipt effect.finished_at")
|
|
268
|
-
unless OUTCOMES.include?(effect.fetch("outcome"))
|
|
269
|
-
raise ArgumentError, "receipt effect.outcome is outside the closed set"
|
|
270
|
-
end
|
|
271
|
-
failure_code = effect.fetch("failure_code")
|
|
272
|
-
unless failure_code.nil? || failure_code == "merge_failed_no_effect"
|
|
273
|
-
raise ArgumentError, "receipt effect.failure_code must be null or merge_failed_no_effect"
|
|
274
|
-
end
|
|
275
|
-
evidence = effect.fetch("no_effect_evidence")
|
|
276
|
-
valid_evidence = evidence.nil? || (evidence.is_a?(Hash) &&
|
|
277
|
-
evidence.keys.sort == %w[reference sha256].sort &&
|
|
278
|
-
bounded?(evidence["reference"], 256) &&
|
|
279
|
-
evidence["sha256"].is_a?(String) && evidence["sha256"].match?(HEX64))
|
|
280
|
-
unless valid_evidence
|
|
281
|
-
raise ArgumentError, "receipt effect.no_effect_evidence must be null or a bounded reference/sha256 object"
|
|
282
|
-
end
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
def validate_observation!(observation, input)
|
|
286
|
-
return if observation.nil?
|
|
287
|
-
exact_object!(observation, OBSERVATION_KEYS, "receipt remote_observation")
|
|
288
|
-
pattern!(observation.fetch("forge_instance"), FORGE_NAME, "remote_observation.forge_instance")
|
|
289
|
-
url!(observation.fetch("pr_identity_url"), "remote_observation.pr_identity_url")
|
|
290
|
-
bounded!(observation.fetch("target_repository_identity"), 1024, "remote_observation.target_repository_identity")
|
|
291
|
-
bounded!(observation.fetch("target_ref_observed"), 255, "remote_observation.target_ref_observed")
|
|
292
|
-
%w[merged_candidate_head admission_target_head resulting_target_head merge_commit].each do |key|
|
|
293
|
-
pattern!(observation.fetch(key), SHA40, "remote_observation.#{key}")
|
|
294
|
-
end
|
|
295
|
-
bounded!(observation.fetch("checkout_id"), 256, "remote_observation.checkout_id")
|
|
296
|
-
bounded!(observation.fetch("worktree_id"), 256, "remote_observation.worktree_id")
|
|
297
|
-
bounded!(observation.fetch("branch"), 255, "remote_observation.branch")
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
# Succeeded (merged/already_merged) versus proven-no-effect failed
|
|
301
|
-
# are mutually exclusive; failure is terminal only with immutable
|
|
302
|
-
# no-effect evidence.
|
|
303
|
-
def enforce_exclusivity!(receipt)
|
|
304
|
-
effect = receipt.fetch("effect")
|
|
305
|
-
case receipt.fetch("terminal_status")
|
|
306
|
-
when "succeeded"
|
|
307
|
-
unless %w[merged already_merged].include?(effect.fetch("outcome")) &&
|
|
308
|
-
effect.fetch("failure_code").nil? && effect.fetch("no_effect_evidence").nil? &&
|
|
309
|
-
effect.fetch("started_at").is_a?(String) && receipt.fetch("remote_observation").is_a?(Hash)
|
|
310
|
-
raise ArgumentError, "succeeded receipt must describe a verified remote outcome without failure evidence"
|
|
311
|
-
end
|
|
312
|
-
when "failed"
|
|
313
|
-
unless effect.fetch("outcome") == "failed_no_effect" &&
|
|
314
|
-
effect.fetch("failure_code") == "merge_failed_no_effect" &&
|
|
315
|
-
effect.fetch("no_effect_evidence").is_a?(Hash) && receipt.fetch("remote_observation").nil?
|
|
316
|
-
raise ArgumentError, "failed receipt must prove no effect and carry no remote observation"
|
|
317
|
-
end
|
|
318
|
-
end
|
|
319
|
-
end
|
|
320
|
-
|
|
321
|
-
# `remote_observation` uses distinct observation keys whose values
|
|
322
|
-
# are joined back to the embedded request; the fixed merge method
|
|
323
|
-
# additionally requires merge_commit == resulting_target_head.
|
|
324
|
-
def join_observation!(receipt, input)
|
|
325
|
-
observation = receipt.fetch("remote_observation")
|
|
326
|
-
return if observation.nil?
|
|
327
|
-
unless observation.fetch("forge_instance") == input.fetch("forge") &&
|
|
328
|
-
observation.fetch("pr_identity_url") == input.fetch("pr_url") &&
|
|
329
|
-
observation.fetch("target_repository_identity") == input.fetch("base_repository") &&
|
|
330
|
-
observation.fetch("target_ref_observed") == input.fetch("base_ref") &&
|
|
331
|
-
observation.fetch("merged_candidate_head") == input.fetch("candidate_head") &&
|
|
332
|
-
observation.fetch("admission_target_head") == input.fetch("expected_base_head") &&
|
|
333
|
-
observation.fetch("merge_commit") == observation.fetch("resulting_target_head")
|
|
334
|
-
raise ArgumentError, "remote observation does not join back to the embedded request"
|
|
335
|
-
end
|
|
336
|
-
end
|
|
337
|
-
|
|
338
|
-
def join_binding!(receipt, binding, input)
|
|
339
|
-
request = receipt.fetch("request")
|
|
340
|
-
expected = binding.fetch(:request)
|
|
341
|
-
unless receipt.fetch("reference") == binding.fetch(:reference) &&
|
|
342
|
-
receipt.fetch("request_id") == binding.fetch(:request_id) &&
|
|
343
|
-
receipt.fetch("input_digest") == binding.fetch(:input_digest) &&
|
|
344
|
-
request.fetch("project") == expected.fetch(:project) &&
|
|
345
|
-
request.fetch("assignment") == expected.fetch(:assignment) &&
|
|
346
|
-
request.fetch("attempt") == expected.fetch(:attempt) &&
|
|
347
|
-
request.fetch("queue_entry") == expected.fetch(:queue_entry) &&
|
|
348
|
-
request.fetch("authorization") == expected.fetch(:authorization) &&
|
|
349
|
-
input == expected.fetch(:input)
|
|
350
|
-
raise ArgumentError, "embedded request selectors differ from the recorded binding"
|
|
351
|
-
end
|
|
352
|
-
end
|
|
353
|
-
|
|
354
|
-
def exact_object!(value, keys, label)
|
|
355
|
-
raise ArgumentError, "#{label} must be a JSON object" unless value.is_a?(Hash)
|
|
356
|
-
unless value.keys.sort == keys.sort
|
|
357
|
-
raise ArgumentError, "#{label} must have exactly the closed field set"
|
|
358
|
-
end
|
|
359
|
-
end
|
|
360
|
-
|
|
361
|
-
def const!(value, expected, label)
|
|
362
|
-
return value if value == expected
|
|
363
|
-
raise ArgumentError, "#{label} must be #{expected.inspect}"
|
|
364
|
-
end
|
|
365
|
-
|
|
366
|
-
def pattern!(value, regex, label)
|
|
367
|
-
return value if value.is_a?(String) && value.match?(regex)
|
|
368
|
-
raise ArgumentError, "#{label} does not match the closed pattern"
|
|
369
|
-
end
|
|
370
|
-
|
|
371
|
-
def bounded?(value, max)
|
|
372
|
-
value.is_a?(String) && !value.empty? && value.length <= max
|
|
373
|
-
end
|
|
374
|
-
|
|
375
|
-
def bounded!(value, max, label)
|
|
376
|
-
return value if bounded?(value, max)
|
|
377
|
-
raise ArgumentError, "#{label} must be a non-empty string of at most #{max} characters"
|
|
378
|
-
end
|
|
379
|
-
|
|
380
|
-
def selector!(value, label)
|
|
381
|
-
bounded!(value, 256, label)
|
|
382
|
-
end
|
|
383
|
-
|
|
384
|
-
def datetime!(value, label)
|
|
385
|
-
unless value.is_a?(String) && value.match?(DATETIME) &&
|
|
386
|
-
Time.iso8601(value)
|
|
387
|
-
raise ArgumentError, "#{label} must be an RFC 3339 date-time"
|
|
388
|
-
end
|
|
389
|
-
rescue ArgumentError
|
|
390
|
-
raise ArgumentError, "#{label} must be an RFC 3339 date-time"
|
|
391
|
-
end
|
|
392
|
-
|
|
393
|
-
# `format: uri` plus the maintained neutral PR URL validation: the
|
|
394
|
-
# normalized URL produced by the selected forge adapter.
|
|
395
|
-
def url!(value, label = "pr_url")
|
|
396
|
-
unless value.is_a?(String) && !value.empty? && value.length <= 2048 &&
|
|
397
|
-
!value.match?(/\s/) && URI.parse(value).absolute? &&
|
|
398
|
-
%w[http https].include?(URI.parse(value).scheme)
|
|
399
|
-
raise ArgumentError, "#{label} must be an absolute http(s) URI"
|
|
400
|
-
end
|
|
401
|
-
Ace::Git::Atoms::DeliveryParameters.validate_url!(value)
|
|
402
|
-
value
|
|
403
|
-
rescue URI::InvalidURIError, Ace::Git::Error
|
|
404
|
-
raise ArgumentError, "#{label} must be a normalized forge PR URL"
|
|
405
|
-
end
|
|
406
|
-
end
|
|
407
|
-
end
|
|
408
|
-
end
|
|
409
|
-
end
|
|
410
|
-
end
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "ace/herdr/molecules/guarded_native_origin"
|
|
4
|
-
require_relative "../models/evidence_event"
|
|
5
|
-
|
|
6
|
-
module Ace
|
|
7
|
-
module Assign
|
|
8
|
-
module Molecules
|
|
9
|
-
# Exact accepted launch record; this reader grants no native effect.
|
|
10
|
-
module OriginalLaunchBinding
|
|
11
|
-
def self.verify!(events:, state:, params:)
|
|
12
|
-
unless state.is_a?(Hash) && state["guarded_origin"].is_a?(Hash) && state["process_binding"].is_a?(Hash)
|
|
13
|
-
raise AttemptErrors::EvidenceUnavailable, "Original guarded record is unavailable"
|
|
14
|
-
end
|
|
15
|
-
unless Models::EvidenceEvent.chain_valid?(events)
|
|
16
|
-
raise AttemptErrors::EvidenceUnavailable, "Original guarded record chain is corrupt"
|
|
17
|
-
end
|
|
18
|
-
records = events.select { |event| event["type"] == "authority_mutation" && event.dig("payload", "operation") == "record_launch" }
|
|
19
|
-
raise AttemptErrors::EvidenceUnavailable, "Original guarded record is ambiguous or absent" unless records.size == 1
|
|
20
|
-
record = records.first
|
|
21
|
-
data = record.fetch("payload").fetch("data")
|
|
22
|
-
unless data.is_a?(Hash) && record["attempt_id"] == params.fetch("attempt_id") &&
|
|
23
|
-
%w[mapping_id assignment_id attempt_id].all? { |key| data[key] == params.fetch(key) } &&
|
|
24
|
-
data["process_binding"] == state.fetch("process_binding") && data["guarded_origin"] == state.fetch("guarded_origin")
|
|
25
|
-
raise AttemptErrors::EvidenceUnavailable, "Original guarded record differs from accepted launch"
|
|
26
|
-
end
|
|
27
|
-
binding = data.fetch("process_binding")
|
|
28
|
-
origin = Ace::Herdr::Molecules::GuardedNativeOrigin.verify!(data.fetch("guarded_origin"),
|
|
29
|
-
terminal_id: binding.fetch("terminal_id"), child: binding.fetch("process_identity"))
|
|
30
|
-
{"binding_digest" => record.fetch("digest").dup.freeze, "origin" => origin}.freeze
|
|
31
|
-
rescue KeyError, TypeError, Ace::Runtime::RuntimeUnavailableError
|
|
32
|
-
raise AttemptErrors::EvidenceUnavailable, "Original guarded record is malformed"
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|