diff --git a/docs/current-handoff.md b/docs/current-handoff.md index 750921f..2103d34 100644 --- a/docs/current-handoff.md +++ b/docs/current-handoff.md @@ -41,7 +41,8 @@ If YES, the next live experiment is one timed/costed OpenAI UI investigation mea - Current deterministic final-fetch schema remains internally valid, yet the live rejection contradicts it. `CONFIDENCE_ENGINE_EXPERIMENT_TRACE_OPENAI_SCHEMA=1` now emits one safe, server-side structural summary immediately before the OpenAI fetch—no prompt, answer, request body, secret, or model output. - The focused-deconstruction route now emits complementary safe server diagnostics for start, provider success/failure, focused validation, and end status; the OpenAI schema trace remains provider-owned. No user content or secrets are logged. - Canonical focused relationship items are now strict `{ from, to, type }`: all required non-empty strings, free-text `type`, and no `rationale`; `relationships` remains required and may be `[]`. Schema, prompt field names, and validator align; the stale rationale-bearing test fixture was corrected. -- Focused and provider deterministic suites pass; the OpenAI projector is unchanged. Zero live calls occurred. Next evidence boundary: live provider compatibility of this same canonical focused contract. +- Live Terra focused deconstruction now passes through the real UI, but the following Current Understanding synthesis exposed a separate caller/schema mismatch: its prompt and validator require `{ currentUnderstanding }` while the provider received the default initial-reconstruction schema. +- Synthesis now supplies its own output schema as provider argument three; provider implementation is unchanged. Synthesis and provider deterministic suites pass with zero live calls. Next boundary: one isolated live synthesis request using the already captured situationGraph and findings payload. ## Repository checkpoint diff --git a/lib/graph/current-understanding-synthesis.js b/lib/graph/current-understanding-synthesis.js index 1fa1c2a..6a6cd24 100644 --- a/lib/graph/current-understanding-synthesis.js +++ b/lib/graph/current-understanding-synthesis.js @@ -186,6 +186,15 @@ export const synthesisResponseSchema = z.object({ .min(1, "currentUnderstanding must be a non-empty string"), }); +export const synthesisOutputSchema = { + type: "object", + properties: { + currentUnderstanding: { type: "string" }, + }, + required: ["currentUnderstanding"], + additionalProperties: false, +}; + /** Validate raw provider output against synthesis response schema */ export function validateSynthesisResponse(raw) { if (raw == null) { @@ -274,7 +283,8 @@ export async function synthesizeCurrentUnderstanding( try { rawResponse = await provider.generateReconstruction( prompt, - modelName + modelName, + synthesisOutputSchema, ); } catch (error) { const err = new Error(error.message ?? "Synthesis provider call failed"); diff --git a/tests/graph/current-understanding-synthesis.test.js b/tests/graph/current-understanding-synthesis.test.js index 2ea3bd0..36e3cdb 100644 --- a/tests/graph/current-understanding-synthesis.test.js +++ b/tests/graph/current-understanding-synthesis.test.js @@ -375,6 +375,12 @@ describe("synthesizeCurrentUnderstanding — full seam", () => { const prompt = fake.generateReconstruction.mock.calls[0][0]; expect(prompt).toContain("Complaint count increased by 35%"); expect(prompt).toContain("[known]"); + expect(fake.generateReconstruction.mock.calls[0][2]).toEqual({ + type: "object", + properties: { currentUnderstanding: { type: "string" } }, + required: ["currentUnderstanding"], + additionalProperties: false, + }); }); it("supported node content flows to provider via synthesis prompt", async () => {