FN-6193: guard merger settings lookup for deleted tasks

Keep AI merge commit finalization running when the task disappears during settings resolution.

- fall back to a synthetic task stub before merging effective settings in runAiAgentForCommit
- add an engine regression test covering deleted-task settings degradation
- add a patch changeset for the published CLI package

Files changed:
 .changeset/fn-6193-merger-deleted-task.md          |  5 +++
 .../src/__tests__/merger-settings-degrade.test.ts  | 43 ++++++++++++++++++++++
 packages/engine/src/merger.ts                      |  3 +-
 3 files changed, 50 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-6193

Fusion-Task-Lineage: 1d63d24e-1321-450c-95c9-ffc058fd8387
This commit is contained in:
gsxdsm
2026-06-10 08:07:25 -07:00
parent 529851a8fe
commit bbf3de97a0
3 changed files with 50 additions and 1 deletions

View File

@@ -0,0 +1,5 @@
---
"@runfusion/fusion": patch
---
Fix merger AI commit finalization so deleted tasks no longer crash settings resolution while the merge is completing.

View File

@@ -0,0 +1,43 @@
import { describe, expect, it, vi } from "vitest";
import { BUILTIN_WORKFLOW_SETTINGS, type Settings } from "@fusion/core";
import { mergeEffectiveSettings } from "../effective-settings.js";
const PROJECT = "proj-1";
function baseSettings(): Settings {
const catalogDefaults = Object.fromEntries(
BUILTIN_WORKFLOW_SETTINGS
.filter((setting) => setting.default !== undefined)
.map((setting) => [setting.id, setting.default]),
);
return {
...catalogDefaults,
persistAgentToolOutput: false,
executionProvider: "project-anthropic",
executionModelId: "claude-project",
} as unknown as Settings;
}
describe("merger settings degrade path", () => {
it("uses a fallback task id so deleted-task getTask failures still return base settings", async () => {
const taskId = "FN-6193-DELETED";
const store = {
getTask: vi.fn(async () => {
throw new Error(`Task ${taskId} not found`);
}),
getTaskWorkflowSelection: vi.fn(() => {
throw new Error(`Task ${taskId} not found`);
}),
getWorkflowDefinition: vi.fn(async () => undefined),
getWorkflowSettingValues: vi.fn(() => ({})),
getWorkflowSettingsProjectId: vi.fn(() => PROJECT),
};
const base = baseSettings();
const taskForSettings = await store.getTask(taskId).catch(() => ({ id: taskId } as const));
const merged = await mergeEffectiveSettings(store as any, taskForSettings, base);
expect(merged).toEqual(base);
});
});

View File

@@ -11679,9 +11679,10 @@ async function runAiAgentForCommit(params: AiAgentParams): Promise<{ success: bo
// Merge per-task effective workflow settings (U3, KTD-3) — this worker re-fetches // Merge per-task effective workflow settings (U3, KTD-3) — this worker re-fetches
// settings independently of aiMergeTask, so apply the same merge here (covers the // settings independently of aiMergeTask, so apply the same merge here (covers the
// titleSummarizer lane reads in resolveSafeCommitBody). Behavior-inert by default. // titleSummarizer lane reads in resolveSafeCommitBody). Behavior-inert by default.
const taskForSettings = await store.getTask(taskId).catch(() => ({ id: taskId } as const));
const settings = await mergeEffectiveSettings( const settings = await mergeEffectiveSettings(
store, store,
await store.getTask(taskId), taskForSettings,
await store.getSettings(), await store.getSettings(),
); );