fix(confidence-engine): supply synthesis output schema

This commit is contained in:
2026-09-07 13:44:57 +01:00
parent 3f2e2e05ae
commit 7548a6af59
3 changed files with 19 additions and 2 deletions
+2 -1
View File
@@ -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
+11 -1
View File
@@ -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");
@@ -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 () => {