import { describe, expect, it } from "vitest"; import { applyValidatedProposal } from "@/lib/graph/apply-proposal.js"; import { makeEdge, makeGraph, makeNode } from "@/lib/graph/schema.js"; function makeFixture() { const parent = makeNode({ id: "n-parent", label: "Explanation for why revenue increased while cash fell", description: "Need to understand what change or event could explain why these observations differ, because that is needed to investigate their relationship.", kind: "unknown", status: "unknown", confidence: "medium", }); const children = [ makeNode({ id: "n-child-1", label: "How the two observations were measured", description: "Need evidence about the measure used for each observation.", kind: "unknown", status: "unknown", confidence: "medium", parentId: parent.id, }), makeNode({ id: "n-child-2", label: "Whether the two observations reflect different timing", description: "Need to know whether the two observations reflect different timing.", kind: "unknown", status: "unknown", confidence: "medium", parentId: parent.id, }), makeNode({ id: "n-child-3", label: "Possible change mainly affecting revenue", description: "Need to know whether a possible change mainly affected revenue.", kind: "unknown", status: "unknown", confidence: "medium", parentId: parent.id, }), makeNode({ id: "n-child-4", label: "Possible one-off event during the period", description: "Need to know whether a possible one-off event happened during the period.", kind: "unknown", status: "unknown", confidence: "medium", parentId: parent.id, }), ]; return makeGraph({ centralStatement: "Revenue increased while cash fell.", nodes: [parent, ...children], edges: children.map((child, index) => makeEdge({ id: `e-${index + 1}`, fromNodeId: child.id, toNodeId: parent.id, relationship: "depends_on", description: `${child.label} feeds the parent.`, }), ), activeUnknownNodeId: "n-child-1", resolvedNodeIds: [], currentSummary: "confidence propagation fixture", }); } function makeProposal({ resolvedIds, contradictedIds = [] }) { return { addedNodes: [ makeNode({ id: "n-anchor", label: "Update anchor", description: "Anchor state introduced by the answer because the update must contain a meaningful change.", kind: "state", status: "known", confidence: "low", }), ], updatedNodes: [ ...resolvedIds.map((id) => ({ nodeId: id, previousStatus: "unknown", newStatus: "resolved", previousValue: null, newValue: `answer:${id}`, reason: "resolved child", })), ...contradictedIds.map((id) => ({ nodeId: id, previousStatus: "unknown", newStatus: "contradicted", previousValue: null, newValue: `contradiction:${id}`, reason: "contradictory child evidence", })), ], addedEdges: [], removedEdgeIds: [], resolvedUnknownNodeIds: resolvedIds, affectedNodeIds: [], selectedQuestion: null, }; } describe("confidence propagation", () => { it("one of four children resolved does not yield high conclusion confidence", () => { const result = applyValidatedProposal({ situationGraph: makeFixture(), proposal: makeProposal({ resolvedIds: ["n-child-1"] }), previousQuestion: "What evidence would clarify how the two observations were measured?", answer: "Same accounting period and same management accounts.", }); const parent = result.updatedSituationGraph.nodes.find( (n) => n.id === "n-parent", ); expect(parent.status).toBe("provisional"); expect(parent.confidence).toBe("medium"); expect(parent.confidenceAssessment).toEqual({ evidenceConfidence: "medium", completenessStatus: "partial", conclusionConfidence: "medium", }); expect(result.confidenceCapReason).toBe( "unresolved_direct_children_cap_conclusion", ); }); it("all children resolved with coherent evidence may yield high confidence", () => { const result = applyValidatedProposal({ situationGraph: makeFixture(), proposal: makeProposal({ resolvedIds: ["n-child-1", "n-child-2", "n-child-3", "n-child-4"], }), previousQuestion: "What evidence would clarify how the two observations were measured?", answer: "All direct child questions are answered.", }); const parent = result.updatedSituationGraph.nodes.find( (n) => n.id === "n-parent", ); expect(parent.status).toBe("resolved"); expect(parent.confidenceAssessment).toEqual({ evidenceConfidence: "high", completenessStatus: "complete", conclusionConfidence: "high", }); }); it("contradictory child evidence prevents high confidence", () => { const result = applyValidatedProposal({ situationGraph: makeFixture(), proposal: makeProposal({ resolvedIds: ["n-child-1"], contradictedIds: ["n-child-2"], }), previousQuestion: "What evidence would clarify how the two observations were measured?", answer: "One child resolved, another contradicted.", }); const parent = result.updatedSituationGraph.nodes.find( (n) => n.id === "n-parent", ); expect(parent.confidenceAssessment.conclusionConfidence).toBe("low"); expect(result.confidenceCapReason).toBe("contradictory_direct_children"); }); });