feat: advance reasoning after comparability is resolved

This commit is contained in:
2026-08-02 17:06:34 +01:00
parent 7d408701b5
commit 25a989450c
9 changed files with 649 additions and 5 deletions
+152
View File
@@ -118,6 +118,90 @@ function makeProposal(overrides = {}) {
};
}
function makeComparabilityScenarioGraph() {
return makeGraph({
centralStatement:
"Revenue increased by 18%, but cash in the bank fell over the same period.",
nodes: [
makeNode({
id: "n-comparability-unknown",
label: "Whether the figures are comparable",
description:
"Need to know whether the figures use the same period, basis, and scale before comparing them.",
kind: "unknown",
status: "unknown",
confidence: "high",
}),
makeNode({
id: "n-revenue-observation",
label: "Revenue increased by 18%.",
description: "Revenue increased by 18%.",
kind: "observation",
status: "supported",
confidence: "high",
}),
makeNode({
id: "n-cash-observation",
label: "Cash in the bank decreased over the same period.",
description: "Cash in the bank decreased over the same period.",
kind: "observation",
status: "supported",
confidence: "high",
}),
],
edges: [],
activeUnknownNodeId: "n-comparability-unknown",
resolvedNodeIds: [],
currentSummary: "Comparability scenario",
reasoningState: {
comparabilityStatus: "uncertain",
comparabilityReason:
"Comparability between the observations is not yet established across period, scale, or measurement basis.",
comparabilityEvidence: [],
relationshipStatus: "insufficient_information",
relationshipReason:
"Relationship classification is deferred until comparability is established.",
relationshipAssessed: false,
contradictionReasoningAllowed: false,
reasoningStages: [
{
stage: "comparability",
status: "uncertain",
outcome:
"Comparability between the observations is not yet established across period, scale, or measurement basis.",
},
{
stage: "relationship",
status: "insufficient_information",
outcome: "not assessed until comparability is established",
},
],
},
});
}
function makeComparabilityProposal() {
return {
addedNodes: [],
updatedNodes: [
{
nodeId: "n-comparability-unknown",
previousStatus: "unknown",
newStatus: "resolved",
previousValue: null,
newValue:
"Both figures cover the same accounting period and are taken from the same management accounts.",
reason: "The answer confirms comparability.",
},
],
addedEdges: [],
removedEdgeIds: [],
resolvedUnknownNodeIds: ["n-comparability-unknown"],
affectedNodeIds: [],
selectedQuestion: null,
};
}
describe("lib/graph/orchestrator startCase", () => {
beforeEach(() => {
vi.resetModules();
@@ -930,6 +1014,74 @@ describe("lib/graph/orchestrator startCase", () => {
});
});
it("advances reasoning after comparability is resolved by the update answer", async () => {
const { updateCase } = await import("@/lib/graph/orchestrator.js");
const provider = {
generateReconstruction: vi
.fn()
.mockResolvedValue(makeComparabilityProposal()),
};
const result = await updateCase(
{
situationGraph: makeComparabilityScenarioGraph(),
previousQuestion:
"Were these figures measured on the same basis and at the same scale?",
answer:
"Yes. Both figures cover the same accounting period and are taken from the same management accounts.",
promptVersion: "v0.4",
},
{
provider,
config: MOCK_CONFIG,
applyProposal: true,
},
);
expect(result.success).toBe(true);
expect(result.resolvedUnknownNodeIds).toEqual(["n-comparability-unknown"]);
expect(result.diagnostics).toMatchObject({
previousComparabilityStatus: "uncertain",
comparabilityStatus: "confirmed",
relationshipStatus: "potentially_related",
relationshipAssessed: true,
resolvedReasoningNodeIds: ["reasoning:comparability"],
});
expect(result.diagnostics.reasoningStagesBefore).toEqual([
{
stage: "comparability",
status: "uncertain",
outcome:
"Comparability between the observations is not yet established across period, scale, or measurement basis.",
},
{
stage: "relationship",
status: "insufficient_information",
outcome: "not assessed until comparability is established",
},
]);
expect(result.diagnostics.reasoningStagesAfter).toEqual([
{
stage: "comparability",
status: "confirmed",
outcome:
"Comparability was confirmed by the user answer covering the same period and source basis.",
},
{
stage: "relationship",
status: "potentially_related",
outcome:
"The observations concern connected business signals but do not establish a direct contradiction or cause.",
},
]);
expect(result.selectedQuestion?.question).toMatch(
/^What changed during that period that could help explain why /,
);
expect(result.selectedQuestion?.question.toLowerCase()).not.toMatch(
/same basis|dso|receivables|debtor days|working capital/,
);
});
it("startCase behaviour remains unchanged", async () => {
mockAnalyseScenario.mockResolvedValue(makeAnalysisResult());
const { startCase } = await import("@/lib/graph/orchestrator.js");