ace-assign 0.42.4 → 0.55.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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/.ace-defaults/assign/catalog/composition-rules.yml +2 -17
  3. data/.ace-defaults/assign/catalog/steps/create-pr.step.yml +0 -26
  4. data/.ace-defaults/assign/catalog/steps/create-retro.step.yml +1 -1
  5. data/.ace-defaults/assign/catalog/steps/mark-task-done.step.yml +1 -2
  6. data/.ace-defaults/assign/catalog/steps/onboard.step.yml +0 -17
  7. data/.ace-defaults/assign/catalog/steps/plan-task.step.yml +0 -11
  8. data/.ace-defaults/assign/catalog/steps/pre-commit-review.step.yml +3 -0
  9. data/.ace-defaults/assign/catalog/steps/reflect-and-refactor.step.yml +3 -2
  10. data/.ace-defaults/assign/catalog/steps/review-pr.step.yml +0 -16
  11. data/.ace-defaults/assign/catalog/steps/split-subtree-root.step.yml +4 -2
  12. data/.ace-defaults/assign/catalog/steps/task-load.step.yml +1 -1
  13. data/.ace-defaults/assign/catalog/steps/verify-test-suite.step.yml +7 -34
  14. data/.ace-defaults/assign/catalog/steps/verify-test.step.yml +7 -4
  15. data/.ace-defaults/assign/catalog/steps/work-on-task.step.yml +0 -17
  16. data/.ace-defaults/assign/config.yml +1 -0
  17. data/.ace-defaults/assign/presets/fix-bug.yml +4 -3
  18. data/.ace-defaults/assign/presets/quick-implement.yml +1 -1
  19. data/.ace-defaults/assign/presets/work-on-task.yml +6 -16
  20. data/CHANGELOG.md +216 -0
  21. data/README.md +20 -43
  22. data/docs/demo/canonical-skill-source.gif +0 -0
  23. data/docs/demo/canonical-skill-source.tape.yml +51 -0
  24. data/docs/demo/fork-provider.cast +834 -0
  25. data/docs/demo/fork-provider.gif +0 -0
  26. data/docs/demo/fork-provider.recording.json +30 -0
  27. data/docs/demo/fork-provider.tape.yml +77 -20
  28. data/docs/getting-started.md +5 -2
  29. data/docs/usage.md +74 -4
  30. data/handbook/guides/fork-context.g.md +31 -7
  31. data/handbook/skills/as-assign-drive/SKILL.md +13 -1
  32. data/handbook/skills/as-create-retro-internal/SKILL.md +29 -0
  33. data/handbook/skills/as-mark-task-done-internal/SKILL.md +29 -0
  34. data/handbook/skills/as-reflect-and-refactor-internal/SKILL.md +30 -0
  35. data/handbook/skills/as-task-load-internal/SKILL.md +28 -0
  36. data/handbook/workflow-instructions/assign/compose.wf.md +3 -3
  37. data/handbook/workflow-instructions/assign/create-retro-internal.wf.md +11 -0
  38. data/handbook/workflow-instructions/assign/create.wf.md +6 -3
  39. data/handbook/workflow-instructions/assign/drive.wf.md +330 -40
  40. data/handbook/workflow-instructions/assign/mark-task-done-internal.wf.md +12 -0
  41. data/handbook/workflow-instructions/assign/prepare.wf.md +10 -5
  42. data/handbook/workflow-instructions/assign/reflect-and-refactor-internal.wf.md +14 -0
  43. data/handbook/workflow-instructions/assign/run-in-batches.wf.md +4 -1
  44. data/handbook/workflow-instructions/assign/start.wf.md +5 -2
  45. data/handbook/workflow-instructions/assign/task-load-internal.wf.md +12 -0
  46. data/handbook/workflow-instructions/assign/verify-test-suite.wf.md +36 -0
  47. data/lib/ace/assign/atoms/catalog_loader.rb +105 -2
  48. data/lib/ace/assign/atoms/preset_expander.rb +4 -0
  49. data/lib/ace/assign/atoms/step_file_parser.rb +15 -0
  50. data/lib/ace/assign/atoms/tree_formatter.rb +2 -2
  51. data/lib/ace/assign/cli/commands/add.rb +20 -11
  52. data/lib/ace/assign/cli/commands/assignment_target.rb +87 -3
  53. data/lib/ace/assign/cli/commands/create.rb +1 -1
  54. data/lib/ace/assign/cli/commands/fail.rb +1 -1
  55. data/lib/ace/assign/cli/commands/finish.rb +32 -8
  56. data/lib/ace/assign/cli/commands/fork_run.rb +58 -16
  57. data/lib/ace/assign/cli/commands/fork_session.rb +52 -0
  58. data/lib/ace/assign/cli/commands/list.rb +4 -3
  59. data/lib/ace/assign/cli/commands/retry_cmd.rb +1 -1
  60. data/lib/ace/assign/cli/commands/start.rb +9 -3
  61. data/lib/ace/assign/cli/commands/status.rb +237 -230
  62. data/lib/ace/assign/cli/commands/step.rb +62 -0
  63. data/lib/ace/assign/cli.rb +8 -1
  64. data/lib/ace/assign/models/assignment_info.rb +33 -4
  65. data/lib/ace/assign/models/queue_state.rb +101 -39
  66. data/lib/ace/assign/models/step.rb +17 -5
  67. data/lib/ace/assign/molecules/fork_session_launcher.rb +218 -21
  68. data/lib/ace/assign/molecules/queue_scanner.rb +1 -0
  69. data/lib/ace/assign/molecules/skill_assign_source_resolver.rb +223 -47
  70. data/lib/ace/assign/molecules/step_writer.rb +3 -3
  71. data/lib/ace/assign/molecules/tmux_control_surface_runner.rb +249 -0
  72. data/lib/ace/assign/organisms/assignment_executor.rb +355 -106
  73. data/lib/ace/assign/version.rb +1 -1
  74. data/lib/ace/assign.rb +1 -0
  75. metadata +35 -5
  76. data/.ace-defaults/assign/catalog/steps/verify-e2e.step.yml +0 -42
