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.
Files changed (181) hide show
  1. checksums.yaml +4 -4
  2. data/.ace-defaults/assign/catalog/steps/auto-merge.step.yml +8 -8
  3. data/.ace-defaults/assign/catalog/steps/create-pr.step.yml +1 -1
  4. data/.ace-defaults/assign/catalog/steps/mark-pr-ready.step.yml +1 -1
  5. data/.ace-defaults/assign/config.yml +0 -13
  6. data/.ace-defaults/assign/presets/fix-bug.yml +1 -1
  7. data/.ace-defaults/assign/presets/work-on-task-ace-development.yml +1 -1
  8. data/.ace-defaults/assign/presets/work-on-task-auto-merge.yml +1 -1
  9. data/.ace-defaults/assign/presets/work-on-task.yml +1 -1
  10. data/CHANGELOG.md +3 -2099
  11. data/README.md +58 -31
  12. data/docs/exit-codes.md +36 -44
  13. data/docs/getting-started.md +9 -4
  14. data/docs/usage.md +129 -522
  15. data/handbook/skills/as-assign-drive/SKILL.md +1 -1
  16. data/handbook/workflow-instructions/assign/drive.wf.md +47 -118
  17. data/handbook/workflow-instructions/assign/prepare.wf.md +1 -1
  18. data/handbook/workflow-instructions/assign/verify-test-suite.wf.md +8 -2
  19. data/lib/ace/assign/cli/commands/assignment_target.rb +2 -15
  20. data/lib/ace/assign/cli/commands/create.rb +1 -8
  21. data/lib/ace/assign/cli/commands/fail.rb +0 -6
  22. data/lib/ace/assign/cli/commands/finish.rb +2 -23
  23. data/lib/ace/assign/cli/commands/fork_session.rb +0 -1
  24. data/lib/ace/assign/cli/commands/select.rb +0 -1
  25. data/lib/ace/assign/cli/commands/start.rb +0 -2
  26. data/lib/ace/assign/cli/commands/status.rb +1 -31
  27. data/lib/ace/assign/cli/commands/step.rb +1 -3
  28. data/lib/ace/assign/cli.rb +1 -85
  29. data/lib/ace/assign/errors.rb +8 -80
  30. data/lib/ace/assign/models/assignment.rb +5 -28
  31. data/lib/ace/assign/molecules/assignment_manager.rb +0 -26
  32. data/lib/ace/assign/molecules/fork_session_launcher.rb +4 -12
  33. data/lib/ace/assign/molecules/lifecycle_exclusion.rb +15 -112
  34. data/lib/ace/assign/molecules/runtime_control_surface_runner.rb +2 -1
  35. data/lib/ace/assign/organisms/assignment_executor.rb +0 -6
  36. data/lib/ace/assign/organisms/task_assignment_creator.rb +3 -8
  37. data/lib/ace/assign/version.rb +1 -1
  38. data/lib/ace/assign.rb +0 -6
  39. metadata +4 -161
  40. data/.ace-defaults/assign/presets/close-delivery.yml +0 -57
  41. data/.ace-defaults/assign/presets/deliver-release.yml +0 -60
  42. data/.ace-defaults/assign/presets/integrate-batch.yml +0 -72
  43. data/.ace-defaults/assign/presets/supervise-delivery.yml +0 -69
  44. data/docs/protected-authority.md +0 -282
  45. data/exe/ace-scope-ready +0 -55
  46. data/lib/ace/assign/atoms/assignment_scope.rb +0 -58
  47. data/lib/ace/assign/atoms/attempt_state_machine.rb +0 -84
  48. data/lib/ace/assign/atoms/delivery_parameters.rb +0 -19
  49. data/lib/ace/assign/atoms/evidence_digest.rb +0 -78
  50. data/lib/ace/assign/atoms/fixed_merge_schemas.rb +0 -398
  51. data/lib/ace/assign/authority/assignment_definition.rb +0 -35
  52. data/lib/ace/assign/authority/assignment_inventory.rb +0 -290
  53. data/lib/ace/assign/authority/campaign_consumer_policy.rb +0 -100
  54. data/lib/ace/assign/authority/campaign_execution.rb +0 -119
  55. data/lib/ace/assign/authority/campaign_round_transfer.rb +0 -82
  56. data/lib/ace/assign/authority/candidate_transfer.rb +0 -327
  57. data/lib/ace/assign/authority/cleanup_result_protection.rb +0 -53
  58. data/lib/ace/assign/authority/client.rb +0 -257
  59. data/lib/ace/assign/authority/deployment.rb +0 -627
  60. data/lib/ace/assign/authority/deployment_history.rb +0 -146
  61. data/lib/ace/assign/authority/endcap.rb +0 -455
  62. data/lib/ace/assign/authority/endcap_attempts.rb +0 -274
  63. data/lib/ace/assign/authority/endcap_campaign_results.rb +0 -232
  64. data/lib/ace/assign/authority/endcap_campaign_rounds.rb +0 -266
  65. data/lib/ace/assign/authority/endcap_contexts.rb +0 -23
  66. data/lib/ace/assign/authority/endcap_inboxes.rb +0 -37
  67. data/lib/ace/assign/authority/endcap_prepared_work.rb +0 -80
  68. data/lib/ace/assign/authority/endcap_publication.rb +0 -207
  69. data/lib/ace/assign/authority/endcap_results.rb +0 -398
  70. data/lib/ace/assign/authority/endcap_review_requests.rb +0 -183
  71. data/lib/ace/assign/authority/endcap_reviews.rb +0 -130
  72. data/lib/ace/assign/authority/endcap_service_settlement.rb +0 -152
  73. data/lib/ace/assign/authority/endcap_services.rb +0 -598
  74. data/lib/ace/assign/authority/evidence_transfer.rb +0 -67
  75. data/lib/ace/assign/authority/execution_scope_observation.rb +0 -903
  76. data/lib/ace/assign/authority/inbox_context_original.rb +0 -113
  77. data/lib/ace/assign/authority/launch_attempt_consumers.rb +0 -51
  78. data/lib/ace/assign/authority/launch_campaign_registration.rb +0 -214
  79. data/lib/ace/assign/authority/launch_control_channel.rb +0 -313
  80. data/lib/ace/assign/authority/launch_control_exclusions.rb +0 -127
  81. data/lib/ace/assign/authority/launch_driver.rb +0 -393
  82. data/lib/ace/assign/authority/launch_input_inhibition.rb +0 -132
  83. data/lib/ace/assign/authority/launch_lifecycle.rb +0 -997
  84. data/lib/ace/assign/authority/launch_prepared_work.rb +0 -122
  85. data/lib/ace/assign/authority/launch_review.rb +0 -91
  86. data/lib/ace/assign/authority/launch_scope_abort.rb +0 -81
  87. data/lib/ace/assign/authority/launch_scope_admission.rb +0 -215
  88. data/lib/ace/assign/authority/launch_scope_close.rb +0 -146
  89. data/lib/ace/assign/authority/launch_scope_parent.rb +0 -44
  90. data/lib/ace/assign/authority/launch_scope_release.rb +0 -859
  91. data/lib/ace/assign/authority/launch_steering.rb +0 -353
  92. data/lib/ace/assign/authority/launch_stop.rb +0 -254
  93. data/lib/ace/assign/authority/maintenance_workspace_retirement.rb +0 -229
  94. data/lib/ace/assign/authority/ordinary_attempt_start.rb +0 -137
  95. data/lib/ace/assign/authority/ordinary_client.rb +0 -70
  96. data/lib/ace/assign/authority/ordinary_completion.rb +0 -94
  97. data/lib/ace/assign/authority/ordinary_effect_settlement.rb +0 -118
  98. data/lib/ace/assign/authority/ordinary_lifecycle.rb +0 -191
  99. data/lib/ace/assign/authority/ordinary_peer.rb +0 -54
  100. data/lib/ace/assign/authority/ordinary_progress.rb +0 -162
  101. data/lib/ace/assign/authority/ordinary_queue.rb +0 -115
  102. data/lib/ace/assign/authority/ordinary_results.rb +0 -196
  103. data/lib/ace/assign/authority/ordinary_server.rb +0 -75
  104. data/lib/ace/assign/authority/original_inbox_selection.rb +0 -41
  105. data/lib/ace/assign/authority/posix_acl.rb +0 -59
  106. data/lib/ace/assign/authority/prepared_input.rb +0 -130
  107. data/lib/ace/assign/authority/prepared_queue.rb +0 -185
  108. data/lib/ace/assign/authority/prepared_task_context.rb +0 -116
  109. data/lib/ace/assign/authority/prepared_work.rb +0 -304
  110. data/lib/ace/assign/authority/prepared_worker.rb +0 -69
  111. data/lib/ace/assign/authority/private_directory.rb +0 -33
  112. data/lib/ace/assign/authority/protected_assignment_context.rb +0 -226
  113. data/lib/ace/assign/authority/readiness_hook.rb +0 -120
  114. data/lib/ace/assign/authority/receipt_transfer.rb +0 -48
  115. data/lib/ace/assign/authority/router.rb +0 -81
  116. data/lib/ace/assign/authority/server.rb +0 -131
  117. data/lib/ace/assign/authority/service_cleanup_dispatch.rb +0 -88
  118. data/lib/ace/assign/authority/service_cleanup_evidence.rb +0 -180
  119. data/lib/ace/assign/authority/service_delivery_evidence.rb +0 -101
  120. data/lib/ace/assign/authority/service_evidence.rb +0 -286
  121. data/lib/ace/assign/authority/service_publication_evidence.rb +0 -110
  122. data/lib/ace/assign/authority/socket_server.rb +0 -255
  123. data/lib/ace/assign/authority/task_context_entry.rb +0 -32
  124. data/lib/ace/assign/authority/transfer_codec.rb +0 -187
  125. data/lib/ace/assign/cli/commands/attempt/base.rb +0 -84
  126. data/lib/ace/assign/cli/commands/attempt/evidence.rb +0 -28
  127. data/lib/ace/assign/cli/commands/attempt/finish.rb +0 -51
  128. data/lib/ace/assign/cli/commands/attempt/reconcile.rb +0 -49
  129. data/lib/ace/assign/cli/commands/attempt/start.rb +0 -45
  130. data/lib/ace/assign/cli/commands/attempt/status.rb +0 -54
  131. data/lib/ace/assign/cli/commands/authority/inbox_context_principal.rb +0 -25
  132. data/lib/ace/assign/cli/commands/authority/inbox_context_selection.rb +0 -28
  133. data/lib/ace/assign/cli/commands/authority/launch.rb +0 -97
  134. data/lib/ace/assign/cli/commands/authority/serve.rb +0 -31
  135. data/lib/ace/assign/cli/commands/authority/status.rb +0 -26
  136. data/lib/ace/assign/cli/commands/authority/task_context.rb +0 -31
  137. data/lib/ace/assign/cli/commands/authority/task_context_principal.rb +0 -25
  138. data/lib/ace/assign/cli/commands/authority/task_context_selection.rb +0 -30
  139. data/lib/ace/assign/cli/commands/authority/terminate.rb +0 -29
  140. data/lib/ace/assign/cli/commands/authority/worker.rb +0 -23
  141. data/lib/ace/assign/cli/commands/campaign_export_result.rb +0 -55
  142. data/lib/ace/assign/cli/commands/campaign_record_round.rb +0 -57
  143. data/lib/ace/assign/cli/commands/delivery.rb +0 -107
  144. data/lib/ace/assign/cli/commands/inbox_bind.rb +0 -34
  145. data/lib/ace/assign/cli/commands/protected_submission.rb +0 -61
  146. data/lib/ace/assign/cli/commands/resume.rb +0 -38
  147. data/lib/ace/assign/cli/commands/submit_candidate.rb +0 -34
  148. data/lib/ace/assign/cli/commands/submit_result.rb +0 -61
  149. data/lib/ace/assign/models/attempt.rb +0 -178
  150. data/lib/ace/assign/models/attempt_binding.rb +0 -116
  151. data/lib/ace/assign/models/evidence_event.rb +0 -86
  152. data/lib/ace/assign/models/execution_receipt.rb +0 -139
  153. data/lib/ace/assign/molecules/attempt_reconciler.rb +0 -127
  154. data/lib/ace/assign/molecules/attempt_store.rb +0 -201
  155. data/lib/ace/assign/molecules/canonical_attempt_state.rb +0 -151
  156. data/lib/ace/assign/molecules/canonical_evidence.rb +0 -153
  157. data/lib/ace/assign/molecules/canonical_read_commands.rb +0 -84
  158. data/lib/ace/assign/molecules/canonical_read_snapshot.rb +0 -313
  159. data/lib/ace/assign/molecules/evidence_calculator.rb +0 -237
  160. data/lib/ace/assign/molecules/evidence_journal.rb +0 -1912
  161. data/lib/ace/assign/molecules/execution_identity_resolver.rb +0 -137
  162. data/lib/ace/assign/molecules/execution_scope_lineage.rb +0 -345
  163. data/lib/ace/assign/molecules/fixed_effect_reference.rb +0 -31
  164. data/lib/ace/assign/molecules/installed_journal_read.rb +0 -143
  165. data/lib/ace/assign/molecules/journal_input_inhibition.rb +0 -78
  166. data/lib/ace/assign/molecules/journal_mutation.rb +0 -273
  167. data/lib/ace/assign/molecules/journal_prompt_mutation.rb +0 -241
  168. data/lib/ace/assign/molecules/ordinary_merge_cli.rb +0 -68
  169. data/lib/ace/assign/molecules/ordinary_merge_contract.rb +0 -410
  170. data/lib/ace/assign/molecules/original_launch_binding.rb +0 -37
  171. data/lib/ace/assign/molecules/proposal_journal.rb +0 -179
  172. data/lib/ace/assign/molecules/protected_control_exclusion.rb +0 -303
  173. data/lib/ace/assign/molecules/protected_workspace_exclusion.rb +0 -330
  174. data/lib/ace/assign/molecules/protected_workspace_provisioner.rb +0 -164
  175. data/lib/ace/assign/molecules/receipt_verifier.rb +0 -314
  176. data/lib/ace/assign/molecules/terminal_scope_receipt.rb +0 -88
  177. data/lib/ace/assign/organisms/attempt_coordinator.rb +0 -1337
  178. data/lib/ace/assign/organisms/delivery_coordinator.rb +0 -342
  179. data/lib/ace/assign/organisms/ordinary_merge_consumer.rb +0 -365
  180. data/lib/ace/assign/organisms/prepared_work_builder.rb +0 -194
  181. data/lib/ace/assign/organisms/protected_delivery_coordinator.rb +0 -100
