Implemented Investigation Strategy
This commit is contained in:
+17
-5
@@ -67,7 +67,11 @@ export const questionPriorityGeneralisationFixtures = [
|
||||
"hire-bottleneck",
|
||||
],
|
||||
prohibitedFirstTopics: ["salary", "job advert", "programming language"],
|
||||
acceptableQuestionStrategies: ["decision criterion", "constraint"],
|
||||
acceptableQuestionStrategies: [
|
||||
"decision_threshold",
|
||||
"evidence_gathering",
|
||||
"definition",
|
||||
],
|
||||
notes:
|
||||
"The first question should establish whether more engineering capacity is justified before compensation or implementation details.",
|
||||
graph: makeScenarioGraph({
|
||||
@@ -143,7 +147,11 @@ export const questionPriorityGeneralisationFixtures = [
|
||||
"paint colour",
|
||||
"finance provider",
|
||||
],
|
||||
acceptableQuestionStrategies: ["decision criterion", "constraint"],
|
||||
acceptableQuestionStrategies: [
|
||||
"decision_threshold",
|
||||
"evidence_gathering",
|
||||
"definition",
|
||||
],
|
||||
notes:
|
||||
"The first question should establish whether the fleet is failing a threshold that justifies replacement.",
|
||||
graph: makeScenarioGraph({
|
||||
@@ -219,7 +227,7 @@ export const questionPriorityGeneralisationFixtures = [
|
||||
"office location",
|
||||
"advertising channel",
|
||||
],
|
||||
acceptableQuestionStrategies: ["actor/customer", "decision criterion"],
|
||||
acceptableQuestionStrategies: ["definition", "decision_threshold"],
|
||||
notes:
|
||||
"The first question should clarify the customer or value case for expansion before rollout logistics.",
|
||||
graph: makeScenarioGraph({
|
||||
@@ -291,7 +299,7 @@ export const questionPriorityGeneralisationFixtures = [
|
||||
"project-remaining-benefit",
|
||||
],
|
||||
prohibitedFirstTopics: ["sunk cost", "project logo", "final launch date"],
|
||||
acceptableQuestionStrategies: ["decision criterion", "objective"],
|
||||
acceptableQuestionStrategies: ["decision_threshold", "definition"],
|
||||
notes:
|
||||
"The first question should establish remaining value or success threshold before sunk-cost framing or launch timing.",
|
||||
graph: makeScenarioGraph({
|
||||
@@ -367,7 +375,11 @@ export const questionPriorityGeneralisationFixtures = [
|
||||
"payment provider",
|
||||
"tier name",
|
||||
],
|
||||
acceptableQuestionStrategies: ["actor/customer", "decision criterion"],
|
||||
acceptableQuestionStrategies: [
|
||||
"definition",
|
||||
"decision_threshold",
|
||||
"baseline_reconstruction",
|
||||
],
|
||||
notes:
|
||||
"The first question should establish who values paid support or what outcome would justify offering it before pricing details.",
|
||||
graph: makeScenarioGraph({
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -127,27 +127,27 @@ describe("question priority generalisation", () => {
|
||||
{
|
||||
"nodeId": "hire-success-criteria",
|
||||
"scenario": "Should we hire another engineer?",
|
||||
"strategy": "decision criterion",
|
||||
"strategy": "decision_threshold",
|
||||
},
|
||||
{
|
||||
"nodeId": "van-reliability-threshold",
|
||||
"scenario": "Should we replace the delivery vans?",
|
||||
"strategy": "decision criterion",
|
||||
"strategy": "decision_threshold",
|
||||
},
|
||||
{
|
||||
"nodeId": "country-value-threshold",
|
||||
"scenario": "Should we launch in another country?",
|
||||
"strategy": "actor/customer",
|
||||
"strategy": "decision_threshold",
|
||||
},
|
||||
{
|
||||
"nodeId": "project-benefit-threshold",
|
||||
"scenario": "Should we continue a project that is over budget?",
|
||||
"strategy": "decision criterion",
|
||||
"strategy": "decision_threshold",
|
||||
},
|
||||
{
|
||||
"nodeId": "support-value-threshold",
|
||||
"scenario": "Should we introduce a paid support tier?",
|
||||
"strategy": "actor/customer",
|
||||
"strategy": "baseline_reconstruction",
|
||||
},
|
||||
]
|
||||
`);
|
||||
|
||||
Reference in New Issue
Block a user