@@ -1,10 +1,19 @@
1
1
  ---
2
+ name: assign-drive
3
+ description: Drive an ace-assign assignment until completion or an explicit blocker.
4
+ allowed-tools:
5
+ - Bash(ace-assign:*)
6
+ - Bash(ace-bundle:*)
7
+ - Read
8
+ - Write
9
+ - AskUserQuestion
10
+ - Skill
2
11
  doc-type: workflow
3
12
  title: Drive Assignment Workflow
4
13
  purpose: workflow instruction for driving ace-assign assignment execution
5
14
  ace-docs:
6
- last-updated: 2026-03-18
7
- last-checked: 2026-03-21
15
+ last-updated: '2026-04-23'
16
+ last-checked: '2026-04-23'
8
17
  ---
9
18
 
10
19
  # Drive Assignment Workflow
@@ -16,7 +25,7 @@ Drive agent execution through an active assignment by continuously checking stat
16
25
  ## Prerequisites
17
26
 
18
27
  - An active assignment exists (created via `ace-assign create` or `/as-assign-create`)
19
- - Assignment has at least one pending or in_progress step
28
+ - Assignment has at least one pending or active step
20
29
 
21
30
  ## Assignment Context Propagation
22
31
 
@@ -27,11 +36,11 @@ When working with multiple concurrent assignments, the active assignment is reso
27
36
  3. `.latest` symlink (auto-updated on any activity)
28
37
  4. Scan all assignments (fallback)
29
38
 
30
- If this workflow is invoked with an argument (for example `/as-assign-drive abc123@010.01`), treat that value as the initial assignment target. If no argument is provided, resolve one active assignment and pin it for the entire loop.
39
+ If this workflow is invoked with an argument (for example `/as-assign-drive abc123@010.01`), treat that exact value as the initial assignment target. If no argument is provided, resolve one active assignment and pin it for the entire loop.
31
40
 
32
41
  ```bash
33
- # Set once from workflow argument (empty when not provided)
34
- ASSIGNMENT_TARGET="${1:-}"
42
+ # Set once from workflow argument or internal scoped default (empty when neither is provided)
43
+ ASSIGNMENT_TARGET="${1:-${ACE_ASSIGN_DEFAULT_TARGET:-}}"
35
44
 
36
45
  # Resolve and pin assignment identity for the full drive loop
37
46
  if [ -n "$ASSIGNMENT_TARGET" ]; then
@@ -50,6 +59,8 @@ if [ -z "$ASSIGNMENT_TARGET" ]; then
50
59
  fi
51
60
  ```
52
61
 
62
+ `ACE_ASSIGN_DEFAULT_TARGET` is an internal fallback passed by `fork-run` launches. If it is present, use it exactly. Do not widen it back to the parent assignment, do not drop the `@<root>` scope suffix, and do not re-resolve an unscoped assignment target later in the loop.
63
+
53
64
  ### Explicit Assignment Targeting (Recommended)
54
65
 
55
66
  Use explicit flags to propagate assignment context across subprocesses and tools:
@@ -72,7 +83,7 @@ ace-assign status --assignment abc123@010.01
72
83
  ace-assign finish --message done.md --assignment abc123@010.01
73
84
  ```
74
85
 
75
- When an assignment target includes scope (`<id>@<root>`), `ace-assign finish --message` advances only within that subtree and stops when the subtree is complete.
86
+ When an assignment target includes scope (`<id>@<root>`), `ace-assign finish --message` finishes only within that subtree. Pending descendants remain pending until scoped execution explicitly starts them.
76
87
 
77
88
  Helper command:
78
89
 
@@ -104,12 +115,57 @@ ace-assign finish --message done.md --assignment <id>
104
115
 
105
116
  Repeat the following cycle until all steps are done or failed:
106
117
 
118
+ ### Run-Until-Blocked Contract
119
+
120
+ Once `ASSIGNMENT_TARGET` is pinned, keep driving that exact target until exactly one of these stop conditions is true:
121
+
122
+ 1. `ace-assign status --assignment "$ASSIGNMENT_TARGET"` shows all steps complete
123
+ 2. A workflow step explicitly requires HITL or other user judgment before execution can continue
124
+ 3. A step reaches an unrecoverable failure path and this workflow instructs you to stop
125
+ 4. The user explicitly interrupts or cancels execution
126
+
127
+ Do **not** stop merely because you have useful progress to report.
128
+
129
+ - Intermediate progress belongs in short progress updates, not in a final completion response.
130
+ - `pending` steps with no active step are not a stop condition. They mean the queue must be advanced and the loop must continue.
131
+ - A batch child subtree finishing is not a completion boundary for the parent assignment.
132
+ - If `ASSIGNMENT_TARGET` includes `@<root>`, that scoped subtree is the entire execution boundary.
133
+ - A paused assignment with remaining runnable work is not "done"; treat it as a recoverable scheduler state and resume the loop.
134
+
135
+ ### Final Response Gate
136
+
137
+ Before sending any final user-facing completion response, re-run:
138
+
139
+ ```bash
140
+ ace-assign status --assignment "$ASSIGNMENT_TARGET" --format json
141
+ ```
142
+
143
+ You may only stop and send a final response when one of these is true:
144
+
145
+ - the pinned assignment has no remaining runnable `pending` or `active` work
146
+ - the workflow recorded an explicit blocker or unrecoverable failure stop condition
147
+ - the user explicitly interrupted or canceled execution
148
+
149
+ Do **not** send a final response merely because:
150
+
151
+ - one child subtree completed in the parent assignment
152
+ - useful progress was made
153
+ - a prior terminal session ended
154
+ - the parent assignment surfaced the next pending step after active work finished
155
+
156
+ Concrete example:
157
+
158
+ - `010.01 done` and `010.02.01 active` means continue driving the assignment. It is not a completion boundary.
159
+ - `040.01 done`, `040.02 done`, and `040.03 done` under `/as-assign-drive <id>@040` means stop the scoped worker. Do not inspect parent step `070` from that scoped process.
160
+
107
161
  ### Step Execution Policy
108
162
 
109
163
  - Planned steps are mandatory work items. Do not skip them by judgment.
110
164
  - For each active step, do exactly one of:
165
+
111
166
  1. Execute the step and report completion with `ace-assign finish --message`
112
167
  2. Attempt execution, capture blocker evidence, and mark failed with `ace-assign fail`
168
+
113
169
  - Never use report text to "skip" or synthesize completion for planned steps.
114
170
  - **Fork-delegation constraint**: If the active step has `FORK: yes`, the driver MUST delegate via `ace-assign fork-run`. The driver MUST NOT execute fork-marked steps inline, absorb remaining fork children after partial failure, or inject retry steps as top-level siblings. All fork recovery goes through re-fork (see [Fork-Run Crash Recovery](#fork-run-crash-recovery-partial-completion)).
115
171
  - **Conditional release in review subtrees**: A `release` step inside a review cycle (e.g., `[review-pr, apply-feedback, release]`) MUST skip the version bump when prior sibling steps produced no code changes. If `apply-feedback` reported no findings or `git diff HEAD~1 --stat` shows only report files, mark release done with "no-op: no changes to release" instead of bumping.
@@ -119,30 +175,40 @@ Repeat the following cycle until all steps are done or failed:
119
175
  After completing or failing each step, evaluate whether the assignment needs adaptation:
120
176
 
121
177
  - **Test failures detected** → Consider adding a fix-tests step:
178
+
122
179
  ```bash
