Merge pull request #937 from abeperl/feat/fn-024-add-dependencies-to-task-update

feat: Add optional dependencies parameter to fn_task_update tool
This commit is contained in:
gsxdsm
2026-05-25 14:18:01 -07:00
committed by GitHub
2 changed files with 47 additions and 1 deletions

View File

@@ -0,0 +1,5 @@
---
"@fusion/engine": minor
---
Add optional dependencies parameter to fn_task_update tool. Executors can now programmatically modify task dependency arrays during execution with `fn_task_update({ id: "FN-XXX", dependencies: ["FN-001", "FN-002"] })`. The parameter is optional and backward-compatible; omitting it preserves existing dependencies. Includes validation for self-dependency and non-existent task IDs. Eliminates the need for direct task.json editing workarounds.

View File

@@ -637,6 +637,9 @@ const taskUpdateParams = Type.Object({
STEP_STATUSES.map((s) => Type.Literal(s)),
{ description: "New status: pending, in-progress, done, or skipped" },
),
dependencies: Type.Optional(Type.Array(Type.String(), {
description: "Optional task dependency array. Replaces existing dependencies. Pass ['FN-001', 'FN-002'] to set dependencies. Pass [] to clear all dependencies. Omit parameter to preserve existing dependencies.",
})),
});
// taskLogParams and taskCreateParams are imported from agent-tools.ts
@@ -5487,10 +5490,11 @@ export class TaskExecutor {
description:
"Update a step's status. Call before starting a step (in-progress), " +
"after completing it (done), or to skip it (skipped). " +
"Optionally update task dependencies by passing a dependencies array. " +
"The board updates in real-time.",
parameters: taskUpdateParams,
execute: async (_id: string, params: Static<typeof taskUpdateParams>) => {
const { step, status } = params;
const { step, status, dependencies } = params;
// Record step progress for stuck task detection.
// Step transitions (in-progress, done, skipped) indicate real progress
@@ -5545,6 +5549,43 @@ export class TaskExecutor {
};
}
// Handle dependencies parameter if provided
if (dependencies !== undefined) {
// Validate: prevent self-dependency
if (dependencies.includes(taskId)) {
return {
content: [{
type: "text" as const,
text: `Cannot add self-dependency: ${taskId} cannot depend on itself.`,
}],
details: {},
};
}
// Validate: all dependency task IDs must exist
const invalidIds: string[] = [];
for (const depId of dependencies) {
try {
await store.getTask(depId);
} catch {
invalidIds.push(depId);
}
}
if (invalidIds.length > 0) {
return {
content: [{
type: "text" as const,
text: `Cannot set dependencies — the following task(s) do not exist: ${invalidIds.join(", ")}`,
}],
details: {},
};
}
// Update dependencies
await store.updateTask(taskId, { dependencies });
}
const task = await store.updateStep(taskId, stepIndex, status as StepStatus);
const stepInfo = task.steps[stepIndex];
if (!stepInfo) {