fix(core): restore executor workflow creation guidance (#3513)

## Summary
- restore explicit executor guidance for assigning workflows to tasks
the agent creates
- keep the existing prohibition on rerouting the task currently being
executed
- restore parity between both built-in executor prompt variants and
their regression test

## Test plan
- `pnpm --filter @fusion/core exec vitest run --silent=passed-only
--reporter=dot src/__tests__/agent-prompts.test.ts`
- `pnpm --filter @fusion/core typecheck`
- `pnpm check:changesets`
- `pnpm exec eslint packages/core/src/agents/agent-prompts.ts`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Improvements**
* Executor workflow guidance now appears only when task creation or
delegation capabilities are available.
* Built-in executor prompts provide clearer task-assignment instructions
based on available capabilities.
  * Custom executor prompts remain unchanged.
* Removed outdated workflow-setting guidance when task-management
capabilities are unavailable.

* **Tests**
* Added coverage for task creation, delegation, and capability-specific
workflow guidance scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Phil Larson
2026-08-23 16:45:03 -07:00
committed by GitHub
parent 6c2a461816
commit 38edc2366b
6 changed files with 122 additions and 2 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Match executor workflow guidance to the task-creation tools available in each session.
category: fix
dev: Built-in executor variants render created-task workflow guidance only on creator-capable tool surfaces.

View File

@@ -240,6 +240,30 @@ describe("resolveAgentPrompt", () => {
}
});
it("renders created-task workflow guidance only for creation tools on the resolved surface", () => {
const unavailable = resolveAgentPrompt("executor", undefined, {
taskCreateToolAvailable: false,
delegateTaskToolAvailable: false,
});
expect(unavailable).not.toContain("set the workflow on tasks you create");
const taskCreateOnly = resolveAgentPrompt("executor", undefined, {
taskCreateToolAvailable: true,
delegateTaskToolAvailable: false,
});
expect(taskCreateOnly).toContain("set the workflow on tasks you create via `fn_task_create`");
expect(taskCreateOnly).not.toContain("set the workflow on tasks you create via `fn_delegate_task`");
const seniorDelegateOnly = resolveAgentPrompt("executor", {
roleAssignments: { executor: "senior-engineer" },
}, {
taskCreateToolAvailable: false,
delegateTaskToolAvailable: true,
});
expect(seniorDelegateOnly).toContain("set the workflow on tasks you create via `fn_delegate_task`");
expect(seniorDelegateOnly).not.toContain("set the workflow on tasks you create via `fn_task_create`");
});
it("senior-engineer prompt limits fixes to impacted failures and follow-ups unrelated broad-suite failures", () => {
const config: AgentPromptsConfig = {
roleAssignments: {

View File

@@ -1381,6 +1381,34 @@ export const BUILTIN_AGENT_PROMPTS: readonly AgentPromptTemplate[] = [
*/
export interface ResolveAgentPromptOptions {
plannerHeartbeatPatrolEnabled?: boolean;
taskCreateToolAvailable?: boolean;
delegateTaskToolAvailable?: boolean;
}
const EXECUTOR_WORKFLOW_SELECTION_GUARDRAIL = "- Do not call `fn_workflow_select` to change the workflow of the task you are executing; you did not create that task, the user or triage did. The only exception is when the user explicitly requested a specific workflow for this task in a steering comment, task instruction, or similar direct instruction.";
/*
FNXC:WorkflowRouting 2026-08-23-18:49:
Workflow assignment guidance must match the executor's real tool surface. Built-in creator-capable
personas may set workflows on tasks they create, while task-execution sessions omit this permission
because they structurally withhold both creation tools. Render the clause at resolution time so the
default and senior-engineer variants cannot drift from session capabilities.
*/
function renderExecutorWorkflowRoutingToolSurface(
prompt: string,
options: ResolveAgentPromptOptions,
): string {
const availableCreationTools = [
...(options.taskCreateToolAvailable !== false ? ["`fn_task_create`"] : []),
...(options.delegateTaskToolAvailable !== false ? ["`fn_delegate_task`"] : []),
];
if (availableCreationTools.length === 0) return prompt;
const toolList = availableCreationTools.join(" or ");
return prompt.replace(
EXECUTOR_WORKFLOW_SELECTION_GUARDRAIL,
`${EXECUTOR_WORKFLOW_SELECTION_GUARDRAIL}\n- You may still set the workflow on tasks you create via ${toolList}.`,
);
}
export function resolveAgentPrompt(
@@ -1410,6 +1438,9 @@ export function resolveAgentPrompt(
if (role === PLANNER_AGENT_ROLE && template.builtIn && template.id === "concise-triage") {
return `${CONCISE_TRIAGE_PROMPT_TEXT}\n\n${buildConciseTriageHeartbeatGuidance(options)}`;
}
if (role === "executor" && template.builtIn) {
return renderExecutorWorkflowRoutingToolSurface(template.prompt, options);
}
return template.prompt;
}
@@ -1418,6 +1449,9 @@ export function resolveAgentPrompt(
if (role === PLANNER_AGENT_ROLE && builtIn?.id === "default-triage") {
return `${TRIAGE_PROMPT_TEXT}\n\n${buildTriageHeartbeatGuidance(options)}`;
}
if (role === "executor" && builtIn) {
return renderExecutorWorkflowRoutingToolSurface(builtIn.prompt, options);
}
return builtIn?.prompt ?? "";
}

View File

@@ -3080,6 +3080,9 @@ describe("executor base prompt runtime self-awareness", () => {
});
describe("completion recommendation prompt contract", () => {
const getCreatedTaskWorkflowRoutingClause = (prompt: string): string | undefined =>
prompt.split("\n").find((line) => line.startsWith("- You may still set the workflow on tasks you create via "));
it.each([
["built-in default", { agentPrompts: undefined }],
["custom executor prompt", {
@@ -3121,6 +3124,48 @@ describe("completion recommendation prompt contract", () => {
expect(customDisabledPrompt).toMatch(/Always send recommendations\.[\s\S]*Ignore any earlier generic recommendation guidance/);
});
/*
FNXC:WorkflowRouting 2026-08-23-13:25:
The production executor prompt resolver treats creation tools as available by default. Only an
explicit withheld=true removes that tool's created-task workflow guidance; omitted and empty
availability inputs preserve both clauses, while one withheld tool preserves the other clause.
*/
it.each([
["task creation only", { taskCreateWithheld: false, delegateWithheld: true }, "`fn_task_create`", "`fn_delegate_task`"],
["delegation only", { taskCreateWithheld: true, delegateWithheld: false }, "`fn_delegate_task`", "`fn_task_create`"],
])("renders workflow routing for the %s surface", async (_label, availability, presentTool, absentTool) => {
const { getExecutorSystemPrompt } = await import("../executor.js");
const prompt = getExecutorSystemPrompt({ agentPrompts: undefined } as any, availability);
const clause = getCreatedTaskWorkflowRoutingClause(prompt);
expect(clause).toBe(`- You may still set the workflow on tasks you create via ${presentTool}.`);
expect(clause).not.toContain(absentTool);
});
it.each([
["omitted availability", undefined],
["empty availability", {}],
])("keeps both workflow creation clauses for %s", async (_label, availability) => {
const { getExecutorSystemPrompt } = await import("../executor.js");
const prompt = getExecutorSystemPrompt({ agentPrompts: undefined } as any, availability);
expect(getCreatedTaskWorkflowRoutingClause(prompt)).toBe(
"- You may still set the workflow on tasks you create via `fn_task_create` or `fn_delegate_task`.",
);
expect(prompt).not.toContain("Follow-up task creation is disabled for this session");
});
it("removes created-task workflow guidance when both creation tools are withheld", async () => {
const { getExecutorSystemPrompt } = await import("../executor.js");
const prompt = getExecutorSystemPrompt(
{ agentPrompts: undefined } as any,
{ taskCreateWithheld: true, delegateWithheld: true },
);
expect(getCreatedTaskWorkflowRoutingClause(prompt)).toBeUndefined();
expect(prompt).toContain("Task-execution sessions structurally withhold `fn_task_create` and `fn_delegate_task`");
});
it.each([
["only task creation is withheld", { taskCreateWithheld: true }],
["only delegation is withheld", { delegateWithheld: true }],

View File

@@ -570,7 +570,8 @@ describe("Code review verdict enforcement - fn_task_update blocking", () => {
expect(capturedSystemPrompt).toContain("Do not call `fn_workflow_select` to change the workflow of the task you are executing");
expect(capturedSystemPrompt).toContain("The only exception is when the user explicitly requested a specific workflow for this task");
expect(capturedSystemPrompt).toContain("Implement required in-scope work directly here");
expect(capturedSystemPrompt).not.toContain("You may still set the workflow on tasks you create via `fn_task_create` or `fn_delegate_task`");
expect(capturedSystemPrompt).not.toContain("set the workflow on tasks you create");
expect(capturedSystemPrompt).toContain("Task-execution sessions structurally withhold `fn_task_create` and `fn_delegate_task`");
});
// Note: The EXECUTOR_SYSTEM_PROMPT constant is tested indirectly via the buildExecutionPrompt test.

View File

@@ -318,7 +318,16 @@ export function getExecutorSystemPrompt(
settings: Settings,
toolAvailability?: { taskCreateWithheld?: boolean; delegateWithheld?: boolean },
): string {
const customPrompt = resolveAgentPrompt("executor", settings.agentPrompts);
/*
FNXC:WorkflowRouting 2026-08-23-13:25:
Tool availability is opt-out at this production boundary: omitted availability, an empty object,
and omitted fields all preserve the core resolver's available-by-default contract. Only an
explicit withheld=true removes that tool's created-task workflow guidance.
*/
const customPrompt = resolveAgentPrompt("executor", settings.agentPrompts, {
taskCreateToolAvailable: toolAvailability?.taskCreateWithheld !== true,
delegateTaskToolAvailable: toolAvailability?.delegateWithheld !== true,
});
const basePrompt = customPrompt || EXECUTOR_SYSTEM_PROMPT;
/*
FNXC:TaskRecommendations 2026-08-10-01:15: