feat(worker): add treatWaitingForChildrenAsBusy to wait_for_task_change
WaitingForChildren already counted as "changed" since it's outside Queued/Running, so waiting on a planning parent returned immediately even though its children were still running. The new opt-in flag (default false, unchanged behavior) keeps polling through WaitingForChildren and only reports changed once the parent reaches WaitingForReview or a terminal status.
This commit is contained in:
+16
-7
@@ -32,11 +32,17 @@ public sealed class TaskWaitMcpTools
|
||||
"Blocks until at least one of the given tasks leaves Queued/Running, or until timeoutSeconds elapses " +
|
||||
"(clamped server-side to 900s). Returns immediately if any task is already outside Queued/Running " +
|
||||
"when called (an unknown id is reported as status \"NotFound\" and counts as changed). Use this instead " +
|
||||
"of polling get_task in a loop. Requires the calling claude process to run with " +
|
||||
"MCP_TOOL_TIMEOUT >= 930000 (ms) for a long wait to actually be held open -- ClaudeDo's own " +
|
||||
"launchers already set this. Result: { changed: [{ taskId, status }], timedOut }.")]
|
||||
"of polling get_task in a loop. Pitfall: a planning parent with children goes Running -> " +
|
||||
"WaitingForChildren while its children are still working, and by default that counts as \"changed\" -- " +
|
||||
"so waiting on a parent returns immediately even though the work isn't done. Set " +
|
||||
"treatWaitingForChildrenAsBusy=true to keep waiting through WaitingForChildren; the call then only " +
|
||||
"returns once the parent reaches WaitingForReview or a terminal status (default: false, unchanged " +
|
||||
"legacy behavior). Requires the calling claude process to run with MCP_TOOL_TIMEOUT >= 930000 (ms) for " +
|
||||
"a long wait to actually be held open -- ClaudeDo's own launchers already set this. " +
|
||||
"Result: { changed: [{ taskId, status }], timedOut }.")]
|
||||
public async Task<WaitForTaskChangeResult> WaitForTaskChange(
|
||||
string[] taskIds, int timeoutSeconds = 60, CancellationToken cancellationToken = default)
|
||||
string[] taskIds, int timeoutSeconds = 60, bool treatWaitingForChildrenAsBusy = false,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (taskIds.Length == 0)
|
||||
throw new ArgumentException("taskIds must not be empty.", nameof(taskIds));
|
||||
@@ -49,7 +55,7 @@ public sealed class TaskWaitMcpTools
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var changed = await CheckOnceAsync(taskIds, linked.Token);
|
||||
var changed = await CheckOnceAsync(taskIds, treatWaitingForChildrenAsBusy, linked.Token);
|
||||
if (changed.Count > 0)
|
||||
return new WaitForTaskChangeResult(changed, TimedOut: false);
|
||||
|
||||
@@ -62,7 +68,8 @@ public sealed class TaskWaitMcpTools
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyList<TaskStatusChangeDto>> CheckOnceAsync(string[] taskIds, CancellationToken ct)
|
||||
private async Task<IReadOnlyList<TaskStatusChangeDto>> CheckOnceAsync(
|
||||
string[] taskIds, bool treatWaitingForChildrenAsBusy, CancellationToken ct)
|
||||
{
|
||||
await using var ctx = await _dbFactory.CreateDbContextAsync(ct);
|
||||
var rows = await ctx.Tasks
|
||||
@@ -80,7 +87,9 @@ public sealed class TaskWaitMcpTools
|
||||
result.Add(new TaskStatusChangeDto(id, "NotFound"));
|
||||
continue;
|
||||
}
|
||||
if (status != TaskStatus.Queued && status != TaskStatus.Running)
|
||||
var busy = status == TaskStatus.Queued || status == TaskStatus.Running
|
||||
|| (treatWaitingForChildrenAsBusy && status == TaskStatus.WaitingForChildren);
|
||||
if (!busy)
|
||||
result.Add(new TaskStatusChangeDto(id, status.ToString()));
|
||||
}
|
||||
return result;
|
||||
|
||||
Reference in New Issue
Block a user