feat(data): add dependency audit and diff-review threshold to list-handler prompts
Triage phase now audits dependsOnTaskId chains before handoff: distinguishes real from artificial (plan-numbered) dependencies, resolves artificial ones via update_task, groups survivors into a wave plan, and checks the wave count against the 4-handoff cap (2 merge rounds) so a chain too long to finish says so explicitly instead of stranding mid-run. Merge phase now reads a task's own diff when preview_merge_set's changedFileCount is below 5, reserving the sonnet review subagent for larger diffs where the context-protection cost is actually worth it. Updates DefaultFor_merge_helper_merge_never_reads_diffs_itself, renamed to DefaultFor_merge_helper_merge_gates_the_diff_subagent_on_a_file_count_threshold, to match the new threshold behavior.
This commit is contained in:
@@ -448,6 +448,15 @@ public static class PromptFiles
|
|||||||
|
|
||||||
Cancel nothing without an explicit answer.
|
Cancel nothing without an explicit answer.
|
||||||
|
|
||||||
|
## Phase 1.5 — Audit dependencies
|
||||||
|
For every surviving task that carries a dependsOnTaskId, decide whether the link is real or artificial before you hand off. Real: task B genuinely needs something task A produces — a symbol/type B's code references, a changed signature, a migration that must land before repository code assumes it. Artificial: the tasks just got numbered in sequence by the plan but touch disjoint files for unrelated reasons. Task descriptions usually name their target files explicitly — use that to check for real file/symbol overlap instead of trusting the order.
|
||||||
|
|
||||||
|
Resolve every artificial link with update_task(taskId, dependsOnTaskId="") — a chain numbered 1→2→3→... is the default shape to suspect, not the default shape to trust.
|
||||||
|
|
||||||
|
Group what's left into waves: batches that can run in parallel, where a later wave depends only on tasks in an earlier one. Report this as a wave plan (e.g. "[3] → [4,5,6] → [7]"), not a flat list — the Wait/Merge sessions after you need the batching, not just which ids survived.
|
||||||
|
|
||||||
|
Then check the wave count against the handoff cap: this list-handler chain allows at most 4 handoffs total (wait, merge, wait_final, merge_final), which is exactly two merge rounds. If your wave plan needs more than two rounds to clear, say so explicitly when you hand off — name which waves won't fit — so the Merge session ends by telling the user "the rest needs manual approval in the UI" instead of silently stranding mid-chain.
|
||||||
|
|
||||||
## Phase 2 — Enhance for execution
|
## Phase 2 — Enhance for execution
|
||||||
Before touching any task, check the list's verify command ONCE for this whole run (not per task): call get_list_config. If verifyCommand is already set, leave it alone — never overwrite an existing one — and move straight to sharpening tasks below.
|
Before touching any task, check the list's verify command ONCE for this whole run (not per task): call get_list_config. If verifyCommand is already set, leave it alone — never overwrite an existing one — and move straight to sharpening tasks below.
|
||||||
|
|
||||||
@@ -471,7 +480,7 @@ public static class PromptFiles
|
|||||||
Once every surviving task is enhanced, print your triage summary — one line per task from the brief:
|
Once every surviving task is enhanced, print your triage summary — one line per task from the brief:
|
||||||
title — dedupe action (kept / merged into X / cancelled as duplicate of X) — enhanced (yes/no).
|
title — dedupe action (kept / merged into X / cancelled as duplicate of X) — enhanced (yes/no).
|
||||||
|
|
||||||
Then call handoff_list_handler(taskId, survivingTaskIds, nextPhase: "wait"). That opens a fresh session to carry out the wait/merge phases with just that list, without dragging along this session's dedupe/rewrite context. Say a short goodbye line, then stop — do not continue into phase 3 yourself.
|
Then print the wave plan from Phase 1.5, including any "won't fit in two rounds" note, and call handoff_list_handler(taskId, survivingTaskIds, nextPhase: "wait"). That opens a fresh session to carry out the wait/merge phases with just that list, without dragging along this session's dedupe/rewrite context. Say a short goodbye line, then stop — do not continue into phase 3 yourself.
|
||||||
""";
|
""";
|
||||||
|
|
||||||
private const string MergeHelperWaitDefault = """
|
private const string MergeHelperWaitDefault = """
|
||||||
@@ -505,7 +514,7 @@ public static class PromptFiles
|
|||||||
|
|
||||||
One task at a time. Default to the order the brief lists them. If the overlap check shows two or more tasks touching the same file, tell the user which tasks collide and merge those in an order you can justify (e.g. the one making the smaller change first) — deviate from brief order only with that stated reason.
|
One task at a time. Default to the order the brief lists them. If the overlap check shows two or more tasks touching the same file, tell the user which tasks collide and merge those in an order you can justify (e.g. the one making the smaller change first) — deviate from brief order only with that stated reason.
|
||||||
|
|
||||||
1. Never read a task's diff yourself. Instead start a sonnet subagent (Task tool) per task to inspect get_task_diff and sanity-check it against the task's title and description; only its short verdict (clean / risky + reason) comes back into your own context. If preview_merge_set flagged this task in an overlap, tell the subagent to also skim the diff of the other task(s) sharing that file.
|
1. Check this task's changedFileCount from preview_merge_set first. Below 5 changed files, read get_task_diff yourself and sanity-check it against the task's title and description — a subagent hop is wasted context for a diff that small. At 5 or more, delegate instead: start a sonnet subagent (Task tool) to inspect get_task_diff and sanity-check it, so only its short verdict (clean / risky + reason) comes back into your own context — the whole point of never reading a large diff yourself. If preview_merge_set flagged this task in an overlap, also skim (or have the subagent skim) the diff of the other task(s) sharing that file.
|
||||||
2. Before approving, cross-check changedFileCount from preview_merge_set (or the length of get_task_diff's files list — the diff tool has no changedFileCount field). 0 changed files means an empty branch, not a clean success — do not approve it; treat it like a risky diff instead.
|
2. Before approving, cross-check changedFileCount from preview_merge_set (or the length of get_task_diff's files list — the diff tool has no changedFileCount field). 0 changed files means an empty branch, not a clean success — do not approve it; treat it like a risky diff instead.
|
||||||
3. If the subagent's verdict is risky, or the change looks wrong or incomplete, STOP and ask the user before merging — offer reject_rerun (with feedback) or skip.
|
3. If the subagent's verdict is risky, or the change looks wrong or incomplete, STOP and ask the user before merging — offer reject_rerun (with feedback) or skip.
|
||||||
4. Otherwise merge with review_task(taskId, decision="approve", leaveConflictsInTree=true, skipVerify=true). skipVerify defers the list's verify command to ONE verify_merges call after the last merge (below) instead of paying it per merge.
|
4. Otherwise merge with review_task(taskId, decision="approve", leaveConflictsInTree=true, skipVerify=true). skipVerify defers the list's verify command to ONE verify_merges call after the last merge (below) instead of paying it per merge.
|
||||||
|
|||||||
@@ -221,11 +221,14 @@ public class PromptFilesTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DefaultFor_merge_helper_merge_never_reads_diffs_itself()
|
public void DefaultFor_merge_helper_merge_gates_the_diff_subagent_on_a_file_count_threshold()
|
||||||
{
|
{
|
||||||
|
// Below 5 changed files a subagent hop is pure context-protection overhead for a tiny
|
||||||
|
// diff; the threshold keeps the subagent mandatory once a diff is actually large.
|
||||||
var d = PromptFiles.DefaultFor(PromptKind.MergeHelperMerge);
|
var d = PromptFiles.DefaultFor(PromptKind.MergeHelperMerge);
|
||||||
Assert.Contains("Never read a task's diff yourself", d);
|
Assert.Contains("Below 5 changed files", d);
|
||||||
Assert.Contains("sonnet subagent", d);
|
Assert.Contains("sonnet subagent", d);
|
||||||
|
Assert.Contains("changedFileCount", d);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
Reference in New Issue
Block a user