Files
confidence-engine/tests/graph/confidence-propagation.test.js

180 lines
5.6 KiB
JavaScript

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");
});
});