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
data/lib/ace/assign/cli.rb
CHANGED
|
@@ -9,10 +9,6 @@ require_relative "models/assignment"
|
|
|
9
9
|
require_relative "models/step"
|
|
10
10
|
require_relative "models/queue_state"
|
|
11
11
|
require_relative "models/assignment_info"
|
|
12
|
-
require_relative "models/attempt_binding"
|
|
13
|
-
require_relative "models/attempt"
|
|
14
|
-
require_relative "models/execution_receipt"
|
|
15
|
-
require_relative "models/evidence_event"
|
|
16
12
|
|
|
17
13
|
# Atoms
|
|
18
14
|
require_relative "atoms/step_numbering"
|
|
@@ -26,14 +22,10 @@ require_relative "atoms/catalog_loader"
|
|
|
26
22
|
require_relative "atoms/composition_rules"
|
|
27
23
|
require_relative "atoms/assign_frontmatter_parser"
|
|
28
24
|
require_relative "atoms/tree_formatter"
|
|
29
|
-
require_relative "atoms/attempt_state_machine"
|
|
30
|
-
require_relative "atoms/evidence_digest"
|
|
31
|
-
require_relative "atoms/assignment_scope"
|
|
32
25
|
|
|
33
26
|
# Molecules
|
|
34
27
|
require_relative "molecules/lifecycle_exclusion"
|
|
35
28
|
require_relative "molecules/assignment_manager"
|
|
36
|
-
require_relative "molecules/attempt_store"
|
|
37
29
|
require_relative "molecules/assignment_discoverer"
|
|
38
30
|
require_relative "molecules/queue_scanner"
|
|
39
31
|
require_relative "molecules/step_writer"
|
|
@@ -42,28 +34,14 @@ require_relative "molecules/skill_assign_source_resolver"
|
|
|
42
34
|
require_relative "molecules/fork_session_launcher"
|
|
43
35
|
require_relative "molecules/runtime_control_surface_runner"
|
|
44
36
|
require_relative "molecules/preset_inferrer"
|
|
45
|
-
require_relative "molecules/evidence_calculator"
|
|
46
|
-
require_relative "molecules/evidence_journal"
|
|
47
|
-
require_relative "molecules/canonical_evidence"
|
|
48
|
-
require_relative "molecules/execution_identity_resolver"
|
|
49
|
-
require_relative "molecules/receipt_verifier"
|
|
50
|
-
require_relative "molecules/attempt_reconciler"
|
|
51
37
|
|
|
52
38
|
# Organisms
|
|
53
39
|
require_relative "organisms/assignment_executor"
|
|
54
|
-
require_relative "organisms/attempt_coordinator"
|
|
55
40
|
|
|
56
41
|
# Commands
|
|
57
|
-
require_relative "cli/commands/delivery"
|
|
58
42
|
require_relative "cli/commands/assignment_target"
|
|
59
43
|
require_relative "cli/commands/create"
|
|
60
44
|
require_relative "cli/commands/status"
|
|
61
|
-
require_relative "cli/commands/resume"
|
|
62
|
-
require_relative "cli/commands/inbox_bind"
|
|
63
|
-
require_relative "cli/commands/submit_candidate"
|
|
64
|
-
require_relative "cli/commands/submit_result"
|
|
65
|
-
require_relative "cli/commands/campaign_record_round"
|
|
66
|
-
require_relative "cli/commands/campaign_export_result"
|
|
67
45
|
require_relative "cli/commands/step"
|
|
68
46
|
require_relative "cli/commands/start"
|
|
69
47
|
require_relative "cli/commands/finish"
|
|
@@ -74,22 +52,6 @@ require_relative "cli/commands/list"
|
|
|
74
52
|
require_relative "cli/commands/select"
|
|
75
53
|
require_relative "cli/commands/fork_run"
|
|
76
54
|
require_relative "cli/commands/fork_session"
|
|
77
|
-
require_relative "cli/commands/authority/serve"
|
|
78
|
-
require_relative "cli/commands/authority/launch"
|
|
79
|
-
require_relative "cli/commands/authority/status"
|
|
80
|
-
require_relative "cli/commands/authority/terminate"
|
|
81
|
-
require_relative "cli/commands/authority/worker"
|
|
82
|
-
require_relative "cli/commands/authority/task_context"
|
|
83
|
-
require_relative "cli/commands/authority/task_context_principal"
|
|
84
|
-
require_relative "cli/commands/authority/task_context_selection"
|
|
85
|
-
require_relative "cli/commands/authority/inbox_context_principal"
|
|
86
|
-
require_relative "cli/commands/authority/inbox_context_selection"
|
|
87
|
-
require_relative "cli/commands/attempt/base"
|
|
88
|
-
require_relative "cli/commands/attempt/start"
|
|
89
|
-
require_relative "cli/commands/attempt/status"
|
|
90
|
-
require_relative "cli/commands/attempt/finish"
|
|
91
|
-
require_relative "cli/commands/attempt/reconcile"
|
|
92
|
-
require_relative "cli/commands/attempt/evidence"
|
|
93
55
|
|
|
94
56
|
module Ace
|
|
95
57
|
module Assign
|
|
@@ -102,21 +64,6 @@ module Ace
|
|
|
102
64
|
# Application commands with descriptions (for help output)
|
|
103
65
|
REGISTERED_COMMANDS = [
|
|
104
66
|
["create", "Create assignment from preset or YAML"],
|
|
105
|
-
["authority serve", "Serve the installed protected assignment owner"],
|
|
106
|
-
["authority launch", "Launch one mapped native gated worker"],
|
|
107
|
-
["authority status", "Inspect protected launch ownership"],
|
|
108
|
-
["authority terminate", "Terminate the original native gated child"],
|
|
109
|
-
["authority worker", "Consume original authenticated prepared work"],
|
|
110
|
-
["authority task-context", "Read captured original task context through the fixed entry"],
|
|
111
|
-
["authority task-context-principal", "Classify the actual caller through retained protected owners"],
|
|
112
|
-
["authority task-context-selection", "Read the authenticated original context entry selection"],
|
|
113
|
-
["authority inbox-context-principal", "Classify the actual caller through every retained protected owner"],
|
|
114
|
-
["authority inbox-context-selection", "Read the fixed installed inbox context selection"],
|
|
115
|
-
["delivery", "Execute or reconcile attempt-bound forge delivery"],
|
|
116
|
-
["submit-candidate", "Submit original protected worker candidate bytes"],
|
|
117
|
-
["submit-result", "Submit original protected worker receipt and artifact bytes"],
|
|
118
|
-
["campaign-export-result", "Download the original parent campaign's verified accepted result"],
|
|
119
|
-
["campaign-record-round", "Consume canonical settled children into the original campaign store"],
|
|
120
67
|
["status", "Show assignment status"],
|
|
121
68
|
["step", "Show step instructions"],
|
|
122
69
|
["start", "Start next workable step"],
|
|
@@ -127,12 +74,6 @@ module Ace
|
|
|
127
74
|
["list", "List all assignments"],
|
|
128
75
|
["select", "Select active assignment"],
|
|
129
76
|
["fork-run", "Run subtree in forked process"],
|
|
130
|
-
["attempt start", "Start a scoped attempt for an assignment step"],
|
|
131
|
-
["attempt status", "Show attempt status for an assignment"],
|
|
132
|
-
["attempt evidence", "Read accepted current-head check evidence"],
|
|
133
|
-
["attempt finish", "Finish a protected canonical result or an ordinary local receipt"],
|
|
134
|
-
["attempt reconcile", "Recover an exact protected attempt or reconcile an ordinary attempt"],
|
|
135
|
-
["inbox-bind", "Bind an original protected Inbox registration"],
|
|
136
77
|
].freeze
|
|
137
78
|
|
|
138
79
|
HELP_EXAMPLES = [
|
|
@@ -142,10 +83,7 @@ module Ace
|
|
|
142
83
|
"ace-assign start # Start next workable step",
|
|
143
84
|
"ace-assign finish --message done.md # Complete active step",
|
|
144
85
|
"cat report.md | ace-assign finish # Complete step via stdin",
|
|
145
|
-
"ace-assign fork-run 010.01 # Run subtree in subprocess"
|
|
146
|
-
"ace-assign attempt start --assignment ID --step 010 --project ID",
|
|
147
|
-
"ace-assign attempt finish --attempt ID --receipt receipt.json",
|
|
148
|
-
"ace-assign resume --assignment ID --dry-run"
|
|
86
|
+
"ace-assign fork-run 010.01 # Run subtree in subprocess"
|
|
149
87
|
].freeze
|
|
150
88
|
|
|
151
89
|
# Captured command exit code from last run
|
|
@@ -181,18 +119,7 @@ module Ace
|
|
|
181
119
|
end
|
|
182
120
|
|
|
183
121
|
# Register commands (wrapped to capture exit codes)
|
|
184
|
-
register "authority serve", wrap_command(Commands::Authority::Serve)
|
|
185
|
-
register "authority launch", wrap_command(Commands::Authority::Launch)
|
|
186
|
-
register "authority status", wrap_command(Commands::Authority::Status)
|
|
187
|
-
register "authority terminate", wrap_command(Commands::Authority::Terminate)
|
|
188
|
-
register "authority worker", wrap_command(Commands::Authority::Worker)
|
|
189
|
-
register "authority task-context", wrap_command(Commands::Authority::TaskContext)
|
|
190
|
-
register "authority task-context-principal", wrap_command(Commands::Authority::TaskContextPrincipal)
|
|
191
|
-
register "authority task-context-selection", wrap_command(Commands::Authority::TaskContextSelection)
|
|
192
|
-
register "authority inbox-context-principal", wrap_command(Commands::Authority::InboxContextPrincipal)
|
|
193
|
-
register "authority inbox-context-selection", wrap_command(Commands::Authority::InboxContextSelection)
|
|
194
122
|
register "create", wrap_command(Commands::Create)
|
|
195
|
-
register "delivery", wrap_command(Commands::Delivery)
|
|
196
123
|
register "status", wrap_command(Commands::Status)
|
|
197
124
|
register "step", wrap_command(Commands::Step)
|
|
198
125
|
register "start", wrap_command(Commands::Start)
|
|
@@ -204,17 +131,6 @@ register "authority inbox-context-selection", wrap_command(Commands::Authority::
|
|
|
204
131
|
register "select", wrap_command(Commands::Select)
|
|
205
132
|
register "fork-run", wrap_command(Commands::ForkRun)
|
|
206
133
|
register "fork-session", wrap_command(Commands::ForkSession)
|
|
207
|
-
register "attempt start", wrap_command(Commands::Attempt::Start)
|
|
208
|
-
register "attempt status", wrap_command(Commands::Attempt::Status)
|
|
209
|
-
register "attempt evidence", wrap_command(Commands::Attempt::Evidence)
|
|
210
|
-
register "attempt finish", wrap_command(Commands::Attempt::Finish)
|
|
211
|
-
register "attempt reconcile", wrap_command(Commands::Attempt::Reconcile)
|
|
212
|
-
register "resume", wrap_command(Commands::Resume)
|
|
213
|
-
register "inbox-bind", wrap_command(Commands::InboxBind)
|
|
214
|
-
register "submit-candidate", wrap_command(Commands::SubmitCandidate)
|
|
215
|
-
register "submit-result", wrap_command(Commands::SubmitResult)
|
|
216
|
-
register "campaign-export-result", wrap_command(Commands::CampaignExportResult)
|
|
217
|
-
register "campaign-record-round", wrap_command(Commands::CampaignRecordRound)
|
|
218
134
|
|
|
219
135
|
# Register version command
|
|
220
136
|
version_cmd = Ace::Support::Cli::VersionCommand.build(
|
data/lib/ace/assign/errors.rb
CHANGED
|
@@ -50,94 +50,22 @@ module Ace
|
|
|
50
50
|
class InvalidState < Error; end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
#
|
|
54
|
-
module
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
super(message, exit_code: 5)
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
class InvalidScope < Error
|
|
62
|
-
def initialize(message = "Invalid attempt scope")
|
|
63
|
-
super(message, exit_code: 5)
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
# Duplicate start with a conflicting binding for an owned subtree
|
|
53
|
+
# Local prune/start exclusion errors (exit code 5).
|
|
54
|
+
module LifecycleErrors
|
|
55
|
+
# The identity was pruned or its exclusion artifacts are unsafe, so a
|
|
56
|
+
# writer must not be started against it.
|
|
68
57
|
class Conflict < Error
|
|
69
|
-
def initialize(message = "Conflicting
|
|
70
|
-
super(message, exit_code: 5)
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
# An exact authorization reference is not currently valid for a new
|
|
75
|
-
# effect: consumed by another live request, expired, or out of scope.
|
|
76
|
-
class AuthorizationConflict < Conflict; end
|
|
77
|
-
|
|
78
|
-
class NotFound < Error
|
|
79
|
-
def initialize(message = "Attempt not found")
|
|
80
|
-
super(message, exit_code: 5)
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# Untrusted or unknown execution identity; authority fails closed
|
|
85
|
-
class UnauthorizedIdentity < Error
|
|
86
|
-
def initialize(message = "Unknown or unauthorized execution identity")
|
|
87
|
-
super(message, exit_code: 5)
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
# Receipt failed verification (binding, head, digest, artifacts, review)
|
|
92
|
-
class ReceiptRejected < Error
|
|
93
|
-
def initialize(message = "Receipt rejected")
|
|
94
|
-
super(message, exit_code: 5)
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
# Byte framing failed before any business receipt could be admitted.
|
|
99
|
-
class MalformedTransfer < ReceiptRejected; end
|
|
100
|
-
|
|
101
|
-
# Recovery settled a prior effect, but did not perform this request.
|
|
102
|
-
class CurrentEffectRequired < ReceiptRejected
|
|
103
|
-
def initialize(operation:, recovered_operation:)
|
|
104
|
-
super("Recovered #{recovered_operation}; requested #{operation} still needs execution")
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
# Attempt state forbids the requested effect (terminal, uncertain, blocked)
|
|
109
|
-
class InvalidState < Error
|
|
110
|
-
def initialize(message = "Attempt state forbids the requested operation")
|
|
58
|
+
def initialize(message = "Conflicting lifecycle identity")
|
|
111
59
|
super(message, exit_code: 5)
|
|
112
60
|
end
|
|
113
61
|
end
|
|
114
62
|
|
|
115
|
-
#
|
|
116
|
-
class
|
|
117
|
-
def initialize(message = "
|
|
63
|
+
# Maintenance exclusion is busy or its admission deadline expired.
|
|
64
|
+
class MaintenanceBusy < Error
|
|
65
|
+
def initialize(message = "Maintenance exclusion is busy")
|
|
118
66
|
super(message, exit_code: 5)
|
|
119
67
|
end
|
|
120
68
|
end
|
|
121
|
-
|
|
122
|
-
# Complete maintenance exclusions are busy or their admission deadline expired.
|
|
123
|
-
class MaintenanceBusy < EvidenceUnavailable
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
# One canonical inventory row cannot fit the fixed protocol frame.
|
|
127
|
-
class BoundedResultUnavailable < EvidenceUnavailable
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
# Exhaustively authenticated service inventory remains unsettled.
|
|
131
|
-
class ServiceSettlementPending < EvidenceUnavailable
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
# Exhaustively authenticated current Inbox claims remain queued.
|
|
135
|
-
class InboxSettlementPending < EvidenceUnavailable
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# Positively admitted original Inbox work remains pending, not corrupt.
|
|
139
|
-
class InboxContextPending < EvidenceUnavailable
|
|
140
|
-
end
|
|
141
69
|
end
|
|
142
70
|
end
|
|
143
71
|
end
|
|
@@ -20,7 +20,7 @@ module Ace
|
|
|
20
20
|
# but tests would need to be updated to provide all required fields.
|
|
21
21
|
class Assignment
|
|
22
22
|
attr_reader :id, :name, :description, :created_at, :updated_at, :source_config, :cache_dir, :parent,
|
|
23
|
-
:task_id, :project_id
|
|
23
|
+
:task_id, :project_id
|
|
24
24
|
|
|
25
25
|
# @param id [String] Unique assignment ID (6-char compact timestamp)
|
|
26
26
|
# @param name [String] Human-readable assignment name
|
|
@@ -33,7 +33,7 @@ module Ace
|
|
|
33
33
|
# @param task_id [String, nil] Source task ID attached to this assignment (nil = taskless)
|
|
34
34
|
# @param project_id [String, nil] Project ID this assignment executes in
|
|
35
35
|
def initialize(id:, name:, created_at:, source_config:, description: nil, updated_at: nil, cache_dir: nil,
|
|
36
|
-
parent: nil, task_id: nil, project_id: nil
|
|
36
|
+
parent: nil, task_id: nil, project_id: nil)
|
|
37
37
|
@id = id.freeze
|
|
38
38
|
@name = name.freeze
|
|
39
39
|
@description = description&.freeze
|
|
@@ -44,29 +44,10 @@ module Ace
|
|
|
44
44
|
@parent = parent&.freeze
|
|
45
45
|
@task_id = task_id&.freeze
|
|
46
46
|
@project_id = project_id&.freeze
|
|
47
|
-
if !campaign_execution.nil? && !review_campaign.nil?
|
|
48
|
-
raise ArgumentError, "assignment cannot own both parent campaign and child execution"
|
|
49
|
-
end
|
|
50
|
-
@campaign_execution = immutable_campaign_execution(campaign_execution) unless campaign_execution.nil?
|
|
51
|
-
@review_campaign = immutable_campaign_execution(review_campaign) unless review_campaign.nil?
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Keep registered definition bytes stable without retaining caller-owned
|
|
55
|
-
# mutable objects. Authority validates the closed linkage and provenance.
|
|
56
|
-
private def immutable_campaign_execution(value)
|
|
57
|
-
case value
|
|
58
|
-
when Hash
|
|
59
|
-
value.to_h { |key, item| [immutable_campaign_execution(key), immutable_campaign_execution(item)] }.freeze
|
|
60
|
-
when Array then value.map { |item| immutable_campaign_execution(item) }.freeze
|
|
61
|
-
when String then value.dup.freeze
|
|
62
|
-
when Integer, NilClass, TrueClass, FalseClass then value
|
|
63
|
-
else raise ArgumentError, "invalid campaign execution value"
|
|
64
|
-
end
|
|
65
47
|
end
|
|
66
48
|
|
|
67
49
|
# @return [Boolean] True when a source task is attached; taskless
|
|
68
|
-
# assignments
|
|
69
|
-
# Git-backed recovery
|
|
50
|
+
# assignments keep their plan state locally only.
|
|
70
51
|
def managed?
|
|
71
52
|
!task_id.nil? && !task_id.to_s.strip.empty?
|
|
72
53
|
end
|
|
@@ -83,9 +64,7 @@ module Ace
|
|
|
83
64
|
"source_config" => source_config,
|
|
84
65
|
"parent" => parent,
|
|
85
66
|
"task_id" => task_id,
|
|
86
|
-
"project_id" => project_id
|
|
87
|
-
"campaign_execution" => campaign_execution,
|
|
88
|
-
"review_campaign" => review_campaign
|
|
67
|
+
"project_id" => project_id
|
|
89
68
|
}.compact
|
|
90
69
|
end
|
|
91
70
|
|
|
@@ -104,9 +83,7 @@ module Ace
|
|
|
104
83
|
cache_dir: cache_dir,
|
|
105
84
|
parent: data["parent"],
|
|
106
85
|
task_id: data["task_id"],
|
|
107
|
-
project_id: data["project_id"]
|
|
108
|
-
campaign_execution: data["campaign_execution"],
|
|
109
|
-
review_campaign: data["review_campaign"]
|
|
86
|
+
project_id: data["project_id"]
|
|
110
87
|
)
|
|
111
88
|
end
|
|
112
89
|
|
|
@@ -195,32 +195,6 @@ module Ace
|
|
|
195
195
|
updated
|
|
196
196
|
end
|
|
197
197
|
|
|
198
|
-
# The attempt store sharing this manager's cache base. The store is
|
|
199
|
-
# the single authority for attempt records and subtree ownership;
|
|
200
|
-
# assignment metadata never carries attempt history.
|
|
201
|
-
#
|
|
202
|
-
# @return [Molecules::AttemptStore] Attempt store
|
|
203
|
-
def attempt_store
|
|
204
|
-
@attempt_store ||= Molecules::AttemptStore.new(cache_base: @cache_base)
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
# Assignment-scoped lookup of the active attempt for a scope.
|
|
208
|
-
#
|
|
209
|
-
# @param assignment_id [String] Assignment ID
|
|
210
|
-
# @param scope [String] Step/subtree scope
|
|
211
|
-
# @return [Models::Attempt, nil] Active attempt or nil
|
|
212
|
-
def active_attempt(assignment_id, scope)
|
|
213
|
-
attempt_store.active(assignment_id, scope)
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
# All attempts recorded for an assignment.
|
|
217
|
-
#
|
|
218
|
-
# @param assignment_id [String] Assignment ID
|
|
219
|
-
# @return [Array<Models::Attempt>] Persisted attempts
|
|
220
|
-
def attempts(assignment_id)
|
|
221
|
-
attempt_store.list(assignment_id)
|
|
222
|
-
end
|
|
223
|
-
|
|
224
198
|
# List all assignments
|
|
225
199
|
#
|
|
226
200
|
# @return [Array<Models::Assignment>] All assignments
|
|
@@ -85,22 +85,14 @@ module Ace
|
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
def launch_provider_session(assignment_id:, fork_root:, provider:, cli_args: nil, timeout: nil, cache_dir: nil,
|
|
88
|
-
last_message_file: nil, session_meta_file: nil
|
|
88
|
+
last_message_file: nil, session_meta_file: nil)
|
|
89
89
|
ensure_not_same_scoped_refork!(assignment_id: assignment_id, fork_root: fork_root)
|
|
90
90
|
resolved_provider = provider || config.dig("execution", "provider") || DEFAULT_PROVIDER
|
|
91
91
|
resolved_timeout = timeout || config.dig("execution", "timeout") || DEFAULT_TIMEOUT
|
|
92
92
|
scoped_assignment = "#{assignment_id}@#{fork_root}"
|
|
93
|
-
|
|
94
|
-
raise Error, "Prepared provider scope differs"
|
|
95
|
-
end
|
|
96
|
-
prompt = prepared_input ? prepared_input.drive_prompt : "/as-assign-drive #{scoped_assignment}"
|
|
93
|
+
prompt = "/as-assign-drive #{scoped_assignment}"
|
|
97
94
|
last_msg_file = last_message_file || build_last_message_file(cache_dir, fork_root)
|
|
98
95
|
scope_env = self.class.fork_scope_env(assignment_id: assignment_id, fork_root: fork_root)
|
|
99
|
-
if prepared_input
|
|
100
|
-
scope_env = scope_env.merge("ACE_ASSIGN_LAUNCH_MAPPING" => prepared_input.descriptor.fetch("mapping_id"),
|
|
101
|
-
"ACE_ASSIGN_ASSIGNMENT_ID" => assignment_id, "ACE_ASSIGN_ATTEMPT_ID" => prepared_input.descriptor.fetch("attempt_id"),
|
|
102
|
-
"ACE_ASSIGN_TASK_CONTEXT_ENTRY" => JSON.generate(prepared_input.descriptor.fetch("task_context_entry").fetch("manifest")))
|
|
103
|
-
end
|
|
104
96
|
|
|
105
97
|
result = query_interface.query(
|
|
106
98
|
resolved_provider,
|
|
@@ -111,7 +103,7 @@ module Ace
|
|
|
111
103
|
fallback: false,
|
|
112
104
|
last_message_file: last_msg_file,
|
|
113
105
|
subprocess_env: scope_env,
|
|
114
|
-
working_dir:
|
|
106
|
+
working_dir: nil
|
|
115
107
|
)
|
|
116
108
|
|
|
117
109
|
if last_msg_file && result[:text] && !result[:text].strip.empty?
|
|
@@ -120,7 +112,7 @@ module Ace
|
|
|
120
112
|
end
|
|
121
113
|
|
|
122
114
|
write_session_metadata(last_msg_file, result, prompt: prompt, session_meta_file: session_meta_file,
|
|
123
|
-
discover_session:
|
|
115
|
+
discover_session: true)
|
|
124
116
|
|
|
125
117
|
result
|
|
126
118
|
rescue Ace::LLM::Error => e
|
|
@@ -6,7 +6,6 @@ require "json"
|
|
|
6
6
|
require "open3"
|
|
7
7
|
require "time"
|
|
8
8
|
require "securerandom"
|
|
9
|
-
require_relative "../atoms/evidence_digest"
|
|
10
9
|
|
|
11
10
|
module Ace
|
|
12
11
|
module Assign
|
|
@@ -17,108 +16,15 @@ module Ace
|
|
|
17
16
|
# common dir — so they survive removal of the assignment cache, the
|
|
18
17
|
# worktree, or a recreated lock pathname. Prune holds the exclusive
|
|
19
18
|
# side from its final evidence reads through completion of removal;
|
|
20
|
-
# every supported start path (
|
|
21
|
-
#
|
|
22
|
-
# registering a writer.
|
|
19
|
+
# every supported start path (fork/driver session launch, worktree
|
|
20
|
+
# provisioning) holds the shared side while registering a writer.
|
|
23
21
|
#
|
|
24
22
|
# A `removed` marker recorded by prune makes post-deletion starts fail
|
|
25
23
|
# closed instead of recreating the deleted identity: acquiring the
|
|
26
|
-
# shared side raises {
|
|
24
|
+
# shared side raises {LifecycleErrors::Conflict} when the marker is
|
|
27
25
|
# present. Legitimate fresh provisioning (a new worktree for a task)
|
|
28
26
|
# clears the marker explicitly via `reset_removed`.
|
|
29
27
|
class LifecycleExclusion
|
|
30
|
-
autoload :ControlExclusion, File.expand_path("protected_control_exclusion", __dir__)
|
|
31
|
-
|
|
32
|
-
def self.control_exclusion(authority:, project_id:, descriptor_sha256:, root_identity: nil, **boundaries)
|
|
33
|
-
ControlExclusion.new(authority: authority, project_id: project_id,
|
|
34
|
-
descriptor_sha256: descriptor_sha256, root_identity: root_identity, **boundaries)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
autoload :WorkspaceNativeReader, File.expand_path("protected_workspace_exclusion", __dir__)
|
|
38
|
-
autoload :WorkspaceReader, File.expand_path("protected_workspace_exclusion", __dir__)
|
|
39
|
-
|
|
40
|
-
autoload :WorkspaceWriter, File.expand_path("protected_workspace_exclusion", __dir__)
|
|
41
|
-
|
|
42
|
-
autoload :WorkspaceFenceReader, File.expand_path("protected_workspace_exclusion", __dir__)
|
|
43
|
-
|
|
44
|
-
def self.workspace_fence_reader(projection:, protection: nil, files: File)
|
|
45
|
-
WorkspaceFenceReader.new(projection: projection, protection: protection, files: files)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def self.workspace_writer(projection:, protection: nil, files: File)
|
|
49
|
-
WorkspaceWriter.new(projection: projection, protection: protection, files: files)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
autoload :WorkspaceProvisioner, File.expand_path("protected_workspace_provisioner", __dir__)
|
|
53
|
-
|
|
54
|
-
def self.provision_workspace!(mapping_id:, project_id:, authority:, cwd_resource:, **boundaries)
|
|
55
|
-
WorkspaceProvisioner.new(mapping_id: mapping_id, project_id: project_id,
|
|
56
|
-
authority: authority, cwd_resource: cwd_resource, **boundaries).provision!
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def self.workspace_reader(projection:, protection: nil, files: File)
|
|
60
|
-
WorkspaceReader.new(projection: projection, protection: protection, files: files)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def self.workspace_selection(mapping_id:, project_id:, authority:, cwd_resource:)
|
|
64
|
-
fields = %w[device filesystem_type gid host_path inode mount_id uid view_path]
|
|
65
|
-
unless authority.is_a?(Hash) && cwd_resource.is_a?(Hash) && cwd_resource.keys.sort == fields &&
|
|
66
|
-
%w[device gid inode uid].all? { |field| cwd_resource[field].is_a?(Integer) && cwd_resource[field] >= 0 } &&
|
|
67
|
-
cwd_resource["mount_id"].is_a?(Integer) && cwd_resource["mount_id"].positive? &&
|
|
68
|
-
%w[host_path view_path].all? { |field| cwd_resource[field].is_a?(String) &&
|
|
69
|
-
cwd_resource[field].valid_encoding? && cwd_resource[field].bytesize.between?(1, 4096) &&
|
|
70
|
-
!cwd_resource[field].include?("\0") && cwd_resource[field].start_with?("/") &&
|
|
71
|
-
File.expand_path(cwd_resource[field]) == cwd_resource[field] } &&
|
|
72
|
-
%w[ext4 xfs btrfs tmpfs].include?(cwd_resource["filesystem_type"]) &&
|
|
73
|
-
[mapping_id, project_id].all? { |value| value.is_a?(String) && value.match?(/\A[A-Za-z0-9][A-Za-z0-9._-]{0,127}\z/) } &&
|
|
74
|
-
%w[uid gid].all? { |field| authority[field].is_a?(Integer) && authority[field].positive? } &&
|
|
75
|
-
authority["state_root"].is_a?(String) && authority["state_root"].valid_encoding? &&
|
|
76
|
-
authority["state_root"].bytesize.between?(1, 4096) && !authority["state_root"].include?("\0") &&
|
|
77
|
-
authority["state_root"].start_with?("/") &&
|
|
78
|
-
File.expand_path(authority["state_root"]) == authority["state_root"]
|
|
79
|
-
raise AttemptErrors::EvidenceUnavailable, "Original lifecycle workspace selection is malformed"
|
|
80
|
-
end
|
|
81
|
-
digest = Atoms::EvidenceDigest.digest(cwd_resource)
|
|
82
|
-
selection = {"key" => "workspace:#{mapping_id}:#{digest}",
|
|
83
|
-
"host_path" => File.join(authority.fetch("state_root"), "lifecycle-exclusion", project_id,
|
|
84
|
-
"workspaces", mapping_id, digest), "view_path" => "/run/ace/lifecycle-exclusion/#{mapping_id}"}
|
|
85
|
-
unless selection.fetch("host_path").bytesize <= 4096
|
|
86
|
-
raise AttemptErrors::EvidenceUnavailable, "Original lifecycle workspace path is oversized"
|
|
87
|
-
end
|
|
88
|
-
selection.each_value(&:freeze)
|
|
89
|
-
selection.freeze
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def self.workspace_initial_marker(key:)
|
|
93
|
-
unless key.is_a?(String) && key.match?(/\Aworkspace:[A-Za-z0-9][A-Za-z0-9._-]{0,127}:[0-9a-f]{64}\z/)
|
|
94
|
-
raise AttemptErrors::EvidenceUnavailable, "Original lifecycle workspace key is malformed"
|
|
95
|
-
end
|
|
96
|
-
Atoms::EvidenceDigest.canonical_json({"key" => key, "removed" => false, "removed_at" => nil}).freeze
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def self.workspace_marker!(bytes:, key:)
|
|
100
|
-
unless bytes.is_a?(String) && bytes.bytesize.between?(1, 8192)
|
|
101
|
-
raise AttemptErrors::EvidenceUnavailable, "Lifecycle marker bytes differ"
|
|
102
|
-
end
|
|
103
|
-
bytes = bytes.dup.force_encoding(Encoding::UTF_8)
|
|
104
|
-
unless bytes.valid_encoding?
|
|
105
|
-
raise AttemptErrors::EvidenceUnavailable, "Lifecycle marker is not bounded UTF8"
|
|
106
|
-
end
|
|
107
|
-
state = JSON.parse(bytes, create_additions: false, max_nesting: 4, allow_duplicate_key: false)
|
|
108
|
-
unless state.is_a?(Hash) && state.keys.sort == %w[key removed removed_at] && state.fetch("key") == key &&
|
|
109
|
-
(state.values_at("removed", "removed_at") == [false, nil] || state["removed"] == true &&
|
|
110
|
-
state["removed_at"].is_a?(String) && Time.iso8601(state["removed_at"]).utc.iso8601 == state["removed_at"])
|
|
111
|
-
raise AttemptErrors::EvidenceUnavailable, "Lifecycle marker schema differs"
|
|
112
|
-
end
|
|
113
|
-
state.each_value { |value| value.freeze }
|
|
114
|
-
state.freeze
|
|
115
|
-
rescue JSON::ParserError, ArgumentError, KeyError
|
|
116
|
-
raise AttemptErrors::EvidenceUnavailable, "Lifecycle marker schema differs"
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
# @param repo_root [String, nil] Repository used to resolve the
|
|
120
|
-
# shared Git common dir (default: project root)
|
|
121
|
-
# @param root [String, nil] Explicit exclusion root (tests)
|
|
122
28
|
def initialize(repo_root: nil, root: nil)
|
|
123
29
|
@root = root || self.class.default_root(repo_root)
|
|
124
30
|
end
|
|
@@ -132,7 +38,7 @@ module Ace
|
|
|
132
38
|
"git", "-C", repo_root, "rev-parse", "--path-format=absolute", "--git-common-dir"
|
|
133
39
|
)
|
|
134
40
|
unless status.success? && !common_dir.to_s.strip.empty? && File.absolute_path(common_dir.strip) == common_dir.strip
|
|
135
|
-
raise
|
|
41
|
+
raise LifecycleErrors::Conflict, "Repository lifecycle root is unavailable"
|
|
136
42
|
end
|
|
137
43
|
File.join(common_dir.strip, "ace", "lifecycle-exclusion")
|
|
138
44
|
end
|
|
@@ -141,9 +47,6 @@ module Ace
|
|
|
141
47
|
"assignment:#{assignment_id}"
|
|
142
48
|
end
|
|
143
49
|
|
|
144
|
-
def slot_key(slot_id)
|
|
145
|
-
"execution-slot:#{slot_id}"
|
|
146
|
-
end
|
|
147
50
|
|
|
148
51
|
# Task identity is the canonical worktree identity: work-on derives
|
|
149
52
|
# the worktree path from the task ref, and prune candidates carry
|
|
@@ -174,7 +77,7 @@ module Ace
|
|
|
174
77
|
begin
|
|
175
78
|
mode = File::LOCK_EX | (deadline ? File::LOCK_NB : 0)
|
|
176
79
|
unless lock.flock(mode)
|
|
177
|
-
raise
|
|
80
|
+
raise LifecycleErrors::MaintenanceBusy, "maintenance exclusion is busy"
|
|
178
81
|
end
|
|
179
82
|
maintenance_deadline!(deadline) unless deadline.nil?
|
|
180
83
|
yield
|
|
@@ -198,7 +101,7 @@ module Ace
|
|
|
198
101
|
begin
|
|
199
102
|
if removed?(key)
|
|
200
103
|
unless reset_removed
|
|
201
|
-
raise
|
|
104
|
+
raise LifecycleErrors::Conflict, removed_message(key)
|
|
202
105
|
end
|
|
203
106
|
|
|
204
107
|
clear_removed!(key)
|
|
@@ -236,7 +139,7 @@ module Ace
|
|
|
236
139
|
ordered.each do |key|
|
|
237
140
|
if removed?(key)
|
|
238
141
|
unless reset_removed
|
|
239
|
-
raise
|
|
142
|
+
raise LifecycleErrors::Conflict, removed_message(key)
|
|
240
143
|
end
|
|
241
144
|
|
|
242
145
|
clear_removed!(key)
|
|
@@ -267,25 +170,25 @@ module Ace
|
|
|
267
170
|
opened = true
|
|
268
171
|
before = file.stat
|
|
269
172
|
unless before.file? && before.nlink == 1 && before.size.between?(1, 8192) && (before.mode & 0022).zero?
|
|
270
|
-
raise
|
|
173
|
+
raise LifecycleErrors::Conflict, "Lifecycle marker is unsafe"
|
|
271
174
|
end
|
|
272
175
|
content = file.read(8193).force_encoding(Encoding::UTF_8)
|
|
273
176
|
unless content.valid_encoding? && content.bytesize == before.size && same_file?(before, file.stat) && same_file?(before, File.lstat(path))
|
|
274
|
-
raise
|
|
177
|
+
raise LifecycleErrors::Conflict, "Lifecycle marker changed"
|
|
275
178
|
end
|
|
276
179
|
state = JSON.parse(content, create_additions: false, max_nesting: 4, allow_duplicate_key: false)
|
|
277
180
|
unless state.is_a?(Hash) && state.keys.sort == %w[key removed removed_at] &&
|
|
278
181
|
state["key"] == key && state["removed"] == true && state["removed_at"].is_a?(String)
|
|
279
|
-
raise
|
|
182
|
+
raise LifecycleErrors::Conflict, "Lifecycle marker is malformed"
|
|
280
183
|
end
|
|
281
184
|
Time.iso8601(state.fetch("removed_at"))
|
|
282
185
|
true
|
|
283
186
|
end
|
|
284
187
|
rescue Errno::ENOENT
|
|
285
|
-
raise
|
|
188
|
+
raise LifecycleErrors::Conflict, "Lifecycle marker disappeared during read" if opened
|
|
286
189
|
false
|
|
287
190
|
rescue JSON::ParserError, ArgumentError, TypeError, SystemCallError, IOError
|
|
288
|
-
raise
|
|
191
|
+
raise LifecycleErrors::Conflict, "Lifecycle marker is unavailable or malformed"
|
|
289
192
|
end
|
|
290
193
|
|
|
291
194
|
def clear_removed!(key)
|
|
@@ -305,12 +208,12 @@ module Ace
|
|
|
305
208
|
file = File.open(lock_path(key), flags, 0600)
|
|
306
209
|
unless file.stat.file? && file.stat.nlink == 1 && (file.stat.mode & 0022).zero? &&
|
|
307
210
|
same_file?(file.stat, File.lstat(lock_path(key)))
|
|
308
|
-
raise
|
|
211
|
+
raise LifecycleErrors::MaintenanceBusy, "maintenance exclusion is not a regular file"
|
|
309
212
|
end
|
|
310
213
|
retained = true
|
|
311
214
|
file
|
|
312
215
|
rescue SystemCallError
|
|
313
|
-
raise
|
|
216
|
+
raise LifecycleErrors::Conflict, "Lifecycle lock is unavailable"
|
|
314
217
|
ensure
|
|
315
218
|
file&.close unless retained
|
|
316
219
|
end
|
|
@@ -320,7 +223,7 @@ module Ace
|
|
|
320
223
|
raise ArgumentError, "maintenance deadline must be finite"
|
|
321
224
|
end
|
|
322
225
|
if Process.clock_gettime(Process::CLOCK_MONOTONIC) >= deadline
|
|
323
|
-
raise
|
|
226
|
+
raise LifecycleErrors::MaintenanceBusy, "maintenance admission deadline expired"
|
|
324
227
|
end
|
|
325
228
|
end
|
|
326
229
|
|
|
@@ -12,9 +12,10 @@ module Ace
|
|
|
12
12
|
# operation delegates to the resolved Ace::Runtime adapter; assign
|
|
13
13
|
# never calls a runtime-native API directly (k86.3 interface rule).
|
|
14
14
|
class RuntimeControlSurfaceRunner
|
|
15
|
-
def initialize(runtime: nil, env: ENV)
|
|
15
|
+
def initialize(runtime: nil, env: ENV, adapter: nil)
|
|
16
16
|
@runtime_name = runtime
|
|
17
17
|
@env = env
|
|
18
|
+
@runtime = adapter
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
attr_reader :runtime_name, :env
|
|
@@ -68,12 +68,6 @@ module Ace
|
|
|
68
68
|
|
|
69
69
|
assignment_config = config["assignment"] || {}
|
|
70
70
|
steps_config = config["steps"] || []
|
|
71
|
-
if config.key?("delivery")
|
|
72
|
-
Atoms::DeliveryParameters.validate(config["delivery"])
|
|
73
|
-
unless Array(steps_config).any? { |step| %w[create-pr update-pr-desc mark-pr-ready].include?(step["name"]) }
|
|
74
|
-
raise ArgumentError, "Explicit delivery inputs require remote steps"
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
71
|
|
|
78
72
|
raise Error, "No steps defined in config" if steps_config.empty?
|
|
79
73
|
|