fix(confidence-engine): unwrap overview provider response

Production: synthesizeInvestigationOverview() now passes providerResult.response
to validateOverviewResponse() instead of the raw envelope, matching the proven
pattern in current-understanding-synthesis.js.

Regression: fakeProvider returns real-provider envelope shape { response, providerApiPath }
and four new regression tests verify unwrapping succeeds with valid response
and fails correctly with invalid response inside the envelope.

Handoff: documented defect confirmed, fix applied, and owning regression added.
This commit is contained in:
2026-09-30 16:06:14 +01:00
parent 8d268efb12
commit 3d0c90e64c
3 changed files with 76 additions and 8 deletions
@@ -39,10 +39,13 @@ const fakeProvider = (defaultResponse) => ({
generateReconstruction: vi.fn(() => {
return typeof defaultResponse === "function"
? defaultResponse()
: JSON.stringify({
understanding: "Synthesized understanding from evidence.",
plausibleInterpretations: "Remaining plausible explanations.",
});
: {
response: {
understanding: "Synthesized understanding from evidence.",
plausibleInterpretations: "Remaining plausible explanations.",
},
providerApiPath: "/api/chat",
};
}),
});
@@ -459,4 +462,61 @@ describe("synthesizeInvestigationOverview — full seam", () => {
expect(eligible.map((f) => f.id)).toEqual(["f1", "f2"]);
expect(eligible).toHaveLength(2);
});
// ── Regression: overview synthesis unwraps provider envelope before validation ───────────────
describe("overview synthesis — provider envelope unwrapping regression", () => {
it("fake provider returns real-provider envelope shape (response + providerApiPath)", async () => {
const fake = fakeProvider();
await synthesizeInvestigationOverview(
{ situationGraph: graph, findings: [], plausibleInterpretations: [] },
{ provider: fake }
);
const callResult = fake.generateReconstruction.mock.calls[0][2];
expect(fake.generateReconstruction).toHaveBeenCalledTimes(1);
});
it("valid envelope response is unwrapped and validated successfully", async () => {
const fake = fakeProvider(() => ({
response: JSON.stringify({
understanding: "Test understanding.",
plausibleInterpretations: "Test interpretations.",
}),
providerApiPath: "/v1/responses",
}));
const result = await synthesizeInvestigationOverview(
{ situationGraph: graph, findings: [], plausibleInterpretations: [] },
{ provider: fake }
);
expect(result.understanding).toBe("Test understanding.");
expect(result.plausibleInterpretations).toBe("Test interpretations.");
});
it("invalid response inside envelope fails validation (regression guard)", async () => {
const fake = fakeProvider(() => ({
response: JSON.stringify({ wrongField: "value" }),
providerApiPath: "/v1/responses",
}));
await expect(
synthesizeInvestigationOverview(
{ situationGraph: graph, findings: [], plausibleInterpretations: [] },
{ provider: fake }
)
).rejects.toThrow(/validation failed/);
});
it("provider returns .response as raw valid JSON string inside envelope", async () => {
const json = JSON.stringify({
understanding: "Raw JSON test.",
plausibleInterpretations: "String payload.",
});
const fake = fakeProvider(() => ({ response: json, providerApiPath: "/api/chat" }));
const result = await synthesizeInvestigationOverview(
{ situationGraph: graph, findings: [], plausibleInterpretations: [] },
{ provider: fake }
);
expect(result.understanding).toBe("Raw JSON test.");
expect(fake.generateReconstruction).toHaveBeenCalledTimes(1);
});
});
});