fix(reasoning): clean proposition question formulation
This commit is contained in:
@@ -84,10 +84,15 @@ function hasAudienceIdentityQuestion(text) {
|
||||
|
||||
function extractMeaning(node) {
|
||||
const raw = `${node?.label || ""} ${node?.description || ""}`.trim();
|
||||
const rawLabel = stripTrailingPunctuation(String(node?.label || "")).trim();
|
||||
let meaning = stripTrailingPunctuation(
|
||||
String(node?.label || node?.description || "this uncertainty"),
|
||||
).trim();
|
||||
|
||||
if (isDirectInterrogativeMeaning(rawLabel)) {
|
||||
return rawLabel;
|
||||
}
|
||||
|
||||
const lowered = normaliseText(raw);
|
||||
if (hasAudienceIdentityQuestion(lowered)) {
|
||||
return "the relevant customer, user, or value recipient";
|
||||
@@ -99,13 +104,23 @@ function extractMeaning(node) {
|
||||
.replace(/^uncertainty about\s+/i, "")
|
||||
.trim();
|
||||
|
||||
const extractWhetherProposition = (text) => {
|
||||
const match = String(text || "")
|
||||
.trim()
|
||||
.match(
|
||||
/^(whether\b[\s\S]*?)(?:;\s+|,\s*(?:so\s+that|because)\b|\s+matters\s+because\b|$)/i,
|
||||
);
|
||||
|
||||
return match?.[1]?.trim() || String(text || "").trim();
|
||||
};
|
||||
|
||||
if (
|
||||
/\b(status|likelihood|probability|chance|risk|uncertainty)\b/i.test(
|
||||
String(node?.label || ""),
|
||||
) &&
|
||||
/^whether\s+/i.test(strippedDescription)
|
||||
) {
|
||||
return sentenceCase(strippedDescription);
|
||||
return sentenceCase(extractWhetherProposition(strippedDescription));
|
||||
}
|
||||
|
||||
meaning = meaning
|
||||
@@ -185,12 +200,13 @@ function isInterrogativeMeaning(meaning) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// "whether" clauses — also already question-shaped
|
||||
if (/^whether\b/i.test(trimmed)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function isWhetherPropositionMeaning(meaning) {
|
||||
return /^whether\b/i.test(String(meaning || "").trim());
|
||||
}
|
||||
|
||||
function wrapInterrogativeForTemplate(meaning) {
|
||||
const stripped = stripTrailingPunctuation(meaning).trim();
|
||||
|
||||
@@ -209,7 +225,7 @@ function wrapInterrogativeForTemplate(meaning) {
|
||||
function buildNeutralClarificationQuestion(meaning) {
|
||||
const content = wrapInterrogativeForTemplate(meaning);
|
||||
// If content is already interrogative (wh-), use it as-is with trailing context
|
||||
if (isInterrogativeMeaning(content)) {
|
||||
if (isDirectInterrogativeMeaning(content)) {
|
||||
return `${content}?`;
|
||||
}
|
||||
return `What would clarify ${content} in this situation?`;
|
||||
@@ -217,12 +233,18 @@ function buildNeutralClarificationQuestion(meaning) {
|
||||
|
||||
function buildEvidenceFallbackQuestion(meaning) {
|
||||
const content = wrapInterrogativeForTemplate(meaning);
|
||||
if (isInterrogativeMeaning(content)) {
|
||||
if (isDirectInterrogativeMeaning(content)) {
|
||||
return `${content}?`;
|
||||
}
|
||||
return `What evidence would confirm or rule out ${content}?`;
|
||||
}
|
||||
|
||||
function isDirectInterrogativeMeaning(meaning) {
|
||||
return (
|
||||
isInterrogativeMeaning(meaning) && !isWhetherPropositionMeaning(meaning)
|
||||
);
|
||||
}
|
||||
|
||||
function extractConstraintClarificationSubject(node) {
|
||||
const label = stripTrailingPunctuation(node?.label || "");
|
||||
const description = String(node?.description || "");
|
||||
@@ -1269,7 +1291,7 @@ function buildQuestionFromFamily({
|
||||
);
|
||||
}
|
||||
// If meaning is already interrogative, use it directly instead of wrapping
|
||||
if (isInterrogativeMeaning(meaning)) {
|
||||
if (isDirectInterrogativeMeaning(meaning)) {
|
||||
return `${wrapInterrogativeForTemplate(meaning)}?`;
|
||||
}
|
||||
return `What evidence would clarify ${stripTrailingPunctuation(meaning)}?`;
|
||||
@@ -1277,7 +1299,7 @@ function buildQuestionFromFamily({
|
||||
|
||||
if (questionFamily === "definition") {
|
||||
// If meaning is already interrogative, use it directly
|
||||
if (isInterrogativeMeaning(meaning)) {
|
||||
if (isDirectInterrogativeMeaning(meaning)) {
|
||||
return `${wrapInterrogativeForTemplate(meaning)}?`;
|
||||
}
|
||||
return `What does ${meaning} mean in this situation?`;
|
||||
@@ -1286,20 +1308,20 @@ function buildQuestionFromFamily({
|
||||
if (reasoningPattern === "comparison") {
|
||||
if (selectedQuestionTemplate === "comparison_timing_basis") {
|
||||
const cmpContent = wrapInterrogativeForTemplate(meaning);
|
||||
if (isInterrogativeMeaning(cmpContent)) {
|
||||
if (isDirectInterrogativeMeaning(cmpContent)) {
|
||||
return `${cmpContent}?`;
|
||||
}
|
||||
return `What evidence would clarify whether ${stripTrailingPunctuation(cmpContent)}?`;
|
||||
}
|
||||
if (selectedQuestionTemplate === "comparison_measurement_basis") {
|
||||
const cmpContent = wrapInterrogativeForTemplate(meaning);
|
||||
if (isInterrogativeMeaning(cmpContent)) {
|
||||
if (isDirectInterrogativeMeaning(cmpContent)) {
|
||||
return `${cmpContent}?`;
|
||||
}
|
||||
return `What evidence would clarify ${stripTrailingPunctuation(cmpContent)}?`;
|
||||
}
|
||||
// Default comparison — handle interrogative meaning
|
||||
if (isInterrogativeMeaning(meaning)) {
|
||||
if (isDirectInterrogativeMeaning(meaning)) {
|
||||
return `${wrapInterrogativeForTemplate(meaning)}?`;
|
||||
}
|
||||
return `What evidence would clarify ${stripTrailingPunctuation(meaning)}?`;
|
||||
@@ -1310,7 +1332,7 @@ function buildQuestionFromFamily({
|
||||
return buildQuestionFromStrategy(investigationStrategy);
|
||||
}
|
||||
// If meaning is interrogative, use it directly
|
||||
if (isInterrogativeMeaning(meaning)) {
|
||||
if (isDirectInterrogativeMeaning(meaning)) {
|
||||
return `${wrapInterrogativeForTemplate(meaning)}?`;
|
||||
}
|
||||
return `What fact would resolve the contradiction about ${stripTrailingPunctuation(meaning)}?`;
|
||||
@@ -1328,7 +1350,10 @@ function buildQuestionFromFamily({
|
||||
if (investigationStrategy) {
|
||||
return buildQuestionFromStrategy(investigationStrategy);
|
||||
}
|
||||
if (isInterrogativeMeaning(meaning)) {
|
||||
if (isWhetherPropositionMeaning(meaning)) {
|
||||
return `What evidence would clarify ${stripTrailingPunctuation(meaning)}?`;
|
||||
}
|
||||
if (isDirectInterrogativeMeaning(meaning)) {
|
||||
return `${wrapInterrogativeForTemplate(meaning)}?`;
|
||||
}
|
||||
return buildNeutralClarificationQuestion(meaning);
|
||||
@@ -1649,7 +1674,11 @@ export function selectInvestigationStrategy({ node, graph, context = {} }) {
|
||||
});
|
||||
}
|
||||
|
||||
if (!selectedStrategy && (hasDecisionValueLanguage || hasCriteriaLanguage)) {
|
||||
if (
|
||||
!selectedStrategy &&
|
||||
(hasCriteriaLanguage ||
|
||||
(hasDecisionValueLanguage && !isWhetherPropositionMeaning(meaning)))
|
||||
) {
|
||||
selectedStrategy = buildInvestigationStrategy({
|
||||
key: "decision_threshold",
|
||||
reason:
|
||||
@@ -1718,7 +1747,7 @@ export function selectInvestigationStrategy({ node, graph, context = {} }) {
|
||||
function buildQuestionFromStrategy(strategy) {
|
||||
// If meaning is interrogative, use it directly instead of embedding in a template
|
||||
const m = strategy.meaning;
|
||||
if (isInterrogativeMeaning(m)) {
|
||||
if (isDirectInterrogativeMeaning(m)) {
|
||||
return `${wrapInterrogativeForTemplate(m)}?`;
|
||||
}
|
||||
|
||||
|
||||
@@ -442,6 +442,100 @@ describe("formulateQuestion", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("60B.24 regression strips whether-rationale and uses evidence framing without mutating the source node", () => {
|
||||
const description =
|
||||
"Whether one prospective enterprise customer will sign if we launch this year; they account for ~£700k of the £1.2M expected annual revenue, so that resolving their intent is needed to assess the financial impact of launching now.";
|
||||
const unknown = makeNode({
|
||||
id: "n-60b24-signing-status",
|
||||
label: "Prospective enterprise customer signing status",
|
||||
description,
|
||||
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).toContain(
|
||||
"whether one prospective enterprise customer will sign if we launch this year",
|
||||
);
|
||||
expect(result.question).toContain("What evidence would clarify");
|
||||
expect(result.question).not.toContain("£700k");
|
||||
expect(result.question).not.toContain("£1.2M");
|
||||
expect(result.question).not.toContain("resolving their intent");
|
||||
expect(result.question).not.toContain("financial impact");
|
||||
expect(unknown.description).toBe(description);
|
||||
});
|
||||
|
||||
it("clean whether proposition uses evidence framing rather than a direct whether-question", () => {
|
||||
const unknown = makeNode({
|
||||
id: "n-supplier-renewal",
|
||||
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?",
|
||||
);
|
||||
expect(result.question).not.toBe(
|
||||
"Whether the supplier will renew the contract?",
|
||||
);
|
||||
});
|
||||
|
||||
it("wh-question remains unchanged", () => {
|
||||
const unknown = makeNode({
|
||||
id: "n-wh-question",
|
||||
label: "What would change the preferred option?",
|
||||
description: "What would change the preferred option?",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
});
|
||||
|
||||
const result = formulateQuestion({
|
||||
node: unknown,
|
||||
graph: makeGraphFor(unknown),
|
||||
});
|
||||
|
||||
expect(result.question).toBe("What would change the preferred option?");
|
||||
});
|
||||
|
||||
it("non-whether semicolon content is not globally truncated", () => {
|
||||
const unknown = makeNode({
|
||||
id: "n-semicolon-baseline",
|
||||
label:
|
||||
"Unknown baseline measurement basis; current and previous figures were prepared differently.",
|
||||
description:
|
||||
"Baseline measurement basis; current and previous figures were prepared differently.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
});
|
||||
|
||||
const result = formulateQuestion({
|
||||
node: unknown,
|
||||
graph: makeGraphFor(unknown),
|
||||
});
|
||||
|
||||
expect(result.question).toContain(
|
||||
"baseline measurement basis; current and previous figures were prepared differently",
|
||||
);
|
||||
expect(result.question).not.toContain("What would clarify whether");
|
||||
});
|
||||
|
||||
it("evidence-resolvable competing-cause unknown stays on an evidence route rather than neutral clarification", () => {
|
||||
const unknown = makeNode({
|
||||
id: "n-delivery-cause",
|
||||
|
||||
Reference in New Issue
Block a user