ace-assign 0.63.1 → 0.66.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 +11 -0
- data/.ace-defaults/assign/catalog/steps/create-pr.step.yml +2 -2
- data/.ace-defaults/assign/catalog/steps/mark-pr-ready.step.yml +1 -1
- data/.ace-defaults/assign/catalog/steps/release-minor.step.yml +4 -2
- data/.ace-defaults/assign/catalog/steps/release.step.yml +4 -2
- data/.ace-defaults/assign/catalog/steps/update-pr-desc.step.yml +1 -1
- data/.ace-defaults/assign/presets/fix-bug.yml +15 -3
- data/.ace-defaults/assign/presets/work-on-task-ace-development.yml +16 -4
- data/.ace-defaults/assign/presets/work-on-task-auto-merge.yml +15 -3
- data/.ace-defaults/assign/presets/work-on-task.yml +16 -4
- data/CHANGELOG.md +62 -0
- data/docs/demo/canonical-skill-source.tape.yml +4 -4
- data/docs/protected-authority.md +282 -0
- data/docs/usage.md +205 -3
- data/exe/ace-scope-ready +55 -0
- data/handbook/workflow-instructions/assign/drive.wf.md +28 -3
- data/handbook/workflow-instructions/assign/prepare.wf.md +2 -2
- data/lib/ace/assign/atoms/attempt_state_machine.rb +8 -0
- data/lib/ace/assign/atoms/delivery_parameters.rb +19 -0
- data/lib/ace/assign/authority/assignment_inventory.rb +290 -0
- data/lib/ace/assign/authority/campaign_consumer_policy.rb +100 -0
- data/lib/ace/assign/authority/campaign_execution.rb +119 -0
- data/lib/ace/assign/authority/campaign_round_transfer.rb +82 -0
- data/lib/ace/assign/authority/candidate_transfer.rb +327 -0
- data/lib/ace/assign/authority/cleanup_result_protection.rb +53 -0
- data/lib/ace/assign/authority/client.rb +257 -0
- data/lib/ace/assign/authority/deployment.rb +627 -0
- data/lib/ace/assign/authority/deployment_history.rb +146 -0
- data/lib/ace/assign/authority/endcap.rb +455 -0
- data/lib/ace/assign/authority/endcap_attempts.rb +274 -0
- data/lib/ace/assign/authority/endcap_campaign_results.rb +232 -0
- data/lib/ace/assign/authority/endcap_campaign_rounds.rb +266 -0
- data/lib/ace/assign/authority/endcap_contexts.rb +23 -0
- data/lib/ace/assign/authority/endcap_inboxes.rb +37 -0
- data/lib/ace/assign/authority/endcap_prepared_work.rb +80 -0
- data/lib/ace/assign/authority/endcap_publication.rb +207 -0
- data/lib/ace/assign/authority/endcap_results.rb +398 -0
- data/lib/ace/assign/authority/endcap_review_requests.rb +183 -0
- data/lib/ace/assign/authority/endcap_reviews.rb +130 -0
- data/lib/ace/assign/authority/endcap_service_settlement.rb +152 -0
- data/lib/ace/assign/authority/endcap_services.rb +598 -0
- data/lib/ace/assign/authority/evidence_transfer.rb +67 -0
- data/lib/ace/assign/authority/execution_scope_observation.rb +903 -0
- data/lib/ace/assign/authority/inbox_context_original.rb +113 -0
- data/lib/ace/assign/authority/launch_attempt_consumers.rb +51 -0
- data/lib/ace/assign/authority/launch_campaign_registration.rb +214 -0
- data/lib/ace/assign/authority/launch_control_channel.rb +313 -0
- data/lib/ace/assign/authority/launch_control_exclusions.rb +141 -0
- data/lib/ace/assign/authority/launch_driver.rb +393 -0
- data/lib/ace/assign/authority/launch_input_inhibition.rb +132 -0
- data/lib/ace/assign/authority/launch_lifecycle.rb +997 -0
- data/lib/ace/assign/authority/launch_prepared_work.rb +122 -0
- data/lib/ace/assign/authority/launch_review.rb +91 -0
- data/lib/ace/assign/authority/launch_scope_abort.rb +81 -0
- data/lib/ace/assign/authority/launch_scope_admission.rb +215 -0
- data/lib/ace/assign/authority/launch_scope_close.rb +146 -0
- data/lib/ace/assign/authority/launch_scope_parent.rb +44 -0
- data/lib/ace/assign/authority/launch_scope_release.rb +859 -0
- data/lib/ace/assign/authority/launch_steering.rb +353 -0
- data/lib/ace/assign/authority/launch_stop.rb +254 -0
- data/lib/ace/assign/authority/maintenance_workspace_retirement.rb +229 -0
- data/lib/ace/assign/authority/original_inbox_selection.rb +41 -0
- data/lib/ace/assign/authority/posix_acl.rb +59 -0
- data/lib/ace/assign/authority/prepared_input.rb +130 -0
- data/lib/ace/assign/authority/prepared_queue.rb +185 -0
- data/lib/ace/assign/authority/prepared_task_context.rb +116 -0
- data/lib/ace/assign/authority/prepared_work.rb +304 -0
- data/lib/ace/assign/authority/prepared_worker.rb +69 -0
- data/lib/ace/assign/authority/private_directory.rb +33 -0
- data/lib/ace/assign/authority/protected_assignment_context.rb +226 -0
- data/lib/ace/assign/authority/readiness_hook.rb +120 -0
- data/lib/ace/assign/authority/receipt_transfer.rb +48 -0
- data/lib/ace/assign/authority/router.rb +76 -0
- data/lib/ace/assign/authority/server.rb +300 -0
- data/lib/ace/assign/authority/service_cleanup_dispatch.rb +88 -0
- data/lib/ace/assign/authority/service_cleanup_evidence.rb +180 -0
- data/lib/ace/assign/authority/service_delivery_evidence.rb +101 -0
- data/lib/ace/assign/authority/service_evidence.rb +286 -0
- data/lib/ace/assign/authority/service_publication_evidence.rb +110 -0
- data/lib/ace/assign/authority/task_context_entry.rb +32 -0
- data/lib/ace/assign/authority/transfer_codec.rb +187 -0
- data/lib/ace/assign/cli/commands/assignment_target.rb +15 -2
- data/lib/ace/assign/cli/commands/attempt/base.rb +49 -0
- data/lib/ace/assign/cli/commands/attempt/finish.rb +20 -4
- data/lib/ace/assign/cli/commands/attempt/reconcile.rb +11 -0
- data/lib/ace/assign/cli/commands/authority/inbox_context_principal.rb +25 -0
- data/lib/ace/assign/cli/commands/authority/inbox_context_selection.rb +28 -0
- data/lib/ace/assign/cli/commands/authority/launch.rb +97 -0
- data/lib/ace/assign/cli/commands/authority/serve.rb +31 -0
- data/lib/ace/assign/cli/commands/authority/status.rb +26 -0
- data/lib/ace/assign/cli/commands/authority/task_context.rb +31 -0
- data/lib/ace/assign/cli/commands/authority/task_context_principal.rb +25 -0
- data/lib/ace/assign/cli/commands/authority/task_context_selection.rb +30 -0
- data/lib/ace/assign/cli/commands/authority/terminate.rb +29 -0
- data/lib/ace/assign/cli/commands/authority/worker.rb +23 -0
- data/lib/ace/assign/cli/commands/campaign_export_result.rb +55 -0
- data/lib/ace/assign/cli/commands/campaign_record_round.rb +57 -0
- data/lib/ace/assign/cli/commands/create.rb +11 -1
- data/lib/ace/assign/cli/commands/delivery.rb +107 -0
- data/lib/ace/assign/cli/commands/fail.rb +6 -0
- data/lib/ace/assign/cli/commands/finish.rb +23 -2
- data/lib/ace/assign/cli/commands/fork_session.rb +6 -2
- data/lib/ace/assign/cli/commands/inbox_bind.rb +34 -0
- data/lib/ace/assign/cli/commands/protected_submission.rb +61 -0
- data/lib/ace/assign/cli/commands/resume.rb +38 -0
- data/lib/ace/assign/cli/commands/select.rb +2 -0
- data/lib/ace/assign/cli/commands/start.rb +2 -0
- data/lib/ace/assign/cli/commands/status.rb +4 -2
- data/lib/ace/assign/cli/commands/step.rb +3 -1
- data/lib/ace/assign/cli/commands/submit_candidate.rb +34 -0
- data/lib/ace/assign/cli/commands/submit_result.rb +61 -0
- data/lib/ace/assign/cli.rb +56 -4
- data/lib/ace/assign/errors.rb +139 -0
- data/lib/ace/assign/models/assignment.rb +26 -4
- data/lib/ace/assign/models/evidence_event.rb +4 -2
- data/lib/ace/assign/molecules/assignment_manager.rb +12 -2
- data/lib/ace/assign/molecules/attempt_reconciler.rb +51 -12
- data/lib/ace/assign/molecules/canonical_attempt_state.rb +151 -0
- data/lib/ace/assign/molecules/canonical_evidence.rb +153 -0
- data/lib/ace/assign/molecules/canonical_read_snapshot.rb +374 -0
- data/lib/ace/assign/molecules/evidence_journal.rb +922 -91
- data/lib/ace/assign/molecules/execution_identity_resolver.rb +49 -9
- data/lib/ace/assign/molecules/execution_scope_lineage.rb +345 -0
- data/lib/ace/assign/molecules/fork_session_launcher.rb +16 -6
- data/lib/ace/assign/molecules/journal_input_inhibition.rb +78 -0
- data/lib/ace/assign/molecules/journal_mutation.rb +271 -0
- data/lib/ace/assign/molecules/journal_prompt_mutation.rb +241 -0
- data/lib/ace/assign/molecules/lifecycle_exclusion.rb +182 -26
- data/lib/ace/assign/molecules/original_launch_binding.rb +37 -0
- data/lib/ace/assign/molecules/proposal_journal.rb +151 -0
- data/lib/ace/assign/molecules/protected_control_exclusion.rb +303 -0
- data/lib/ace/assign/molecules/protected_workspace_exclusion.rb +330 -0
- data/lib/ace/assign/molecules/protected_workspace_provisioner.rb +164 -0
- data/lib/ace/assign/molecules/queue_scanner.rb +26 -11
- data/lib/ace/assign/molecules/receipt_verifier.rb +68 -32
- data/lib/ace/assign/molecules/step_writer.rb +27 -5
- data/lib/ace/assign/molecules/terminal_scope_receipt.rb +88 -0
- data/lib/ace/assign/organisms/assignment_executor.rb +10 -4
- data/lib/ace/assign/organisms/attempt_coordinator.rb +302 -13
- data/lib/ace/assign/organisms/delivery_coordinator.rb +342 -0
- data/lib/ace/assign/organisms/prepared_work_builder.rb +194 -0
- data/lib/ace/assign/organisms/protected_delivery_coordinator.rb +100 -0
- data/lib/ace/assign/organisms/task_assignment_creator.rb +11 -4
- data/lib/ace/assign/version.rb +1 -1
- data/lib/ace/assign.rb +4 -102
- metadata +186 -8
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ace
|
|
4
|
+
module Assign
|
|
5
|
+
module Authority
|
|
6
|
+
class LaunchLifecycle
|
|
7
|
+
# Source-owned close core. Its first accepted mutation seals admission
|
|
8
|
+
# and caches the retained running reply before any manager stop. A
|
|
9
|
+
# subsequent fresh mutation may record positive observation; replay of
|
|
10
|
+
# the first mutation always keeps its original reply and performs no I/O.
|
|
11
|
+
def close_execution_scope!(params:, peer:, role:)
|
|
12
|
+
strict!(params, %w[mapping_id assignment_id attempt_id mutation_id expected_generation])
|
|
13
|
+
%w[assignment_id attempt_id mutation_id].each { |key| token!(params.fetch(key)) }
|
|
14
|
+
generation!(params.fetch("expected_generation"))
|
|
15
|
+
@kernel.live!(peer)
|
|
16
|
+
map = @deployment.mapping(params.fetch("mapping_id"))
|
|
17
|
+
journal = journal_for(map)
|
|
18
|
+
input = params.reject { |key, _| key == "mutation_id" }
|
|
19
|
+
digest = Digest::SHA256.hexdigest(JSON.generate(canonical(input)))
|
|
20
|
+
stop_after_commit = false
|
|
21
|
+
inhibit_after_commit = false
|
|
22
|
+
result = with_containment_exclusion(params, map, journal) do
|
|
23
|
+
result = @mutex.synchronize do
|
|
24
|
+
current = journal.read_events(params.fetch("assignment_id")).select { |event| event["attempt_id"] == params.fetch("attempt_id") }
|
|
25
|
+
scope_close_owner!(params, map, current, peer, role)
|
|
26
|
+
journal.mutate(assignment_id: params.fetch("assignment_id"), attempt_id: params.fetch("attempt_id"),
|
|
27
|
+
mutation_id: params.fetch("mutation_id"), operation: "close_execution_scope", parameters_digest: digest,
|
|
28
|
+
expected_generation: params.fetch("expected_generation"), with_replay: true) do |events, _commit, _generation|
|
|
29
|
+
lineage = scope_close_owner!(params, map, events, peer, role)
|
|
30
|
+
closure = scope_closure_plan!(lineage: lineage, params: params, map: map, events: events, journal: journal, commit: _commit)
|
|
31
|
+
stop_after_commit = closure.fetch(:stop_required)
|
|
32
|
+
inhibit_after_commit = closure.fetch(:inhibit_required)
|
|
33
|
+
closure.fetch(:plan)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
result
|
|
37
|
+
end
|
|
38
|
+
if !result.fetch(:replayed) && inhibit_after_commit
|
|
39
|
+
request_original_input_inhibition!(params, map, journal)
|
|
40
|
+
end
|
|
41
|
+
if !result.fetch(:replayed) && stop_after_commit
|
|
42
|
+
with_containment_exclusion(params, map, journal) do
|
|
43
|
+
@mutex.synchronize do
|
|
44
|
+
current = journal.read_events(params.fetch("assignment_id")).select { |event| event["attempt_id"] == params.fetch("attempt_id") }
|
|
45
|
+
scope_close_owner!(params, map, current, peer, role)
|
|
46
|
+
end
|
|
47
|
+
scope_observer_for(params.fetch("mapping_id")).stop_sealed_service!
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
result
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def observe_execution_scope!(params:, peer:, role:)
|
|
54
|
+
strict!(params, %w[mapping_id assignment_id attempt_id])
|
|
55
|
+
%w[assignment_id attempt_id].each { |key| token!(params.fetch(key)) }
|
|
56
|
+
@kernel.live!(peer)
|
|
57
|
+
map = @deployment.mapping(params.fetch("mapping_id"))
|
|
58
|
+
journal = journal_for(map)
|
|
59
|
+
with_containment_exclusion(params, map, journal) do
|
|
60
|
+
@mutex.synchronize do
|
|
61
|
+
commit = journal.ref_value
|
|
62
|
+
events = journal.read_events(params.fetch("assignment_id"), commit: commit).select { |event| event["attempt_id"] == params.fetch("attempt_id") }
|
|
63
|
+
lineage = scope_close_owner!(params, map, events, peer, role)
|
|
64
|
+
projection = {"attempt_id" => params.fetch("attempt_id"), "scope_generation" => lineage.binding&.fetch("scope_generation"),
|
|
65
|
+
"state" => "unverifiable", "proof_id" => nil, "required_action" => "inspect_exact_scope",
|
|
66
|
+
"generation" => journal.authority_generation(events), "journal_commit" => commit}
|
|
67
|
+
return projection unless lineage.binding
|
|
68
|
+
begin
|
|
69
|
+
observation = scope_observer_for(params.fetch("mapping_id")).observe(lineage)
|
|
70
|
+
if pending_prompt_issuers?(events, journal, commit)
|
|
71
|
+
projection.merge("required_action" => "drain_original_prompt_issuer")
|
|
72
|
+
elsif lineage.proof_event
|
|
73
|
+
scope_observer_for(params.fetch("mapping_id")).verify_closed!(lineage)
|
|
74
|
+
projection.merge("state" => "closed_no_writers", "proof_id" => lineage.proof_id, "required_action" => nil)
|
|
75
|
+
elsif lineage.sealed? && observation.fetch("populated").zero?
|
|
76
|
+
projection.merge("required_action" => "close_scope")
|
|
77
|
+
else
|
|
78
|
+
projection.merge("state" => "running", "required_action" => "close_scope")
|
|
79
|
+
end
|
|
80
|
+
rescue Ace::Runtime::RuntimeUnavailableError, AttemptErrors::EvidenceUnavailable
|
|
81
|
+
projection
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
# Shared source-only plan for close and public stop. It does not issue
|
|
90
|
+
# manager/native control, mutate canonical state or acquire exclusions.
|
|
91
|
+
# Each CAS invocation derives its own post-commit work flags anew.
|
|
92
|
+
def scope_closure_plan!(lineage:, params:, map:, events:, journal:, commit:)
|
|
93
|
+
stop_required = false
|
|
94
|
+
inhibit_required = false
|
|
95
|
+
binding = lineage.binding
|
|
96
|
+
raise AttemptErrors::EvidenceUnavailable, "original parent binding is missing" unless binding
|
|
97
|
+
projection = {"attempt_id" => params.fetch("attempt_id"), "scope_generation" => binding.fetch("scope_generation"),
|
|
98
|
+
"state" => "running", "proof_id" => nil, "required_action" => "close_scope"}
|
|
99
|
+
plan = if !lineage.sealed?
|
|
100
|
+
stop_required = true
|
|
101
|
+
inhibit_required = true
|
|
102
|
+
payload = {"scope_generation" => binding.fetch("scope_generation"), "scope_binding_event_id" => lineage.binding_event.fetch("digest")}
|
|
103
|
+
{events: [{type: "scope_sealed", payload: payload}], blobs: {}, data: projection}
|
|
104
|
+
elsif @native_issuers.key?(native_issuer_key(params, map)) || pending_prompt_issuers?(events, journal, commit)
|
|
105
|
+
# A live issuer may still start after this inactive snapshot.
|
|
106
|
+
inhibit_required = true
|
|
107
|
+
{events: [], blobs: {}, data: projection}
|
|
108
|
+
elsif lineage.proof_event
|
|
109
|
+
scope_observer_for(params.fetch("mapping_id")).verify_closed!(lineage)
|
|
110
|
+
{events: [], blobs: {}, data: projection.merge("state" => "closed_no_writers", "proof_id" => lineage.proof_id, "required_action" => nil)}
|
|
111
|
+
elsif scope_observer_for(params.fetch("mapping_id")).sealed_service_stop_required?(lineage)
|
|
112
|
+
# A fresh canonical close resumes a stop lost after the seal.
|
|
113
|
+
# Replaying either mutation performs no manager I/O.
|
|
114
|
+
stop_required = true
|
|
115
|
+
{events: [], blobs: {}, data: projection}
|
|
116
|
+
else
|
|
117
|
+
# This call must return a fresh validated observation, never
|
|
118
|
+
# a cached population, caller JSON or a manifest boolean.
|
|
119
|
+
payload = scope_observer_for(params.fetch("mapping_id")).closed_observation_for_proof!(lineage, events: events)
|
|
120
|
+
expected = binding.slice("scope_generation", "boot_id", "slice_invocation_id", "cgroup_identity").merge(
|
|
121
|
+
"scope_binding_event_id" => lineage.binding_event.fetch("digest"), "seal_event_id" => lineage.seal_event.fetch("digest"), "populated" => 0)
|
|
122
|
+
unless payload == expected
|
|
123
|
+
raise AttemptErrors::EvidenceUnavailable, "fresh observation does not name the retained sealed parent"
|
|
124
|
+
end
|
|
125
|
+
at = Time.now.utc
|
|
126
|
+
event = Models::EvidenceEvent.build(type: "scope_closed_no_writers", attempt_id: params.fetch("attempt_id"),
|
|
127
|
+
payload: payload, previous_digest: events.last&.fetch("digest"), recorded_at: at)
|
|
128
|
+
{events: [{type: "scope_closed_no_writers", payload: payload, recorded_at: at}], blobs: {},
|
|
129
|
+
data: projection.merge("state" => "closed_no_writers", "proof_id" => event.fetch("digest"), "required_action" => nil)}
|
|
130
|
+
end
|
|
131
|
+
{plan: plan, stop_required: stop_required, inhibit_required: inhibit_required}
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def scope_close_owner!(params, map, events, peer, role)
|
|
135
|
+
state = states(events)[params.fetch("attempt_id")]
|
|
136
|
+
unless state && state["mapping_id"] == params.fetch("mapping_id") &&
|
|
137
|
+
(role == :supervisor || role == :launcher && @kernel.same?(state.fetch("launcher_identity"), peer))
|
|
138
|
+
raise AttemptErrors::UnauthorizedIdentity, "scope closure requires the original launch owner"
|
|
139
|
+
end
|
|
140
|
+
Molecules::ExecutionScopeLineage.new(events: events, project_id: map.fetch("project_id"),
|
|
141
|
+
assignment_id: params.fetch("assignment_id"), attempt_id: params.fetch("attempt_id"), mapping_id: params.fetch("mapping_id"))
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ace
|
|
4
|
+
module Assign
|
|
5
|
+
module Authority
|
|
6
|
+
class LaunchLifecycle
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
# Called inside the existing slot/assignment exclusion, after the fresh
|
|
10
|
+
# reservation CAS and outside its authority mutex/journal ref lock.
|
|
11
|
+
# Mutation replay and restart never call this activation path.
|
|
12
|
+
def provision_reserved_parent_held!(reservation, map, journal)
|
|
13
|
+
params = reservation.slice("mapping_id", "assignment_id", "attempt_id")
|
|
14
|
+
context = params.merge("project_id" => map.fetch("project_id"),
|
|
15
|
+
"reservation_generation" => reservation.fetch("reservation_generation"),
|
|
16
|
+
"scope_generation" => reservation.fetch("generation") + 1)
|
|
17
|
+
observer = scope_observer_for(params.fetch("mapping_id"))
|
|
18
|
+
binding = observer.activate_parent!(context)
|
|
19
|
+
@mutex.synchronize do
|
|
20
|
+
journal.mutate(assignment_id: params.fetch("assignment_id"), attempt_id: params.fetch("attempt_id"),
|
|
21
|
+
mutation_id: "scope-parent-#{Digest::SHA256.hexdigest(params.fetch('attempt_id'))[0, 48]}",
|
|
22
|
+
operation: "scope_parent_binding", parameters_digest: Digest::SHA256.hexdigest(JSON.generate(canonical(context))),
|
|
23
|
+
expected_generation: reservation.fetch("generation"), with_replay: true) do |events, _commit, _generation|
|
|
24
|
+
state = states(events)[params.fetch("attempt_id")]
|
|
25
|
+
unless state && state["phase"] == "reserved" && state["launch_ticket"] == reservation.fetch("launch_ticket") &&
|
|
26
|
+
events.none? { |event| %w[scope_bound scope_native_bound scope_child_bound scope_sealed].include?(event["type"]) ||
|
|
27
|
+
event["type"] == "authority_mutation" && event.dig("payload", "operation") == "scope_service_admission" }
|
|
28
|
+
raise AttemptErrors::Conflict, "parent activation cannot replace or reopen an existing generation"
|
|
29
|
+
end
|
|
30
|
+
at = Time.now.utc
|
|
31
|
+
event = Models::EvidenceEvent.build(type: "scope_bound", attempt_id: params.fetch("attempt_id"),
|
|
32
|
+
payload: binding, previous_digest: events.last&.fetch("digest"), recorded_at: at)
|
|
33
|
+
lineage = Molecules::ExecutionScopeLineage.new(events: events + [event], project_id: map.fetch("project_id"),
|
|
34
|
+
assignment_id: params.fetch("assignment_id"), attempt_id: params.fetch("attempt_id"), mapping_id: params.fetch("mapping_id"))
|
|
35
|
+
observer.observe(lineage)
|
|
36
|
+
{events: [{type: "scope_bound", payload: binding, recorded_at: at}], blobs: {}, data: state.merge(
|
|
37
|
+
"scope_generation" => binding.fetch("scope_generation"), "scope_binding_event_id" => event.fetch("digest"))}
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|