feat(worker): PowerUse-Addendum an die Handler-Prompts, Cap aus den Settings
HandoffRoundTracker.Register nimmt den Cap jetzt als Parameter statt aus einer Konstante; HandoffMcpTools liest ihn pro Aufruf aus app_settings. Werte unter 1 werden auf 1 geklemmt (in WorkerHub UND in Register): der Cap ist das Einzige, was eine entlaufene Handoff-Kette begrenzt, "0 = unbegrenzt" darf kein Aufrufer waehlen koennen. InteractiveLaunchSpecService.ComposeHandlerPromptAsync haengt das PowerUse- Addendum an den Phasen-Prompt, aber nur wenn globaler Schalter und Listen-Opt-in beide gesetzt sind. Bewusst ein Addendum statt einer zweiten Prompt-Garnitur: die gemeinsamen 95% bleiben an einer Stelle, und ein Run ohne PowerUse bekommt einen byte-identischen Prompt wie vorher. Das Addendum ist als Override der interaktiven Regeln formuliert (nie fragen, entscheiden und protokollieren; Konflikte loesen oder abbrechen, nie im Tree liegen lassen; Arbeit ernten, wenn eine Runde sonst leer laeuft). Drei Tests pinnen alle vier Ecken des UND, weil die Sicherheitsaussage des Features genau daran haengt.
This commit is contained in:
@@ -5,7 +5,7 @@ using System.Text.RegularExpressions;
|
||||
|
||||
namespace ClaudeDo.Data;
|
||||
|
||||
public enum PromptKind { System, Planning, PlanningInitial, Retry, DailyPrep, WeeklyReport, ImprovementChild, Refine, MergeHelperTriage, MergeHelperWait, MergeHelperMerge, MergeHelperInitial, MergeHelperHandoff }
|
||||
public enum PromptKind { System, Planning, PlanningInitial, Retry, DailyPrep, WeeklyReport, ImprovementChild, Refine, MergeHelperTriage, MergeHelperWait, MergeHelperMerge, MergeHelperInitial, MergeHelperHandoff, PowerUseAddendum }
|
||||
|
||||
/// <summary>
|
||||
/// How a prompt kind's on-disk override (if any) relates to the bundled default.
|
||||
@@ -44,6 +44,7 @@ public static class PromptFiles
|
||||
PromptKind.MergeHelperMerge => "merge-helper-merge.md",
|
||||
PromptKind.MergeHelperInitial => "merge-helper-initial.md",
|
||||
PromptKind.MergeHelperHandoff => "merge-helper-handoff.md",
|
||||
PromptKind.PowerUseAddendum => "power-use.md",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(kind))
|
||||
};
|
||||
|
||||
@@ -221,6 +222,7 @@ public static class PromptFiles
|
||||
PromptKind.MergeHelperMerge => MergeHelperMergeDefault,
|
||||
PromptKind.MergeHelperInitial => MergeHelperInitialDefault,
|
||||
PromptKind.MergeHelperHandoff => MergeHelperHandoffDefault,
|
||||
PromptKind.PowerUseAddendum => PowerUseAddendumDefault,
|
||||
_ => ""
|
||||
};
|
||||
|
||||
@@ -455,7 +457,7 @@ public static class PromptFiles
|
||||
|
||||
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.
|
||||
Then check the wave count against the handoff cap. This list-handler chain allows a limited number of handoffs (wait, merge, wait_final, merge_final) — by default 4, i.e. exactly two merge rounds, but it is configurable and may be higher for this run. Assume two rounds unless the kickoff tells you otherwise. If your wave plan needs more rounds than that 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
|
||||
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.
|
||||
@@ -620,6 +622,78 @@ public static class PromptFiles
|
||||
{finalNote}
|
||||
""";
|
||||
|
||||
// Appended to the triage/wait/merge system prompt when a run is in PowerUse (global switch AND
|
||||
// the list's own opt-in). Deliberately an addendum rather than a second set of prompts: the
|
||||
// shared 95% stays in one place, and a normal run's prompt is byte-identical to what it was.
|
||||
// It therefore has to read as an OVERRIDE of the rules above it, not as standalone instructions.
|
||||
private const string PowerUseAddendumDefault = """
|
||||
# PowerUse mode — these rules supersede the ones above
|
||||
|
||||
PowerUse is ON for this run. Everything above was written for a session with a user watching it. That assumption no longer holds, and where the two conflict, this section wins.
|
||||
|
||||
## No user exists
|
||||
|
||||
Nobody is watching and nobody can answer a question. Every instruction above that says to ask the user, confirm with the user, or stop and let the user decide is replaced by: decide it yourself, act, and record the decision in your summary. A question asked here is a hung session and a wasted run.
|
||||
|
||||
This does not lower the bar for what you may do — it removes escalation as an option, so the decisions you make must be ones you can defend in writing afterwards. Where a rule above offered "ask the user" as the safe path, the safe path is now the conservative action plus a written record, never the bold one.
|
||||
|
||||
Specifically:
|
||||
- **Duplicate pairs (Triage Phase 1):** decide them. Clear duplicate — same goal, or one fully contained in the other — fold the loser's unique content into the survivor and cancel the loser. Genuinely different intent that merely touches the same area — keep both and chain them with dependsOnTaskId so they cannot run against the same files at once.
|
||||
- **A task too vague to sharpen (Triage Phase 2):** cancel it with a recorded reason rather than guessing a specification for someone else's half-sentence. Guessing burns a whole slot on work nobody asked for.
|
||||
- **Verify command not set (Triage Phase 2):** determine it, run it once via Bash to prove it works, then set it. Never write a command you have not actually run.
|
||||
- **A risky or wrong-looking diff (Merge Phase 4):** if it contradicts the task's own acceptance criteria, reject_rerun with concrete feedback naming what was missing. If it merely looks unusual but meets the criteria, merge it and say why. If it looks actively destructive — deletes code no task asked to delete, drops a migration, rewrites unrelated files — leave it unmerged and record it as HELD FOR REVIEW. Never merge something you would describe as destructive.
|
||||
- **A conflict you cannot resolve with confidence (Merge Phase 4):** call abort_merge(taskId) and record it as HELD FOR REVIEW. Aborting is always correct over guessing: the task keeps its status, and a rerun against the new main usually removes the conflict entirely.
|
||||
|
||||
## Conflicts are the normal case
|
||||
|
||||
Never end your turn with a conflicted merge left in the working tree. A half-merged tree makes the repo unbuildable for every session and every task after you, and any verify that runs against it reports a false red.
|
||||
|
||||
Prefer get_merge_conflicts + resolve_conflict_hunk over reading marker text by hand — for a large conflict that is the only tractable route; a 200k-character marker dump is not something to read linearly.
|
||||
|
||||
The common collision shape in a parallel run is one task MOVING code out of a file while another task ADDS to it. The resolution is almost always to keep the move and re-apply the addition in the file the code moved to — not to pick a side. Picking "ours" silently deletes the other task's work.
|
||||
|
||||
When ordering merges that overlap on a file, merge the task that moves or deletes code BEFORE the task that only adds to it. A move landing second turns every addition into a conflict.
|
||||
|
||||
## Rerun budget
|
||||
|
||||
At most TWO reruns per task across the whole run, replacing the single-rerun limit above. A task that has already been rerun twice is recorded as HELD FOR REVIEW instead of getting a third attempt — three failures on the same task is a specification problem, not bad luck, and further attempts starve the rest of the run.
|
||||
|
||||
## Harvest — so the run does not starve
|
||||
|
||||
This applies to the Merge phase only, after its merges and reruns are done.
|
||||
|
||||
If you have fewer than five tasks to hand to the next round, harvest new work for this list's repo before handing off. Only for the list named in the brief — never another list or repo.
|
||||
|
||||
Sources, in this order; prefer one you have not exhausted yet:
|
||||
1. **Verify/build output** — real compiler warnings. One task per warning group.
|
||||
2. **`ponytail:` comments** — deliberate shortcuts with a named ceiling and upgrade path. Take the ones whose upgrade is small.
|
||||
3. **TODO / HACK / FIXME** — only the concretely formulated ones.
|
||||
4. **Unfinished plan slices** — `docs/superpowers/plans/*.md` whose last slices never landed. Check against `git log` before believing a slice is open. One task per slice, naming the plan path and slice number.
|
||||
5. **Test gaps at bug hotspots** — `git log --grep="^fix"` over recent commits; files with repeated fixes and no test.
|
||||
6. **Over-engineering** — the ponytail-audit skill against individual subfolders, never the whole repo at once. Deletion candidates.
|
||||
7. **Feature work** — improvements that follow clearly from what the code already does and from the plans and specs under `docs/`. Ground each one in something you actually read; a feature you cannot point at a file for is a guess, not a proposal.
|
||||
|
||||
Rules for every harvested task, without exception:
|
||||
- Write it the way the Triage phase would: concrete acceptance criteria, real file paths found with Read/Grep/Glob, and what is out of scope. A vague harvested task wastes a whole slot.
|
||||
- Keep it narrow — one concern, ideally under five changed files. Narrow tasks merge in parallel; broad ones collide with everything.
|
||||
- Do not hand two new tasks to the same round if they touch the same file. Stagger them across rounds, or chain them with dependsOnTaskId.
|
||||
- Tag every harvested task `createdBy: "power-use"` so a later review can tell your proposals from the ones a human asked for.
|
||||
- A task that only verifies, audits or researches and is not meant to change code must be created with `expectsNoDiff: true`, or the next Merge session will read its empty diff as a failure.
|
||||
|
||||
## Verify red is the one thing you must not paper over
|
||||
|
||||
If verify_merges returns verify_failed, the target branch is now RED. Do not try to fix it inside this session. Create ONE task with add_task(queueImmediately: true, model: "sonnet") titled `fix: verify red after this round`, put the failing output tail in its description, and hand that task to the next round as its ONLY entry. Merge nothing further until it has landed.
|
||||
|
||||
## The summary is the only record
|
||||
|
||||
Nobody saw you work, so anything not written down did not happen. In addition to the per-task lines the Merge phase already asks for, your summary must carry these sections. An empty section is a valid answer; an omitted one is not:
|
||||
|
||||
- **HELD FOR REVIEW** — every task you did not merge, with the reason.
|
||||
- **Conflicts resolved** — per conflict: the file, which two tasks collided, and which way you resolved it. Someone must be able to find your resolution without re-deriving it.
|
||||
- **Harvested** — every task you created, grouped by source.
|
||||
- **Round state** — which round this was and what you handed to the next one.
|
||||
""";
|
||||
|
||||
private const string WeeklyReportDefault = """
|
||||
You are generating a concise weekly standup report for a software developer,
|
||||
covering {start} to {end}.
|
||||
|
||||
Reference in New Issue
Block a user