From 40bef6dfcea382d9348a4a1ac42136608880a9f4 Mon Sep 17 00:00:00 2001 From: Phil Larson Date: Tue, 23 Jun 2026 08:31:47 -0700 Subject: [PATCH 1/3] fix(triage): pass fallback model settings to spec review --- packages/engine/src/__tests__/triage.test.ts | 54 ++++++++++++++++++++ packages/engine/src/triage.ts | 3 ++ 2 files changed, 57 insertions(+) diff --git a/packages/engine/src/__tests__/triage.test.ts b/packages/engine/src/__tests__/triage.test.ts index 56731468fd..7e9d5b30a4 100644 --- a/packages/engine/src/__tests__/triage.test.ts +++ b/packages/engine/src/__tests__/triage.test.ts @@ -1010,6 +1010,60 @@ describe("fast-mode triage", () => { } }); + it("threads global fallback model settings into spec reviewer sessions", async () => { + const rootDir = await createTriageFixtureRoot("fusion-triage-review-fallback-"); + try { + const taskId = "FN-REVIEW-FALLBACK"; + await mkdir(join(rootDir, ".fusion", "tasks", taskId), { recursive: true }); + await writeFile( + join(rootDir, ".fusion", "tasks", taskId, "PROMPT.md"), + "# Task\n\n## Mission\n\nDo the work.\n\n## Steps\n\n### Step 0: Implement\n\nShip it.", + ); + mockReviewStep.mockResolvedValue({ verdict: "APPROVE", review: "ok", summary: "ok" }); + + const store = createMockStore({ + getTask: vi.fn().mockResolvedValue({ ...mockTaskDetail, id: taskId, comments: [] }), + getSettings: vi.fn().mockResolvedValue({ + maxConcurrent: 2, + maxWorktrees: 4, + pollIntervalMs: 10000, + groupOverlappingFiles: false, + autoMerge: true, + defaultProvider: "anthropic", + defaultModelId: "claude-opus-4-8", + defaultProviderOverride: "openai-codex", + defaultModelIdOverride: "gpt-5.5", + fallbackProvider: "openai-codex", + fallbackModelId: "gpt-5.5", + } as Settings), + }); + const processor = new TriageProcessor(store, rootDir); + const tool = (processor as any).createReviewSpecTool( + taskId, + `.fusion/tasks/${taskId}/PROMPT.md`, + { current: null }, + { current: null }, + { current: null }, + { current: "" }, + {}, + false, + ); + + await tool.execute({}); + + const reviewOptions = mockReviewStep.mock.calls[0]?.[7]; + expect(reviewOptions).toMatchObject({ + projectDefaultOverrideProvider: "openai-codex", + projectDefaultOverrideModelId: "gpt-5.5", + fallbackProvider: "openai-codex", + fallbackModelId: "gpt-5.5", + }); + } finally { + mockReviewStep.mockReset(); + await cleanupTriageFixtureRoot(rootDir); + } + }); + it("passes post-session gate in fast mode after fn_review_spec auto-approval", async () => { const rootDir = await createTriageFixtureRoot("fusion-triage-fast-gate-"); try { diff --git a/packages/engine/src/triage.ts b/packages/engine/src/triage.ts index b62a246bf0..64ff213927 100644 --- a/packages/engine/src/triage.ts +++ b/packages/engine/src/triage.ts @@ -1890,6 +1890,9 @@ export class TriageProcessor { // Project-level validator fallback projectValidatorFallbackProvider: currentSettings.validatorFallbackProvider, projectValidatorFallbackModelId: currentSettings.validatorFallbackModelId, + // Global/default fallback, used when no validator-specific fallback is configured + fallbackProvider: currentSettings.fallbackProvider, + fallbackModelId: currentSettings.fallbackModelId, // Global validator lane globalValidatorProvider: currentSettings.validatorGlobalProvider, globalValidatorModelId: currentSettings.validatorGlobalModelId, From 945f0f176b48d49e7a4ceb493f01cfe7b4f37434 Mon Sep 17 00:00:00 2001 From: Phil Larson Date: Tue, 23 Jun 2026 08:43:11 -0700 Subject: [PATCH 2/3] chore: add triage fallback changeset --- .changeset/friendly-reviewers-fallback.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/friendly-reviewers-fallback.md diff --git a/.changeset/friendly-reviewers-fallback.md b/.changeset/friendly-reviewers-fallback.md new file mode 100644 index 0000000000..cbc241edef --- /dev/null +++ b/.changeset/friendly-reviewers-fallback.md @@ -0,0 +1,5 @@ +--- +"@fusion/engine": patch +--- + +Pass project fallback model settings into triage spec reviewer sessions so global default overrides are honored during review. From 823623e1c685671df23fe2838f7a0e62ac997651 Mon Sep 17 00:00:00 2001 From: Phil Larson Date: Tue, 23 Jun 2026 08:51:58 -0700 Subject: [PATCH 3/3] fix(triage): address spec review fallback PR comments --- .changeset/friendly-reviewers-fallback.md | 2 +- packages/engine/src/__tests__/triage.test.ts | 7 +++++++ packages/engine/src/triage.ts | 5 ++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.changeset/friendly-reviewers-fallback.md b/.changeset/friendly-reviewers-fallback.md index cbc241edef..a5efe10946 100644 --- a/.changeset/friendly-reviewers-fallback.md +++ b/.changeset/friendly-reviewers-fallback.md @@ -1,5 +1,5 @@ --- -"@fusion/engine": patch +"@runfusion/fusion": patch --- Pass project fallback model settings into triage spec reviewer sessions so global default overrides are honored during review. diff --git a/packages/engine/src/__tests__/triage.test.ts b/packages/engine/src/__tests__/triage.test.ts index 7e9d5b30a4..a8a4a59ca0 100644 --- a/packages/engine/src/__tests__/triage.test.ts +++ b/packages/engine/src/__tests__/triage.test.ts @@ -1035,6 +1035,8 @@ describe("fast-mode triage", () => { defaultModelIdOverride: "gpt-5.5", fallbackProvider: "openai-codex", fallbackModelId: "gpt-5.5", + memoryEnabled: false, + agentPrompts: { roleAssignments: { reviewer: "custom-reviewer" } }, } as Settings), }); const processor = new TriageProcessor(store, rootDir); @@ -1057,6 +1059,11 @@ describe("fast-mode triage", () => { projectDefaultOverrideModelId: "gpt-5.5", fallbackProvider: "openai-codex", fallbackModelId: "gpt-5.5", + agentPrompts: { roleAssignments: { reviewer: "custom-reviewer" } }, + }); + expect(reviewOptions.settings).toMatchObject({ + memoryEnabled: false, + agentPrompts: { roleAssignments: { reviewer: "custom-reviewer" } }, }); } finally { mockReviewStep.mockReset(); diff --git a/packages/engine/src/triage.ts b/packages/engine/src/triage.ts index 64ff213927..3e7eee5d9f 100644 --- a/packages/engine/src/triage.ts +++ b/packages/engine/src/triage.ts @@ -1890,7 +1890,8 @@ export class TriageProcessor { // Project-level validator fallback projectValidatorFallbackProvider: currentSettings.validatorFallbackProvider, projectValidatorFallbackModelId: currentSettings.validatorFallbackModelId, - // Global/default fallback, used when no validator-specific fallback is configured + // FNXC:SpecReviewerFallback 2026-06-23-08:50: + // Spec review must inherit global/default fallback reviewer model settings when no validator-specific fallback is configured, plus the project settings/prompt payload that reviewer sessions use for memory and custom prompt behavior. fallbackProvider: currentSettings.fallbackProvider, fallbackModelId: currentSettings.fallbackModelId, // Global validator lane @@ -1904,8 +1905,10 @@ export class TriageProcessor { taskId, task: currentDetail, userComments: currentUserComments.length > 0 ? currentUserComments : undefined, + agentPrompts: currentSettings.agentPrompts, agentStore: this.options.agentStore, rootDir, + settings: currentSettings, // Track the spec reviewer's session under this task so it's // disposed alongside the main triage session on global pause. onSessionCreated: (s) => this.registerSubagentSession(taskId, s),