fix(FN-2392): normalize skill-facing tool references to fn_*

- Update Fusion skill docs, prompts, and capability references to use public fn_* tool names consistently
- Align engine system prompts and tool schemas for messaging/task actions with fn_send_message, fn_read_messages, fn_task_* naming
- Refresh related tests across CLI, engine, dashboard, and core to match normalized tool naming and behavior
- Add a patch changeset for @runfusion/fusion describing the skill-tool namespace normalization
This commit is contained in:
Fusion
2026-04-24 03:11:09 -07:00
committed by gsxdsm
parent fa0cbe2327
commit eef56af706
33 changed files with 927 additions and 764 deletions

View File

@@ -117,8 +117,8 @@ Follow this structure exactly:
### Step {N}: Documentation & Delivery
- [ ] Update relevant documentation
- [ ] Save documentation deliverables as task documents via \`task_document_write\` (key="docs", content=...)
- [ ] Out-of-scope findings created as new tasks via \`task_create\` tool
- [ ] Save documentation deliverables as task documents via \`fn_task_document_write\` (key="docs", content=...)
- [ ] Out-of-scope findings created as new tasks via \`fn_task_create\` tool
## Documentation Requirements
@@ -151,7 +151,7 @@ Commits at step boundaries. All commits include the task ID:
- Refuse necessary fixes just because they touch files outside the initial File Scope
- Commit without the task ID prefix
- Remove, delete, or gut modules, settings, interfaces, exports, or test files outside the File Scope
- Remove features as "cleanup" — if something seems unused, create a task via \`task_create\`
- Remove features as "cleanup" — if something seems unused, create a task via \`fn_task_create\`
## Changeset Requirements
@@ -173,13 +173,13 @@ tests. Manual verification is NOT a test.
as part of this task (not just skipping tests)
## Duplicate check
Before writing a spec, call \`task_list\` to see existing tasks.
Before writing a spec, call \`fn_task_list\` to see existing tasks.
If a task already covers the same work (even if worded differently), do NOT
write a PROMPT.md. Instead, write a single line to the output file:
\`DUPLICATE: {existing-task-id}\`
## Dependency awareness
When you plan to list a task in the \`## Dependencies\` section, first call \`task_get\` on that task ID to read its PROMPT.md.
When you plan to list a task in the \`## Dependencies\` section, first call \`fn_task_get\` on that task ID to read its PROMPT.md.
Use what you learn — file scope, APIs, patterns, completion criteria — to make the new spec accurate: reference the right paths, avoid conflicting assumptions, and describe what the dependency must deliver before this task starts.
If the dependency task has no PROMPT.md yet (not yet specified), note that in the Dependencies section.
@@ -187,8 +187,8 @@ If the dependency task has no PROMPT.md yet (not yet specified), note that in th
When the task includes \`breakIntoSubtasks: true\`, first decide whether it should be split.
- Split only when the work is meaningfully decomposable into 2-5 independently executable child tasks.
- If splitting: use the \`task_create\` tool to create child tasks in triage, include clear descriptions and dependencies between them, then stop. Do NOT write a PROMPT.md for the parent task.
- **CRITICAL — subtask dependencies:** the parent task is deleted once all subtasks are created. \`dependencies\` on a new subtask may ONLY reference sibling subtasks you have created earlier in this same split (or unrelated existing tasks). **Never depend on the parent task's id.** If a child conceptually "waits for the parent's remaining work", create a sibling subtask that does that work and depend on the sibling instead. The \`task_create\` tool will reject parent-id dependencies with an error.
- If splitting: use the \`fn_task_create\` tool to create child tasks in triage, include clear descriptions and dependencies between them, then stop. Do NOT write a PROMPT.md for the parent task.
- **CRITICAL — subtask dependencies:** the parent task is deleted once all subtasks are created. \`dependencies\` on a new subtask may ONLY reference sibling subtasks you have created earlier in this same split (or unrelated existing tasks). **Never depend on the parent task's id.** If a child conceptually "waits for the parent's remaining work", create a sibling subtask that does that work and depend on the sibling instead. The \`fn_task_create\` tool will reject parent-id dependencies with an error.
- If not splitting: proceed with a normal PROMPT.md specification.
## Proactive Subtask Breakdown for M/L Tasks
@@ -211,20 +211,20 @@ For tasks you assess as Size M or L, proactively evaluate whether splitting into
## Triage tools
You have these extra tools during triage:
- \`task_list\` — list existing active tasks
- \`task_get\` — inspect a task and its PROMPT.md
- \`task_create\` — create a child/follow-up task while triaging
- \`task_document_write\` — save a planning document (e.g., key="plan")
- \`task_document_read\` — read back a previously saved document
- \`fn_task_list\` — list existing active tasks
- \`fn_task_get\` — inspect a task and its PROMPT.md
- \`fn_task_create\` — create a child/follow-up task while triaging
- \`fn_task_document_write\` — save a planning document (e.g., key="plan")
- \`fn_task_document_read\` — read back a previously saved document
When the planning conversation produces a structured plan, save it as a document with \`task_document_write(key='plan', content='...')\` so the executor can reference it during implementation.
When the planning conversation produces a structured plan, save it as a document with \`fn_task_document_write(key='plan', content='...')\` so the executor can reference it during implementation.
## Guidelines
- Read the project structure and relevant source files to understand context BEFORE writing
- Be specific — name actual files, functions, and patterns from the codebase
- Steps should express OUTCOMES, not micro-instructions (2-5 checkboxes per step)
- Always include a testing step and a documentation step
- For tasks whose primary deliverable is documentation (updating docs, writing README, API references), include an explicit step or checkbox instructing the executor to save the final documentation content via \`task_document_write\`
- For tasks whose primary deliverable is documentation (updating docs, writing README, API references), include an explicit step or checkbox instructing the executor to save the final documentation content via \`fn_task_document_write\`
- Include a "Do NOT" section with project-appropriate guardrails
- Size assessment: S (<2h), M (2-4h), L (4-8h). Split if XL (8h+)
- Review level scoring: Blast radius (0-2), Pattern novelty (0-2), Security (0-2), Reversibility (0-2)
@@ -238,16 +238,16 @@ package.json when explicit commands are provided.
## Spec Review
After writing the PROMPT.md, call \`review_spec()\` to get an independent quality review.
After writing the PROMPT.md, call \`fn_review_spec()\` to get an independent quality review.
- **APPROVE** → your spec is accepted, you're done
- **REVISE** → fix the issues described in the review feedback, rewrite the PROMPT.md, and call \`review_spec()\` again. Repeat until approved.
- **REVISE** → fix the issues described in the review feedback, rewrite the PROMPT.md, and call \`fn_review_spec()\` again. Repeat until approved.
- **RETHINK** → your approach was fundamentally rejected. The conversation will rewind. Read the feedback carefully and take a completely different approach. Do NOT repeat the rejected strategy.
You MUST call \`review_spec()\` after writing the PROMPT.md. Do not finish without getting an APPROVE verdict.
You MUST call \`fn_review_spec()\` after writing the PROMPT.md. Do not finish without getting an APPROVE verdict.
## Output
Write the PROMPT.md directly using the write tool, then call \`review_spec()\` for review.
Write the PROMPT.md directly using the write tool, then call \`fn_review_spec()\` for review.
## Frontend UX Criteria Injection
@@ -621,11 +621,11 @@ export class TriageProcessor {
/**
* Specify a triage task by spawning an AI agent to generate a PROMPT.md.
*
* After the agent writes the PROMPT.md, it calls `review_spec()` to spawn
* After the agent writes the PROMPT.md, it calls `fn_review_spec()` to spawn
* an independent reviewer agent that evaluates the specification quality.
* The review loop works as follows:
* - **APPROVE**: the spec is accepted and the task moves to `todo`
* - **REVISE**: the agent revises the spec and calls `review_spec()` again.
* - **REVISE**: the agent revises the spec and calls `fn_review_spec()` again.
* If the agent finishes without getting APPROVE, the task is NOT moved to
* `todo` — a post-session gate requires an explicit APPROVE verdict.
* - **RETHINK**: the conversation rewinds to a pre-specification checkpoint
@@ -670,7 +670,7 @@ export class TriageProcessor {
// Mutable ref — populated after createFnAgent, tools access lazily via closure
const sessionRef: { current: AgentSession | null } = { current: null };
// Checkpoint for RETHINK rewind — captured lazily on first review_spec call
// Checkpoint for RETHINK rewind — captured lazily on first fn_review_spec call
const checkpointRef: { current: string | null } = { current: null };
// Track the last spec review verdict for post-session enforcement
const specReviewVerdictRef: { current: ReviewVerdict | null } = {
@@ -789,7 +789,7 @@ export class TriageProcessor {
"triage",
);
// Make session available to review_spec tool (for RETHINK rewind)
// Make session available to fn_review_spec tool (for RETHINK rewind)
sessionRef.current = session;
// Register session so the global pause listener can terminate it
@@ -875,7 +875,7 @@ export class TriageProcessor {
triageLog.log(`${task.id} split into subtasks (${childTaskIds}) and closed`);
} catch (err: unknown) {
// deleteTask refuses when live tasks still depend on this id.
// If task_create's validation worked correctly this branch is
// If fn_task_create's validation worked correctly this branch is
// unreachable, but we keep it as defense-in-depth: leaving the
// parent alive is always safer than stranding dependents.
const msg = err instanceof Error ? err.message : String(err);
@@ -902,7 +902,7 @@ export class TriageProcessor {
if (canRetryWithPlanningFallback) {
const verdictDesc =
specReviewVerdictRef.current === null
? "review_spec was never called"
? "fn_review_spec was never called"
: `verdict was ${specReviewVerdictRef.current}`;
const fallbackDesc = `${planningFallbackProvider}/${planningFallbackModelId}`;
triageLog.warn(
@@ -979,7 +979,7 @@ export class TriageProcessor {
if (specReviewVerdictRef.current !== "APPROVE") {
const verdictDesc =
specReviewVerdictRef.current === null
? "review_spec was never called"
? "fn_review_spec was never called"
: `verdict was ${specReviewVerdictRef.current}`;
const decision = computeRecoveryDecision({
recoveryRetryCount: task.recoveryRetryCount,
@@ -1195,7 +1195,7 @@ export class TriageProcessor {
});
const taskList: ToolDefinition = {
name: "task_list",
name: "fn_task_list",
label: "List Tasks",
description:
"List all tasks that aren't done. Returns ID, description, column, " +
@@ -1225,7 +1225,7 @@ export class TriageProcessor {
};
const taskGet: ToolDefinition = {
name: "task_get",
name: "fn_task_get",
label: "Get Task",
description:
"Get full details of a specific task including its PROMPT.md content. " +
@@ -1254,7 +1254,7 @@ export class TriageProcessor {
};
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : String(err);
triageLog.warn(`${options.parentTaskId}: task_get lookup failed for ${params.id}: ${msg}`);
triageLog.warn(`${options.parentTaskId}: fn_task_get lookup failed for ${params.id}: ${msg}`);
return {
content: [
{ type: "text" as const, text: `Task ${params.id} not found.` },
@@ -1266,7 +1266,7 @@ export class TriageProcessor {
};
const taskCreate: ToolDefinition = {
name: "task_create",
name: "fn_task_create",
label: "Create Child Task",
description:
"Create a child task (subtask) while breaking a larger task into smaller pieces. " +
@@ -1282,7 +1282,7 @@ export class TriageProcessor {
_callId: string,
params: Static<typeof taskCreateParams>,
) => {
// task_create is always available during triage to support both
// fn_task_create is always available during triage to support both
// explicit breakIntoSubtasks and proactive splitting of oversized tasks.
try {
// Validate dependencies before creating the child:
@@ -1328,8 +1328,8 @@ export class TriageProcessor {
{
type: "text" as const,
text:
`ERROR: task_create rejected. Invalid dependencies:\n${summary}\n\n` +
`Remove or replace these ids and call task_create again.`,
`ERROR: fn_task_create rejected. Invalid dependencies:\n${summary}\n\n` +
`Remove or replace these ids and call fn_task_create again.`,
},
],
details: { rejectedDependencies: rejected },
@@ -1342,7 +1342,7 @@ export class TriageProcessor {
parentTask = await store.getTask(options.parentTaskId);
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : String(err);
triageLog.warn(`${options.parentTaskId}: failed to load parent task for task_create inheritance: ${msg}`);
triageLog.warn(`${options.parentTaskId}: failed to load parent task for fn_task_create inheritance: ${msg}`);
// Parent task not found or error - proceed without inheritance
parentTask = undefined;
}
@@ -1389,13 +1389,13 @@ export class TriageProcessor {
}
/**
* Create the `review_spec` tool for the triage agent.
* Create the `fn_review_spec` tool for the triage agent.
*
* Spawns an independent reviewer agent to evaluate the generated PROMPT.md.
* Verdict handling:
* - **APPROVE**: returns "APPROVE" — the triage agent's work is done.
* - **REVISE**: returns the review feedback. The triage agent must fix the
* PROMPT.md and call `review_spec` again. A post-session gate in
* PROMPT.md and call `fn_review_spec` again. A post-session gate in
* `specifyTask()` prevents moving to `todo` if the last verdict is REVISE.
* - **RETHINK**: rewinds the conversation to a pre-specification checkpoint
* using `session.navigateTree()`. Returns a re-prompt instructing the agent
@@ -1421,7 +1421,7 @@ export class TriageProcessor {
const options = this.options;
return {
name: "review_spec",
name: "fn_review_spec",
label: "Review Specification",
description:
"Spawn a reviewer agent to evaluate the generated PROMPT.md specification. " +
@@ -1448,7 +1448,7 @@ export class TriageProcessor {
"utf-8",
).catch((err: unknown) => {
const msg = err instanceof Error ? err.message : String(err);
triageLog.warn(`${taskId}: failed to read PROMPT.md for review_spec (${promptPath}): ${msg}`);
triageLog.warn(`${taskId}: failed to read PROMPT.md for fn_review_spec (${promptPath}): ${msg}`);
return "";
});
@@ -1457,7 +1457,7 @@ export class TriageProcessor {
content: [
{
type: "text" as const,
text: "UNAVAILABLE — PROMPT.md file not found or empty. Write the specification first, then call review_spec.",
text: "UNAVAILABLE — PROMPT.md file not found or empty. Write the specification first, then call fn_review_spec.",
},
],
details: {},
@@ -1525,7 +1525,7 @@ export class TriageProcessor {
text = "APPROVE";
break;
case "REVISE":
text = `REVISE — fix the issues below, rewrite the PROMPT.md, and call review_spec() again.\n\n${result.review}`;
text = `REVISE — fix the issues below, rewrite the PROMPT.md, and call fn_review_spec() again.\n\n${result.review}`;
break;
case "RETHINK": {
// Rewind conversation to pre-specification checkpoint
@@ -1900,12 +1900,12 @@ The user has requested that this task be broken into smaller subtasks if it is c
**How to split:**
1. First, analyze the task to determine if it should be split
2. If splitting: use the \\\`task_create\\\` tool to create child tasks in order, setting up dependencies as needed
2. If splitting: use the \\\`fn_task_create\\\` tool to create child tasks in order, setting up dependencies as needed
3. Include clear descriptions and acceptance criteria for each child task
4. After creating all subtasks, stop — do NOT write a PROMPT.md for the parent task
5. If NOT splitting: proceed with a normal PROMPT.md specification for this task
**Subtask dependencies rule:** \`dependencies\` on a child may only reference **sibling subtasks created earlier in this same split** or **pre-existing tasks in the store**. They must NEVER reference the parent task being split — the parent is deleted after the split completes, and a dependency on a deleted task permanently blocks the dependent. If a child "needs the rest of the parent's work to finish first", create another sibling subtask for that remaining work and depend on the sibling. The \`task_create\` tool rejects parent-id dependencies.
**Subtask dependencies rule:** \`dependencies\` on a child may only reference **sibling subtasks created earlier in this same split** or **pre-existing tasks in the store**. They must NEVER reference the parent task being split — the parent is deleted after the split completes, and a dependency on a deleted task permanently blocks the dependent. If a child "needs the rest of the parent's work to finish first", create another sibling subtask for that remaining work and depend on the sibling. The \`fn_task_create\` tool rejects parent-id dependencies.
**Important:** If you create subtasks, this parent task will be closed and replaced by the children. Make sure each child is a complete, executable task.`;
} else {
@@ -1931,7 +1931,7 @@ The user did not explicitly request subtask breakdown, so you should first asses
- Adding a small feature to one module with 5 steps
**How to decide:**
- If you choose to split: use the \\\`task_create\\\` tool to create the child tasks, set dependencies where needed, and then stop without writing a PROMPT.md for the parent task.
- If you choose to split: use the \\\`fn_task_create\\\` tool to create the child tasks, set dependencies where needed, and then stop without writing a PROMPT.md for the parent task.
- **Subtask dependencies must only reference sibling subtasks created earlier in this same split, or pre-existing tasks. NEVER depend on the parent task being split — the parent is deleted after splitting, and the tool will reject parent-id dependencies.**
- If the work appears to be Size S, or if an M/L task genuinely has 5 or fewer focused steps with a clear scope, proceed with a normal PROMPT.md specification.
- If size is uncertain at first, make a quick assessment from the available context before deciding.`;