feat(FN-2662): enforce model override fallback hierarchy

- Fix triage planning model resolution to fall back through project/global planning settings and default overrides
- Fix reviewer model selection to honor validator-specific settings before default provider/model overrides
- Update merger model resolution to apply default override fallback and align shared task setting types/executor flow
- Add regression coverage for triage, reviewer, and merger fallback behavior and update settings hierarchy documentation
This commit is contained in:
Fusion
2026-04-27 02:02:01 -07:00
committed by gsxdsm
parent 221ae06a81
commit 4330eef126
9 changed files with 288 additions and 24 deletions

View File

@@ -425,6 +425,51 @@ describe("reviewStep — validator model overrides", () => {
expect(opts.defaultModelId).toBe("gemini-2.5");
});
it("uses project default override when validator lanes are absent", async () => {
mockedCreateFnAgent.mockResolvedValue(
createMockSession("### Verdict: APPROVE\n### Summary\nLooks good."),
);
await reviewStep(
"/tmp/worktree", "FN-100", 1, "Test Step", "plan", "# prompt",
undefined,
{
defaultProvider: "anthropic",
defaultModelId: "claude-sonnet-4-5",
projectDefaultOverrideProvider: "openai",
projectDefaultOverrideModelId: "gpt-4o",
// No validator lanes set
},
);
expect(mockedCreateFnAgent).toHaveBeenCalledTimes(1);
const opts = mockedCreateFnAgent.mock.calls[0][0];
expect(opts.defaultProvider).toBe("openai");
expect(opts.defaultModelId).toBe("gpt-4o");
});
it("falls through to execution default when project default override is incomplete", async () => {
mockedCreateFnAgent.mockResolvedValue(
createMockSession("### Verdict: APPROVE\n### Summary\nLooks good."),
);
await reviewStep(
"/tmp/worktree", "FN-100", 1, "Test Step", "plan", "# prompt",
undefined,
{
defaultProvider: "anthropic",
defaultModelId: "claude-sonnet-4-5",
projectDefaultOverrideProvider: "openai",
// projectDefaultOverrideModelId intentionally omitted
},
);
expect(mockedCreateFnAgent).toHaveBeenCalledTimes(1);
const opts = mockedCreateFnAgent.mock.calls[0][0];
expect(opts.defaultProvider).toBe("anthropic");
expect(opts.defaultModelId).toBe("claude-sonnet-4-5");
});
it("falls back to execution default when no validator lanes are set", async () => {
mockedCreateFnAgent.mockResolvedValue(
createMockSession("### Verdict: APPROVE\n### Summary\nLooks good."),