fix: route vision debate subagents correctly

This commit is contained in:
Mikael Hugo 2026-04-30 22:02:41 +02:00
parent 9a0fdbe7bd
commit 2bc8d0cdd3
3 changed files with 30 additions and 18 deletions

View file

@ -1,6 +1,6 @@
Plan milestone {{milestoneId}} ("{{milestoneTitle}}"). Read `.sf/DECISIONS.md` if it exists — respect existing decisions. Read `.sf/REQUIREMENTS.md` if it exists and treat Active requirements as the capability contract. If `REQUIREMENTS.md` is missing, continue in legacy compatibility mode but explicitly note missing requirement coverage. Use the **Roadmap** output template below to shape the milestone planning payload you send to `sf_plan_milestone`. Call `sf_plan_milestone` to persist the milestone planning fields and render `{{milestoneId}}-ROADMAP.md` from DB state. Do **not** write `{{milestoneId}}-ROADMAP.md`, `ROADMAP.md`, or other planning artifacts manually. If planning produces structural decisions, append them to `.sf/DECISIONS.md`. {{skillActivation}} Fill the Horizontal Checklist section with cross-cutting concerns considered during planning (requirements re-read, decisions re-evaluated, graceful shutdown, revenue paths, auth boundary, shared resources, reconnection). Omit for trivial milestones.
Before calling `sf_plan_milestone`, run a bounded **Vision Alignment Meeting** for the milestone and roadmap as a real multi-agent review. Use the `subagent` tool in `mode: "debate"` with `rounds: 2` and a separate task for each participant lens below. Do **not** merely simulate every participant inside this planner response. If the `subagent` tool is unavailable or fails after one retry, record that explicitly in `trigger` and run the structured meeting inline as a degraded fallback. This is allowed to be broader and more nuanced than slice planning. Include at least these participant lenses:
Before calling `sf_plan_milestone`, run a bounded **Vision Alignment Meeting** for the milestone and roadmap as a real multi-agent review. Use the `subagent` tool in `mode: "debate"` with `rounds: 2` and a separate task for each participant lens below. Do **not** merely simulate every participant inside this planner response. Use only supported agent names: `planner`, `reviewer`, `researcher`, and `scout`. Put the stakeholder role name inside the task text; do not invent agent names such as `combatant`, `delivery-lead`, `product-manager`, or `customer-panel`. If the `subagent` tool is unavailable or fails after one retry, record that explicitly in `trigger` and run the structured meeting inline as a degraded fallback. This is allowed to be broader and more nuanced than slice planning. Include at least these participant lenses:
- Product Manager
- User Advocate
- Customer Panel

View file

@ -43,7 +43,7 @@ If milestone research exists (inlined above), trust those findings and skip redu
Narrate your decomposition reasoning — why you're grouping work this way, what risks are driving the order, what verification strategy you're choosing and why. Use complete sentences rather than planner shorthand or fragmentary notes.
Before you persist the roadmap, run a bounded **Vision Alignment Meeting** as a real multi-agent review. Use the `subagent` tool in `mode: "debate"` with `rounds: 2` and a separate task for each participant lens below. Do **not** merely simulate every participant inside this planner response. If the `subagent` tool is unavailable or fails after one retry, record that explicitly in `trigger` and run the structured meeting inline as a degraded fallback. This is broader than slice planning and should feel allowed to be chatty and nuanced. Gather the strongest additions, cuts, and ordering changes from these participant lenses:
Before you persist the roadmap, run a bounded **Vision Alignment Meeting** as a real multi-agent review. Use the `subagent` tool in `mode: "debate"` with `rounds: 2` and a separate task for each participant lens below. Do **not** merely simulate every participant inside this planner response. Use only supported agent names: `planner`, `reviewer`, `researcher`, and `scout`. Put the stakeholder role name inside the task text; do not invent agent names such as `combatant`, `delivery-lead`, `product-manager`, or `customer-panel`. If the `subagent` tool is unavailable or fails after one retry, record that explicitly in `trigger` and run the structured meeting inline as a degraded fallback. This is broader than slice planning and should feel allowed to be chatty and nuanced. Gather the strongest additions, cuts, and ordering changes from these participant lenses:
- **Product Manager:** what is the real product move and what should the roadmap prove?
- **User Advocate:** what must matter for the user experience and trust surface?
- **Customer Panel:** multiple likely customer viewpoints, not a single flattened “user”.

View file

@ -50,6 +50,15 @@ const AGENT_ALIASES: Record<string, string> = {
default: "worker",
code: "reviewer",
coder: "typescript-pro",
"product-manager": "planner",
"user-advocate": "reviewer",
"customer-panel": "reviewer",
business: "planner",
"delivery-lead": "planner",
partner: "reviewer",
combatant: "reviewer",
architect: "planner",
moderator: "planner",
["g" + "sd-executor"]: "worker",
"sf-worker": "worker",
"sf-scout": "scout",
@ -918,7 +927,13 @@ function buildSubagentProcessArgs(
tmpPromptPath: string | null,
modelOverride?: string,
): string[] {
const args: string[] = ["--mode", "json", "-p", "--no-session"];
const args: string[] = [
...getBundledExtensionCliArgs(),
"--mode",
"json",
"-p",
"--no-session",
];
const modelToUse = modelOverride ?? agent.model;
if (modelToUse) args.push("--model", modelToUse);
if (agent.tools && agent.tools.length > 0)
@ -928,6 +943,14 @@ function buildSubagentProcessArgs(
return args;
}
function getBundledExtensionCliArgs(): string[] {
return (process.env.SF_BUNDLED_EXTENSION_PATHS ?? "")
.split(path.delimiter)
.map((s) => s.trim())
.filter(Boolean)
.flatMap((p) => ["--extension", p]);
}
interface SubagentLaunchSpec {
command: string;
args: string[];
@ -1204,14 +1227,9 @@ async function runSingleAgent(
let wasAborted = false;
const exitCode = await new Promise<number>((resolve) => {
const bundledPaths = (process.env.SF_BUNDLED_EXTENSION_PATHS ?? "")
.split(path.delimiter)
.map((s) => s.trim())
.filter(Boolean);
const extensionArgs = bundledPaths.flatMap((p) => ["--extension", p]);
const proc = spawn(
launchSpec.command,
[...extensionArgs, ...launchSpec.args],
launchSpec.args,
{
cwd: cwd ?? defaultCwd,
env: launchSpec.env,
@ -1388,15 +1406,9 @@ async function runSingleAgentInCmuxSplit(
);
}
const bundledPaths = (process.env.SF_BUNDLED_EXTENSION_PATHS ?? "")
.split(path.delimiter)
.map((s) => s.trim())
.filter(Boolean);
const extensionArgs = bundledPaths.flatMap((p) => ["--extension", p]);
const launchSpec = resolveSubagentLaunchSpec([
...extensionArgs,
...buildSubagentProcessArgs(agent, task, tmpPromptPath, modelOverride),
]);
const launchSpec = resolveSubagentLaunchSpec(
buildSubagentProcessArgs(agent, task, tmpPromptPath, modelOverride),
);
const launcherPath = writeNodeSubagentLauncher(
launchSpec,
cwd ?? defaultCwd,