123
180
  ace-assign add "fix-tests" --instructions "Fix failing tests identified in step NNN" --assignment "$ASSIGNMENT_TARGET"
124
181
  ```
125
182
 
183
+ - **E2E failures detected** (`ace-test-e2e` command, `.ace-local/test-e2e/` evidence, or explicit failing scenario IDs) → add an E2E-specific fix step instead of generic `fix-tests` / `fix-issue`:
184
+
185
+ ```bash
186
+ ace-assign add "fix-e2e" --instructions "Use /as-e2e-fix for the failing package and scenario IDs from the recorded evidence. If analysis is missing, let /as-e2e-fix generate it via wfi://e2e/analyze-failures before applying fixes. Re-run the targeted failing scenarios before completing this step." --assignment "$ASSIGNMENT_TARGET"
187
+ ```
188
+
126
189
  - **Review found critical issues** → Consider adding an apply-critical-fixes step:
190
+
127
191
  ```bash
128
192
  ace-assign add "apply-critical-fixes" --instructions "Address critical review findings before proceeding" --assignment "$ASSIGNMENT_TARGET"
129
193
  ```
130
194
 
131
195
  - **Missing prerequisite discovered** → Consider adding the prerequisite step:
196
+
132
197
  ```bash
133
198
  ace-assign add "missing-prereq" --instructions "Complete prerequisite work discovered during step NNN" --assignment "$ASSIGNMENT_TARGET"
134
199
  ```
135
200
 
136
- - **Metadata hint**: Step file contains `trigger_on_failure` if the step failed, inject the referenced step type
201
+ - **Metadata hint**: Step file contains `trigger_on_failure` -- if the step failed, inject the referenced step type
137
202
 
138
203
  Use `decision_notes` from step metadata (if present) as additional guidance for these assessments.
139
204
 
140
205
  - **Review-cycle circuit breaker**: When a review fork subtree fails due to provider unavailability (not code bugs), evaluate whether to attempt the next review cycle:
206
+
141
207
  - If the **first** review cycle (valid) failed on providers: skip remaining cycles (fit, shine). Mark them done with "skipped: provider unavailable for prior cycle" reports.
142
208
  - If the **second** cycle (fit) failed after valid succeeded: skip shine. Valid already captured correctness issues.
143
209
  - **Never retry a provider-failed review cycle more than once.** If the re-fork also fails on providers, mark the cycle done-with-skip and move on.
144
210
 
145
- - **Transient network failure retry**: When a fork subtree fails due to a transient network error (connection reset, DNS timeout, socket hangup) as opposed to provider unavailability or auth failure wait 30 seconds and re-fork once. If the re-fork also fails on a network error, treat it as a hard failure and apply the circuit breaker rules above. Auth errors (401/403) and not-found errors (404) are never transient fail immediately on those.
211
+ - **Transient network failure retry**: When a fork subtree fails due to a transient network error (connection reset, DNS timeout, socket hangup) -- as opposed to provider unavailability or auth failure -- wait 30 seconds and re-fork once. If the re-fork also fails on a network error, treat it as a hard failure and apply the circuit breaker rules above. Auth errors (401/403) and not-found errors (404) are never transient -- fail immediately on those.
146
212
 
147
213
  ### 1. Check Status
148
214
 
@@ -152,14 +218,20 @@ echo "$STATUS_OUTPUT"
152
218
  ```
153
219
 
154
220
  Read the output to identify:
221
+
155
222
  - Assignment ID (must remain equal to pinned `ASSIGNMENT_ID`)
156
- - Current step number, name, and status
157
- - Current step's instructions
158
- - Current step's skill reference (if any)
159
- - Remaining steps in the queue
223
+ - Active step list in scope, or next step when nothing is active
224
+ - Remaining visible steps in the queue preview
225
+ - Hidden-step counts for large queues
160
226
 
161
227
  **Note:** `ace-assign status` is the source of truth for assignment state. The step files in the `steps/` directory are the backing store, but always query status via the command for accurate information.
162
228
 
229
+ Load instructions separately when needed:
230
+
231
+ ```bash
232
+ ace-assign step --assignment "$ASSIGNMENT_TARGET"
233
+ ```
234
+
163
235
  ### 2. Auto-Delegate Fork Subtrees (When Applicable)
164
236
 
165
237
  Before executing the current step inline, check whether the active step is inside a fork-enabled subtree.
