fix(reasoning): clean proposition question formulation
This commit is contained in:
@@ -84,10 +84,15 @@ function hasAudienceIdentityQuestion(text) {
|
|||||||
|
|
||||||
function extractMeaning(node) {
|
function extractMeaning(node) {
|
||||||
const raw = `${node?.label || ""} ${node?.description || ""}`.trim();
|
const raw = `${node?.label || ""} ${node?.description || ""}`.trim();
|
||||||
|
const rawLabel = stripTrailingPunctuation(String(node?.label || "")).trim();
|
||||||
let meaning = stripTrailingPunctuation(
|
let meaning = stripTrailingPunctuation(
|
||||||
String(node?.label || node?.description || "this uncertainty"),
|
String(node?.label || node?.description || "this uncertainty"),
|
||||||
).trim();
|
).trim();
|
||||||
|
|
||||||
|
if (isDirectInterrogativeMeaning(rawLabel)) {
|
||||||
|
return rawLabel;
|
||||||
|
}
|
||||||
|
|
||||||
const lowered = normaliseText(raw);
|
const lowered = normaliseText(raw);
|
||||||
if (hasAudienceIdentityQuestion(lowered)) {
|
if (hasAudienceIdentityQuestion(lowered)) {
|
||||||
return "the relevant customer, user, or value recipient";
|
return "the relevant customer, user, or value recipient";
|
||||||
@@ -99,13 +104,23 @@ function extractMeaning(node) {
|
|||||||
.replace(/^uncertainty about\s+/i, "")
|
.replace(/^uncertainty about\s+/i, "")
|
||||||
.trim();
|
.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 (
|
if (
|
||||||
/\b(status|likelihood|probability|chance|risk|uncertainty)\b/i.test(
|
/\b(status|likelihood|probability|chance|risk|uncertainty)\b/i.test(
|
||||||
String(node?.label || ""),
|
String(node?.label || ""),
|
||||||
) &&
|
) &&
|
||||||
/^whether\s+/i.test(strippedDescription)
|
/^whether\s+/i.test(strippedDescription)
|
||||||
) {
|
) {
|
||||||
return sentenceCase(strippedDescription);
|
return sentenceCase(extractWhetherProposition(strippedDescription));
|
||||||
}
|
}
|
||||||
|
|
||||||
meaning = meaning
|
meaning = meaning
|
||||||
@@ -185,12 +200,13 @@ function isInterrogativeMeaning(meaning) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// "whether" clauses — also already question-shaped
|
|
||||||
if (/^whether\b/i.test(trimmed)) return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isWhetherPropositionMeaning(meaning) {
|
||||||
|
return /^whether\b/i.test(String(meaning || "").trim());
|
||||||
|
}
|
||||||
|
|
||||||
function wrapInterrogativeForTemplate(meaning) {
|
function wrapInterrogativeForTemplate(meaning) {
|
||||||
const stripped = stripTrailingPunctuation(meaning).trim();
|
const stripped = stripTrailingPunctuation(meaning).trim();
|
||||||
|
|
||||||
@@ -209,7 +225,7 @@ function wrapInterrogativeForTemplate(meaning) {
|
|||||||
function buildNeutralClarificationQuestion(meaning) {
|
function buildNeutralClarificationQuestion(meaning) {
|
||||||
const content = wrapInterrogativeForTemplate(meaning);
|
const content = wrapInterrogativeForTemplate(meaning);
|
||||||
// If content is already interrogative (wh-), use it as-is with trailing context
|
// If content is already interrogative (wh-), use it as-is with trailing context
|
||||||
if (isInterrogativeMeaning(content)) {
|
if (isDirectInterrogativeMeaning(content)) {
|
||||||
return `${content}?`;
|
return `${content}?`;
|
||||||
}
|
}
|
||||||
return `What would clarify ${content} in this situation?`;
|
return `What would clarify ${content} in this situation?`;
|
||||||
@@ -217,12 +233,18 @@ function buildNeutralClarificationQuestion(meaning) {
|
|||||||
|
|
||||||
function buildEvidenceFallbackQuestion(meaning) {
|
function buildEvidenceFallbackQuestion(meaning) {
|
||||||
const content = wrapInterrogativeForTemplate(meaning);
|
const content = wrapInterrogativeForTemplate(meaning);
|
||||||
if (isInterrogativeMeaning(content)) {
|
if (isDirectInterrogativeMeaning(content)) {
|
||||||
return `${content}?`;
|
return `${content}?`;
|
||||||
}
|
}
|
||||||
return `What evidence would confirm or rule out ${content}?`;
|
return `What evidence would confirm or rule out ${content}?`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isDirectInterrogativeMeaning(meaning) {
|
||||||
|
return (
|
||||||
|
isInterrogativeMeaning(meaning) && !isWhetherPropositionMeaning(meaning)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function extractConstraintClarificationSubject(node) {
|
function extractConstraintClarificationSubject(node) {
|
||||||
const label = stripTrailingPunctuation(node?.label || "");
|
const label = stripTrailingPunctuation(node?.label || "");
|
||||||
const description = String(node?.description || "");
|
const description = String(node?.description || "");
|
||||||
@@ -1269,7 +1291,7 @@ function buildQuestionFromFamily({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
// If meaning is already interrogative, use it directly instead of wrapping
|
// If meaning is already interrogative, use it directly instead of wrapping
|
||||||
if (isInterrogativeMeaning(meaning)) {
|
if (isDirectInterrogativeMeaning(meaning)) {
|
||||||
return `${wrapInterrogativeForTemplate(meaning)}?`;
|
return `${wrapInterrogativeForTemplate(meaning)}?`;
|
||||||
}
|
}
|
||||||
return `What evidence would clarify ${stripTrailingPunctuation(meaning)}?`;
|
return `What evidence would clarify ${stripTrailingPunctuation(meaning)}?`;
|
||||||
@@ -1277,7 +1299,7 @@ function buildQuestionFromFamily({
|
|||||||
|
|
||||||
if (questionFamily === "definition") {
|
if (questionFamily === "definition") {
|
||||||
// If meaning is already interrogative, use it directly
|
// If meaning is already interrogative, use it directly
|
||||||
if (isInterrogativeMeaning(meaning)) {
|
if (isDirectInterrogativeMeaning(meaning)) {
|
||||||
return `${wrapInterrogativeForTemplate(meaning)}?`;
|
return `${wrapInterrogativeForTemplate(meaning)}?`;
|
||||||
}
|
}
|
||||||
return `What does ${meaning} mean in this situation?`;
|
return `What does ${meaning} mean in this situation?`;
|
||||||
@@ -1286,20 +1308,20 @@ function buildQuestionFromFamily({
|
|||||||
if (reasoningPattern === "comparison") {
|
if (reasoningPattern === "comparison") {
|
||||||
if (selectedQuestionTemplate === "comparison_timing_basis") {
|
if (selectedQuestionTemplate === "comparison_timing_basis") {
|
||||||
const cmpContent = wrapInterrogativeForTemplate(meaning);
|
const cmpContent = wrapInterrogativeForTemplate(meaning);
|
||||||
if (isInterrogativeMeaning(cmpContent)) {
|
if (isDirectInterrogativeMeaning(cmpContent)) {
|
||||||
return `${cmpContent}?`;
|
return `${cmpContent}?`;
|
||||||
}
|
}
|
||||||
return `What evidence would clarify whether ${stripTrailingPunctuation(cmpContent)}?`;
|
return `What evidence would clarify whether ${stripTrailingPunctuation(cmpContent)}?`;
|
||||||
}
|
}
|
||||||
if (selectedQuestionTemplate === "comparison_measurement_basis") {
|
if (selectedQuestionTemplate === "comparison_measurement_basis") {
|
||||||
const cmpContent = wrapInterrogativeForTemplate(meaning);
|
const cmpContent = wrapInterrogativeForTemplate(meaning);
|
||||||
if (isInterrogativeMeaning(cmpContent)) {
|
if (isDirectInterrogativeMeaning(cmpContent)) {
|
||||||
return `${cmpContent}?`;
|
return `${cmpContent}?`;
|
||||||
}
|
}
|
||||||
return `What evidence would clarify ${stripTrailingPunctuation(cmpContent)}?`;
|
return `What evidence would clarify ${stripTrailingPunctuation(cmpContent)}?`;
|
||||||
}
|
}
|
||||||
// Default comparison — handle interrogative meaning
|
// Default comparison — handle interrogative meaning
|
||||||
if (isInterrogativeMeaning(meaning)) {
|
if (isDirectInterrogativeMeaning(meaning)) {
|
||||||
return `${wrapInterrogativeForTemplate(meaning)}?`;
|
return `${wrapInterrogativeForTemplate(meaning)}?`;
|
||||||
}
|
}
|
||||||
return `What evidence would clarify ${stripTrailingPunctuation(meaning)}?`;
|
return `What evidence would clarify ${stripTrailingPunctuation(meaning)}?`;
|
||||||
@@ -1310,7 +1332,7 @@ function buildQuestionFromFamily({
|
|||||||
return buildQuestionFromStrategy(investigationStrategy);
|
return buildQuestionFromStrategy(investigationStrategy);
|
||||||
}
|
}
|
||||||
// If meaning is interrogative, use it directly
|
// If meaning is interrogative, use it directly
|
||||||
if (isInterrogativeMeaning(meaning)) {
|
if (isDirectInterrogativeMeaning(meaning)) {
|
||||||
return `${wrapInterrogativeForTemplate(meaning)}?`;
|
return `${wrapInterrogativeForTemplate(meaning)}?`;
|
||||||
}
|
}
|
||||||
return `What fact would resolve the contradiction about ${stripTrailingPunctuation(meaning)}?`;
|
return `What fact would resolve the contradiction about ${stripTrailingPunctuation(meaning)}?`;
|
||||||
@@ -1328,7 +1350,10 @@ function buildQuestionFromFamily({
|
|||||||
if (investigationStrategy) {
|
if (investigationStrategy) {
|
||||||
return buildQuestionFromStrategy(investigationStrategy);
|
return buildQuestionFromStrategy(investigationStrategy);
|
||||||
}
|
}
|
||||||
if (isInterrogativeMeaning(meaning)) {
|
if (isWhetherPropositionMeaning(meaning)) {
|
||||||
|
return `What evidence would clarify ${stripTrailingPunctuation(meaning)}?`;
|
||||||
|
}
|
||||||
|
if (isDirectInterrogativeMeaning(meaning)) {
|
||||||
return `${wrapInterrogativeForTemplate(meaning)}?`;
|
return `${wrapInterrogativeForTemplate(meaning)}?`;
|
||||||
}
|
}
|
||||||
return buildNeutralClarificationQuestion(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({
|
selectedStrategy = buildInvestigationStrategy({
|
||||||
key: "decision_threshold",
|
key: "decision_threshold",
|
||||||
reason:
|
reason:
|
||||||
@@ -1718,7 +1747,7 @@ export function selectInvestigationStrategy({ node, graph, context = {} }) {
|
|||||||
function buildQuestionFromStrategy(strategy) {
|
function buildQuestionFromStrategy(strategy) {
|
||||||
// If meaning is interrogative, use it directly instead of embedding in a template
|
// If meaning is interrogative, use it directly instead of embedding in a template
|
||||||
const m = strategy.meaning;
|
const m = strategy.meaning;
|
||||||
if (isInterrogativeMeaning(m)) {
|
if (isDirectInterrogativeMeaning(m)) {
|
||||||
return `${wrapInterrogativeForTemplate(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", () => {
|
it("evidence-resolvable competing-cause unknown stays on an evidence route rather than neutral clarification", () => {
|
||||||
const unknown = makeNode({
|
const unknown = makeNode({
|
||||||
id: "n-delivery-cause",
|
id: "n-delivery-cause",
|
||||||
|
|||||||
Reference in New Issue
Block a user