ace-assign 0.53.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.
- checksums.yaml +4 -4
- data/.ace-defaults/assign/catalog/steps/split-subtree-root.step.yml +4 -2
- data/.ace-defaults/assign/config.yml +1 -0
- data/.ace-defaults/assign/presets/work-on-task.yml +3 -0
- data/CHANGELOG.md +15 -0
- data/docs/demo/fork-provider.cast +814 -937
- data/docs/demo/fork-provider.gif +0 -0
- data/docs/demo/fork-provider.recording.json +15 -17
- data/docs/demo/fork-provider.tape.yml +16 -4
- data/docs/usage.md +30 -7
- data/handbook/guides/fork-context.g.md +29 -5
- data/handbook/skills/as-assign-drive/SKILL.md +2 -2
- data/handbook/workflow-instructions/assign/drive.wf.md +109 -36
- data/handbook/workflow-instructions/assign/prepare.wf.md +5 -0
- data/lib/ace/assign/atoms/preset_expander.rb +4 -0
- data/lib/ace/assign/atoms/tree_formatter.rb +2 -2
- data/lib/ace/assign/cli/commands/add.rb +20 -11
- data/lib/ace/assign/cli/commands/assignment_target.rb +49 -18
- data/lib/ace/assign/cli/commands/create.rb +1 -1
- data/lib/ace/assign/cli/commands/fail.rb +1 -1
- data/lib/ace/assign/cli/commands/finish.rb +26 -5
- data/lib/ace/assign/cli/commands/fork_run.rb +56 -17
- data/lib/ace/assign/cli/commands/list.rb +4 -3
- data/lib/ace/assign/cli/commands/retry_cmd.rb +1 -1
- data/lib/ace/assign/cli/commands/status.rb +60 -34
- data/lib/ace/assign/cli/commands/step.rb +4 -4
- data/lib/ace/assign/cli.rb +1 -1
- data/lib/ace/assign/models/assignment_info.rb +33 -4
- data/lib/ace/assign/models/queue_state.rb +101 -39
- data/lib/ace/assign/models/step.rb +13 -3
- data/lib/ace/assign/molecules/fork_session_launcher.rb +76 -60
- data/lib/ace/assign/molecules/step_writer.rb +3 -3
- data/lib/ace/assign/molecules/tmux_control_surface_runner.rb +249 -0
- data/lib/ace/assign/organisms/assignment_executor.rb +132 -82
- data/lib/ace/assign/version.rb +1 -1
- data/lib/ace/assign.rb +1 -0
- metadata +17 -3
- data/lib/ace/assign/molecules/tmux_fork_runner.rb +0 -191
data/docs/demo/fork-provider.gif
CHANGED
|
Binary file
|
|
@@ -1,29 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"backend": "asciinema",
|
|
3
|
-
"tape_path": "/home/mc/ace-t.
|
|
4
|
-
"cast_path": "/home/mc/ace-t.
|
|
5
|
-
"visual_path": "/home/mc/ace-t.
|
|
6
|
-
"sandbox_path": "/home/mc/ace-t.
|
|
3
|
+
"tape_path": "/home/mc/ace-t.n1d/ace-assign/docs/demo/fork-provider.tape.yml",
|
|
4
|
+
"cast_path": "/home/mc/ace-t.n1d/ace-assign/docs/demo/fork-provider.cast",
|
|
5
|
+
"visual_path": "/home/mc/ace-t.n1d/ace-assign/docs/demo/fork-provider.gif",
|
|
6
|
+
"sandbox_path": "/home/mc/ace-t.n1d/.ace-local/demo/sandbox/8rfe9v",
|
|
7
7
|
"verification": {
|
|
8
|
-
"status": "
|
|
9
|
-
"classification": "
|
|
10
|
-
"summary": "
|
|
11
|
-
"retryable":
|
|
12
|
-
"report_path":
|
|
8
|
+
"status": "pass",
|
|
9
|
+
"classification": "pass",
|
|
10
|
+
"summary": "Verification passed",
|
|
11
|
+
"retryable": false,
|
|
12
|
+
"report_path": null,
|
|
13
13
|
"details": {
|
|
14
|
-
"cast_path": "/home/mc/ace-t.
|
|
14
|
+
"cast_path": "/home/mc/ace-t.n1d/ace-assign/docs/demo/fork-provider.cast",
|
|
15
15
|
"inputs_recorded": 0,
|
|
16
|
-
"echoed_commands_recorded":
|
|
16
|
+
"echoed_commands_recorded": 435,
|
|
17
17
|
"script_commands_recorded": 0,
|
|
18
|
-
"commands_expected":
|
|
18
|
+
"commands_expected": 3,
|
|
19
19
|
"captured_vars": {
|
|
20
|
-
"ACE_TMUX_SESSION": "fork-demo PROJECT_ROOT_PATH=\"$PWD\" ace-assign
|
|
20
|
+
"ACE_TMUX_SESSION": "fork-demo PROJECT_ROOT_PATH=\"$PWD\" ace-assign status --assignment \"$(cat ASSIGN_ID)@010\""
|
|
21
21
|
},
|
|
22
22
|
"missing_vars": [],
|
|
23
|
-
"missing_output": [
|
|
24
|
-
|
|
25
|
-
],
|
|
26
|
-
"missing_output_sequence": null,
|
|
23
|
+
"missing_output": [],
|
|
24
|
+
"missing_output_sequence": [],
|
|
27
25
|
"forbidden_hits": [],
|
|
28
26
|
"assertion_failures": [],
|
|
29
27
|
"assertions_skipped": false
|
|
@@ -34,13 +34,17 @@ setup:
|
|
|
34
34
|
PROJECT_ROOT_PATH="$PWD" ace-assign create --yaml fork-demo-job.yaml
|
|
35
35
|
ruby -e 'link = ".ace-local/assign/.latest"; abort "no latest assignment" unless File.symlink?(link); File.write("ASSIGN_ID", File.basename(File.readlink(link)))'
|
|
36
36
|
- run: tmux new-session -d -s fork-demo -n work -c "$PWD" "bash --noprofile --norc -i"
|
|
37
|
-
- run: |
|
|
38
|
-
bash -lc 'sleep 45; tmux detach-client -s fork-demo || true' >/tmp/ace-demo-fork-provider-detach.log 2>&1 &
|
|
39
37
|
scenes:
|
|
40
38
|
- name: Attach to the operator work window
|
|
41
39
|
commands:
|
|
42
|
-
-
|
|
43
|
-
|
|
40
|
+
- tmux:
|
|
41
|
+
action: attach
|
|
42
|
+
session: fork-demo
|
|
43
|
+
- tmux:
|
|
44
|
+
action: wait
|
|
45
|
+
for: window-active
|
|
46
|
+
session: fork-demo
|
|
47
|
+
window: work
|
|
44
48
|
- type: tmux display-message -p '#W'
|
|
45
49
|
sleep: 2s
|
|
46
50
|
- name: Launch the fork into a visible tmux window
|
|
@@ -49,6 +53,14 @@ scenes:
|
|
|
49
53
|
sleep: 2s
|
|
50
54
|
- type: bash -lc 'ACE_TMUX_SESSION=fork-demo PROJECT_ROOT_PATH="$PWD" ace-assign fork-run --assignment "$(cat ASSIGN_ID)@010" --launch-mode tmux --provider codex:gpt@yolo --cli-args "--no-alt-screen" --timeout 120 &'
|
|
51
55
|
sleep: 35s
|
|
56
|
+
- tmux:
|
|
57
|
+
action: wait
|
|
58
|
+
for: window-exists
|
|
59
|
+
session: fork-demo
|
|
60
|
+
window: work-fs
|
|
61
|
+
- tmux:
|
|
62
|
+
action: detach
|
|
63
|
+
session: fork-demo
|
|
52
64
|
verify:
|
|
53
65
|
require_output:
|
|
54
66
|
- work
|
data/docs/usage.md
CHANGED
|
@@ -4,8 +4,8 @@ title: ace-assign Usage Guide
|
|
|
4
4
|
purpose: Complete command reference for ace-assign queue orchestration, hierarchy,
|
|
5
5
|
and fork execution.
|
|
6
6
|
ace-docs:
|
|
7
|
-
last-updated: '2026-04-
|
|
8
|
-
last-checked: '2026-04-
|
|
7
|
+
last-updated: '2026-04-23'
|
|
8
|
+
last-checked: '2026-04-23'
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# ace-assign Usage Guide
|
|
@@ -47,6 +47,7 @@ ace-test-suite --target all
|
|
|
47
47
|
```bash
|
|
48
48
|
ace-assign create --yaml job.yaml
|
|
49
49
|
ace-assign status
|
|
50
|
+
ace-assign start
|
|
50
51
|
ace-assign step
|
|
51
52
|
ace-assign finish --message step-010.md
|
|
52
53
|
ace-assign status
|
|
@@ -57,6 +58,7 @@ Use scoped targeting when needed:
|
|
|
57
58
|
|
|
58
59
|
```bash
|
|
59
60
|
ace-assign status --assignment abc123@010.01
|
|
61
|
+
ace-assign start --assignment abc123@010.01
|
|
60
62
|
ace-assign finish --message done.md --assignment abc123@010.01
|
|
61
63
|
```
|
|
62
64
|
|
|
@@ -118,6 +120,7 @@ Text modes:
|
|
|
118
120
|
- `compact` (default) prints a short summary, hidden-step stats, and up to 5 upcoming step lines
|
|
119
121
|
- `progress` prints a single summary line
|
|
120
122
|
- `full` prints the full tree/table without step instructions
|
|
123
|
+
- JSON emits `active_steps` for all active steps in scope and `next_step` only when no step is active in that scope
|
|
121
124
|
|
|
122
125
|
HITL stall behavior:
|
|
123
126
|
|
|
@@ -133,7 +136,7 @@ HITL stall behavior:
|
|
|
133
136
|
|
|
134
137
|
### `ace-assign step [STEP]`
|
|
135
138
|
|
|
136
|
-
Show instructions for the
|
|
139
|
+
Show instructions for the deepest active step in scope, the next workable pending step when nothing is active, or an explicit step number.
|
|
137
140
|
|
|
138
141
|
Options:
|
|
139
142
|
|
|
@@ -143,7 +146,7 @@ Options:
|
|
|
143
146
|
|
|
144
147
|
### `ace-assign start [STEP]`
|
|
145
148
|
|
|
146
|
-
|
|
149
|
+
Mark the next workable pending step active, or mark an explicit pending step active in the targeted assignment or subtree.
|
|
147
150
|
|
|
148
151
|
Options:
|
|
149
152
|
|
|
@@ -153,11 +156,11 @@ Options:
|
|
|
153
156
|
|
|
154
157
|
### `ace-assign finish [STEP] --message VALUE`
|
|
155
158
|
|
|
156
|
-
Complete current
|
|
159
|
+
Complete the current active step (or explicit active step in the active assignment) with report content.
|
|
157
160
|
Use positional `STEP` only for the active assignment. When targeting another
|
|
158
161
|
assignment or a scoped subtree, pass `--assignment <id>` or
|
|
159
162
|
`--assignment <id@step>` without a positional `STEP`; the command finishes the
|
|
160
|
-
|
|
163
|
+
deepest active step in that target.
|
|
161
164
|
|
|
162
165
|
`--message` accepts:
|
|
163
166
|
|
|
@@ -222,6 +225,7 @@ Options:
|
|
|
222
225
|
- `--cli-args <args>`
|
|
223
226
|
- `--timeout <seconds>`
|
|
224
227
|
- `--launch-mode auto|headless|tmux`
|
|
228
|
+
- `--callback`
|
|
225
229
|
- `--quiet, -q`
|
|
226
230
|
- `--debug, -d`
|
|
227
231
|
|
|
@@ -229,7 +233,25 @@ Launch modes:
|
|
|
229
233
|
|
|
230
234
|
- `auto` (default): use tmux when the current process is already inside tmux or `ACE_TMUX_SESSION` is set; otherwise use the headless subprocess path
|
|
231
235
|
- `headless`: force the existing provider subprocess path and never create tmux panes
|
|
232
|
-
- `tmux`: require tmux context, create or reuse `<
|
|
236
|
+
- `tmux`: require tmux context, create or reuse `<origin-window>-fs`, start a real interactive agent in a pane there via `ace-llm --interactive`, and send the scoped `/as-assign-drive <assignment>@<root>` handoff automatically. The fork window name uses the shared `ace-tmux` safe-name policy, so punctuation in the base window is replaced with `-`. Fork windows and panes are created detached, so the current tmux focus stays where the user left it.
|
|
237
|
+
- `tmux`: require tmux context, create or reuse `<origin-window>-fs`, start a real interactive agent in a pane there via `ace-llm --interactive`, and send the scoped `/as-assign-drive <assignment>@<root>` handoff automatically
|
|
238
|
+
- This mode consumes the shared `ace-tmux` runtime/control surface for tmux targeting, pane dispatch, and diagnostics.
|
|
239
|
+
- The fork window name uses the shared `ace-tmux` safe-name policy, so punctuation in the base window is replaced with `-`.
|
|
240
|
+
- Fork windows and panes are created detached, so the current tmux focus stays where the user left it.
|
|
241
|
+
- Assignment step state remains the source of truth for subtree completion or failure; pane capture is diagnostic support only.
|
|
242
|
+
|
|
243
|
+
Callback mode:
|
|
244
|
+
|
|
245
|
+
- `--callback`: tmux-only fork mode that captures the pane where `fork-run` was started and passes it into the child fork session as `ACE_ASSIGN_CALLBACK_PANE`
|
|
246
|
+
- In callback mode the child agent is instructed to send one final status sentence back to the origin pane with `ace-tmux send` before stopping
|
|
247
|
+
- Callback mode is intended for interactive parent/child agent tmux flows where the parent stays idle until the child sends the final message back
|
|
248
|
+
|
|
249
|
+
Launch-mode precedence for fork execution:
|
|
250
|
+
|
|
251
|
+
1. CLI `--launch-mode`
|
|
252
|
+
2. Step frontmatter `fork.mode`
|
|
253
|
+
3. Config `execution.launch_mode`
|
|
254
|
+
4. Built-in default `auto`
|
|
233
255
|
|
|
234
256
|
Provider resolution precedence for fork execution:
|
|
235
257
|
|
|
@@ -247,6 +269,7 @@ status: pending
|
|
|
247
269
|
context: fork
|
|
248
270
|
fork:
|
|
249
271
|
provider: "claude:sonnet@yolo"
|
|
272
|
+
mode: "tmux"
|
|
250
273
|
---
|
|
251
274
|
```
|
|
252
275
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
doc-type: guide
|
|
3
3
|
title: Fork Context Guide
|
|
4
|
-
purpose: Explain fork context execution model, boundaries, and recovery patterns for
|
|
4
|
+
purpose: Explain fork context execution model, boundaries, and recovery patterns for
|
|
5
|
+
ace-assign subtree delegation.
|
|
5
6
|
ace-docs:
|
|
6
|
-
last-updated: 2026-
|
|
7
|
-
last-checked: 2026-
|
|
7
|
+
last-updated: '2026-04-23'
|
|
8
|
+
last-checked: '2026-04-23'
|
|
8
9
|
---
|
|
9
10
|
|
|
10
11
|
# Fork Context Guide
|
|
@@ -13,13 +14,14 @@ ace-docs:
|
|
|
13
14
|
|
|
14
15
|
Fork context enables step files to run in isolated agent contexts using the Task tool. When a step has `context: fork` in its frontmatter, ace-assign outputs instructions for the orchestrating agent to execute the step via a subagent.
|
|
15
16
|
|
|
16
|
-
You can also set
|
|
17
|
+
You can also set per-step launch overrides with `fork.provider` and `fork.mode`:
|
|
17
18
|
|
|
18
19
|
```yaml
|
|
19
20
|
---
|
|
20
21
|
context: fork
|
|
21
22
|
fork:
|
|
22
23
|
provider: "claude:sonnet@yolo"
|
|
24
|
+
mode: "tmux"
|
|
23
25
|
---
|
|
24
26
|
```
|
|
25
27
|
|
|
@@ -30,6 +32,13 @@ Provider precedence during `ace-assign fork-run`:
|
|
|
30
32
|
3. Assign config `execution.provider`
|
|
31
33
|
4. Built-in default
|
|
32
34
|
|
|
35
|
+
Launch-mode precedence during `ace-assign fork-run`:
|
|
36
|
+
|
|
37
|
+
1. CLI `--launch-mode`
|
|
38
|
+
2. Step `fork.mode`
|
|
39
|
+
3. Assign config `execution.launch_mode`
|
|
40
|
+
4. Built-in default `auto`
|
|
41
|
+
|
|
33
42
|
For hierarchical split workflows, use **parent-only** fork markers:
|
|
34
43
|
- Split parent step: `context: fork`
|
|
35
44
|
- Child steps (`onboard-base`, `task-load`, `plan-task`, `work-on-task`, `verify-test`, `release-minor`): no `context: fork`
|
|
@@ -127,6 +136,21 @@ The orchestrating agent:
|
|
|
127
136
|
- Processes subagent reports
|
|
128
137
|
- Handles failures and retries
|
|
129
138
|
|
|
139
|
+
## Scoped Status Semantics
|
|
140
|
+
|
|
141
|
+
Fork execution uses two layers of active ownership:
|
|
142
|
+
|
|
143
|
+
- The fork root stays `active` while the delegated session owns that subtree.
|
|
144
|
+
- Inside that subtree, zero or one deepest started descendant may also be `active`.
|
|
145
|
+
|
|
146
|
+
Status reporting is scope-aware:
|
|
147
|
+
|
|
148
|
+
- Unscoped status lists all `active_steps` in queue order and hides pending descendants under an active fork root from global `next_step` selection.
|
|
149
|
+
- Scoped status (`--assignment <id>@<root>`) reports activity only inside that subtree.
|
|
150
|
+
- When nothing is active inside the current scope, status reports `next_step` instead of predicting started work.
|
|
151
|
+
|
|
152
|
+
This keeps queue eligibility separate from execution state: pending descendants stay pending until scoped execution explicitly starts them.
|
|
153
|
+
|
|
130
154
|
## Recovery From Failed Fork Subtrees
|
|
131
155
|
|
|
132
156
|
When a forked subtree fails, use **adaptive minimal-safe replay**:
|
|
@@ -245,4 +269,4 @@ ACE_DEBUG=1 ace-assign status
|
|
|
245
269
|
|
|
246
270
|
- [ace-assign README](../../README.md) - Main documentation
|
|
247
271
|
- [Work Queue Model](../workflow-instructions/drive-assignment.wf.md) - Assignment management
|
|
248
|
-
- `ace-assign fork-run --root <step> --assignment <id>` - Prepare subtree-scoped fork session
|
|
272
|
+
- `ace-assign fork-run --root <step> --assignment <id>` - Prepare subtree-scoped fork session
|
|
@@ -12,7 +12,7 @@ allowed-tools:
|
|
|
12
12
|
- AskUserQuestion
|
|
13
13
|
- Skill
|
|
14
14
|
argument-hint: "[assignment[@scope]]"
|
|
15
|
-
last_modified: 2026-04-
|
|
15
|
+
last_modified: 2026-04-23
|
|
16
16
|
source: ace-assign
|
|
17
17
|
skill:
|
|
18
18
|
kind: workflow
|
|
@@ -29,7 +29,7 @@ Hard stop rule:
|
|
|
29
29
|
- Do not stop while waiting on a forked subtree; keep polling and resume the parent drive loop as soon as the subtree reaches a terminal state.
|
|
30
30
|
- Treat `ace-assign status --assignment <id>@<root>` as the source of truth for fork completion; quiet terminal output is not enough reason to stop or declare a stall.
|
|
31
31
|
- If a prior terminal or drive session ended, re-enter from assignment state and continue from the next runnable work instead of depending on the old terminal handle.
|
|
32
|
-
- Before any final response, re-check pinned assignment status. If any runnable `pending` or `
|
|
32
|
+
- Before any final response, re-check pinned assignment status. If any runnable `pending` or `active` work remains, continue driving.
|
|
33
33
|
- If unrelated dirty files are generated side effects outside task scope, clean/reset them instead of auto-committing them.
|
|
34
34
|
- Use progress updates for partial status only.
|
|
35
35
|
- Return a final user-facing completion response only when the assignment is complete or the workflow reaches an explicit blocker/failure stop condition.
|
|
@@ -12,8 +12,8 @@ doc-type: workflow
|
|
|
12
12
|
title: Drive Assignment Workflow
|
|
13
13
|
purpose: workflow instruction for driving ace-assign assignment execution
|
|
14
14
|
ace-docs:
|
|
15
|
-
last-updated: 2026-04-
|
|
16
|
-
last-checked: 2026-
|
|
15
|
+
last-updated: '2026-04-23'
|
|
16
|
+
last-checked: '2026-04-23'
|
|
17
17
|
---
|
|
18
18
|
|
|
19
19
|
# Drive Assignment Workflow
|
|
@@ -25,7 +25,7 @@ Drive agent execution through an active assignment by continuously checking stat
|
|
|
25
25
|
## Prerequisites
|
|
26
26
|
|
|
27
27
|
- An active assignment exists (created via `ace-assign create` or `/as-assign-create`)
|
|
28
|
-
- Assignment has at least one pending or
|
|
28
|
+
- Assignment has at least one pending or active step
|
|
29
29
|
|
|
30
30
|
## Assignment Context Propagation
|
|
31
31
|
|
|
@@ -36,11 +36,11 @@ When working with multiple concurrent assignments, the active assignment is reso
|
|
|
36
36
|
3. `.latest` symlink (auto-updated on any activity)
|
|
37
37
|
4. Scan all assignments (fallback)
|
|
38
38
|
|
|
39
|
-
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.
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
# Set once from workflow argument (empty when
|
|
43
|
-
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:-}}"
|
|
44
44
|
|
|
45
45
|
# Resolve and pin assignment identity for the full drive loop
|
|
46
46
|
if [ -n "$ASSIGNMENT_TARGET" ]; then
|
|
@@ -59,6 +59,8 @@ if [ -z "$ASSIGNMENT_TARGET" ]; then
|
|
|
59
59
|
fi
|
|
60
60
|
```
|
|
61
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
|
+
|
|
62
64
|
### Explicit Assignment Targeting (Recommended)
|
|
63
65
|
|
|
64
66
|
Use explicit flags to propagate assignment context across subprocesses and tools:
|
|
@@ -81,7 +83,7 @@ ace-assign status --assignment abc123@010.01
|
|
|
81
83
|
ace-assign finish --message done.md --assignment abc123@010.01
|
|
82
84
|
```
|
|
83
85
|
|
|
84
|
-
When an assignment target includes scope (`<id>@<root>`), `ace-assign finish --message`
|
|
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.
|
|
85
87
|
|
|
86
88
|
Helper command:
|
|
87
89
|
|
|
@@ -115,7 +117,7 @@ Repeat the following cycle until all steps are done or failed:
|
|
|
115
117
|
|
|
116
118
|
### Run-Until-Blocked Contract
|
|
117
119
|
|
|
118
|
-
Once `ASSIGNMENT_TARGET` is pinned, keep driving
|
|
120
|
+
Once `ASSIGNMENT_TARGET` is pinned, keep driving that exact target until exactly one of these stop conditions is true:
|
|
119
121
|
|
|
120
122
|
1. `ace-assign status --assignment "$ASSIGNMENT_TARGET"` shows all steps complete
|
|
121
123
|
2. A workflow step explicitly requires HITL or other user judgment before execution can continue
|
|
@@ -127,6 +129,7 @@ Do **not** stop merely because you have useful progress to report.
|
|
|
127
129
|
- Intermediate progress belongs in short progress updates, not in a final completion response.
|
|
128
130
|
- `pending` steps with no active step are not a stop condition. They mean the queue must be advanced and the loop must continue.
|
|
129
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.
|
|
130
133
|
- A paused assignment with remaining runnable work is not "done"; treat it as a recoverable scheduler state and resume the loop.
|
|
131
134
|
|
|
132
135
|
### Final Response Gate
|
|
@@ -139,20 +142,21 @@ ace-assign status --assignment "$ASSIGNMENT_TARGET" --format json
|
|
|
139
142
|
|
|
140
143
|
You may only stop and send a final response when one of these is true:
|
|
141
144
|
|
|
142
|
-
- the pinned assignment has no remaining runnable `pending` or `
|
|
145
|
+
- the pinned assignment has no remaining runnable `pending` or `active` work
|
|
143
146
|
- the workflow recorded an explicit blocker or unrecoverable failure stop condition
|
|
144
147
|
- the user explicitly interrupted or canceled execution
|
|
145
148
|
|
|
146
149
|
Do **not** send a final response merely because:
|
|
147
150
|
|
|
148
|
-
- one child subtree completed
|
|
151
|
+
- one child subtree completed in the parent assignment
|
|
149
152
|
- useful progress was made
|
|
150
153
|
- a prior terminal session ended
|
|
151
|
-
- the parent assignment
|
|
154
|
+
- the parent assignment surfaced the next pending step after active work finished
|
|
152
155
|
|
|
153
156
|
Concrete example:
|
|
154
157
|
|
|
155
|
-
- `010.01 done` and `010.02.01
|
|
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.
|
|
156
160
|
|
|
157
161
|
### Step Execution Policy
|
|
158
162
|
|
|
@@ -216,7 +220,7 @@ echo "$STATUS_OUTPUT"
|
|
|
216
220
|
Read the output to identify:
|
|
217
221
|
|
|
218
222
|
- Assignment ID (must remain equal to pinned `ASSIGNMENT_ID`)
|
|
219
|
-
-
|
|
223
|
+
- Active step list in scope, or next step when nothing is active
|
|
220
224
|
- Remaining visible steps in the queue preview
|
|
221
225
|
- Hidden-step counts for large queues
|
|
222
226
|
|
|
@@ -264,7 +268,7 @@ Dirty-tree classification rule:
|
|
|
264
268
|
|
|
265
269
|
Example:
|
|
266
270
|
|
|
267
|
-
- A bulk untracked `.ace/...` tree created by `ace-config
|
|
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.
|
|
268
272
|
|
|
269
273
|
#### Delegation Rule
|
|
270
274
|
|
|
@@ -293,8 +297,15 @@ ace-assign fork-run --assignment <id>@020
|
|
|
293
297
|
**Delegation boundary rule**
|
|
294
298
|
|
|
295
299
|
- Outside a delegated fork scope, do NOT execute fork steps inline.
|
|
300
|
+
- First decide whether the fork boundary is already entered before issuing `fork-run`.
|
|
296
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>`.
|
|
297
|
-
-
|
|
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`.
|
|
298
309
|
|
|
299
310
|
#### Nested Batch Containers (Container → Fork Children)
|
|
300
311
|
|
|
@@ -374,11 +385,29 @@ Conversational boundary rule:
|
|
|
374
385
|
```bash
|
|
375
386
|
STATUS_JSON=$(ace-assign status --assignment "$ASSIGNMENT_TARGET" --format json)
|
|
376
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
|
+
|
|
377
401
|
FORK_ROOT=$(echo "$STATUS_JSON" | ruby -rjson -e '
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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
|
|
382
411
|
ace-assign fork-run --assignment "${ASSIGNMENT_ID}@${FORK_ROOT}"
|
|
383
412
|
# Re-check status after subtree delegation completes
|
|
384
413
|
continue
|
|
@@ -391,6 +420,14 @@ fi
|
|
|
391
420
|
|
|
392
421
|
After launching `ace-assign fork-run`, the driver remains inside the same drive session.
|
|
393
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
|
+
|
|
394
431
|
- Treat "fork subtree is still running" as an internal progress state, not as a stop condition.
|
|
395
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.
|
|
396
433
|
- Poll the forked subtree every 6 minutes by default. Use two signals on each poll:
|
|
@@ -409,6 +446,26 @@ After launching `ace-assign fork-run`, the driver remains inside the same drive
|
|
|
409
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.
|
|
410
447
|
- When the wait ends, immediately re-enter the parent drive loop. Do not stop between "fork finished" and "next runnable step started."
|
|
411
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
|
+
|
|
412
469
|
#### Post-Fork Resume Checklist
|
|
413
470
|
|
|
414
471
|
Immediately after the fork wait ends, run this checklist in order:
|
|
@@ -423,9 +480,20 @@ Immediately after the fork wait ends, run this checklist in order:
|
|
|
423
480
|
ace-assign start --assignment "$ASSIGNMENT_TARGET"
|
|
424
481
|
```
|
|
425
482
|
|
|
426
|
-
6. If pending or `
|
|
483
|
+
6. If pending or `active` work remains and no blocker was recorded, continue the main loop immediately.
|
|
427
484
|
7. Only stop if the assignment now satisfies a real stop condition from [Run-Until-Blocked Contract](#run-until-blocked-contract).
|
|
428
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
|
+
|
|
429
497
|
Detached-resume rule:
|
|
430
498
|
|
|
431
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.
|
|
@@ -509,7 +577,7 @@ After fork-run returns and completion is verified, the driver acts as the **guar
|
|
|
509
577
|
|
|
510
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.
|
|
511
579
|
|
|
512
|
-
**After verifying all fork subtree reports**, if `ace-assign status` shows no
|
|
580
|
+
**After verifying all fork subtree reports**, if `ace-assign status` shows no active step (all completed steps but no new active step), run:
|
|
513
581
|
|
|
514
582
|
```bash
|
|
515
583
|
ace-assign start --assignment "$ASSIGNMENT_TARGET"
|
|
@@ -717,7 +785,7 @@ Creates a new step linked to the original. Original remains visible as failed.
|
|
|
717
785
|
ace-assign add "fix-issue" --instructions "Fix the failing tests and verify" --assignment "$ASSIGNMENT_TARGET"
|
|
718
786
|
```
|
|
719
787
|
|
|
720
|
-
New step is inserted after the current
|
|
788
|
+
New step is inserted after the current active step.
|
|
721
789
|
|
|
722
790
|
When the failure evidence is E2E-specific (`ace-test-e2e`, scenario IDs, or `.ace-local/test-e2e/` artifacts), prefer:
|
|
723
791
|
|
|
@@ -748,8 +816,8 @@ echo "$FINAL_STATUS"
|
|
|
748
816
|
|
|
749
817
|
Required checks:
|
|
750
818
|
|
|
751
|
-
- If any steps remain `pending` or `
|
|
752
|
-
- If the assignment is `paused`, `
|
|
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:
|
|
753
821
|
|
|
754
822
|
```bash
|
|
755
823
|
ace-assign start --assignment "$ASSIGNMENT_TARGET"
|
|
@@ -832,7 +900,7 @@ When executing a step with a `skill:` field:
|
|
|
832
900
|
| Status | Meaning | Next Action |
|
|
833
901
|
|--------|---------|-------------|
|
|
834
902
|
| `pending` | Step not started | Cannot execute (wait for queue) |
|
|
835
|
-
| `
|
|
903
|
+
| `active` | Step is active | Execute this step |
|
|
836
904
|
| `done` | Step completed | Move to next step |
|
|
837
905
|
| `failed` | Step failed | Decide: retry, add fix, or abort |
|
|
838
906
|
|
|
@@ -846,11 +914,11 @@ When executing a step with a `skill:` field:
|
|
|
846
914
|
│ ├── assignment.yaml # Assignment metadata
|
|
847
915
|
│ ├── steps/ # Step files (.st.md extension)
|
|
848
916
|
│ │ ├── 010-init.st.md # done
|
|
849
|
-
│ │ ├── 020-implement.st.md #
|
|
917
|
+
│ │ ├── 020-implement.st.md # active
|
|
850
918
|
│ │ └── 030-test.st.md # pending
|
|
851
919
|
│ └── reports/ # Report files (.r.md extension)
|
|
852
920
|
│ ├── 010-init.r.md # completed report
|
|
853
|
-
│ └── 020-implement.r.md #
|
|
921
|
+
│ └── 020-implement.r.md # active-step report
|
|
854
922
|
└── def456/
|
|
855
923
|
├── assignment.yaml
|
|
856
924
|
├── steps/
|
|
@@ -897,28 +965,33 @@ $ ASSIGNMENT_TARGET=8or5kx
|
|
|
897
965
|
|
|
898
966
|
# 1. Check status
|
|
899
967
|
$ ace-assign status --assignment "$ASSIGNMENT_TARGET"
|
|
900
|
-
|
|
968
|
+
Next: 010 onboard
|
|
969
|
+
|
|
970
|
+
# 2. Start the next pending step
|
|
971
|
+
$ ace-assign start --assignment "$ASSIGNMENT_TARGET"
|
|
972
|
+
[Step 010 becomes active]
|
|
901
973
|
|
|
902
|
-
#
|
|
974
|
+
# 3. Execute step (has skill: onboard)
|
|
903
975
|
$ /as-onboard
|
|
904
976
|
[Onboarding workflow runs...]
|
|
905
977
|
|
|
906
|
-
#
|
|
978
|
+
# 4. Write report
|
|
907
979
|
$ ace-assign finish --message onboard-complete.md --assignment "$ASSIGNMENT_TARGET"
|
|
908
|
-
Step 010 marked done
|
|
980
|
+
[Step 010 marked done]
|
|
909
981
|
|
|
910
|
-
#
|
|
982
|
+
# 5. Check status again
|
|
911
983
|
$ ace-assign status --assignment "$ASSIGNMENT_TARGET"
|
|
912
|
-
|
|
984
|
+
Next: 020 work-on-task
|
|
913
985
|
|
|
914
|
-
#
|
|
986
|
+
# 6. Start and execute next step (has skill: as-task-work)
|
|
987
|
+
$ ace-assign start --assignment "$ASSIGNMENT_TARGET"
|
|
915
988
|
$ /as-task-work 148
|
|
916
989
|
[Task workflow runs...]
|
|
917
990
|
|
|
918
|
-
#
|
|
991
|
+
# 7. Report and continue...
|
|
919
992
|
$ ace-assign finish --message task-done.md --assignment "$ASSIGNMENT_TARGET"
|
|
920
993
|
|
|
921
|
-
#
|
|
994
|
+
# 8. Eventually...
|
|
922
995
|
$ ace-assign status --assignment "$ASSIGNMENT_TARGET"
|
|
923
996
|
All steps complete!
|
|
924
997
|
```
|
|
@@ -141,6 +141,11 @@ For `--taskrefs 148,149,150`, expansion generates:
|
|
|
141
141
|
030 finalize
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
+
Generated batch parents should carry scheduler metadata:
|
|
145
|
+
|
|
146
|
+
- `010 batch-tasks (parent, auto-completes, batch_parent: true, parallel: false)`
|
|
147
|
+
- `fork_retry_limit: 1`
|
|
148
|
+
|
|
144
149
|
The hierarchical numbering enables:
|
|
145
150
|
- Parent auto-completion when all children are done
|
|
146
151
|
- Parent-only fork markers for subtree delegation
|
|
@@ -192,6 +192,10 @@ module Ace
|
|
|
192
192
|
|
|
193
193
|
step["skill"] = config["skill"] if config["skill"]
|
|
194
194
|
step["context"] = config["context"] if config["context"]
|
|
195
|
+
step["batch_parent"] = config["batch_parent"] unless config["batch_parent"].nil?
|
|
196
|
+
step["parallel"] = config["parallel"] unless config["parallel"].nil?
|
|
197
|
+
step["max_parallel"] = config["max_parallel"] if config["max_parallel"]
|
|
198
|
+
step["fork_retry_limit"] = config["fork_retry_limit"] unless config["fork_retry_limit"].nil?
|
|
195
199
|
|
|
196
200
|
step
|
|
197
201
|
end
|
|
@@ -14,14 +14,14 @@ module Ace
|
|
|
14
14
|
# # +-- onboard (completed)
|
|
15
15
|
# # +-- work-on-task (running)
|
|
16
16
|
# # | +-- onboard (completed)
|
|
17
|
-
# # | +-- implement (
|
|
17
|
+
# # | +-- implement (active)
|
|
18
18
|
# # | \-- verify-tests (pending)
|
|
19
19
|
# # \-- review-pr (pending)
|
|
20
20
|
module TreeFormatter
|
|
21
21
|
# State display labels matching list command
|
|
22
22
|
STATE_LABELS = {
|
|
23
23
|
pending: "pending",
|
|
24
|
-
|
|
24
|
+
active: "active",
|
|
25
25
|
running: "running",
|
|
26
26
|
paused: "paused",
|
|
27
27
|
completed: "completed",
|