@@ -1,97 +0,0 @@
1
- # frozen_string_literal: true
2
- require "json"
3
- require_relative "../../../authority/launch_driver"
4
- module Ace
5
- module Assign
6
- module CLI
7
- module Commands
8
- module Authority
9
- class Launch < Ace::Support::Cli::Command
10
- desc "Launch one mapped worker and retain its original foreground control channel"
11
- READY_FIELDS = %w[assignment_id attempt_id generation journal_commit mapping_id original_binding_digest type version].freeze
12
- READY_LIMIT = 16_384
13
- option :mapping, required: true, desc: "Installed launch mapping ID"
14
- option :assignment, desc: "Managed assignment ID"
15
- option :definition, desc: "Retained final prepared assignment definition JSON file"
16
- option :prepared_bundle, desc: "Retained original prepared input Git bundle"
17
- option :step, desc: "Assignment subtree scope"
18
- option :base_head, desc: "Exact base commit SHA"
19
- option :mutation, desc: "Stable invocation ID for safe replay"
20
- option :dry_run, type: :boolean, default: false, desc: "Verify installed boundary without mutation or creation"
21
- def call(**options)
22
- driver = build_driver(options.fetch(:mapping))
23
- result = if options[:dry_run]
24
- driver.preflight
25
- else
26
- %i[assignment definition prepared_bundle step base_head].each do |key|
27
- raise ArgumentError, "Missing --#{key.to_s.tr('_', '-')}" unless options[key].is_a?(String) && !options[key].empty?
28
- end
29
- definition = retained_input(options[:definition], 32_768).force_encoding(Encoding::UTF_8)
30
- raise ArgumentError, "assignment definition is not UTF-8" unless definition.valid_encoding?
31
- bundle = retained_input(options[:prepared_bundle], Ace::Assign::Authority::CandidateTransfer::MAX_BYTES)
32
- args = {prepared_bundle: bundle, assignment_id: options[:assignment], definition_bytes: definition,
33
- scope: options[:step], base_head: options[:base_head]}
34
- args[:mutation_id] = options[:mutation] if options[:mutation]
35
- driver.launch(**args)
36
- end
37
- if !options[:dry_run] && result["phase"] == "issued"
38
- retaining_control = true
39
- announced = false
40
- driver.serve_control!(state: result) do |ready|
41
- raise ArgumentError, "original launch readiness was already announced" if announced
42
- line = readiness_line(ready, result, options)
43
- $stdout.write(line)
44
- $stdout.flush
45
- announced = true
46
- end
47
- raise ArgumentError, "original control ended before authenticated readiness" unless announced
48
- else
49
- puts JSON.generate(result)
50
- end
51
- rescue ArgumentError, SystemCallError, Ace::Runtime::Error, Ace::Assign::Error => error
52
- raise Ace::Support::Cli::Error, error.message
53
- ensure
54
- # Local foreground-loop cancellation is not a stopped/settled proof.
55
- driver.request_control_cancel if retaining_control
56
- end
57
- private
58
- def retained_input(path, limit)
59
- File.open(path, File::RDONLY | File::NOFOLLOW | File::NONBLOCK) do |file|
60
- before = file.stat
61
- unless before.file? && before.size.between?(1, limit)
62
- raise ArgumentError, "retained launch input is not a bounded regular file"
63
- end
64
- bytes = (file.read(limit + 1) || "").b
65
- after = file.stat
66
- identity = ->(stat) { [stat.dev, stat.ino, stat.size, stat.mtime, stat.ctime] }
67
- unless bytes.bytesize == before.size && bytes.bytesize <= limit && identity.call(before) == identity.call(after)
68
- raise ArgumentError, "retained launch input changed while reading"
69
- end
70
- bytes
71
- end
72
- end
73
-
74
- def readiness_line(ready, result, options)
75
- unless ready.is_a?(Hash) && ready.keys.all? { |key| key.is_a?(String) } && ready.keys.sort == READY_FIELDS &&
76
- ready["version"].is_a?(Integer) && ready["version"] == 1 && ready["type"] == "launch_ready" &&
77
- ready.values_at("mapping_id", "assignment_id", "attempt_id") ==
78
- [options.fetch(:mapping), options.fetch(:assignment), result.fetch("attempt_id")] &&
79
- ready["generation"].is_a?(Integer) && ready["generation"].positive? &&
80
- ready["journal_commit"].is_a?(String) && ready["journal_commit"].match?(/\A(?:[0-9a-f]{40}|[0-9a-f]{64})\z/) &&
81
- ready["original_binding_digest"].is_a?(String) && ready["original_binding_digest"].match?(/\A[0-9a-f]{64}\z/)
82
- raise ArgumentError, "original launch readiness is malformed or mismatched"
83
- end
84
- line = JSON.generate(ready) + "\n"
85
- raise ArgumentError, "original launch readiness is oversized" if line.bytesize > READY_LIMIT
86
- line
87
- end
88
-
89
- def build_driver(mapping)
90
- Ace::Assign::Authority::LaunchDriver.new(mapping_id: mapping)
91
- end
92
- end
93
- end
94
- end
95
- end
96
- end
97
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
- require_relative "../../../authority/server"
3
- require_relative "../../../authority/router"
4
- require_relative "../../../authority/launch_lifecycle"
5
- module Ace
6
- module Assign
7
- module CLI
8
- module Commands
9
- module Authority
10
- class Serve < Ace::Support::Cli::Command
11
- desc "Serve the installed protected assignment authority"
12
- option :authority, required: true, desc: "Installed authority ID"
13
- def call(**options)
14
- deployment = Ace::Assign::Authority::Deployment.load
15
- history = Ace::Assign::Authority::DeploymentHistory.load
16
- launch = Ace::Assign::Authority::LaunchLifecycle.new(deployment: deployment, deployment_history: history)
17
- router = Ace::Assign::Authority::Router.new(launch: launch)
18
- server = Ace::Assign::Authority::Server.new(authority_id: options.fetch(:authority), deployment: deployment, lifecycle: router)
19
- signals = %w[INT TERM].to_h { |signal| [signal, Signal.trap(signal) { server.request_stop }] }
20
- server.serve
21
- rescue ArgumentError, Ace::Runtime::Error, Ace::Assign::Error => error
22
- raise Ace::Support::Cli::Error, error.message
23
- ensure
24
- signals&.each { |signal, handler| Signal.trap(signal, handler) }
25
- end
26
- end
27
- end
28
- end
29
- end
30
- end
31
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
- require "json"
3
- require_relative "../../../authority/client"
4
- module Ace
5
- module Assign
6
- module CLI
7
- module Commands
8
- module Authority
9
- class Status < Ace::Support::Cli::Command
10
- desc "Inspect canonical protected launch ownership"
11
- option :mapping, required: true, desc: "Installed launch mapping ID"
12
- option :assignment, required: true, desc: "Assignment ID"
13
- option :attempt, required: true, desc: "Attempt ID"
14
- def call(**options)
15
- reply = Ace::Assign::Authority::Client.new(mapping_id: options.fetch(:mapping)).call("attempt_status",
16
- {"assignment_id" => options.fetch(:assignment), "attempt_id" => options.fetch(:attempt), "result_candidate_generation" => nil})
17
- puts JSON.generate(reply.data)
18
- rescue ArgumentError, Ace::Runtime::Error, Ace::Assign::Error => error
19
- raise Ace::Support::Cli::Error, error.message
20
- end
21
- end
22
- end
23
- end
24
- end
25
- end
26
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
- require_relative "../../../authority/prepared_task_context"
3
-
4
- module Ace
5
- module Assign
6
- module CLI
7
- module Commands
8
- module Authority
9
- class TaskContext < Ace::Support::Cli::Command
10
- desc "Return the captured original task context through the fixed selected entry"
11
- option :mapping, required: true, desc: "Installed original mapping ID"
12
- option :assignment, required: true, desc: "Original assignment ID@SCOPE"
13
- option :attempt, required: true, desc: "Original protected attempt ID"
14
- option :task, required: true, desc: "Captured canonical task ID"
15
-
16
- def call(**options)
17
- unless options.keys.sort == %i[assignment attempt mapping task]
18
- raise ArgumentError, "fixed task-context accepts only its four selectors"
19
- end
20
- consumer = Ace::Assign::Authority::PreparedTaskContext.new(context: @protected_assignment_context)
21
- $stdout.write(consumer.call(**options))
22
- 0
23
- rescue ArgumentError, Ace::Runtime::Error, Ace::Assign::Error => error
24
- raise Ace::Support::Cli::Error, error.message
25
- end
26
- end
27
- end
28
- end
29
- end
30
- end
31
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
- require_relative "../../../authority/prepared_task_context"
3
-
4
- module Ace
5
- module Assign
6
- module CLI
7
- module Commands
8
- module Authority
9
- class TaskContextPrincipal < Ace::Support::Cli::Command
10
- desc "Classify this actual caller through the installed and retained protected owners"
11
-
12
- def call(**options)
13
- raise ArgumentError, "fixed task-context-principal accepts no options" unless options.empty?
14
- consumer = Ace::Assign::Authority::PreparedTaskContext.new(context: @protected_assignment_context)
15
- $stdout.write(consumer.principal)
16
- 0
17
- rescue ArgumentError, Ace::Runtime::Error, Ace::Assign::Error => error
18
- raise Ace::Support::Cli::Error, error.message
19
- end
20
- end
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
- require_relative "../../../authority/prepared_task_context"
3
-
4
- module Ace
5
- module Assign
6
- module CLI
7
- module Commands
8
- module Authority
9
- class TaskContextSelection < Ace::Support::Cli::Command
10
- desc "Return the authenticated original task-context entry without captured text"
11
- option :mapping, required: true, desc: "Installed original mapping ID"
12
- option :assignment, required: true, desc: "Original assignment ID@SCOPE"
13
- option :attempt, required: true, desc: "Original protected attempt ID"
14
-
15
- def call(**options)
16
- unless options.keys.sort == %i[assignment attempt mapping]
17
- raise ArgumentError, "fixed task-context-selection accepts only its three selectors"
18
- end
19
- consumer = Ace::Assign::Authority::PreparedTaskContext.new(context: @protected_assignment_context)
20
- $stdout.write(consumer.selection(**options))
21
- 0
22
- rescue ArgumentError, Ace::Runtime::Error, Ace::Assign::Error => error
23
- raise Ace::Support::Cli::Error, error.message
24
- end
25
- end
26
- end
27
- end
28
- end
29
- end
30
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
- require "json"
3
- require_relative "../../../authority/launch_driver"
4
- module Ace
5
- module Assign
6
- module CLI
7
- module Commands
8
- module Authority
9
- class Terminate < Ace::Support::Cli::Command
10
- desc "Close the original gated native child and request proven abort"
11
- option :mapping, required: true, desc: "Installed launch mapping ID"
12
- option :assignment, required: true, desc: "Assignment ID"
13
- option :attempt, required: true, desc: "Attempt ID"
14
- def call(**options)
15
- client = Ace::Assign::Authority::Client.new(mapping_id: options.fetch(:mapping))
16
- state = client.call("inspect_launch", {"assignment_id" => options.fetch(:assignment), "attempt_id" => options.fetch(:attempt)}).data
17
- binding = state.fetch("process_binding") { raise ArgumentError, "no positively recorded original child; supervisor inspection required" }
18
- result = Ace::Assign::Authority::LaunchDriver.new(mapping_id: options.fetch(:mapping)).terminate(
19
- state: state, binding: binding, evidence: "protected exact-child termination requested")
20
- puts JSON.generate(result)
21
- rescue ArgumentError, Ace::Runtime::Error, Ace::Assign::Error => error
22
- raise Ace::Support::Cli::Error, error.message
23
- end
24
- end
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
- require_relative "../../../authority/prepared_worker"
3
-
4
- module Ace
5
- module Assign
6
- module CLI
7
- module Commands
8
- module Authority
9
- class Worker < Ace::Support::Cli::Command
10
- desc "Consume the original authenticated prepared subtree as its fixed worker"
11
-
12
- def call(**options)
13
- raise ArgumentError, "fixed worker accepts no options" unless options.empty?
14
- Ace::Assign::Authority::PreparedWorker.new.run
15
- rescue ArgumentError, Ace::Runtime::Error, Ace::Assign::Error => error
16
- raise Ace::Support::Cli::Error, error.message
17
- end
18
- end
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
- require_relative "protected_submission"
3
-
4
- module Ace
5
- module Assign
6
- module CLI
7
- module Commands
8
- class CampaignExportResult < Ace::Support::Cli::Command
9
- include Ace::Support::Cli::Base
10
- include ProtectedSubmission
11
- desc "Download a verified accepted campaign result through its original authority"
12
- option :mapping, desc: "Original installed parent mapping"
13
- option :assignment, desc: "Original parent assignment"
14
- option :attempt, desc: "Original parent attempt"
15
- option :head, desc: "Exact parent candidate head"
16
- option :candidate_generation, desc: "Exact parent candidate counter"
17
- option :output, desc: "Result byte file (created once, exact retries allowed)"
18
-
19
- def call(**options)
20
- usage = "campaign-export-result with original parent selectors and --output FILE"
21
- context = protected_context(options)
22
- raise AttemptErrors::EvidenceUnavailable, "Campaign export requires installed authority" unless context
23
- params = {"assignment_id" => require_option(options, :assignment, usage),
24
- "attempt_id" => require_option(options, :attempt, usage), "head" => require_option(options, :head, usage),
25
- "candidate_generation" => integer_option(options, :candidate_generation, usage, positive: true)}
26
- raise Ace::Support::Cli::Error, "Campaign head must be an exact SHA-1" unless params["head"].match?(/\A[0-9a-f]{40}\z/)
27
- context.verify_attempt_hints!(assignment_id: params.fetch("assignment_id"), attempt_id: params.fetch("attempt_id"))
28
- path = require_option(options, :output, usage)
29
- reply = context.client(options: options).call("campaign_export_result", params, download: true, purpose: :artifacts, timeout: 30)
30
- bytes = reply.parts&.first
31
- unless reply.parts&.size == 1 && bytes.is_a?(String) && bytes.bytesize <= 64 * 1024 &&
32
- reply.data.values_at("head", "candidate_generation", "bytes", "sha256") ==
33
- [params.fetch("head"), params.fetch("candidate_generation"), bytes.bytesize, Digest::SHA256.hexdigest(bytes)]
34
- raise AttemptErrors::EvidenceUnavailable, "Campaign result download differs"
35
- end
36
- begin
37
- File.open(path, File::WRONLY | File::CREAT | File::EXCL | File::NOFOLLOW, 0o600) do |file|
38
- file.write(bytes)
39
- file.flush
40
- file.fsync
41
- end
42
- rescue Errno::EEXIST
43
- raise Ace::Support::Cli::Error, "Existing campaign result file differs" unless input_bytes(path, limit: 64 * 1024) == bytes
44
- end
45
- emit_json(reply.data.except("transfer").merge("path" => path))
46
- rescue SecurityError, Ace::Runtime::RuntimeUnavailableError
47
- protected_transport_unavailable!
48
- rescue SystemCallError, IOError
49
- raise Ace::Support::Cli::Error, "Campaign result file or transport is unavailable"
50
- end
51
- end
52
- end
53
- end
54
- end
55
- end
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
- require_relative "protected_submission"
3
- require_relative "../../authority/campaign_round_transfer"
4
-
5
- module Ace
6
- module Assign
7
- module CLI
8
- module Commands
9
- class CampaignRecordRound < Ace::Support::Cli::Command
10
- include Ace::Support::Cli::Base
11
- include ProtectedSubmission
12
- desc "Consume settled protected campaign children into the original R1 store"
13
- option :mapping, desc: "Original installed parent mapping"
14
- option :assignment, desc: "Original parent assignment"
15
- option :attempt, desc: "Original parent attempt"
16
- option :head, desc: "Exact parent candidate head"
17
- option :candidate_generation, desc: "Exact parent candidate counter"
18
- option :mutation, desc: "Stable R1 round attempt ID"
19
- option :input, desc: "Closed bounded round envelope"
20
- option :artifact, type: :array, desc: "Ordered report byte files"
21
-
22
- def call(**options)
23
- usage = "campaign-record-round with original parent selectors, --mutation ID and --input FILE"
24
- context = protected_context(options)
25
- raise AttemptErrors::EvidenceUnavailable, "Campaign consumption requires installed authority" unless context
26
- params = {"assignment_id" => require_option(options, :assignment, usage),
27
- "attempt_id" => require_option(options, :attempt, usage), "head" => require_option(options, :head, usage),
28
- "candidate_generation" => integer_option(options, :candidate_generation, usage, positive: true)}
29
- unless params["head"].match?(/\A[0-9a-f]{40}\z/)
30
- raise Ace::Support::Cli::Error, "Campaign head must be an exact SHA-1"
31
- end
32
- context.verify_attempt_hints!(assignment_id: params.fetch("assignment_id"), attempt_id: params.fetch("attempt_id"))
33
- mutation = require_option(options, :mutation, usage)
34
- bytes = input_bytes(require_option(options, :input, usage), limit: 16 * 1024)
35
- files = options.fetch(:artifact, [])
36
- raise Ace::Support::Cli::Error, "Campaign artifacts require at most 16 files" unless files.is_a?(Array) && files.size <= 16
37
- parts = [bytes, *files.map { |path| input_bytes(path, limit: 64 * 1024, nonempty: false) }]
38
- params["input_sha256"] = Digest::SHA256.hexdigest(bytes)
39
- input = Struct.new(:parts) do
40
- def count = parts.length
41
- def bytes(index:) = parts.fetch(index)
42
- end.new(parts)
43
- admitted = Ace::Assign::Authority::CampaignRoundTransfer.decode(input: input, sha256: params.fetch("input_sha256"))
44
- unless admitted.fetch(:round).values_at("attempt_id", "head") == [mutation, params.fetch("head")]
45
- raise Ace::Support::Cli::Error, "Campaign input request ID or head differs"
46
- end
47
- emit_json(upload_call(context.client(options: options), "campaign_record_round", params, mutation, parts, :receipt_artifacts))
48
- rescue SecurityError, Ace::Runtime::RuntimeUnavailableError
49
- protected_transport_unavailable!
50
- rescue ArgumentError => error
51
- raise Ace::Support::Cli::Error, error.message
52
- end
53
- end
54
- end
55
- end
56
- end
57
- end
@@ -1,107 +0,0 @@
1
- # frozen_string_literal: true
2
- require "json"
3
- require_relative "../../authority/protected_assignment_context"
4
- require_relative "../../organisms/protected_delivery_coordinator"
5
-
6
- module Ace
7
- module Assign
8
- module CLI
9
- module Commands
10
- class Delivery < Ace::Support::Cli::Command
11
- desc "Execute or reconcile attempt-bound forge delivery"
12
- option :assignment, required: true, desc: "Managed assignment ID"
13
- option :attempt, required: true, desc: "Active delivery attempt ID"
14
- option :operation, required: true, desc: "create, update, ready, review, merge, status"
15
- option :parameters, desc: "JSON delivery parameters (otherwise assignment source delivery mapping)"
16
- option :title, desc: "PR title"
17
- option :body_file, desc: "PR description file"
18
- option :tests, desc: "JSON reference to coordinator-accepted test attempt and receipt digest"
19
- option :review, desc: "JSON reference to coordinator-accepted independent review attempt and receipt digest"
20
- option :service_request, desc: "Original authorized PR service request ID"
21
- option :mapping, desc: "Installed original protected mapping selector"
22
- option :scope, desc: "Original prepared worker scope"
23
- option :candidate_head, desc: "Exact original protected candidate SHA"
24
- option :candidate_generation, type: :integer, desc: "Exact original protected candidate generation"
25
- option :input_digest, desc: "Exact original accepted service input SHA256"
26
- option :target, desc: "Exact original target repository or PR URL"
27
- option :artifact_digest, desc: "Original target artifact SHA256, if present"
28
-
29
- def initialize(protected_context: nil)
30
- @protected_assignment_context = protected_context
31
- end
32
-
33
- def call(**options)
34
- context = @protected_assignment_context ||= Ace::Assign::Authority::ProtectedAssignmentContext.load
35
- protected = context.protected_participant? || options[:mapping] || context.mapping_hint?
36
- if protected
37
- unless context.protected_worker?
38
- raise AttemptErrors::EvidenceUnavailable, "protected delivery is worker-only"
39
- end
40
- unless %w[create update ready merge status].include?(options[:operation]) && %i[title body_file tests review].all? { |key| options[key].nil? }
41
- raise ArgumentError, "protected delivery accepts canonical PR results/status only; local input/evidence flags are unavailable"
42
- end
43
- input = context.resolve(options: options, assignment_id: options[:assignment], scope: options[:scope])
44
- raise AttemptErrors::EvidenceUnavailable, "original protected prepared input is unavailable" unless input
45
- result = Organisms::ProtectedDeliveryCoordinator.new(client: context.client(options: options),
46
- project_id: input.work.manifest.fetch("project_id")).perform(assignment_id: options[:assignment],
47
- attempt_id: options[:attempt], operation: options[:operation], service_request_id: options[:service_request],
48
- candidate_head: options[:candidate_head], candidate_generation: options[:candidate_generation],
49
- input_digest: options[:input_digest], target: {"resource" => options[:target], "artifact_digest" => options[:artifact_digest]},
50
- parameters: read_json(options[:parameters]))
51
- puts JSON.generate(json_value(result))
52
- return
53
- end
54
- if %i[mapping scope candidate_head candidate_generation input_digest target artifact_digest].any? { |key| options[key] }
55
- raise ArgumentError, "protected delivery selectors cannot select ordinary local delivery"
56
- end
57
- result = build_delivery.perform(assignment_id: options[:assignment], attempt_id: options[:attempt],
58
- operation: options[:operation], parameters: read_json(options[:parameters]),
59
- title: options[:title], body: options[:body_file] && File.read(options[:body_file]),
60
- tests: read_json(options[:tests]), review: read_json(options[:review]),
61
- service_request_id: options[:service_request])
62
- puts JSON.generate(json_value(result))
63
- rescue ArgumentError, Ace::Git::Error, Ace::Assign::Error, Ace::Runtime::RuntimeUnavailableError,
64
- JSON::ParserError, SecurityError, SystemCallError => e
65
- message = e.is_a?(JSON::ParserError) ? "protected installed selection unavailable" : e.message
66
- raise Ace::Support::Cli::Error, message
67
- end
68
-
69
- private
70
-
71
- def build_delivery
72
- Organisms::DeliveryCoordinator.new(repo_root: Ace::Support::Fs::Molecules::ProjectRootFinder.find_or_current)
73
- end
74
-
75
- def read_json(path)
76
- return nil unless path
77
- bytes = File.open(path, File::RDONLY | File::NOFOLLOW | File::NONBLOCK) do |file|
78
- before = file.stat
79
- raise ArgumentError, "Delivery input must be a bounded regular file" unless before.file? && before.size <= 65_536
80
- value = file.read(65_537)
81
- after = file.stat
82
- unless value && value.bytesize <= 65_536 &&
83
- [before.dev, before.ino, before.size, before.mtime, before.ctime] == [after.dev, after.ino, after.size, after.mtime, after.ctime]
84
- raise ArgumentError, "Delivery input changed or exceeds its bound"
85
- end
86
- value
87
- end
88
- data = JSON.parse(bytes, max_nesting: 16, create_additions: false,
89
- allow_duplicate_key: false, allow_comments: false, allow_nan: false)
90
- raise ArgumentError, "Delivery input must be a JSON object" unless data.is_a?(Hash)
91
- data
92
- rescue JSON::ParserError
93
- raise ArgumentError, "Delivery input must be valid JSON"
94
- end
95
-
96
- def json_value(value)
97
- case value
98
- when Hash then value.transform_values { |item| json_value(item) }
99
- when Array then value.map { |item| json_value(item) }
100
- else value.respond_to?(:to_h) ? json_value(value.to_h) : value
101
- end
102
- end
103
- end
104
- end
105
- end
106
- end
107
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
- require_relative "attempt/base"
3
-
4
- module Ace
5
- module Assign
6
- module CLI
7
- module Commands
8
- class InboxBind < Ace::Support::Cli::Command
9
- include Ace::Support::Cli::Base
10
- include Attempt::Base
11
-
12
- desc "Bind an original protected Inbox registration before scope closure"
13
- option :mapping, desc: "Exact installed mapping ID"
14
- option :assignment, desc: "Exact original assignment ID"
15
- option :attempt, desc: "Exact original attempt ID"
16
- option :event, desc: "Original Inbox event ID"
17
- option :inbox_context, desc: "Installed Inbox context ID"
18
- option :mutation, desc: "Stable original authority mutation ID"
19
- option :expected_generation, desc: "Original authority mutation generation"
20
-
21
- def call(**options)
22
- context = protected_context(options)
23
- raise AttemptErrors::EvidenceUnavailable, "Inbox binding requires installed protected authority" unless context
24
- usage = "inbox-bind --mapping MAP --assignment ID --attempt ID --event ID --inbox-context ID --mutation ID --expected-generation N"
25
- client, params, mutation = protected_attempt_request(context, options, usage)
26
- params.merge!("event_id" => require_option(options, :event, usage),
27
- "inbox_context_id" => require_option(options, :inbox_context, usage))
28
- emit_json(protected_call(client, "bind_inbox", params, mutation))
29
- end
30
- end
31
- end
32
- end
33
- end
34
- end
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
- require_relative "attempt/base"
3
- require "digest"
4
-
5
- module Ace
6
- module Assign
7
- module CLI
8
- module Commands
9
- # Local files provide bounded bytes; only the existing authority grants admission.
10
- module ProtectedSubmission
11
- include Attempt::Base
12
- private
13
-
14
- def require_option(options, key, usage)
15
- value = options[key].to_s.strip
16
- raise Ace::Support::Cli::Error, "Missing --#{key}: usage: ace-assign #{usage}" if value.empty?
17
- value
18
- end
19
-
20
- def submission_request(options, usage)
21
- context = protected_context(options)
22
- raise AttemptErrors::EvidenceUnavailable, "Submission requires installed protected authority" unless context
23
- protected_attempt_request(context, options, usage)
24
- end
25
-
26
- def input_bytes(path, limit:, nonempty: true)
27
- File.open(path, File::RDONLY | File::NOFOLLOW | File::NONBLOCK) do |file|
28
- before = file.stat
29
- raise Ace::Support::Cli::Error, "Input must be a bounded regular file" unless before.file? && before.size.between?(nonempty ? 1 : 0, limit)
30
- bytes = file.read(limit + 1)
31
- after = file.stat
32
- selected = File.lstat(path)
33
- identity = ->(stat) { [stat.dev, stat.ino, stat.size, stat.mtime, stat.ctime] }
34
- unless bytes.bytesize == before.size && identity.call(before) == identity.call(after) &&
35
- identity.call(after) == identity.call(selected) && !selected.symlink?
36
- raise Ace::Support::Cli::Error, "Input changed during bounded read"
37
- end
38
- bytes.freeze
39
- end
40
- rescue SystemCallError, IOError
41
- raise Ace::Support::Cli::Error, "Input file is unavailable"
42
- end
43
-
44
- def input_json(bytes)
45
- text = bytes.dup.force_encoding(Encoding::UTF_8)
46
- raise Ace::Support::Cli::Error, "Input JSON is malformed" unless text.valid_encoding?
47
- JSON.parse(text, create_additions: false, max_nesting: 32, allow_duplicate_key: false, allow_comments: false)
48
- rescue JSON::ParserError
49
- raise Ace::Support::Cli::Error, "Input JSON is malformed"
50
- end
51
-
52
- def upload_call(client, operation, params, mutation, parts, purpose)
53
- client.call(operation, params, mutation_id: mutation, upload_parts: parts, purpose: purpose, timeout: 30).data
54
- rescue SecurityError, Ace::Runtime::RuntimeUnavailableError, SystemCallError, IOError
55
- protected_transport_unavailable!
56
- end
57
- end
58
- end
59
- end
60
- end
61
- end
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "json"
4
-
5
- module Ace
6
- module Assign
7
- module CLI
8
- module Commands
9
- class Resume < Ace::Support::Cli::Command
10
- include Ace::Support::Cli::Base
11
- include AssignmentTarget
12
- desc "Recover accepted assignment state without launching another writer"
13
- option :assignment, desc: "Assignment ID"
14
- option :mapping, desc: "Installed protected launch mapping ID"
15
- option :attempt, desc: "Original protected attempt ID"
16
- option :dry_run, type: :boolean, default: false, desc: "Inspect recovery without writing observations"
17
-
18
- def call(**options)
19
- target = resolve_assignment_target(options)
20
- if target.prepared_input
21
- raise Ace::Support::Cli::Error, "protected resume only supports --dry-run" unless options[:dry_run]
22
- result = build_executor_for_target(target).status
23
- descriptor = target.prepared_input.descriptor
24
- puts JSON.generate(descriptor.slice("assignment_id", "mapping_id", "attempt_id", "scope", "definition_digest", "selection_sha256").merge(
25
- "state" => result.fetch(:state).assignment_state.to_s, "dry_run" => true))
26
- return
27
- end
28
- id = options[:assignment].to_s.strip
29
- raise Ace::Support::Cli::Error, "--assignment ID is required" if id.empty?
30
-
31
- puts JSON.generate(Organisms::AttemptCoordinator.new.resume(
32
- assignment_id: id, dry_run: options[:dry_run]))
33
- end
34
- end
35
- end
36
- end
37
- end
38
- end