fix(graph): preserve grammar for question-like unknown labels

This commit is contained in:
2026-08-12 15:02:56 +01:00
parent b1914f5da7
commit fa42a2643a
2 changed files with 387 additions and 10 deletions
+106 -10
View File
@@ -115,12 +115,72 @@ function sanitizeQuestionText(question) {
.trim();
}
/**
* Detect whether a meaning string is already an interrogative (wh-question,
* yes/no question, or modal auxiliary inversion). This covers the class of
* labels that were previously interpolated raw into template frames, producing
* output like "What would clarify are the projected office savings from
* relocation realistic in this situation?".
*/
function isInterrogativeMeaning(meaning) {
const trimmed = String(meaning || "").trim().toLowerCase();
if (!trimmed) return false;
// Wh-questions (direct or indirect): who/what/where/when/how ...
if (/^(who|what|where|when|how)\b/.test(trimmed)) {
return true;
}
// Yes/no questions via subject-auxiliary inversion: the first word is a
// modal/auxiliary verb followed by a subject determiner (the / a / an / this / etc.)
// This covers "Is the …", "Are the …", "Will we …", "Does it …", etc.
const auxVerbs =
"is|are|was|were|do|does|did|have|has|had|will|would|can|could|should|may|might|must";
if (
new RegExp(
`^(?:${auxVerbs})\\s+(?:the|a|an|this|that|these|those|my|your|our|their|some|any|each|every|both|i|you|we|he|she|it|they)`,
"i",
).test(trimmed)
) {
return true;
}
// "whether" clauses — also already question-shaped
if (/^whether\b/i.test(trimmed)) return true;
return false;
}
function wrapInterrogativeForTemplate(meaning) {
const stripped = stripTrailingPunctuation(meaning).trim();
// Direct interrogatives → return unchanged (already coherent standalone questions)
if (/^(who|what|where|when|how)\b/.test(stripped.toLowerCase())) {
return stripped;
}
if (isInterrogativeMeaning(meaning)) {
return stripped;
}
return stripped;
}
function buildNeutralClarificationQuestion(meaning) {
return `What would clarify ${stripTrailingPunctuation(meaning)} in this situation?`;
const content = wrapInterrogativeForTemplate(meaning);
// If content is already interrogative (wh-), use it as-is with trailing context
if (isInterrogativeMeaning(content)) {
return `${content}?`;
}
return `What would clarify ${content} in this situation?`;
}
function buildEvidenceFallbackQuestion(meaning) {
return `What evidence would confirm or rule out ${stripTrailingPunctuation(meaning)}?`;
const content = wrapInterrogativeForTemplate(meaning);
if (isInterrogativeMeaning(content)) {
return `${content}?`;
}
return `What evidence would confirm or rule out ${content}?`;
}
function extractConstraintClarificationSubject(node) {
@@ -1172,27 +1232,52 @@ function buildQuestionFromFamily({
},
);
}
// If meaning is already interrogative, use it directly instead of wrapping
if (isInterrogativeMeaning(meaning)) {
return `${wrapInterrogativeForTemplate(meaning)}?`;
}
return `What evidence would clarify ${stripTrailingPunctuation(meaning)}?`;
}
if (questionFamily === "definition") {
// If meaning is already interrogative, use it directly
if (isInterrogativeMeaning(meaning)) {
return `${wrapInterrogativeForTemplate(meaning)}?`;
}
return `What does ${meaning} mean in this situation?`;
}
if (reasoningPattern === "comparison") {
if (selectedQuestionTemplate === "comparison_timing_basis") {
return `What evidence would clarify whether ${stripTrailingPunctuation(meaning)}?`;
const cmpContent = wrapInterrogativeForTemplate(meaning);
if (isInterrogativeMeaning(cmpContent)) {
return `${cmpContent}?`;
}
return `What evidence would clarify whether ${stripTrailingPunctuation(cmpContent)}?`;
}
if (selectedQuestionTemplate === "comparison_measurement_basis") {
return `What evidence would clarify ${stripTrailingPunctuation(meaning)}?`;
const cmpContent = wrapInterrogativeForTemplate(meaning);
if (isInterrogativeMeaning(cmpContent)) {
return `${cmpContent}?`;
}
return `What evidence would clarify ${stripTrailingPunctuation(cmpContent)}?`;
}
// Default comparison — handle interrogative meaning
if (isInterrogativeMeaning(meaning)) {
return `${wrapInterrogativeForTemplate(meaning)}?`;
}
return `What evidence would clarify ${stripTrailingPunctuation(meaning)}?`;
}
if (reasoningPattern === "contradiction") {
return investigationStrategy?.key === "contradiction_resolution"
? buildQuestionFromStrategy(investigationStrategy)
: `What fact would resolve the contradiction about ${stripTrailingPunctuation(meaning)}?`;
if (investigationStrategy?.key === "contradiction_resolution") {
return buildQuestionFromStrategy(investigationStrategy);
}
// If meaning is interrogative, use it directly
if (isInterrogativeMeaning(meaning)) {
return `${wrapInterrogativeForTemplate(meaning)}?`;
}
return `What fact would resolve the contradiction about ${stripTrailingPunctuation(meaning)}?`;
}
if (reasoningPattern === "explanation") {
@@ -1203,9 +1288,14 @@ function buildQuestionFromFamily({
return `Which option should be investigated first, and why?`;
}
return investigationStrategy
? buildQuestionFromStrategy(investigationStrategy)
: buildNeutralClarificationQuestion(meaning);
// Default (diagnosis) — handle interrogative meaning
if (investigationStrategy) {
return buildQuestionFromStrategy(investigationStrategy);
}
if (isInterrogativeMeaning(meaning)) {
return `${wrapInterrogativeForTemplate(meaning)}?`;
}
return buildNeutralClarificationQuestion(meaning);
}
export function formulateTieResolutionQuestion({ graph }) {
@@ -1590,6 +1680,12 @@ 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)) {
return `${wrapInterrogativeForTemplate(m)}?`;
}
switch (strategy.key) {
case "decision_threshold":
return strategy.actionPhrase