ace-assign 0.41.10 → 0.53.4
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/composition-rules.yml +2 -17
- data/.ace-defaults/assign/catalog/steps/create-pr.step.yml +0 -26
- data/.ace-defaults/assign/catalog/steps/create-retro.step.yml +1 -1
- data/.ace-defaults/assign/catalog/steps/mark-task-done.step.yml +1 -2
- data/.ace-defaults/assign/catalog/steps/onboard.step.yml +0 -17
- data/.ace-defaults/assign/catalog/steps/plan-task.step.yml +0 -15
- data/.ace-defaults/assign/catalog/steps/pre-commit-review.step.yml +3 -0
- data/.ace-defaults/assign/catalog/steps/reflect-and-refactor.step.yml +3 -2
- data/.ace-defaults/assign/catalog/steps/review-pr.step.yml +0 -20
- data/.ace-defaults/assign/catalog/steps/task-load.step.yml +1 -1
- data/.ace-defaults/assign/catalog/steps/verify-test-suite.step.yml +7 -34
- data/.ace-defaults/assign/catalog/steps/verify-test.step.yml +7 -4
- data/.ace-defaults/assign/catalog/steps/work-on-task.step.yml +0 -21
- data/.ace-defaults/assign/presets/fix-bug.yml +4 -3
- data/.ace-defaults/assign/presets/quick-implement.yml +1 -1
- data/.ace-defaults/assign/presets/work-on-task.yml +3 -16
- data/CHANGELOG.md +260 -0
- data/README.md +20 -43
- data/docs/demo/canonical-skill-source.gif +0 -0
- data/docs/demo/canonical-skill-source.tape.yml +51 -0
- data/docs/demo/fork-provider.cast +957 -0
- data/docs/demo/fork-provider.gif +0 -0
- data/docs/demo/fork-provider.recording.json +32 -0
- data/docs/demo/fork-provider.tape.yml +65 -20
- data/docs/getting-started.md +5 -2
- data/docs/usage.md +63 -3
- data/handbook/guides/fork-context.g.md +2 -2
- data/handbook/skills/as-assign-drive/SKILL.md +13 -1
- data/handbook/skills/as-create-retro-internal/SKILL.md +29 -0
- data/handbook/skills/as-mark-task-done-internal/SKILL.md +29 -0
- data/handbook/skills/as-reflect-and-refactor-internal/SKILL.md +30 -0
- data/handbook/skills/as-task-load-internal/SKILL.md +28 -0
- data/handbook/workflow-instructions/assign/compose.wf.md +3 -3
- data/handbook/workflow-instructions/assign/create-retro-internal.wf.md +11 -0
- data/handbook/workflow-instructions/assign/create.wf.md +6 -3
- data/handbook/workflow-instructions/assign/drive.wf.md +273 -15
- data/handbook/workflow-instructions/assign/mark-task-done-internal.wf.md +12 -0
- data/handbook/workflow-instructions/assign/prepare.wf.md +5 -5
- data/handbook/workflow-instructions/assign/reflect-and-refactor-internal.wf.md +14 -0
- data/handbook/workflow-instructions/assign/run-in-batches.wf.md +4 -1
- data/handbook/workflow-instructions/assign/start.wf.md +5 -2
- data/handbook/workflow-instructions/assign/task-load-internal.wf.md +12 -0
- data/handbook/workflow-instructions/assign/verify-test-suite.wf.md +36 -0
- data/lib/ace/assign/atoms/catalog_loader.rb +105 -2
- data/lib/ace/assign/atoms/preset_expander.rb +12 -1
- data/lib/ace/assign/atoms/step_file_parser.rb +15 -0
- data/lib/ace/assign/cli/commands/assignment_target.rb +53 -0
- data/lib/ace/assign/cli/commands/finish.rb +7 -4
- data/lib/ace/assign/cli/commands/fork_run.rb +4 -1
- data/lib/ace/assign/cli/commands/fork_session.rb +52 -0
- data/lib/ace/assign/cli/commands/start.rb +9 -3
- data/lib/ace/assign/cli/commands/status.rb +231 -226
- data/lib/ace/assign/cli/commands/step.rb +62 -0
- data/lib/ace/assign/cli.rb +8 -1
- data/lib/ace/assign/models/step.rb +4 -2
- data/lib/ace/assign/molecules/fork_session_launcher.rb +189 -8
- data/lib/ace/assign/molecules/queue_scanner.rb +1 -0
- data/lib/ace/assign/molecules/skill_assign_source_resolver.rb +252 -50
- data/lib/ace/assign/molecules/tmux_fork_runner.rb +191 -0
- data/lib/ace/assign/organisms/assignment_executor.rb +294 -40
- data/lib/ace/assign/version.rb +1 -1
- metadata +21 -5
- data/.ace-defaults/assign/catalog/steps/verify-e2e.step.yml +0 -42
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# task-load-internal
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Load task behavioral specification and dependency context for assignment execution.
|
|
6
|
+
|
|
7
|
+
## Steps
|
|
8
|
+
|
|
9
|
+
1. Run `ace-bundle task://<taskref>` for the target task reference.
|
|
10
|
+
2. If task dependencies are declared, run `ace-bundle task://<dep-ref>` for each dependency.
|
|
11
|
+
3. Review relevant dependency reports under `.ace-local/assign/` so the plan/work steps build on prior implementation.
|
|
12
|
+
4. Confirm context is loaded before proceeding.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
doc-type: workflow
|
|
3
|
+
title: Assign Verify Test Suite Workflow
|
|
4
|
+
purpose: Narrow deterministic verification contract for ace-assign orchestration
|
|
5
|
+
ace-docs:
|
|
6
|
+
last-updated: 2026-04-13
|
|
7
|
+
last-checked: 2026-04-13
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Verify Test Suite Workflow
|
|
11
|
+
|
|
12
|
+
## Purpose
|
|
13
|
+
|
|
14
|
+
Run the deterministic verification contract used by `ace-assign`:
|
|
15
|
+
- package-scoped `ace-test <package> all --profile 6` for modified packages
|
|
16
|
+
- monorepo verification with `ace-test-suite --target all`
|
|
17
|
+
|
|
18
|
+
Do not run `ace-test-e2e` or `ace-test-e2e-suite` from this workflow.
|
|
19
|
+
|
|
20
|
+
## Steps
|
|
21
|
+
|
|
22
|
+
1. Detect whether modified files affect runnable code.
|
|
23
|
+
2. If changes are docs-only or otherwise non-runnable, skip with a clear reason.
|
|
24
|
+
3. Detect modified packages from the current diff or working tree.
|
|
25
|
+
4. Run `ace-test <package> all --profile 6` for each modified package.
|
|
26
|
+
5. Run `ace-test-suite --target all`.
|
|
27
|
+
|
|
28
|
+
## Skip Guidance
|
|
29
|
+
|
|
30
|
+
Skip only when all modified files are documentation, retrospectives, task specs, or similarly non-runnable metadata.
|
|
31
|
+
|
|
32
|
+
## Success Criteria
|
|
33
|
+
|
|
34
|
+
- All modified packages pass `ace-test <package> all --profile 6`
|
|
35
|
+
- `ace-test-suite --target all` passes
|
|
36
|
+
- No E2E commands are used as part of this step
|
|
@@ -21,13 +21,19 @@ module Ace
|
|
|
21
21
|
# Load all step definitions from a catalog directory.
|
|
22
22
|
#
|
|
23
23
|
# @param steps_dir [String] Path to catalog/steps/ directory
|
|
24
|
+
# @param canonical_steps [Array<Hash>, Symbol, Boolean, nil] Canonical
|
|
25
|
+
# step metadata to merge. `:auto` (default) loads from skill sources.
|
|
26
|
+
# `false` disables canonical merge and returns raw YAML definitions.
|
|
24
27
|
# @return [Array<Hash>] Array of step definition hashes
|
|
25
|
-
def self.load_all(steps_dir)
|
|
28
|
+
def self.load_all(steps_dir, canonical_steps: :auto)
|
|
26
29
|
return [] unless File.directory?(steps_dir)
|
|
27
30
|
|
|
28
|
-
Dir.glob(File.join(steps_dir, "*.step.yml")).sort.filter_map do |path|
|
|
31
|
+
yaml_steps = Dir.glob(File.join(steps_dir, "*.step.yml")).sort.filter_map do |path|
|
|
29
32
|
parse_step_file(path)
|
|
30
33
|
end
|
|
34
|
+
return [] if yaml_steps.empty?
|
|
35
|
+
|
|
36
|
+
merge_step_catalog(yaml_steps, resolve_canonical_steps(canonical_steps))
|
|
31
37
|
end
|
|
32
38
|
|
|
33
39
|
# Find a step definition by name.
|
|
@@ -94,6 +100,103 @@ module Ace
|
|
|
94
100
|
warn "Warning: Failed to parse step file #{path}: #{e.message}"
|
|
95
101
|
nil
|
|
96
102
|
end
|
|
103
|
+
|
|
104
|
+
def self.resolve_canonical_steps(canonical_steps)
|
|
105
|
+
case canonical_steps
|
|
106
|
+
when false
|
|
107
|
+
[]
|
|
108
|
+
when :auto, nil
|
|
109
|
+
begin
|
|
110
|
+
require_relative "../molecules/skill_assign_source_resolver"
|
|
111
|
+
Molecules::SkillAssignSourceResolver.new.assign_step_catalog
|
|
112
|
+
rescue LoadError, StandardError
|
|
113
|
+
[]
|
|
114
|
+
end
|
|
115
|
+
when Array
|
|
116
|
+
canonical_steps
|
|
117
|
+
else
|
|
118
|
+
[]
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
private_class_method :resolve_canonical_steps
|
|
122
|
+
|
|
123
|
+
def self.merge_step_catalog(base_steps, override_steps)
|
|
124
|
+
index = {}
|
|
125
|
+
order = []
|
|
126
|
+
|
|
127
|
+
Array(base_steps).each do |step|
|
|
128
|
+
name = step["name"]
|
|
129
|
+
next if name.nil? || name.empty?
|
|
130
|
+
|
|
131
|
+
index[name] = step
|
|
132
|
+
order << name
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
Array(override_steps).each do |step|
|
|
136
|
+
name = step["name"]
|
|
137
|
+
next if name.nil? || name.empty?
|
|
138
|
+
|
|
139
|
+
order << name unless index.key?(name)
|
|
140
|
+
index[name] = deep_merge_step_definition(index[name], step)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
order.map { |name| index[name] }.compact
|
|
144
|
+
end
|
|
145
|
+
private_class_method :merge_step_catalog
|
|
146
|
+
|
|
147
|
+
def self.deep_merge_step_definition(base, override)
|
|
148
|
+
return override unless base.is_a?(Hash)
|
|
149
|
+
return base unless override.is_a?(Hash)
|
|
150
|
+
|
|
151
|
+
merged = base.dup
|
|
152
|
+
override.each do |key, value|
|
|
153
|
+
if runtime_binding_override_key?(key, base, override)
|
|
154
|
+
merged[key] = base[key]
|
|
155
|
+
next
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
merged[key] =
|
|
159
|
+
if merged[key].is_a?(Hash) && value.is_a?(Hash)
|
|
160
|
+
deep_merge_step_definition(merged[key], value)
|
|
161
|
+
else
|
|
162
|
+
value
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
merged
|
|
166
|
+
end
|
|
167
|
+
private_class_method :deep_merge_step_definition
|
|
168
|
+
|
|
169
|
+
def self.runtime_binding_override_key?(key, base, override)
|
|
170
|
+
return false unless %w[source workflow skill source_skill].include?(key)
|
|
171
|
+
return false unless local_runtime_binding_present?(base)
|
|
172
|
+
canonical_binding_present?(override)
|
|
173
|
+
end
|
|
174
|
+
private_class_method :runtime_binding_override_key?
|
|
175
|
+
|
|
176
|
+
def self.local_runtime_binding_present?(entry)
|
|
177
|
+
entry.is_a?(Hash) && (
|
|
178
|
+
present_string?(entry["source"]) ||
|
|
179
|
+
present_string?(entry["workflow"]) ||
|
|
180
|
+
present_string?(entry["skill"])
|
|
181
|
+
)
|
|
182
|
+
end
|
|
183
|
+
private_class_method :local_runtime_binding_present?
|
|
184
|
+
|
|
185
|
+
def self.canonical_binding_present?(entry)
|
|
186
|
+
entry.is_a?(Hash) && (
|
|
187
|
+
present_string?(entry["source"]) ||
|
|
188
|
+
present_string?(entry["workflow"]) ||
|
|
189
|
+
present_string?(entry["skill"]) ||
|
|
190
|
+
present_string?(entry["source_skill"])
|
|
191
|
+
)
|
|
192
|
+
end
|
|
193
|
+
private_class_method :canonical_binding_present?
|
|
194
|
+
|
|
195
|
+
def self.present_string?(value)
|
|
196
|
+
value.is_a?(String) && !value.strip.empty?
|
|
197
|
+
end
|
|
198
|
+
private_class_method :present_string?
|
|
199
|
+
|
|
97
200
|
private_class_method :parse_step_file
|
|
98
201
|
end
|
|
99
202
|
end
|
|
@@ -233,7 +233,8 @@ module Ace
|
|
|
233
233
|
# @param parameters [Hash] Parameter values
|
|
234
234
|
# @return [Hash] Step with substituted values
|
|
235
235
|
def self.substitute_parameters(step, parameters)
|
|
236
|
-
substitute_value(step, parameters)
|
|
236
|
+
substituted = substitute_value(step, parameters)
|
|
237
|
+
mark_preset_sub_steps_origin(substituted)
|
|
237
238
|
end
|
|
238
239
|
private_class_method :substitute_parameters
|
|
239
240
|
|
|
@@ -253,6 +254,16 @@ module Ace
|
|
|
253
254
|
end
|
|
254
255
|
private_class_method :substitute_value
|
|
255
256
|
|
|
257
|
+
def self.mark_preset_sub_steps_origin(step)
|
|
258
|
+
return step unless step.is_a?(Hash)
|
|
259
|
+
|
|
260
|
+
sub_steps = step["sub_steps"] || step["sub-steps"]
|
|
261
|
+
return step unless sub_steps.is_a?(Array) && sub_steps.any?
|
|
262
|
+
|
|
263
|
+
step.merge("sub_steps_origin" => "preset")
|
|
264
|
+
end
|
|
265
|
+
private_class_method :mark_preset_sub_steps_origin
|
|
266
|
+
|
|
256
267
|
# Substitute {{placeholder}} tokens in a string.
|
|
257
268
|
#
|
|
258
269
|
# @param text [String, nil] Text with placeholders
|
|
@@ -49,6 +49,7 @@ module Ace
|
|
|
49
49
|
{
|
|
50
50
|
name: fm["name"],
|
|
51
51
|
status: (fm["status"] || "pending").to_sym,
|
|
52
|
+
source: normalize_source(fm["source"], fm["workflow"], fm["skill"]),
|
|
52
53
|
skill: fm["skill"],
|
|
53
54
|
workflow: fm["workflow"],
|
|
54
55
|
context: context, # "fork" triggers Task tool execution
|
|
@@ -212,6 +213,20 @@ module Ace
|
|
|
212
213
|
parsed
|
|
213
214
|
end
|
|
214
215
|
private_class_method :parse_non_negative_integer
|
|
216
|
+
|
|
217
|
+
def self.normalize_source(source, workflow, skill)
|
|
218
|
+
source_ref = source.to_s.strip
|
|
219
|
+
return source_ref unless source_ref.empty?
|
|
220
|
+
|
|
221
|
+
workflow_ref = workflow.to_s.strip
|
|
222
|
+
return workflow_ref unless workflow_ref.empty?
|
|
223
|
+
|
|
224
|
+
skill_ref = skill.to_s.strip
|
|
225
|
+
return nil if skill_ref.empty?
|
|
226
|
+
|
|
227
|
+
"skill://#{skill_ref}"
|
|
228
|
+
end
|
|
229
|
+
private_class_method :normalize_source
|
|
215
230
|
end
|
|
216
231
|
end
|
|
217
232
|
end
|
|
@@ -11,6 +11,7 @@ module Ace
|
|
|
11
11
|
# - <assignment-id>@<step-number>
|
|
12
12
|
module AssignmentTarget
|
|
13
13
|
Target = Struct.new(:assignment_id, :scope, keyword_init: true)
|
|
14
|
+
View = Struct.new(:assignment, :state, :scoped_state, :current_step, :scope_root, keyword_init: true)
|
|
14
15
|
|
|
15
16
|
private
|
|
16
17
|
|
|
@@ -48,6 +49,58 @@ module Ace
|
|
|
48
49
|
executor.assignment_manager.define_singleton_method(:find_active) { assignment }
|
|
49
50
|
executor
|
|
50
51
|
end
|
|
52
|
+
|
|
53
|
+
def resolve_assignment_view(target)
|
|
54
|
+
executor = build_executor_for_target(target)
|
|
55
|
+
result = executor.status
|
|
56
|
+
state = result[:state]
|
|
57
|
+
scoped = scoped_status_view(state, target.scope)
|
|
58
|
+
|
|
59
|
+
View.new(
|
|
60
|
+
assignment: result[:assignment],
|
|
61
|
+
state: state,
|
|
62
|
+
scoped_state: scoped[:state],
|
|
63
|
+
current_step: scoped[:current],
|
|
64
|
+
scope_root: scoped[:root]
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def scoped_status_view(state, scope)
|
|
69
|
+
return {state: state, current: state.current || state.next_workable, root: nil} if scope.nil? || scope.strip.empty?
|
|
70
|
+
|
|
71
|
+
root = state.find_by_number(scope.strip)
|
|
72
|
+
raise StepErrors::NotFound, "Step #{scope} not found in queue" unless root
|
|
73
|
+
|
|
74
|
+
scoped_steps = state.subtree_steps(root.number)
|
|
75
|
+
scoped_state = Models::QueueState.new(steps: scoped_steps, assignment: state.assignment)
|
|
76
|
+
current = scoped_state.current || scoped_state.next_workable
|
|
77
|
+
|
|
78
|
+
{state: scoped_state, current: current, root: root.number}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def fork_scope_root(state, current_step)
|
|
82
|
+
return nil unless current_step
|
|
83
|
+
return current_step if current_step.fork?
|
|
84
|
+
|
|
85
|
+
state.nearest_fork_ancestor(current_step.number)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def scoped_fork_metadata_step(state, current_step, scope, scope_root)
|
|
89
|
+
return nil unless current_step
|
|
90
|
+
|
|
91
|
+
if scope && !scope.strip.empty?
|
|
92
|
+
return state.find_by_number(scope_root || scope.strip)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
fork_scope_root(state, current_step)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def effective_fork_provider_for(current_step, scoped_fork_step)
|
|
99
|
+
return nil unless current_step
|
|
100
|
+
|
|
101
|
+
provider = current_step.fork_provider || scoped_fork_step&.fork_provider
|
|
102
|
+
provider.to_s.strip.empty? ? nil : provider
|
|
103
|
+
end
|
|
51
104
|
end
|
|
52
105
|
end
|
|
53
106
|
end
|
|
@@ -43,11 +43,8 @@ module Ace
|
|
|
43
43
|
|
|
44
44
|
if result[:current]
|
|
45
45
|
puts "Advancing to step #{result[:current].number}: #{result[:current].name}"
|
|
46
|
-
puts
|
|
47
|
-
puts "Instructions:"
|
|
48
|
-
puts result[:current].instructions
|
|
46
|
+
puts "Next: ace-assign step#{step_target_suffix(result[:current].number, options[:assignment])}"
|
|
49
47
|
else
|
|
50
|
-
puts
|
|
51
48
|
fork_root = target.scope&.strip
|
|
52
49
|
if fork_root && result[:state].subtree_complete?(fork_root)
|
|
53
50
|
puts "Fork subtree #{fork_root} completed."
|
|
@@ -81,6 +78,12 @@ module Ace
|
|
|
81
78
|
rescue IOError, Errno::EBADF
|
|
82
79
|
nil
|
|
83
80
|
end
|
|
81
|
+
|
|
82
|
+
def step_target_suffix(step_number, assignment_target)
|
|
83
|
+
return " #{step_number}" if assignment_target.nil? || assignment_target.to_s.strip.empty?
|
|
84
|
+
|
|
85
|
+
%( #{step_number} --assignment "#{assignment_target}")
|
|
86
|
+
end
|
|
84
87
|
end
|
|
85
88
|
end
|
|
86
89
|
end
|
|
@@ -21,6 +21,7 @@ module Ace
|
|
|
21
21
|
option :provider, desc: "LLM provider:model override (e.g., codex:gpt-5, claude:sonnet)"
|
|
22
22
|
option :cli_args, desc: "Extra CLI args for provider process"
|
|
23
23
|
option :timeout, type: :integer, desc: "Execution timeout in seconds"
|
|
24
|
+
option :launch_mode, desc: "Launch mode: auto, headless, or tmux"
|
|
24
25
|
option :quiet, aliases: ["-q"], type: :boolean, default: false, desc: "Suppress non-essential output"
|
|
25
26
|
option :debug, aliases: ["-d"], type: :boolean, default: false, desc: "Show debug output"
|
|
26
27
|
|
|
@@ -51,6 +52,7 @@ module Ace
|
|
|
51
52
|
puts "Starting fork subtree execution: #{root_step.number} - #{root_step.name}"
|
|
52
53
|
puts "Assignment: #{assignment.id}"
|
|
53
54
|
puts "Provider: #{resolved_provider}"
|
|
55
|
+
puts "Launch mode: #{options[:launch_mode] || Molecules::ForkSessionLauncher::DEFAULT_LAUNCH_MODE}"
|
|
54
56
|
puts "Timeout: #{options[:timeout] || Ace::Assign.config.dig("execution", "timeout") || Molecules::ForkSessionLauncher::DEFAULT_TIMEOUT}s"
|
|
55
57
|
puts "Next step: #{next_step.number} - #{next_step.name}" if next_step
|
|
56
58
|
end
|
|
@@ -77,7 +79,8 @@ module Ace
|
|
|
77
79
|
provider: resolved_provider,
|
|
78
80
|
cli_args: options[:cli_args],
|
|
79
81
|
timeout: options[:timeout],
|
|
80
|
-
cache_dir: assignment.cache_dir
|
|
82
|
+
cache_dir: assignment.cache_dir,
|
|
83
|
+
launch_mode: options[:launch_mode]
|
|
81
84
|
)
|
|
82
85
|
record_fork_pid_info(root_step, launch_result)
|
|
83
86
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ace
|
|
4
|
+
module Assign
|
|
5
|
+
module CLI
|
|
6
|
+
module Commands
|
|
7
|
+
# Internal command used by tmux-backed fork panes to launch the provider session once.
|
|
8
|
+
class ForkSession < Ace::Support::Cli::Command
|
|
9
|
+
include Ace::Support::Cli::Base
|
|
10
|
+
|
|
11
|
+
desc "Run one provider-backed fork session for a subtree"
|
|
12
|
+
|
|
13
|
+
option :assignment, required: true, desc: "Target assignment ID"
|
|
14
|
+
option :root, required: true, desc: "Fork subtree root step number"
|
|
15
|
+
option :provider, desc: "LLM provider:model override (e.g., codex:gpt-5, claude:sonnet)"
|
|
16
|
+
option :cli_args, desc: "Extra CLI args for provider process"
|
|
17
|
+
option :timeout, type: :integer, desc: "Execution timeout in seconds"
|
|
18
|
+
option :cache_dir, desc: "Assignment cache directory override"
|
|
19
|
+
option :last_message_file, desc: "Explicit path for fork last-message capture"
|
|
20
|
+
option :session_meta_file, desc: "Explicit path for fork session metadata"
|
|
21
|
+
|
|
22
|
+
def initialize(launcher: nil)
|
|
23
|
+
super()
|
|
24
|
+
@launcher = launcher || Molecules::ForkSessionLauncher.new
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def call(**options)
|
|
28
|
+
launcher.launch_provider_session(
|
|
29
|
+
assignment_id: options[:assignment],
|
|
30
|
+
fork_root: options[:root],
|
|
31
|
+
provider: options[:provider],
|
|
32
|
+
cli_args: options[:cli_args],
|
|
33
|
+
timeout: options[:timeout],
|
|
34
|
+
cache_dir: options[:cache_dir],
|
|
35
|
+
last_message_file: options[:last_message_file],
|
|
36
|
+
session_meta_file: options[:session_meta_file]
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
0
|
|
40
|
+
rescue Error, Ace::LLM::Error => e
|
|
41
|
+
warn e.message
|
|
42
|
+
1
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
attr_reader :launcher
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -29,9 +29,15 @@ module Ace
|
|
|
29
29
|
|
|
30
30
|
started = result[:started]
|
|
31
31
|
puts "Step #{started.number} (#{started.name}) started"
|
|
32
|
-
puts
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
puts "Next: ace-assign step#{step_target_suffix(started.number, options[:assignment])}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def step_target_suffix(step_number, assignment_target)
|
|
38
|
+
return " #{step_number}" if assignment_target.nil? || assignment_target.to_s.strip.empty?
|
|
39
|
+
|
|
40
|
+
%( #{step_number} --assignment "#{assignment_target}")
|
|
35
41
|
end
|
|
36
42
|
end
|
|
37
43
|
end
|