@@ -184,6 +256,20 @@ fi
184
256
 
185
257
  This prevents fork agents from stalling on pre-existing unrelated changes. Assignment metadata files (`.ace-local/`, `.ace-tasks/`, `.ace-retros/`) are expected to be dirty during drive execution and are excluded.
186
258
 
259
+ Dirty-tree classification rule:
260
+
261
+ - Do not auto-commit every unrelated dirty path.
262
+ - First classify the dirty state:
263
+ - **intentional work**: user edits, task implementation, or other meaningful repo changes that must be preserved
264
+ - **generated side effects**: bootstrap/config scaffolding, handbook projection output, caches, or other machine-generated files outside the current task scope
265
+ - If the dirty paths are intentional work, preserve them and either continue with scope awareness or commit them deliberately.
266
+ - If the dirty paths are generated side effects outside the current task scope, clean/reset them before `fork-run` instead of committing them.
267
+ - Only stop for user input when classification remains ambiguous after inspection.
268
+
269
+ Example:
270
+
271
+ - A bulk untracked `.ace/...` tree created by `ace-config sync` is generated side-effect output. Clean it; do not create a "pre-fork" commit for it.
272
+
187
273
  #### Delegation Rule
188
274
 
189
275
  **FORK SIGNAL**: If a step row shows `yes` in the `FORK` column, the step itself has `context: fork` and MUST be delegated via `fork-run`.
@@ -194,6 +280,7 @@ This prevents fork agents from stalling on pre-existing unrelated changes. Assig
194
280
  | `FORK: ` (empty) | Step is not fork-enabled | Execute inline (or inspect fork-enabled children if batch parent) |
195
281
 
196
282
  **Example status output:**
283
+
197
284
  ```
198
285
  NUMBER STATUS NAME FORK CHILDREN
199
286
  ------------------------------------------------------------------------------
@@ -202,6 +289,7 @@ NUMBER STATUS NAME FORK CHILDREN
202
289
  ```
203
290
 
204
291
  Step 020 shows `FORK: yes` → run:
292
+
205
293
  ```bash
206
294
  ace-assign fork-run --assignment <id>@020
207
295
  ```
@@ -209,8 +297,15 @@ ace-assign fork-run --assignment <id>@020
209
297
  **Delegation boundary rule**
210
298
 
211
299
  - Outside a delegated fork scope, do NOT execute fork steps inline.
212
- - If status output is already scoped to `Current Step: <root>.*` via `--assignment <id>@<root>`, the fork boundary is already entered: continue inline and never call `fork-run` again for the same `<root>`.
213
- - If the current step is a top-level step with `FORK: yes` and no matching scope is active, delegate immediately.
300
+ - First decide whether the fork boundary is already entered before issuing `fork-run`.
301
+ - If scoped status for `--assignment <id>@<root>` already resolves work inside `<root>`, the fork boundary is already entered: continue inline and never call `fork-run` again for the same `<root>`.
302
+ - A same-root call to `ace-assign fork-run --assignment <id>@<root>` from inside that exact scoped subtree is invalid and must be treated as an error, not a retry path.
303
+ - If the scoped root itself is active and no child in that subtree is active yet, run:
304
+
305
+ - `ace-assign start --assignment "$ASSIGNMENT_TARGET"`
306
+
307
+ - After a child step inside the scoped subtree becomes active, continue executing that child inline within the same scope.
308
+ - Only if the current step is a top-level step with `FORK: yes` and no matching scope is already active should the driver delegate via `fork-run`.
214
309
 
215
310
  #### Nested Batch Containers (Container → Fork Children)
216
311
 
