diff --git a/lib/graph/prompt-builder.js b/lib/graph/prompt-builder.js index dc11a9a..ebd2338 100644 --- a/lib/graph/prompt-builder.js +++ b/lib/graph/prompt-builder.js @@ -134,6 +134,14 @@ The JSON object must contain exactly these top-level fields: 31. If the answer explicitly states a hard constraint, state that directly in userSupportedMeaning. 32. Populate resolutionGuidance when the user's meaning genuinely implies must_remain_unresolved, may_resolve, or must_resolve. Keep it null only when no existing resolution state actually applies. +## Decision Sufficiency Rule + +An unresolved decision between options should not remain open merely because some uncertainty still exists. + +Keep a decision context unresolved only when you can identify a specific unresolved factor that could materially change which option is preferred. + +If the currently supported evidence is sufficient to distinguish the options and no such material unresolved factor remains, resolve the existing decision context and do not ask a generic continuation question. + ## Decision Option Structure Rules When the user presents mutually exclusive candidate actions for one unresolved choice: diff --git a/tests/graph/prompt-builder.test.js b/tests/graph/prompt-builder.test.js index e22d4c8..c73abe0 100644 --- a/tests/graph/prompt-builder.test.js +++ b/tests/graph/prompt-builder.test.js @@ -764,3 +764,102 @@ describe("60A.3 decision option prompt rules", () => { expect(altLine).toBeDefined(); }); }); + +// ── Experiment 60B.4 — Decision Materiality Rule ────────────── + +describe("60B.4 decision materiality rule", () => { + const prompt = buildGraphUpdatePrompt(makeContext()); + + it("rule: unresolved decision not kept open merely because uncertainty remains", () => { + expect(prompt).toContain( + "should not remain open merely because some uncertainty still exists", + ); + }); + + it("rule: continuation requires a specific unresolved factor", () => { + expect(prompt).toContain("specific unresolved factor"); + }); + + it("rule: that factor must be capable of materially changing the preferred option", () => { + expect(prompt).toContain( + "could materially change which option is preferred", + ); + }); + + it("rule: if no material factor remains, resolve existing decision context rather than asking generic question", () => { + expect(prompt).toContain( + "resolve the existing decision context", + ); + expect(prompt).toContain( + "do not ask a generic continuation question", + ); + }); + + it("does NOT introduce financial thresholds", () => { + expect(prompt).not.toContain("threshold"); + expect(prompt).not.toContain("£600k"); + expect(prompt).not.toContain("£2m"); + expect(prompt).not.toContain("payback"); + }); + + it("does NOT introduce relocation-specific examples", () => { + expect(prompt).not.toContain("relocation"); + expect(prompt).not.toContain("Manchester"); + expect(prompt).not.toContain("London"); + expect(prompt).not.toContain("engineer"); + }); + + it("does NOT introduce automatic resolution whenever one option appears better", () => { + // The rule must not say "always resolve" or "resolve when one looks better" + const sufficiencySection = prompt.split( + "## Decision Sufficiency Rule", + )[1].split("## Decision Option Structure Rules")[0]; + expect(sufficiencySection).not.toContain("always resolve"); + expect(sufficiencySection).not.toContain( + "resolve when one option", + ); + expect(sufficiencySection).not.toContain("looks better"); + }); + + it("does NOT introduce new schema fields or node kinds", () => { + // The rule text must not reference schema fields or node kinds outside the contract + expect(prompt).not.toContain("materiality_score"); + expect(prompt).not.toContain("decision_sufficiency"); + expect(prompt).not.toContain('kind "decision"'); + }); + + it("does NOT mention specific currency or amounts", () => { + expect(prompt).not.toContain("£2 million"); + expect(prompt).not.toContain("£600k"); + expect(prompt).not.toContain("$1M"); + expect(prompt).not.toContain("financial threshold"); + }); + + it("existing Rule 20 preserved", () => { + expect(prompt).toContain( + "Return selectedQuestion as null only when no consequential unresolved unknown remains.", + ); + }); + + it("is domain-general — no provider-specific, taxonomic, or keyword-based language", () => { + expect(prompt).not.toContain("qwen"); + expect(prompt).not.toContain("claude"); + expect(prompt).not.toContain("gpt"); + expect(prompt).not.toContain("keyword"); + expect(prompt).not.toContain("synonym"); + }); + + it("preserves the possibility of keeping a decision open when grounded factor exists", () => { + // The rule must allow continuation when a specific unresolved factor could change the outcome + expect(prompt).toContain("specific unresolved factor"); + expect(prompt).toContain("could materially change which option is preferred"); + }); + + it("distinguishes uncertainty from decision-relevant uncertainty", () => { + // The rule must distinguish mere uncertainty from uncertainty that matters to the decision + const sufficiencySection = prompt.split( + "## Decision Sufficiency Rule", + )[1].split("## Decision Option Structure Rules")[0]; + expect(sufficiencySection).toContain("should not remain open merely because some uncertainty still exists"); + }); +});