fix(prompts): correct the list-handler wait cap and wait through WaitingForChildren

The execute prompt told the handler to wait with timeoutSeconds up to 170 -- a
leftover from the retired MCP_TOOL_TIMEOUT=200000ms era. The real server-side
clamp is TaskWaitMcpTools.MaxTimeoutSeconds = 900 and every launcher sets
930000ms, so the handler was making ~5x the wait_for_task_change calls it needed
and burning turns on re-waiting.

It also never passed treatWaitingForChildrenAsBusy, and only waited on ids that
were Queued or Running. A task with children reports "changed" the moment it
reaches WaitingForChildren, so such a task both dropped out of the wait set and
signalled completion early -- the handler could reach review/merge while
children were still running.
This commit is contained in:
mika kuns
2026-08-06 21:24:03 +02:00
parent 028ac57398
commit c4e4e0976a
2 changed files with 25 additions and 2 deletions
+4 -2
View File
@@ -470,10 +470,12 @@ public static class PromptFiles
Then, for each surviving task:
- Idle or Failed update_task_status(id, "Queued"). For a Failed task ask first whether to reset_failed_task and re-queue it, or skip it.
- Queued leave it; it is already waiting for a slot.
- Running or WaitingForChildren leave it; only poll.
- Running or WaitingForChildren leave it; the wait below covers it.
- WaitingForReview leave it; it goes straight to Phase 4.
Call wait_for_task_change with the ids of every task still Queued or Running (timeoutSeconds up to 170) instead of sleeping and polling get_task yourself. It returns as soon as any of them leaves Queued/Running WaitingForReview on success, Failed on error or reports timedOut if none did. Report progress as tasks land, then call it again with whatever ids are still Queued/Running until none remain.
Then wait with wait_for_task_change instead of sleeping and polling get_task yourself. Pass the ids of every task not yet in WaitingForReview or a terminal status Queued, Running and WaitingForChildren alike and set treatWaitingForChildrenAsBusy=true. Without that flag a task with children returns the moment it goes Running WaitingForChildren, while its children are still working, and you would walk into Phase 4 with unfinished work. Use timeoutSeconds 900: the server clamps there anyway, and ClaudeDo's launchers already raise MCP_TOOL_TIMEOUT above it, so one long wait costs one turn where six short ones cost six.
It returns as soon as a task reaches WaitingForReview or fails, or reports timedOut if none did. Report progress as tasks land, then call it again with whatever ids are still outstanding until none remain.
## Phase 4 Review and merge
Before merging anything, call preview_merge_set with every surviving task's id (the same targetBranch you are about to merge into). It tells you, per task, whether a clean merge-tree preview is even possible (status/conflictFiles/changedFileCount/behind) and which files more than one of the tasks changed (overlaps). Read the overlaps: a file two tasks both touch is where a same-branch collision could happen. This is a HINT, not proof it only catches same-file overlap, not a cross-file break (e.g. one task deletes a symbol another task's file still references), and a clean preview never guarantees the result compiles or passes tests. Use it to decide merge order and to know which pairs to look at extra carefully in step 1 below; it does not replace reading the diffs.
@@ -143,6 +143,27 @@ public class PromptFilesTests
Assert.DoesNotContain("run_task_now(", d); // single override slot — must not batch-start
}
[Fact]
public void DefaultFor_merge_helper_execute_waits_with_the_real_server_side_cap()
{
// TaskWaitMcpTools.MaxTimeoutSeconds is 900 and every ClaudeDo launcher sets
// MCP_TOOL_TIMEOUT=930000ms. The prompt used to say 170 -- a leftover from the retired
// 200000ms era -- which burned ~5x the turns on re-waiting.
var d = PromptFiles.DefaultFor(PromptKind.MergeHelperExecute);
Assert.Contains("timeoutSeconds 900", d);
Assert.DoesNotContain("170", d);
}
[Fact]
public void DefaultFor_merge_helper_execute_waits_through_waiting_for_children()
{
// Without the flag, a parent with children reports "changed" as soon as it reaches
// WaitingForChildren, so the handler would advance to review/merge while children run.
var d = PromptFiles.DefaultFor(PromptKind.MergeHelperExecute);
Assert.Contains("treatWaitingForChildrenAsBusy=true", d);
Assert.Contains("WaitingForChildren", d);
}
[Fact]
public void DefaultFor_merge_helper_execute_keeps_the_shared_checkout_git_guard()
{