ace-assign 0.42.4 → 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 -11
- 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 -16
- 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 -17
- 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 +201 -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 +47 -0
- 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 +231 -14
- 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/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 +208 -227
- 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 +223 -47
- data/lib/ace/assign/molecules/tmux_fork_runner.rb +191 -0
- data/lib/ace/assign/organisms/assignment_executor.rb +223 -24
- data/lib/ace/assign/version.rb +1 -1
- metadata +21 -5
- data/.ace-defaults/assign/catalog/steps/verify-e2e.step.yml +0 -42
|
@@ -12,15 +12,40 @@ module Ace
|
|
|
12
12
|
# start → advance → complete (with fail/add/retry branches)
|
|
13
13
|
class AssignmentExecutor
|
|
14
14
|
DEFAULT_DYNAMIC_STEP_INSTRUCTIONS = "Complete this step and finish with: ace-assign finish --message report.md".freeze
|
|
15
|
+
PROJECT_ROOT_SIGNAL = "project_root".freeze
|
|
16
|
+
CATALOG_SIGNAL = "catalog".freeze
|
|
15
17
|
|
|
16
18
|
attr_reader :assignment_manager, :queue_scanner, :step_writer, :step_renumberer, :skill_source_resolver
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
class << self
|
|
21
|
+
def clear_caches!
|
|
22
|
+
@cache_store = { step_catalog_cache: {} }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def cache_store
|
|
26
|
+
@cache_store ||= { step_catalog_cache: {} }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def cached_value(store_key, key)
|
|
32
|
+
cache_store[store_key][key]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def store_cached_value(store_key, key, value)
|
|
36
|
+
cache_store[store_key][key] = value
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def initialize(cache_base: nil, skill_source_resolver: nil, step_catalog: nil)
|
|
19
41
|
@assignment_manager = Molecules::AssignmentManager.new(cache_base: cache_base)
|
|
20
42
|
@queue_scanner = Molecules::QueueScanner.new
|
|
21
43
|
@step_writer = Molecules::StepWriter.new
|
|
22
|
-
@skill_source_resolver = Molecules::SkillAssignSourceResolver.new
|
|
44
|
+
@skill_source_resolver = skill_source_resolver || Molecules::SkillAssignSourceResolver.new
|
|
23
45
|
@step_catalog = nil
|
|
46
|
+
@step_catalog_from_fixture = step_catalog
|
|
47
|
+
@step_catalog_from_fixture_set = !step_catalog.nil?
|
|
48
|
+
@step_catalog_loaded = false
|
|
24
49
|
@step_renumberer = Molecules::StepRenumberer.new(
|
|
25
50
|
step_writer: @step_writer,
|
|
26
51
|
queue_scanner: @queue_scanner
|
|
@@ -573,7 +598,10 @@ module Ace
|
|
|
573
598
|
# @param sub_steps [Array<String>] Declared sub-step names
|
|
574
599
|
# @return [Hash] Parent step config for runtime queue
|
|
575
600
|
def build_split_parent_step(step:, parent_number:, parent_context:, sub_steps:)
|
|
576
|
-
source_skill = step["skill"]
|
|
601
|
+
source_skill = step["source_skill"] || step["skill"]
|
|
602
|
+
if (source_skill.nil? || source_skill.to_s.strip.empty?) && step["source"].to_s.start_with?("skill://")
|
|
603
|
+
source_skill = step["source"].to_s.delete_prefix("skill://").strip
|
|
604
|
+
end
|
|
577
605
|
original_text = normalize_instructions(step["instructions"]).strip
|
|
578
606
|
definition = find_step_definition("split-subtree-root") || {}
|
|
579
607
|
|
|
@@ -600,6 +628,7 @@ module Ace
|
|
|
600
628
|
)
|
|
601
629
|
parent_step.delete("sub_steps")
|
|
602
630
|
parent_step.delete("sub-steps")
|
|
631
|
+
parent_step.delete("source")
|
|
603
632
|
parent_step.delete("skill")
|
|
604
633
|
parent_step.delete("workflow")
|
|
605
634
|
parent_step["source_skill"] = source_skill if source_skill
|
|
@@ -712,6 +741,13 @@ module Ace
|
|
|
712
741
|
child["workflow"] = step_def["workflow"] if step_def["workflow"]
|
|
713
742
|
preserve_explicit_skill = (sub_steps_origin == "explicit")
|
|
714
743
|
child["skill"] = step_def["skill"] if step_def["skill"] && (preserve_explicit_skill || !step_def["workflow"])
|
|
744
|
+
child["source"] = if step_def["source"]
|
|
745
|
+
step_def["source"]
|
|
746
|
+
elsif step_def["workflow"]
|
|
747
|
+
step_def["workflow"]
|
|
748
|
+
elsif step_def["skill"]
|
|
749
|
+
"skill://#{step_def["skill"]}"
|
|
750
|
+
end
|
|
715
751
|
|
|
716
752
|
context_default = step_def.dig("context", "default")
|
|
717
753
|
child["context"] = context_default if context_default && !fork_context_value?(parent_context)
|
|
@@ -811,14 +847,9 @@ module Ace
|
|
|
811
847
|
when "pre-commit-review"
|
|
812
848
|
pre_commit_review_action_instructions(task_hint: task_hint)
|
|
813
849
|
when "verify-test"
|
|
814
|
-
"- Identify modified packages#{task_hint}.\n- For each modified package, run: cd <package> && ace-test --profile 6\n- If no package-level code changes are present, mark this step skipped with a clear reason."
|
|
850
|
+
"- Identify modified packages#{task_hint}.\n- For each modified package, run: cd <package> && ace-test all --profile 6\n- This subtree step verifies modified packages only; do not run the monorepo suite here.\n- If no package-level code changes are present, mark this step skipped with a clear reason."
|
|
815
851
|
when /\Arelease(?:-.+)?\z/
|
|
816
852
|
"- Release all modified packages and update both package and root changelogs.\n- Follow semantic versioning expectations for this step.\n- When auto-detecting packages, include `git diff origin/main...HEAD --name-only` in addition to working-tree state — prior steps may have already committed changes."
|
|
817
|
-
when "verify-e2e"
|
|
818
|
-
"- Check change scope: run `git diff origin/main --name-only` to list modified files.\n" \
|
|
819
|
-
"- **Skip criteria**: If ALL modified files match `*.md`, `*.yml` (non-CI config), `.ace-tasks/**`, or `.ace-retros/**`, skip E2E verification — mark step done with \"skipped: docs/task-spec only changes, no runnable code affected\".\n" \
|
|
820
|
-
"- Otherwise: detect modified packages, run E2E scenarios for each package with `test/e2e/` scenarios#{task_hint}.\n" \
|
|
821
|
-
"- If no modified package has E2E scenarios, mark step done with \"skipped: no E2E scenarios for modified packages\"."
|
|
822
853
|
else
|
|
823
854
|
"- Execute the #{sub_name} step."
|
|
824
855
|
end
|
|
@@ -861,6 +892,8 @@ module Ace
|
|
|
861
892
|
"instructions" => rendered_instructions,
|
|
862
893
|
"workflow" => rendering["workflow"]
|
|
863
894
|
)
|
|
895
|
+
resolved_source = resolved_step_source(step, rendering)
|
|
896
|
+
materialized["source"] = resolved_source if resolved_source && !resolved_source.empty?
|
|
864
897
|
unless split_child_without_explicit_fork?(step)
|
|
865
898
|
context_default = rendering.dig("context", "default")
|
|
866
899
|
materialized["context"] ||= context_default if context_default
|
|
@@ -879,6 +912,24 @@ module Ace
|
|
|
879
912
|
end
|
|
880
913
|
|
|
881
914
|
def resolve_step_rendering(step)
|
|
915
|
+
explicit_source = step["source"]&.to_s&.strip
|
|
916
|
+
if explicit_source && !explicit_source.empty?
|
|
917
|
+
canonical_step = find_step_definition_with_source_fallback(step, explicit_source: explicit_source)
|
|
918
|
+
if canonical_step && split_child_without_explicit_fork?(step)
|
|
919
|
+
canonical_step = canonical_step.dup
|
|
920
|
+
canonical_step.delete("context")
|
|
921
|
+
canonical_step.delete("fork")
|
|
922
|
+
end
|
|
923
|
+
source_skill = step["source_skill"]&.to_s&.strip
|
|
924
|
+
source_skill = canonical_step&.dig("source_skill") if source_skill.nil? || source_skill.empty?
|
|
925
|
+
rendering = skill_source_resolver.resolve_source_rendering(
|
|
926
|
+
explicit_source,
|
|
927
|
+
step_name: step["name"]&.to_s,
|
|
928
|
+
source_skill: source_skill
|
|
929
|
+
)
|
|
930
|
+
return canonical_step ? canonical_step.merge(rendering || {}) : rendering if rendering
|
|
931
|
+
end
|
|
932
|
+
|
|
882
933
|
explicit_workflow = step["workflow"]&.to_s&.strip
|
|
883
934
|
if explicit_workflow && !explicit_workflow.empty?
|
|
884
935
|
canonical_step = find_step_definition(step["name"]&.to_s)
|
|
@@ -1039,6 +1090,15 @@ module Ace
|
|
|
1039
1090
|
end
|
|
1040
1091
|
|
|
1041
1092
|
def resolve_step_assign_config(step)
|
|
1093
|
+
source_ref = step["source"]&.to_s&.strip
|
|
1094
|
+
if source_ref && !source_ref.empty?
|
|
1095
|
+
return skill_source_resolver.resolve_source_assign_config(
|
|
1096
|
+
source_ref,
|
|
1097
|
+
step_name: step["name"]&.to_s,
|
|
1098
|
+
source_skill: step["source_skill"]&.to_s
|
|
1099
|
+
)
|
|
1100
|
+
end
|
|
1101
|
+
|
|
1042
1102
|
explicit_workflow = step["workflow"]&.to_s&.strip
|
|
1043
1103
|
if explicit_workflow && !explicit_workflow.empty?
|
|
1044
1104
|
return skill_source_resolver.resolve_workflow_assign_config(
|
|
@@ -1119,28 +1179,118 @@ module Ace
|
|
|
1119
1179
|
Atoms::CatalogLoader.find_by_name(step_catalog, step_name)
|
|
1120
1180
|
end
|
|
1121
1181
|
|
|
1182
|
+
def find_step_definition_with_source_fallback(step, explicit_source:)
|
|
1183
|
+
step_name = step["name"]&.to_s
|
|
1184
|
+
canonical_step = find_step_definition(step_name)
|
|
1185
|
+
return canonical_step if canonical_step
|
|
1186
|
+
|
|
1187
|
+
source = explicit_source.to_s.strip
|
|
1188
|
+
return nil if source.empty?
|
|
1189
|
+
|
|
1190
|
+
source_skill = step["source_skill"]&.to_s&.strip
|
|
1191
|
+
source_skill = source.delete_prefix("skill://").strip if source_skill.to_s.empty? && source.start_with?("skill://")
|
|
1192
|
+
|
|
1193
|
+
step_catalog.find do |entry|
|
|
1194
|
+
next unless entry.is_a?(Hash)
|
|
1195
|
+
|
|
1196
|
+
entry_source = entry["source"]&.to_s&.strip
|
|
1197
|
+
entry_workflow = entry["workflow"]&.to_s&.strip
|
|
1198
|
+
entry_source_skill = entry["source_skill"]&.to_s&.strip
|
|
1199
|
+
entry_skill = entry["skill"]&.to_s&.strip
|
|
1200
|
+
|
|
1201
|
+
next true if entry_source == source || entry_workflow == source
|
|
1202
|
+
next true if !source_skill.to_s.empty? && (entry_source_skill == source_skill || entry_skill == source_skill)
|
|
1203
|
+
|
|
1204
|
+
false
|
|
1205
|
+
end
|
|
1206
|
+
end
|
|
1207
|
+
|
|
1122
1208
|
# Load step catalog from project override or gem defaults.
|
|
1123
1209
|
#
|
|
1124
1210
|
# @return [Array<Hash>] Loaded step definitions
|
|
1125
1211
|
def step_catalog
|
|
1126
|
-
@step_catalog
|
|
1127
|
-
project_root = Ace::Support::Fs::Molecules::ProjectRootFinder.find_or_current
|
|
1128
|
-
gem_root = Gem.loaded_specs["ace-assign"]&.gem_dir || File.expand_path("../../../..", __dir__)
|
|
1212
|
+
return @step_catalog if @step_catalog_loaded
|
|
1129
1213
|
|
|
1130
|
-
|
|
1131
|
-
|
|
1214
|
+
if @step_catalog_from_fixture_set
|
|
1215
|
+
@step_catalog_loaded = true
|
|
1216
|
+
@step_catalog = @step_catalog_from_fixture
|
|
1217
|
+
return @step_catalog
|
|
1218
|
+
end
|
|
1132
1219
|
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1220
|
+
cached = self.class.send(:cached_value, :step_catalog_cache, step_catalog_signature)
|
|
1221
|
+
return @step_catalog = cached if cached
|
|
1222
|
+
|
|
1223
|
+
@step_catalog_loaded = true
|
|
1224
|
+
@step_catalog = load_step_catalog
|
|
1225
|
+
self.class.send(:store_cached_value, :step_catalog_cache, step_catalog_signature, @step_catalog)
|
|
1226
|
+
@step_catalog
|
|
1227
|
+
end
|
|
1228
|
+
|
|
1229
|
+
def step_catalog_signature
|
|
1230
|
+
[
|
|
1231
|
+
PROJECT_ROOT_SIGNAL,
|
|
1232
|
+
project_catalog_signature,
|
|
1233
|
+
default_catalog_signature,
|
|
1234
|
+
step_catalog_cache_token,
|
|
1235
|
+
CATALOG_SIGNAL
|
|
1236
|
+
].join("|")
|
|
1237
|
+
end
|
|
1238
|
+
|
|
1239
|
+
def project_catalog_signature
|
|
1240
|
+
@project_catalog_signature ||= catalog_signature(File.join(project_root, ".ace", "assign", "catalog", "steps"))
|
|
1241
|
+
end
|
|
1242
|
+
|
|
1243
|
+
def default_catalog_signature
|
|
1244
|
+
@default_catalog_signature ||= catalog_signature(File.join(gem_root, ".ace-defaults", "assign", "catalog", "steps"))
|
|
1245
|
+
end
|
|
1246
|
+
|
|
1247
|
+
def load_step_catalog
|
|
1248
|
+
project_catalog = File.join(project_root, ".ace", "assign", "catalog", "steps")
|
|
1249
|
+
default_catalog = File.join(gem_root, ".ace-defaults", "assign", "catalog", "steps")
|
|
1250
|
+
|
|
1251
|
+
canonical_steps = @skill_source_resolver.assign_step_catalog
|
|
1252
|
+
default_steps = Atoms::CatalogLoader.load_all(default_catalog, canonical_steps: false)
|
|
1253
|
+
base_catalog = merge_step_catalog(default_steps, canonical_steps)
|
|
1254
|
+
|
|
1255
|
+
if File.directory?(project_catalog)
|
|
1256
|
+
project_steps = Atoms::CatalogLoader.load_all(project_catalog, canonical_steps: false)
|
|
1257
|
+
merge_step_catalog(base_catalog, project_steps)
|
|
1258
|
+
else
|
|
1259
|
+
base_catalog
|
|
1260
|
+
end
|
|
1261
|
+
end
|
|
1262
|
+
|
|
1263
|
+
def catalog_signature(catalog_dir)
|
|
1264
|
+
return "missing" unless File.directory?(catalog_dir)
|
|
1140
1265
|
|
|
1141
|
-
|
|
1142
|
-
|
|
1266
|
+
Dir.glob(File.join(catalog_dir, "*.step.yml")).sort.map do |path|
|
|
1267
|
+
"#{path}:#{file_signature(path)}"
|
|
1268
|
+
end.join("|")
|
|
1269
|
+
end
|
|
1270
|
+
|
|
1271
|
+
def file_signature(path)
|
|
1272
|
+
stat = File.stat(path)
|
|
1273
|
+
"#{stat.mtime.to_f}:#{stat.size}"
|
|
1274
|
+
rescue
|
|
1275
|
+
"missing"
|
|
1276
|
+
end
|
|
1277
|
+
|
|
1278
|
+
def step_catalog_cache_token
|
|
1279
|
+
token = if @skill_source_resolver.respond_to?(:cache_signature)
|
|
1280
|
+
@skill_source_resolver.cache_signature
|
|
1281
|
+
else
|
|
1282
|
+
"resolver:#{@skill_source_resolver.object_id}"
|
|
1143
1283
|
end
|
|
1284
|
+
|
|
1285
|
+
"resolver:#{token}"
|
|
1286
|
+
end
|
|
1287
|
+
|
|
1288
|
+
def project_root
|
|
1289
|
+
@project_root ||= Ace::Support::Fs::Molecules::ProjectRootFinder.find_or_current
|
|
1290
|
+
end
|
|
1291
|
+
|
|
1292
|
+
def gem_root
|
|
1293
|
+
@gem_root ||= Gem.loaded_specs["ace-assign"]&.gem_dir || File.expand_path("../../../..", __dir__)
|
|
1144
1294
|
end
|
|
1145
1295
|
|
|
1146
1296
|
# Merge default and project step catalogs by step name.
|
|
@@ -1178,6 +1328,11 @@ module Ace
|
|
|
1178
1328
|
|
|
1179
1329
|
merged = base.dup
|
|
1180
1330
|
override.each do |key, value|
|
|
1331
|
+
if runtime_binding_override_key?(key, base, override)
|
|
1332
|
+
merged[key] = base[key]
|
|
1333
|
+
next
|
|
1334
|
+
end
|
|
1335
|
+
|
|
1181
1336
|
merged[key] =
|
|
1182
1337
|
if merged[key].is_a?(Hash) && value.is_a?(Hash)
|
|
1183
1338
|
deep_merge_step_definition(merged[key], value)
|
|
@@ -1188,6 +1343,33 @@ module Ace
|
|
|
1188
1343
|
merged
|
|
1189
1344
|
end
|
|
1190
1345
|
|
|
1346
|
+
def runtime_binding_override_key?(key, base, override)
|
|
1347
|
+
return false unless %w[source workflow skill source_skill].include?(key)
|
|
1348
|
+
return false unless local_runtime_binding_present?(base)
|
|
1349
|
+
canonical_binding_present?(override)
|
|
1350
|
+
end
|
|
1351
|
+
|
|
1352
|
+
def local_runtime_binding_present?(entry)
|
|
1353
|
+
entry.is_a?(Hash) && (
|
|
1354
|
+
present_string?(entry["source"]) ||
|
|
1355
|
+
present_string?(entry["workflow"]) ||
|
|
1356
|
+
present_string?(entry["skill"])
|
|
1357
|
+
)
|
|
1358
|
+
end
|
|
1359
|
+
|
|
1360
|
+
def canonical_binding_present?(entry)
|
|
1361
|
+
entry.is_a?(Hash) && (
|
|
1362
|
+
present_string?(entry["source"]) ||
|
|
1363
|
+
present_string?(entry["workflow"]) ||
|
|
1364
|
+
present_string?(entry["skill"]) ||
|
|
1365
|
+
present_string?(entry["source_skill"])
|
|
1366
|
+
)
|
|
1367
|
+
end
|
|
1368
|
+
|
|
1369
|
+
def present_string?(value)
|
|
1370
|
+
value.is_a?(String) && !value.strip.empty?
|
|
1371
|
+
end
|
|
1372
|
+
|
|
1191
1373
|
# Archive source config into the task's jobs/ directory.
|
|
1192
1374
|
# If config is already in a jobs/ or steps/ directory, keeps it in place.
|
|
1193
1375
|
# Otherwise moves job.yaml to <task>/jobs/<assignment_id>-job.yml for provenance.
|
|
@@ -1281,10 +1463,27 @@ module Ace
|
|
|
1281
1463
|
def canonical_batch_insert_requested?(step_config)
|
|
1282
1464
|
raw_sub_steps = step_config["sub_steps"] || step_config["sub-steps"]
|
|
1283
1465
|
has_declared_sub_steps = raw_sub_steps.is_a?(Array) && raw_sub_steps.any?
|
|
1466
|
+
has_source = !step_config["source"].to_s.strip.empty?
|
|
1284
1467
|
has_workflow = !step_config["workflow"].to_s.strip.empty?
|
|
1285
1468
|
has_skill = !step_config["skill"].to_s.strip.empty?
|
|
1286
1469
|
|
|
1287
|
-
has_declared_sub_steps || has_workflow || has_skill
|
|
1470
|
+
has_declared_sub_steps || has_source || has_workflow || has_skill
|
|
1471
|
+
end
|
|
1472
|
+
|
|
1473
|
+
def resolved_step_source(step, rendering)
|
|
1474
|
+
explicit_source = step["source"]&.to_s&.strip
|
|
1475
|
+
return explicit_source unless explicit_source.nil? || explicit_source.empty?
|
|
1476
|
+
|
|
1477
|
+
rendered_source = rendering["source"]&.to_s&.strip
|
|
1478
|
+
return rendered_source unless rendered_source.nil? || rendered_source.empty?
|
|
1479
|
+
|
|
1480
|
+
workflow_source = rendering["workflow"]&.to_s&.strip
|
|
1481
|
+
return workflow_source unless workflow_source.nil? || workflow_source.empty?
|
|
1482
|
+
|
|
1483
|
+
skill_name = rendering["skill"]&.to_s&.strip
|
|
1484
|
+
return nil if skill_name.nil? || skill_name.empty?
|
|
1485
|
+
|
|
1486
|
+
"skill://#{skill_name}"
|
|
1288
1487
|
end
|
|
1289
1488
|
|
|
1290
1489
|
def insert_canonical_batch_step_tree(step_config, after:, as_child:, added_by:, location:)
|
data/lib/ace/assign/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ace-assign
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.53.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michal Czyz
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-04-
|
|
10
|
+
date: 2026-04-20 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: ace-support-cli
|
|
@@ -99,14 +99,14 @@ dependencies:
|
|
|
99
99
|
requirements:
|
|
100
100
|
- - "~>"
|
|
101
101
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: '0.
|
|
102
|
+
version: '0.34'
|
|
103
103
|
type: :runtime
|
|
104
104
|
prerelease: false
|
|
105
105
|
version_requirements: !ruby/object:Gem::Requirement
|
|
106
106
|
requirements:
|
|
107
107
|
- - "~>"
|
|
108
108
|
- !ruby/object:Gem::Version
|
|
109
|
-
version: '0.
|
|
109
|
+
version: '0.34'
|
|
110
110
|
- !ruby/object:Gem::Dependency
|
|
111
111
|
name: ace-task
|
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -206,7 +206,6 @@ files:
|
|
|
206
206
|
- ".ace-defaults/assign/catalog/steps/task-load.step.yml"
|
|
207
207
|
- ".ace-defaults/assign/catalog/steps/update-docs.step.yml"
|
|
208
208
|
- ".ace-defaults/assign/catalog/steps/update-pr-desc.step.yml"
|
|
209
|
-
- ".ace-defaults/assign/catalog/steps/verify-e2e.step.yml"
|
|
210
209
|
- ".ace-defaults/assign/catalog/steps/verify-test-suite.step.yml"
|
|
211
210
|
- ".ace-defaults/assign/catalog/steps/verify-test.step.yml"
|
|
212
211
|
- ".ace-defaults/assign/catalog/steps/work-on-task.step.yml"
|
|
@@ -221,6 +220,11 @@ files:
|
|
|
221
220
|
- CHANGELOG.md
|
|
222
221
|
- README.md
|
|
223
222
|
- Rakefile
|
|
223
|
+
- docs/demo/canonical-skill-source.gif
|
|
224
|
+
- docs/demo/canonical-skill-source.tape.yml
|
|
225
|
+
- docs/demo/fork-provider.cast
|
|
226
|
+
- docs/demo/fork-provider.gif
|
|
227
|
+
- docs/demo/fork-provider.recording.json
|
|
224
228
|
- docs/demo/fork-provider.tape.yml
|
|
225
229
|
- docs/exit-codes.md
|
|
226
230
|
- docs/getting-started.md
|
|
@@ -236,14 +240,23 @@ files:
|
|
|
236
240
|
- handbook/skills/as-assign-recover-fork/SKILL.md
|
|
237
241
|
- handbook/skills/as-assign-run-in-batches/SKILL.md
|
|
238
242
|
- handbook/skills/as-assign-start/SKILL.md
|
|
243
|
+
- handbook/skills/as-create-retro-internal/SKILL.md
|
|
244
|
+
- handbook/skills/as-mark-task-done-internal/SKILL.md
|
|
245
|
+
- handbook/skills/as-reflect-and-refactor-internal/SKILL.md
|
|
246
|
+
- handbook/skills/as-task-load-internal/SKILL.md
|
|
239
247
|
- handbook/workflow-instructions/assign/add-task.wf.md
|
|
240
248
|
- handbook/workflow-instructions/assign/compose.wf.md
|
|
249
|
+
- handbook/workflow-instructions/assign/create-retro-internal.wf.md
|
|
241
250
|
- handbook/workflow-instructions/assign/create.wf.md
|
|
242
251
|
- handbook/workflow-instructions/assign/drive.wf.md
|
|
252
|
+
- handbook/workflow-instructions/assign/mark-task-done-internal.wf.md
|
|
243
253
|
- handbook/workflow-instructions/assign/prepare.wf.md
|
|
244
254
|
- handbook/workflow-instructions/assign/recover-fork.wf.md
|
|
255
|
+
- handbook/workflow-instructions/assign/reflect-and-refactor-internal.wf.md
|
|
245
256
|
- handbook/workflow-instructions/assign/run-in-batches.wf.md
|
|
246
257
|
- handbook/workflow-instructions/assign/start.wf.md
|
|
258
|
+
- handbook/workflow-instructions/assign/task-load-internal.wf.md
|
|
259
|
+
- handbook/workflow-instructions/assign/verify-test-suite.wf.md
|
|
247
260
|
- lib/ace/assign.rb
|
|
248
261
|
- lib/ace/assign/atoms/assign_frontmatter_parser.rb
|
|
249
262
|
- lib/ace/assign/atoms/catalog_loader.rb
|
|
@@ -263,11 +276,13 @@ files:
|
|
|
263
276
|
- lib/ace/assign/cli/commands/fail.rb
|
|
264
277
|
- lib/ace/assign/cli/commands/finish.rb
|
|
265
278
|
- lib/ace/assign/cli/commands/fork_run.rb
|
|
279
|
+
- lib/ace/assign/cli/commands/fork_session.rb
|
|
266
280
|
- lib/ace/assign/cli/commands/list.rb
|
|
267
281
|
- lib/ace/assign/cli/commands/retry_cmd.rb
|
|
268
282
|
- lib/ace/assign/cli/commands/select.rb
|
|
269
283
|
- lib/ace/assign/cli/commands/start.rb
|
|
270
284
|
- lib/ace/assign/cli/commands/status.rb
|
|
285
|
+
- lib/ace/assign/cli/commands/step.rb
|
|
271
286
|
- lib/ace/assign/models/assignment.rb
|
|
272
287
|
- lib/ace/assign/models/assignment_info.rb
|
|
273
288
|
- lib/ace/assign/models/queue_state.rb
|
|
@@ -280,6 +295,7 @@ files:
|
|
|
280
295
|
- lib/ace/assign/molecules/skill_assign_source_resolver.rb
|
|
281
296
|
- lib/ace/assign/molecules/step_renumberer.rb
|
|
282
297
|
- lib/ace/assign/molecules/step_writer.rb
|
|
298
|
+
- lib/ace/assign/molecules/tmux_fork_runner.rb
|
|
283
299
|
- lib/ace/assign/organisms/assignment_executor.rb
|
|
284
300
|
- lib/ace/assign/organisms/task_assignment_creator.rb
|
|
285
301
|
- lib/ace/assign/version.rb
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
name: verify-e2e
|
|
2
|
-
skill: as-e2e-review
|
|
3
|
-
render: step_template
|
|
4
|
-
description: Review E2E coverage for modified packages and run targeted scenarios
|
|
5
|
-
|
|
6
|
-
prerequisites:
|
|
7
|
-
- name: work-on-task
|
|
8
|
-
strength: required
|
|
9
|
-
reason: "Must have implementation to test"
|
|
10
|
-
- name: verify-test-suite
|
|
11
|
-
strength: recommended
|
|
12
|
-
reason: "Unit tests should pass before running E2E"
|
|
13
|
-
|
|
14
|
-
produces: [e2e-results, coverage-matrix]
|
|
15
|
-
consumes: [code-changes]
|
|
16
|
-
|
|
17
|
-
context:
|
|
18
|
-
default: null
|
|
19
|
-
reason: "E2E review and execution run in the project environment"
|
|
20
|
-
|
|
21
|
-
when_to_skip:
|
|
22
|
-
- "No public CLI API changes (internal-only refactoring)"
|
|
23
|
-
- "Package has no E2E test scenarios and no coverage gaps identified"
|
|
24
|
-
- "All modified files are docs-only (*.md, *.yml non-config, task specs, retros)"
|
|
25
|
-
|
|
26
|
-
effort: medium
|
|
27
|
-
tags: [testing, e2e, verification]
|
|
28
|
-
|
|
29
|
-
steps:
|
|
30
|
-
- name: review-coverage
|
|
31
|
-
description: "Run /as-e2e-review for each heavily modified package to get coverage matrix"
|
|
32
|
-
tool: "ace-e2e-review <package>"
|
|
33
|
-
note: "Identify gaps, overlaps, and which TCs need updating before running"
|
|
34
|
-
|
|
35
|
-
- name: update-if-needed
|
|
36
|
-
description: "If coverage matrix shows gaps or stale TCs, update or create E2E tests"
|
|
37
|
-
conditional: "coverage-matrix shows gaps or outdated TCs"
|
|
38
|
-
|
|
39
|
-
- name: run-targeted
|
|
40
|
-
description: "Run ace-test-e2e-suite for each heavily modified package (not full suite)"
|
|
41
|
-
tool: "ace-test-e2e-suite <package>"
|
|
42
|
-
note: "Pass package name(s) of heavily modified packages only"
|