feat: introduce reasoning pattern selection
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
formulateQuestion,
|
||||
formulateTieResolutionQuestion,
|
||||
selectReasoningPattern,
|
||||
} from "@/lib/graph/question-formulator.js";
|
||||
import { makeGraph, makeNode } from "@/lib/graph/schema.js";
|
||||
|
||||
function makeGraphFor(node, extra = {}) {
|
||||
return makeGraph({
|
||||
centralStatement: extra.centralStatement || "Decision context",
|
||||
nodes: [node, ...(extra.nodes || [])],
|
||||
edges: extra.edges || [],
|
||||
activeUnknownNodeId: node.id,
|
||||
resolvedNodeIds: extra.resolvedNodeIds || [],
|
||||
currentSummary: "Test summary",
|
||||
reasoningState: extra.reasoningState,
|
||||
});
|
||||
}
|
||||
|
||||
describe("reasoning pattern selection", () => {
|
||||
it("commercial-method scenario selects the decision pattern and rejects explanation family", () => {
|
||||
const unknown = makeNode({
|
||||
id: "n-commercial-method",
|
||||
label:
|
||||
"Whether the method addresses a genuine, high-priority problem for a specific audience",
|
||||
description:
|
||||
"Need to know whether this solves a real problem for a clear audience before continuing development.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "high",
|
||||
});
|
||||
const graph = makeGraphFor(unknown, {
|
||||
centralStatement:
|
||||
"Before investing significant time and money, we need to know whether continuing development is commercially justified.",
|
||||
});
|
||||
|
||||
const result = formulateQuestion({ node: unknown, graph });
|
||||
|
||||
expect(result.reasoningPattern).toBe("decision");
|
||||
expect(result.questionFamily).not.toBe("explanation");
|
||||
expect(result.allowedQuestionFamilies).toContain("decision_foundation");
|
||||
expect(result.rejectedQuestionFamilies).toContain("explanation");
|
||||
});
|
||||
|
||||
it("revenue and cash relationship scenario allows the explanation family", () => {
|
||||
const unknown = makeNode({
|
||||
id: "n-revenue-cash-explanation",
|
||||
label:
|
||||
"Explanation for why revenue increased by 18%, but cash in the bank fell over the same period",
|
||||
description:
|
||||
"Need to understand what change or event could explain why these observations differ.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
});
|
||||
const graph = makeGraphFor(unknown, {
|
||||
centralStatement:
|
||||
"Revenue increased by 18%, but cash in the bank fell over the same period.",
|
||||
nodes: [
|
||||
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 fell over the same period.",
|
||||
description: "Cash in the bank fell over the same period.",
|
||||
kind: "observation",
|
||||
status: "supported",
|
||||
confidence: "high",
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const result = formulateQuestion({ node: unknown, graph });
|
||||
|
||||
expect(result.reasoningPattern).toBe("explanation");
|
||||
expect(result.allowedQuestionFamilies).toContain("explanation");
|
||||
});
|
||||
|
||||
it("duplicate observations reject explanation family during tie resolution", () => {
|
||||
const graph = makeGraph({
|
||||
centralStatement: "The same figure was repeated twice.",
|
||||
nodes: [
|
||||
makeNode({
|
||||
id: "n-obs-1",
|
||||
label: "Revenue increased by 10%.",
|
||||
description: "Revenue increased by 10%.",
|
||||
kind: "observation",
|
||||
status: "supported",
|
||||
confidence: "high",
|
||||
}),
|
||||
makeNode({
|
||||
id: "n-obs-2",
|
||||
label: "Revenue increased by 10%.",
|
||||
description: "Revenue increased by 10%.",
|
||||
kind: "observation",
|
||||
status: "supported",
|
||||
confidence: "high",
|
||||
}),
|
||||
],
|
||||
edges: [],
|
||||
activeUnknownNodeId: null,
|
||||
resolvedNodeIds: [],
|
||||
currentSummary: "Duplicate observation fixture",
|
||||
});
|
||||
|
||||
const result = formulateTieResolutionQuestion({ graph });
|
||||
|
||||
expect(result.questionFamily).not.toBe("explanation");
|
||||
expect(result.rejectedQuestionFamilies).toContain("explanation");
|
||||
});
|
||||
|
||||
it("definition scenario selects the definition pattern", () => {
|
||||
const unknown = makeNode({
|
||||
id: "n-definition-pattern",
|
||||
label: "Definition of justified confidence",
|
||||
description: "The term needs clearer boundaries.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
});
|
||||
|
||||
const result = selectReasoningPattern({
|
||||
node: unknown,
|
||||
graph: makeGraphFor(unknown),
|
||||
});
|
||||
|
||||
expect(result.pattern).toBe("definition");
|
||||
});
|
||||
|
||||
it("comparison scenario selects the comparison pattern", () => {
|
||||
const unknown = makeNode({
|
||||
id: "n-comparison-pattern-2",
|
||||
label: "How the two observations were measured",
|
||||
description:
|
||||
"Need evidence about the measure used for each observation before comparing them.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
});
|
||||
const graph = makeGraphFor(unknown, {
|
||||
centralStatement: "Traffic increased, but sales stayed flat.",
|
||||
nodes: [
|
||||
makeNode({
|
||||
id: "n-traffic-2",
|
||||
label: "Traffic increased.",
|
||||
description: "Traffic increased.",
|
||||
kind: "observation",
|
||||
status: "supported",
|
||||
confidence: "high",
|
||||
}),
|
||||
makeNode({
|
||||
id: "n-sales-2",
|
||||
label: "Sales stayed flat.",
|
||||
description: "Sales stayed flat.",
|
||||
kind: "observation",
|
||||
status: "supported",
|
||||
confidence: "high",
|
||||
}),
|
||||
],
|
||||
reasoningState: {
|
||||
comparabilityStatus: "uncertain",
|
||||
relationshipStatus: "insufficient_information",
|
||||
},
|
||||
});
|
||||
|
||||
const result = selectReasoningPattern({ node: unknown, graph });
|
||||
|
||||
expect(result.pattern).toBe("comparison");
|
||||
});
|
||||
|
||||
it("question family stays compatible with the reasoning pattern", () => {
|
||||
const unknown = makeNode({
|
||||
id: "n-decision-family-compatibility",
|
||||
label: "Who experiences this problem",
|
||||
description:
|
||||
"Need to know who experiences this problem before continuing development.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
});
|
||||
const graph = makeGraphFor(unknown, {
|
||||
centralStatement:
|
||||
"We need to know whether continuing development is commercially justified.",
|
||||
});
|
||||
|
||||
const result = formulateQuestion({ node: unknown, graph });
|
||||
|
||||
expect(result.reasoningPattern).toBe("decision");
|
||||
expect(result.allowedQuestionFamilies).toContain(result.questionFamily);
|
||||
expect(result.rejectedQuestionFamilies).not.toContain(
|
||||
result.questionFamily,
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user