Files
confidence-engine/tests/graph/question-formulation-v0.24.test.js

282 lines
10 KiB
JavaScript

import { describe, expect, it } from "vitest";
import { formulateQuestion } 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",
});
}
/**
* Experiment 58A.4 — question formulation for pre-question-shaped labels
*
* Defect: when an unknown's label is already question or clause shaped,
* the engine interpolates it raw into a generic template frame, producing
* ungrammatical output such as "What would clarify are the projected office
* savings from relocation realistic in this situation?".
*
* The fix must generalise structurally to ANY interrogative/clause label,
* not just the specific patterns seen so far.
*/
describe("question formulation — pre-question labels (58A.4)", () => {
// ─── Helper: run formulateQuestion on an unknown and extract the question text ───
function questionFor(label, extra = {}) {
const node = makeNode({
id: "n-test",
label,
kind: "unknown",
status: "unknown",
confidence: "medium",
...extra,
});
const result = formulateQuestion({
node,
graph: makeGraphFor(node),
});
return result.question;
}
// ─── Test 1: the exact defect from 58A.3 — wh-question label ───
it("produces a grammatical question when label is a wh-question", () => {
const q = questionFor(
"Are the projected office savings from relocation realistic?",
);
// Must NOT contain template injection artefact
expect(q).not.toMatch(/what would clarify are .* in this situation/i);
// Should be grammatical — either a standalone wh-question or wrapped properly
expect(q.endsWith("?")).toBe(true);
// The core meaning must survive
expect(q.toLowerCase()).toMatch(/office savings|relocation/);
});
// ─── Test 2: clause-shaped statement label (no question mark) ───
it("produces a grammatical question when label is a declarative clause", () => {
const q = questionFor(
"The projected office savings from relocation are unrealistic",
);
// Should be grammatical question form
expect(q.endsWith("?")).toBe(true);
expect(q.toLowerCase()).toMatch(/office savings|relocation/);
});
// ─── Test 3: yes/no question label ───
it("produces a grammatical question when label is a yes/no question", () => {
const q = questionFor("Is the budget sufficient for this project?");
expect(q.endsWith("?")).toBe(true);
expect(q.toLowerCase()).toMatch(/budget|sufficient/);
});
// ─── Test 4: whether-clause label ───
it("produces a grammatical question when label starts with 'whether'", () => {
const q = questionFor(
"Whether the new pricing strategy will increase revenue",
);
expect(q.endsWith("?")).toBe(true);
expect(q.toLowerCase()).toMatch(/pricing|revenue/);
});
// ─── Test 5: what-question label ───
it("produces a grammatical question when label is a what-question", () => {
const q = questionFor(
"What are the key risks of this project?",
);
expect(q.endsWith("?")).toBe(true);
// Should not double-wrap with "what would clarify"
expect(q.toLowerCase()).not.toMatch(/what would clarify what/i);
expect(q.toLowerCase()).toMatch(/key risks/);
});
// ─── Test 6: how-question label ───
it("produces a grammatical question when label is a how-question", () => {
const q = questionFor(
"How do we measure success for this initiative?",
);
expect(q.endsWith("?")).toBe(true);
expect(q.toLowerCase()).not.toMatch(/what would clarify how/i);
expect(q.toLowerCase()).toMatch(/measure success/);
});
// ─── Test 7: why-question label ───
it("produces a grammatical question when label is a why-question", () => {
const q = questionFor(
"Why did the previous quarter underperform?",
);
expect(q.endsWith("?")).toBe(true);
expect(q.toLowerCase()).not.toMatch(/what would clarify why/i);
expect(q.toLowerCase()).toMatch(/quarter|underperform/);
});
// ─── Test 8: who-question label ───
it("produces a grammatical question when label is a who-question", () => {
const q = questionFor(
"Who is responsible for the client onboarding process?",
);
expect(q.endsWith("?")).toBe(true);
expect(q.toLowerCase()).not.toMatch(/what would clarify who/i);
expect(q.toLowerCase()).toMatch(/responsible|client/);
});
// ─── Test 9: when-question label ───
it("produces a grammatical question when label is a when-question", () => {
const q = questionFor(
"When does the current contract expire?",
);
expect(q.endsWith("?")).toBe(true);
expect(q.toLowerCase()).not.toMatch(/what would clarify when/i);
expect(q.toLowerCase()).toMatch(/contract|expire/);
});
// ─── Test 10: where-question label ───
it("produces a grammatical question when label is a where-question", () => {
const q = questionFor(
"Where should the new warehouse be located?",
);
expect(q.endsWith("?")).toBe(true);
expect(q.toLowerCase()).not.toMatch(/what would clarify where/i);
expect(q.toLowerCase()).toMatch(/warehouse|located/);
});
// ─── Test 11: non-question labels should NOT be affected ───
it("still applies template frames to declarative labels", () => {
const q = questionFor(
"Office lease exit penalty amount",
);
expect(q.endsWith("?")).toBe(true);
// For a normal declarative label, the template frame should apply
// The key is that it's grammatical, not that it uses a specific frame
expect(q.toLowerCase()).toMatch(/lease|penalty/);
});
// ─── Test 12: evidence-fallback path with pre-question label ───
it("produces grammatical output via evidence fallback for question labels", () => {
const q = questionFor(
"Are the projected office savings from relocation realistic?",
);
expect(q.endsWith("?")).toBe(true);
// The output should be a single coherent question
const questionMarks = (q.match(/\?/g) || []).length;
expect(questionMarks).toBe(1);
});
// ─── Test 13: clause without question mark — statement form ───
it("handles label shaped like a that-clause", () => {
const q = questionFor(
"That the migration will reduce operational costs by at least 20%",
);
expect(q.endsWith("?")).toBe(true);
expect(q.toLowerCase()).toMatch(/migration|cost/);
});
// ─── Test 14: complex interrogative with embedded clause ───
it("handles label shaped like an indirect question", () => {
const q = questionFor(
"How much funding we need to complete the product launch",
);
expect(q.endsWith("?")).toBe(true);
expect(q.toLowerCase()).toMatch(/funding|launch/);
});
// ─── Test 15: the exact output from 58A.2 template — should not recur ───
it("does not produce the 58A.2 malformed question", () => {
const q = questionFor(
"Are the projected office savings from relocation realistic?",
);
expect(q).not.toBe(
"What was the comparable state before are the projected office savings from relocation realistic?",
);
expect(q).not.toMatch(/before .* are .* realistic/);
});
// ─── Test 16: short yes/no question label ───
it("handles very short interrogative labels", () => {
const q = questionFor("Is this the right approach?");
expect(q.endsWith("?")).toBe(true);
expect(q.toLowerCase()).toMatch(/right|approach/);
});
// ─── Test 17: label with trailing punctuation already stripped in extractMeaning ───
it("handles label that is interrogative but loses its question mark via stripTrailingPunctuation", () => {
// This tests the internal pipeline: extractMeaning strips trailing ?,
// then the meaning feeds into buildNeutralClarificationQuestion or evidence fallback
const q = questionFor(
"What is the total addressable market for this segment?",
);
expect(q.endsWith("?")).toBe(true);
const questionMarks = (q.match(/\?/g) || []).length;
expect(questionMarks).toBe(1);
});
// ─── Test 18: long complex interrogative label ───
it("handles very long interrogative labels without template doubling", () => {
const q = questionFor(
"Are the current employee satisfaction scores sufficient to justify continuing the remote work policy?",
);
expect(q.endsWith("?")).toBe(true);
expect(q.toLowerCase()).not.toMatch(/what would clarify are .* sufficient/i);
expect(q.toLowerCase()).toMatch(/satisfaction|remote/);
});
// ─── Test 19: interrogative label in definition reasoning pattern ───
it("handles interrogative labels through definition path", () => {
const node = makeNode({
id: "n-def",
label: "What does 'customer' mean in this context?",
kind: "unknown",
status: "unknown",
confidence: "medium",
});
const result = formulateQuestion({
node,
graph: makeGraphFor(node),
});
expect(result.question.endsWith("?")).toBe(true);
const questionMarks = (result.question.match(/\?/g) || []).length;
expect(questionMarks).toBe(1);
});
// ─── Test 20: interrogative label in evidence gathering pattern ───
it("handles interrogative labels through evidence path", () => {
const node = makeNode({
id: "n-evidence",
label: "What additional data do we need to validate the hypothesis?",
kind: "unknown",
status: "unknown",
confidence: "medium",
});
const result = formulateQuestion({
node,
graph: makeGraphFor(node),
});
expect(result.question.endsWith("?")).toBe(true);
const questionMarks = (result.question.match(/\?/g) || []).length;
expect(questionMarks).toBe(1);
});
});