diff --git a/lib/graph/question-formulator.js b/lib/graph/question-formulator.js index 355751d..21b7125 100644 --- a/lib/graph/question-formulator.js +++ b/lib/graph/question-formulator.js @@ -102,6 +102,8 @@ function extractMeaning(node) { const strippedDescription = stripTrailingPunctuation(description) .replace(/^uncertainty regarding\s+/i, "") .replace(/^uncertainty about\s+/i, "") + .replace(/^unknown\s+/i, "") + .replace(/^uncertain\s+/i, "") .trim(); const extractWhetherProposition = (text) => { diff --git a/tests/graph/question-formulator.test.js b/tests/graph/question-formulator.test.js index 8a4d85c..fbc90ac 100644 --- a/tests/graph/question-formulator.test.js +++ b/tests/graph/question-formulator.test.js @@ -795,4 +795,179 @@ describe("formulateQuestion", () => { "What would clarify magnitude and nature of cash outflows (operating expenses) in this situation?", ); }); + + // ── 60B.28: uncertainty-prefix coverage ──────────────────────────── + + it("60B.27 regression: 'Unknown whether...' preserves full proposition", () => { + const unknown = makeNode({ + id: "unc-60b27", + label: "Prospective enterprise customer signing likelihood", + description: + "Unknown whether one prospective enterprise customer will sign if we launch this year, so that the remaining £700k of the expected £1.2M annual revenue is realized.", + kind: "unknown", + status: "unknown", + confidence: "medium", + }); + + const graph = makeGraphFor(unknown, { + centralStatement: + "We need to decide which launch timing option provides superior net value.", + }); + + const result = formulateQuestion({ node: unknown, graph }); + + expect(result.question).toBe( + "What evidence would clarify whether one prospective enterprise customer will sign if we launch this year?", + ); + // Rationale must NOT leak into the final question + expect(result.question).not.toContain("£700k"); + expect(result.question).not.toContain("£1.2M"); + expect(result.question.toLowerCase()).not.toContain("annual revenue"); + }); + + it("'Uncertain whether...' exposes the full proposition with evidence framing", () => { + const unknown = makeNode({ + id: "unc-uncertain", + label: "Supplier renewal uncertainty", + description: + "Uncertain whether the supplier will renew, because the contract is still under review.", + kind: "unknown", + status: "unknown", + confidence: "medium", + }); + + const result = formulateQuestion({ + node: unknown, + graph: makeGraphFor(unknown), + }); + + expect(result.question).toBe( + "What evidence would clarify whether the supplier will renew?", + ); + }); + + it("bare 'Whether...' remains unchanged (existing behaviour)", () => { + const unknown = makeNode({ + id: "unc-bare-whether", + label: "Supplier renewal likelihood", + description: "Whether the supplier will renew the contract.", + kind: "unknown", + status: "unknown", + confidence: "medium", + }); + + const result = formulateQuestion({ + node: unknown, + graph: makeGraphFor(unknown), + }); + + expect(result.question).toBe( + "What evidence would clarify whether the supplier will renew the contract?", + ); + }); + + it("'Uncertainty about whether...' remains unchanged (existing behaviour)", () => { + const unknown = makeNode({ + id: "unc-about", + label: "Customer renewal likelihood", + description: + "Uncertainty about whether the customer will renew next year, because that affects the decision.", + kind: "unknown", + status: "unknown", + confidence: "medium", + }); + + const result = formulateQuestion({ + node: unknown, + graph: makeGraphFor(unknown), + }); + + expect(result.question.toLowerCase()).toContain( + "whether the customer will renew next year", + ); + }); + + it("'Uncertainty regarding whether...' remains unchanged (existing behaviour)", () => { + const unknown = makeNode({ + id: "unc-regarding", + label: "Client retention uncertainty", + description: + "Uncertainty regarding whether our largest client would depart following a relocation to Manchester; matters because their departure would cost approximately £5M per year.", + kind: "unknown", + status: "unknown", + confidence: "medium", + }); + + const graph = makeGraphFor(unknown, { + centralStatement: + "We need to decide whether relocating is commercially justified.", + }); + + const result = formulateQuestion({ node: unknown, graph }); + + expect(result.question.toLowerCase()).toContain( + "whether our largest client would depart following a relocation to manchester", + ); + }); + + it("direct interrogative label remains direct", () => { + const unknown = makeNode({ + id: "unc-direct", + label: "Will our largest client leave if we relocate?", + description: + "Uncertainty regarding whether our largest client would depart following a relocation to Manchester; matters because their departure would cost approximately £5M per year.", + kind: "unknown", + status: "unknown", + confidence: "medium", + }); + + const graph = makeGraphFor(unknown, { + centralStatement: + "We need to decide whether relocating is commercially justified.", + }); + + const result = formulateQuestion({ node: unknown, graph }); + + expect(result.question).toBe("Will our largest client leave if we relocate?"); + }); + + it("nominal label without explicit 'whether...' falls back to label-based extraction", () => { + const unknown = makeNode({ + id: "unc-nominal", + label: "Service reliability uncertainty", + description: "Unknown service reliability.", + kind: "unknown", + status: "unknown", + confidence: "medium", + }); + + const result = formulateQuestion({ + node: unknown, + graph: makeGraphFor(unknown), + }); + + expect(result.question).not.toContain("How should uncertainty regarding"); + }); + + it("source description is never mutated by question formulation", () => { + const description = + "Unknown whether one prospective enterprise customer will sign if we launch this year, so that the remaining £700k of the expected £1.2M annual revenue is realized."; + const unknown = makeNode({ + id: "unc-preserve", + label: "Prospective enterprise customer signing likelihood", + description, + kind: "unknown", + status: "unknown", + confidence: "medium", + }); + + const graph = makeGraphFor(unknown, { + centralStatement: + "We need to decide which launch timing option provides superior net value.", + }); + + formulateQuestion({ node: unknown, graph }); + + expect(unknown.description).toBe(description); + }); });