@@ -222,10 +317,12 @@ A batch container (e.g., `010`) may have children but no fork context itself (`F
222
317
  - `fork_retry_limit: <N>` (default `1`)
223
318
 
224
319
  **How to distinguish:**
320
+
225
321
  - **Direct fork target**: `FORK: yes` on the current step → fork-run the current step.
226
322
  - **Batch container**: `FORK: ` on parent, but children include `FORK: yes` steps.
227
323
 
228
324
  **Pattern for batch containers:**
325
+
229
326
  ```bash
230
327
  # Read scheduler metadata from parent step 010
231
328
  # parallel=false => sequential, still fork every child
@@ -236,9 +333,11 @@ A batch container (e.g., `010`) may have children but no fork context itself (`F
236
333
 
237
334
  - Iterate pending child steps in number order.
238
335
  - For each child with `FORK: yes`, run:
336
+
239
337
  - `ace-assign fork-run --assignment <id>@<child>`
338
+
240
339
  - Re-check status after each child.
241
- - Do not pause for user input between children treat the batch loop as a single unit (see Batch Continuation Rule below).
340
+ - Do not pause for user input between children -- treat the batch loop as a single unit (see Batch Continuation Rule below).
242
341
 
243
342
  **Parallel mode (`parallel: true`)**
244
343
 
@@ -262,12 +361,19 @@ The driver MUST NOT pause for user input between child fork-runs within a batch
262
361
 
263
362
  1. Verify the child's reports (see Subtree Guard below).
264
363
  2. If reports indicate successful completion, immediately launch the next pending child.
265
- 3. Treat the entire batch loop as a single unit of execution only pause on quality concerns flagged during report review.
364
+ 3. Treat the entire batch loop as a single unit of execution -- only pause on quality concerns flagged during report review.
266
365
  4. For timeout-constrained environments: launch `fork-run` in background, poll for completion, then loop to the next child without pausing.
267
366
 
367
+ Conversational boundary rule:
368
+
369
+ - Do not end the turn or emit a final user-facing completion summary after a child subtree unless the entire assignment now satisfies a real stop condition from [Run-Until-Blocked Contract](#run-until-blocked-contract).
370
+ - If child `010.01` completes, reports are clean, and `010.02` is still pending, immediately advance/resume the queue and continue driving.
371
+ - Treat child-subtree completion as progress within the same drive session, not as permission to stop.
372
+
268
373
  **Failure policy (retry-then-stop)**
269
374
 
270
375
  - On any child failure:
376
+
271
377
  - Pause launching new children immediately.
272
378
  - Wait for in-flight children to finish.
273
379
  - Retry failed child once (`fork_retry_limit=1` default).
@@ -279,11 +385,29 @@ The driver MUST NOT pause for user input between child fork-runs within a batch
279
385
  ```bash
280
386
  STATUS_JSON=$(ace-assign status --assignment "$ASSIGNMENT_TARGET" --format json)
281
387
  ASSIGNMENT_ID=$(echo "$STATUS_JSON" | ruby -rjson -e 'puts JSON.parse(STDIN.read).dig("assignment", "id")')
388
+ SCOPED_ROOT="${ASSIGNMENT_TARGET#*@}"
389
+ ACTIVE_STEP=$(echo "$STATUS_JSON" | ruby -rjson -e '
390
+ scoped_root = ARGV[0].to_s
391
+ active = Array(JSON.parse(STDIN.read)["active_steps"])
392
+ child = active.find { |step| step["number"] != scoped_root && step["number"].start_with?("#{scoped_root}.") }
393
+ puts(child ? child["number"] : active.first&.fetch("number", nil))
394
+ ' "$SCOPED_ROOT")
395
+
396
+ if [ -n "$SCOPED_ROOT" ] && [ "$ACTIVE_STEP" = "$SCOPED_ROOT" ]; then
397
+ ace-assign start --assignment "$ASSIGNMENT_TARGET"
398
+ continue
399
+ fi
400
+
282
401
  FORK_ROOT=$(echo "$STATUS_JSON" | ruby -rjson -e '
283
- p = JSON.parse(STDIN.read)["current_step"]
284
- puts p["number"] if p && p["context"] == "fork"
285
- ')
286
- if [ -n "$FORK_ROOT" ]; then
402
+ scoped_root = ARGV[0].to_s
403
+ active = Array(JSON.parse(STDIN.read)["active_steps"])
404
+ next_fork = active.find do |step|
405
+ step["context"] == "fork" &&
406
+ (scoped_root.empty? || (step["number"] != scoped_root && !step["number"].start_with?("#{scoped_root}.")))
407
+ end
408
+ puts next_fork["number"] if next_fork
409
+ ' "$SCOPED_ROOT")
410
+ if [ -n "$FORK_ROOT" ] && [ "$FORK_ROOT" != "$SCOPED_ROOT" ]; then
287
411
  ace-assign fork-run --assignment "${ASSIGNMENT_ID}@${FORK_ROOT}"
288
412
  # Re-check status after subtree delegation completes
289
413
  continue
@@ -292,13 +416,103 @@ fi
292
416
 
293
417
  `fork-run` executes the entire subtree in one dedicated process and returns when the subtree is complete or failed.
294
418
 
419
+ #### Fork Wait Continuation Rule
420
+
421
+ After launching `ace-assign fork-run`, the driver remains inside the same drive session.
422
+
423
+ - If fork session metadata includes `callback_pane`, callback mode is active. In that case:
424
+
425
+ - do not poll the forked subtree on a timer
426
+ - do not run the `sleep 360` loop
427
+ - remain idle after launch and wait for the child forked agent to send a final status message back into the origin pane
428
+ - treat the callback message as the trigger to resume the parent drive loop
429
+ - if the callback never arrives and the session is resumed later, recover from assignment state and scoped status instead of waiting forever
430
+
431
+ - Treat "fork subtree is still running" as an internal progress state, not as a stop condition.
432
+ - Do not end the turn, emit a final user-facing completion summary, or hand control back to the user merely because the driver is waiting on fork completion.
433
+ - Poll the forked subtree every 6 minutes by default. Use two signals on each poll:
434
+
435
+ 1. poll the live fork session/process handle
436
+ 2. poll scoped assignment status with `ace-assign status --assignment "${ASSIGNMENT_ID}@${FORK_ROOT}"`
437
+
438
+ - Treat scoped assignment status as the source of truth for subtree completion. Terminal PTY output is helpful telemetry, but it is not the canonical completion signal.
439
+ - Continue polling until one of these is true:
440
+
441
+ - the `fork-run` process exits
442
+ - scoped status for the subtree proves every step inside that subtree is terminal (`done` or `failed`)
443
+ - the workflow reaches a documented blocker or failure path
444
+
445
+ - If scoped subtree status is terminal, immediately treat the fork as complete even if the PTY stayed quiet or the original terminal handle has already disappeared.
446
+ - A quiet terminal is not a stall by itself. Only treat the fork as stalled when there is no scoped status movement, no new subtree reports, and no process exit for about 30 minutes.
447
+ - When the wait ends, immediately re-enter the parent drive loop. Do not stop between "fork finished" and "next runnable step started."
448
+
449
+ #### Fork Callback Rule
450
+
451
+ When `ACE_ASSIGN_CALLBACK_PANE` is present in the forked child environment:
452
+
453
+ - before stopping for either success or failure, send one final sentence back to the origin pane with direct `ace-tmux send`
454
+ - use the exact pane target from `ACE_ASSIGN_CALLBACK_PANE`
455
+ - use a success sentence shaped like:
456
+
457
+ ```bash
458
+ ace-tmux send --pane "$ACE_ASSIGN_CALLBACK_PANE" --msg "Fork subtree ${FORK_ROOT} for assignment ${ASSIGNMENT_ID} completed. Resume parent assignment drive now." --key Enter
459
+ ```
460
+
461
+ - use a failure sentence shaped like:
462
+
463
+ ```bash
464
+ ace-tmux send --pane "$ACE_ASSIGN_CALLBACK_PANE" --msg "Fork subtree ${FORK_ROOT} for assignment ${ASSIGNMENT_ID} failed. Resume parent assignment drive and inspect scoped status." --key Enter
465
+ ```
466
+
467
+ - do not invent a new skill or wrapper for this callback; use `ace-tmux send` directly
468
+
469
+ #### Post-Fork Resume Checklist
470
+
471
+ Immediately after the fork wait ends, run this checklist in order:
472
+
473
+ 1. If `fork-run` exited non-zero, enter [Fork-Run Recovery](#fork-run-recovery).
474
+ 2. Read and review subtree reports.
475
+ 3. Check for uncommitted changes and commit safety-net leftovers if needed.
476
+ 4. Query parent assignment status.
477
+ 5. If no active step exists but pending work remains, run:
478
+
479
+ ```bash
480
+ ace-assign start --assignment "$ASSIGNMENT_TARGET"
481
+ ```
482
+
483
+ 6. If pending or `active` work remains and no blocker was recorded, continue the main loop immediately.
484
+ 7. Only stop if the assignment now satisfies a real stop condition from [Run-Until-Blocked Contract](#run-until-blocked-contract).
485
+
486
+ #### Scoped Worker Completion Boundary
487
+
488
+ When `ASSIGNMENT_TARGET` already includes `@<root>`, the current process is the subtree worker, not the parent orchestrator.
489
+
490
+ - After report review, re-check `ace-assign status --assignment "$ASSIGNMENT_TARGET"`.
491
+ - If that scoped status is terminal, stop immediately.
492
+ - Do not query the parent assignment from that scoped worker.
493
+ - Do not widen a scoped target back to parent assignment status.
494
+ - Do not continue into later sibling roots such as `070` from `/as-assign-drive <id>@040`.
495
+ - Parent resume after subtree completion belongs only to the unscoped driver that launched the fork.
496
+
497
+ Detached-resume rule:
498
+
499
+ - If a prior drive session or terminal ended, a new `/as-assign-drive` invocation MUST recover from assignment state, not from the old terminal handle.
500
+ - On re-entry, first inspect parent `ace-assign status --assignment "$ASSIGNMENT_TARGET"` and, when applicable, scoped subtree status for the last in-flight fork root.
501
+ - If the child subtree is already terminal, run the same post-fork checklist immediately and continue the parent queue without waiting for any historical `fork-run` session to be observed again.
502
+
503
+ Concrete example:
504
+
505
+ - Incorrect: launch `fork-run` for `040`, post "waiting on subtree", stop responding, and never resume `070`.
506
+ - Correct: launch `fork-run` for `040`, poll until it finishes, review reports, re-check `ace-assign status --assignment "$ASSIGNMENT_TARGET"`, then advance and launch `070` if it is the next runnable step.
507
+ - Correct after interruption: re-run `/as-assign-drive <assignment-id>`, detect that `040` is already terminal from scoped status/reports, then immediately advance and launch `070`.
508
+
295
509
  > **Long-running execution:** `fork-run` typically takes 10-30 minutes depending on subtree complexity. If your environment has bash timeout limits (e.g., Claude Code's 10-minute Bash tool limit), run `fork-run` in background and poll for completion:
296
510
  >
297
511
  > ```bash
298
512
  > # Run fork-run in background (use run_in_background: true in Claude Code)
299
513
  > ace-assign fork-run --assignment "${ASSIGNMENT_ID}@${FORK_ROOT}" &
300
514
  >
301
- > # Poll scoped status every 5 minutes until subtree completes
515
+ > # Poll scoped status every 6 minutes until subtree completes
302
516
  > while true; do
303
517
  > STATUS_JSON=$(ace-assign status --assignment "${ASSIGNMENT_ID}@${FORK_ROOT}" --format json)
304
518
  > COMPLETE=$(echo "$STATUS_JSON" | ruby -rjson -e '
@@ -307,36 +521,41 @@ fi
307
521
  > puts steps.all? { |step| step["status"] == "done" || step["status"] == "failed" }
308
522
  > ')
309
523
  > [ "$COMPLETE" = "true" ] && break
310
- > sleep 300
524
+ > sleep 360
311
525
  > done
526
+ > ace-assign status --assignment "$ASSIGNMENT_TARGET"
312
527
  > ```
313
528
 
314
529
  #### Subtree Completion: Task Status Verification
315
530
 
316
531
  After a fork subtree completes (work-on-task finishes successfully):
317
532
 
318
- 1. **Verify ace-taskflow status matches assignment status.** If the assignment shows `work-on-task` as done but ace-taskflow still shows `in-progress`, status drift has occurred.
533
+ 1. **Verify ace-task status matches assignment status.** If the assignment shows `work-on-task` as done but ace-task still shows `in-progress`, status drift has occurred.
319
534
 
320
535
  2. **If mark-task-done step was NOT included in the assignment** (common for ad-hoc assignments):
536
+
321
537
  ```bash
322
538
  # Manually sync status before reporting subtree complete
323
539
  ace-task done {taskref}
324
540
  ace-task {taskref} # Verify it shows status: done
325
541
  ```
326
542
 
327
- 3. **Report the subtree complete only after verification.** This prevents the orchestrator from showing work as done while ace-taskflow shows it as in-progress.
543
+ 3. **Report the subtree complete only after verification.** This prevents the orchestrator from showing work as done while ace-task shows it as in-progress.
328
544
 
329
545
  #### Subtree Guard: Review Fork Reports Before Continuing
330
546
 
331
547
  After fork-run returns and completion is verified, the driver acts as the **guard** for the subtree. Before continuing to the next step:
332
548
 
333
549
  1. **Read all subtree report files** from `.ace-local/assign/<assignment-id>/reports/`:
550
+
334
551
  ```bash
335
552
  # List and read all reports for the completed subtree
336
553
  ls .ace-local/assign/${ASSIGNMENT_ID}/reports/${FORK_ROOT}.*
337
554
  # Read each report file to review the forked agent's work
338
555
  ```
556
+
339
557
  2. **Check for uncommitted changes** left by the fork agent:
558
+
340
559
  ```bash
341
560
  DIRTY=$(git status --short)
342
561
  if [ -n "$DIRTY" ]; then
@@ -346,7 +565,8 @@ After fork-run returns and completion is verified, the driver acts as the **guar
346
565
  ace-git-commit -i "commit changes left by fork subtree ${FORK_ROOT}"
347
566
  fi
348
567
  ```
349
- Fork agents are expected to commit all their work. Uncommitted files indicate incomplete commit discipline — note this when reviewing reports.
568
+
569
+ Fork agents are expected to commit all their work. Uncommitted files indicate incomplete commit discipline -- note this when reviewing reports.
350
570
  3. **Verify quality**: Check that reports indicate successful completion, not just step advancement.
351
571
  4. **Flag concerns**: If any report indicates partial work, errors, or skipped steps, stop and ask the user before continuing.
352
572
  5. **Only then continue** the main drive loop to the next step.
@@ -357,12 +577,33 @@ After fork-run returns and completion is verified, the driver acts as the **guar
357
577
 
358
578
  After all fork subtrees within a batch container complete, the container auto-marks as Done. However, the queue pointer may not automatically advance to the next top-level step.
359
579
 
360
- **After verifying all fork subtree reports**, if `ace-assign status` shows no Active step (all completed steps but no new in-progress step), run:
580
+ **After verifying all fork subtree reports**, if `ace-assign status` shows no active step (all completed steps but no new active step), run:
581
+
361
582
  ```bash
362
583
  ace-assign start --assignment "$ASSIGNMENT_TARGET"
363
584
  ```
585
+
364
586
  This advances the queue to the next pending top-level step.
365
587
 
588
+ If pending steps still remain after this queue advancement, continue the drive loop immediately. Do not stop after printing a partial progress summary.
589
+
590
+ Concrete example:
591
+
592
+ ```text
593
+ 010.01 done
594
+ 010.02 pending
595
+ 010.03 pending
596
+ Current Step: none
597
+ ```
598
+
599
+ This state means "resume driving", not "return final status". Run:
600
+
601
+ ```bash
602
+ ace-assign start --assignment "$ASSIGNMENT_TARGET"
603
+ ```
604
+
605
+ then continue the loop from status check.
606
+
366
607
  #### Fork-Run Recovery
367
608
 
368
609
  When `fork-run` exits non-zero, invoke the fork recovery workflow:
@@ -428,9 +669,11 @@ For external-facing steps (for example PR/review/release/push/update lifecycle s
428
669
 
429
670
  - Attempt the step command(s) first.
430
671
  - If blocked, capture concrete evidence:
672
+
431
673
  - command attempted
432
674
  - exact error output
433
675
  - why the step cannot proceed
676
+
434
677
  - Mark step failed with evidence (do not report synthetic completion).
435
678
 
436
679
  ```bash
@@ -449,27 +692,35 @@ Use HITL when:
449
692
  For a blocked step:
450
693
 
451
694
  1. Create a HITL event with assignment and step context:
695
+
452
696
  ```bash
453
697
  ace-hitl create "Need product decision" --question "Should retries be visible?" --assignment <id> --step <number> --step-name <name> --resume "/as-assign-drive <id>"
454
698
  ```
699
+
455
700
  2. Fail the step using canonical stall format:
701
+
456
702
  ```bash
457
703
  ace-assign fail --message "HITL: <hitl-id> <hitl-path>" --assignment "$ASSIGNMENT_TARGET"
458
704
  ```
705
+
459
706
  3. Human/operator resolves:
707
+
460
708
  ```bash
461
709
  ace-hitl show <hitl-id>
462
710
  ace-hitl update <hitl-id> --answer "Yes, show retries in user-facing output."
463
711
  ace-hitl wait <hitl-id>
464
712
  ```
713
+
465
714
  4. Discover pending HITL work:
466
715
  - Main checkout default (smart local-first): `ace-hitl list`
467
716
  - Explicit scope controls: `ace-hitl list --scope current` and `ace-hitl list --scope all`
468
717
  5. Polling is default: requesting agent waits on its own HITL id (`ace-hitl wait <hitl-id>`), not global queues.
469
718
  6. Resume dispatch is fallback: if waiter is no longer active, run:
719
+
470
720
  ```bash
471
721
  ace-hitl update <hitl-id> --answer "<decision>" --resume
472
722
  ```
723
+
473
724
  7. On retry/resume, read the answer from the HITL event and continue normal fail/retry mechanics. Do not introduce gate phases, assignment-level paused state, or extra resume commands in `ace-assign`.
474
725
 
475
726
  ### 5. Write Report (Only After Real Execution)
@@ -504,6 +755,7 @@ echo "$POST_STATUS"
504
755
  ```
505
756
 
506
757
  Required checks:
758
+
507
759
  - If report succeeded: active step advanced consistently with work performed
508
760
  - If fail succeeded: assignment is stalled or moved according to retry/add logic
509
761
  - If output mismatches expected transition: stop and ask user before continuing
@@ -533,7 +785,13 @@ Creates a new step linked to the original. Original remains visible as failed.
533
785
  ace-assign add "fix-issue" --instructions "Fix the failing tests and verify" --assignment "$ASSIGNMENT_TARGET"
534
786
  ```
535
787
 
536
- New step is inserted after the current in-progress step.
788
+ New step is inserted after the current active step.
789
+
790
+ When the failure evidence is E2E-specific (`ace-test-e2e`, scenario IDs, or `.ace-local/test-e2e/` artifacts), prefer:
791
+
792
+ ```bash
793
+ ace-assign add "fix-e2e" --instructions "Use /as-e2e-fix for the failing package and scenario IDs from the recorded evidence. If analysis is missing, let /as-e2e-fix generate it via wfi://e2e/analyze-failures before applying fixes. Re-run the targeted failing scenarios before completing this step." --assignment "$ASSIGNMENT_TARGET"
794
+ ```
537
795
 
538
796
  #### Option C: Ask the User
539
797
 
@@ -542,10 +800,33 @@ If uncertain, ask the user whether to retry, add a fix step, or abort.
542
800
  ### 8. Repeat
543
801
 
544
802
  Check status again:
803
+
545
804
  - If there is a next step, continue the loop from step 1
546
805
  - If all steps are `done`, proceed to Completion
547
806
  - If assignment has failed steps and no fix is planned, report to user
548
807
 
808
+ ### 9. Pre-Exit Verification (Required)
809
+
810
+ Before ending the drive session with a final user-facing response, re-run:
811
+
812
+ ```bash
813
+ FINAL_STATUS=$(ace-assign status --assignment "$ASSIGNMENT_TARGET" --format json)
814
+ echo "$FINAL_STATUS"
815
+ ```
816
+
817
+ Required checks:
818
+
819
+ - If any steps remain `pending` or `active` and no explicit blocker was recorded, resume the loop instead of stopping.
820
+ - If the assignment is `paused`, `active_steps` is empty, and `next_step` is present, run:
821
+
822
+ ```bash
823
+ ace-assign start --assignment "$ASSIGNMENT_TARGET"
824
+ ```
825
+
826
+ then continue the loop.
827
+
828
+ - Only produce a final completion response when the assignment is actually complete or when this workflow has already reached a documented blocker/failure stop condition.
829
+
549
830
  ## Completion
550
831
 
551
832
  When `ace-assign status` shows all steps as `done`:
@@ -555,6 +836,7 @@ ace-assign status --assignment "$ASSIGNMENT_TARGET"
555
836
  ```
556
837
 
557
838
  Example output:
839
+
558
840
  ```
559
841
  Assignment: work-on-task-123 (8or5kx)
560
842
 
@@ -567,6 +849,7 @@ All steps complete!
567
849
  ```
568
850
 
569
851
  Summarize the assignment results to the user:
852
+
570
853
  - What was accomplished
571
854
  - Any artifacts created (PRs, commits, etc.)
572
855
  - Next steps or follow-up actions
@@ -604,6 +887,7 @@ When executing a step with a `skill:` field:
604
887
  |----------|--------|
605
888
  | No active assignment | Create an assignment first via `/as-assign-create` |
606
889
  | All steps done | Report completion to user |
890
+ | Assignment paused with pending work | Run `ace-assign start --assignment "$ASSIGNMENT_TARGET"` and continue driving |
607
891
  | Step fails | Attempt first, then use `fail` with command/error evidence; decide retry/add/ask |
608
892
  | Skill not found | Execute instructions directly without skill |
609
893
  | Unclear instructions | Ask user for clarification |
@@ -616,7 +900,7 @@ When executing a step with a `skill:` field:
616
900
  | Status | Meaning | Next Action |
617
901
  |--------|---------|-------------|
618
902
  | `pending` | Step not started | Cannot execute (wait for queue) |
619
- | `in_progress` | Step is active | Execute this step |
903
+ | `active` | Step is active | Execute this step |
620
904
  | `done` | Step completed | Move to next step |
621
905
  | `failed` | Step failed | Decide: retry, add fix, or abort |
622
906
 
@@ -630,11 +914,11 @@ When executing a step with a `skill:` field:
630
914
  │ ├── assignment.yaml # Assignment metadata
631
915
  │ ├── steps/ # Step files (.st.md extension)
632
916
  │ │ ├── 010-init.st.md # done
633
- │ │ ├── 020-implement.st.md # in_progress
917
+ │ │ ├── 020-implement.st.md # active
634
918
  │ │ └── 030-test.st.md # pending
635
919
  │ └── reports/ # Report files (.r.md extension)
636
920
  │ ├── 010-init.r.md # completed report
637
- │ └── 020-implement.r.md # in-progress report
921
+ │ └── 020-implement.r.md # active-step report
638
922
  └── def456/
639
923
  ├── assignment.yaml
640
924
  ├── steps/
@@ -642,6 +926,7 @@ When executing a step with a `skill:` field:
642
926
  ```
643
927
 
644
928
  Each step has:
929
+
645
930
  - **Step file** (`steps/NNN-name.st.md`) - Contains step instructions and status
646
931
  - **Report file** (`reports/NNN-name.r.md`) - Contains completion report (created when step is done)
647
932
 
@@ -680,28 +965,33 @@ $ ASSIGNMENT_TARGET=8or5kx
680
965
 
681
966
  # 1. Check status
682
967
  $ ace-assign status --assignment "$ASSIGNMENT_TARGET"
683
- Step 010: onboard [in_progress]
968
+ Next: 010 onboard
969
+
970
+ # 2. Start the next pending step
971
+ $ ace-assign start --assignment "$ASSIGNMENT_TARGET"
972
+ [Step 010 becomes active]
684
973
 
685
- # 2. Execute step (has skill: onboard)
974
+ # 3. Execute step (has skill: onboard)
686
975
  $ /as-onboard
687
976
  [Onboarding workflow runs...]
688
977
 
689
- # 3. Write report
978
+ # 4. Write report
690
979
  $ ace-assign finish --message onboard-complete.md --assignment "$ASSIGNMENT_TARGET"
691
- Step 010 marked done, advancing to 020
980
+ [Step 010 marked done]
692
981
 
693
- # 4. Check status again
982
+ # 5. Check status again
694
983
  $ ace-assign status --assignment "$ASSIGNMENT_TARGET"
695
- Step 020: work-on-task [in_progress]
984
+ Next: 020 work-on-task
696
985
 
697
- # 5. Execute next step (has skill: as-task-work)
986
+ # 6. Start and execute next step (has skill: as-task-work)
987
+ $ ace-assign start --assignment "$ASSIGNMENT_TARGET"
698
988
  $ /as-task-work 148
699
989
  [Task workflow runs...]
700
990
 
701
- # 6. Report and continue...
991
+ # 7. Report and continue...
702
992
  $ ace-assign finish --message task-done.md --assignment "$ASSIGNMENT_TARGET"
703
993
 
704
- # 7. Eventually...
994
+ # 8. Eventually...
705
995
  $ ace-assign status --assignment "$ASSIGNMENT_TARGET"
706
996
  All steps complete!
707
997
  ```