Implemented Investigation Strategy

This commit is contained in:
2026-08-02 15:10:49 +01:00
parent a79a7bd524
commit 5ef9710293
7 changed files with 293 additions and 121 deletions
+108 -18
View File
@@ -1,5 +1,8 @@
import { describe, expect, it } from "vitest";
import { formulateQuestion } from "@/lib/graph/question-formulator.js";
import {
formulateQuestion,
selectInvestigationStrategy,
} from "@/lib/graph/question-formulator.js";
import { makeEdge, makeGraph, makeNode } from "@/lib/graph/schema.js";
function makeGraphFor(node, extra = {}) {
@@ -14,7 +17,7 @@ function makeGraphFor(node, extra = {}) {
}
describe("formulateQuestion", () => {
it("commercial viability plus build decision produces a decision-criterion question", () => {
it("commercial viability plus build decision produces a decision-threshold question", () => {
const unknown = makeNode({
id: "n-commercial",
label: "Uncertainty regarding the commercial value of the product",
@@ -42,7 +45,7 @@ describe("formulateQuestion", () => {
const result = formulateQuestion({ node: unknown, graph });
expect(result.strategy).toBe("decision criterion");
expect(result.strategy).toBe("decision_threshold");
expect(result.question).toContain("What outcome");
expect(result.question.toLowerCase()).toContain("justify");
});
@@ -100,7 +103,7 @@ describe("formulateQuestion", () => {
graph: makeGraphFor(unknown),
});
expect(result.strategy).toBe("evidence");
expect(result.strategy).toBe("evidence_gathering");
expect(result.question).toContain("What evidence");
});
@@ -120,33 +123,41 @@ describe("formulateQuestion", () => {
graph: makeGraphFor(unknown),
});
expect(result.strategy).toBe("baseline");
expect(result.strategy).toBe("baseline_reconstruction");
expect(result.question).toContain("What was the comparable state before");
});
it("unknown customer produces an actor/customer question", () => {
it("conflicting claim produces a contradiction-resolution question", () => {
const unknown = makeNode({
id: "n-customer",
label: "Target customer",
id: "n-conflict",
label: "Conflicting churn claim",
description:
"Need to know the customer because value depends on who receives it.",
"Need to resolve the inconsistency because the current figures contradict each other.",
kind: "unknown",
status: "unknown",
confidence: "high",
confidence: "medium",
});
const contradiction = makeNode({
id: "n-contradiction",
label: "Contradicted report",
description: "Two sources disagree about churn.",
kind: "conclusion",
status: "contradicted",
confidence: "low",
childIds: [unknown.id],
});
const result = formulateQuestion({
node: unknown,
graph: makeGraphFor(unknown),
graph: makeGraphFor(unknown, { nodes: [contradiction] }),
});
expect(result.strategy).toBe("actor/customer");
expect(result.question).toContain(
"Who experiences the problem or receives the value",
);
expect(result.strategy).toBe("contradiction_resolution");
expect(result.question).toContain("resolve the contradiction");
});
it("constraint unknown produces a constraint question", () => {
it("constraint unknown uses evidence-gathering within the fixed strategy set", () => {
const unknown = makeNode({
id: "n-constraint",
label: "Budget constraint",
@@ -162,8 +173,87 @@ describe("formulateQuestion", () => {
graph: makeGraphFor(unknown),
});
expect(result.strategy).toBe("constraint");
expect(result.question).toContain("What constraint most limits");
expect(result.strategy).toBe("evidence_gathering");
expect(result.question).toContain("What evidence");
});
it("the same unknown can produce different questions when paired with different strategies", () => {
const unknown = makeNode({
id: "n-same-unknown",
label: "Value threshold",
description: "Need to resolve the value threshold.",
kind: "unknown",
status: "unknown",
confidence: "high",
});
const decisionGraph = makeGraphFor(unknown, {
centralStatement: "We are deciding whether to launch this product.",
nodes: [
makeNode({
id: "n-decision",
label: "Launch decision",
description: "Decision depends on the value threshold.",
kind: "state",
status: "known",
confidence: "medium",
childIds: [unknown.id],
value: "Deciding whether to launch the product",
}),
],
});
const definitionGraph = makeGraphFor(unknown, {
centralStatement:
"The team uses the term value threshold inconsistently.",
nodes: [
makeNode({
id: "n-definition",
label: "Definition disagreement",
description:
"Need a definition of value threshold before comparing options.",
kind: "state",
status: "known",
confidence: "medium",
childIds: [unknown.id],
}),
],
});
const decisionResult = formulateQuestion({
node: unknown,
graph: decisionGraph,
});
const definitionResult = formulateQuestion({
node: unknown,
graph: definitionGraph,
});
expect(decisionResult.strategy).toBe("decision_threshold");
expect(definitionResult.strategy).toBe("definition");
expect(decisionResult.question).not.toBe(definitionResult.question);
});
it("strategy selection is deterministic and explainable", () => {
const unknown = makeNode({
id: "n-threshold",
label: "Success threshold",
description:
"Need the success threshold because the decision depends on it.",
kind: "unknown",
status: "unknown",
confidence: "high",
});
const graph = makeGraphFor(unknown, {
centralStatement: "We need to decide whether to continue investing.",
});
const first = selectInvestigationStrategy({ node: unknown, graph });
const second = selectInvestigationStrategy({ node: unknown, graph });
expect(first).toEqual(second);
expect(first.key).toBe("decision_threshold");
expect(first.reason).toContain("threshold");
});
it("question is singular and answerable", () => {