feat: decompose composite unknowns before questioning

This commit is contained in:
2026-08-02 19:24:38 +01:00
parent b1c633ba5c
commit 0723c2f49a
9 changed files with 768 additions and 25 deletions
+55
View File
@@ -508,6 +508,61 @@ function isRelationshipExplanationUnknown(node, graph) {
);
}
function isBroadCompositeUnknownText(text) {
return /\b(possible causes|possible reasons|root causes|causes of|drivers of|factors behind|factors affecting|what changed|explanation for why|why .* but|difference between|divergence|moved differently|broad explanation|independent dimensions)\b/.test(
text,
);
}
function isFocusedAtomicUnknownText(text) {
return /\b(define|definition|meaning|term|threshold|criterion|criteria|baseline|evidence|measure|metric|denominator|rate|date|period|budget|constraint|customer|actor|owner)\b/.test(
text,
);
}
export function assessUnknownAtomicity({ node, graph }) {
const nodeText = normaliseText(
`${node?.label || ""} ${node?.description || ""}`,
);
if (isRelationshipExplanationUnknown(node, graph)) {
return {
atomicity: "composite",
reason:
"This unknown asks for a broad explanation across multiple observations, so it should be decomposed before asking a direct question.",
decompositionKind: "relationship_explanation",
};
}
if (
isFocusedAtomicUnknownText(nodeText) &&
!isBroadCompositeUnknownText(nodeText)
) {
return {
atomicity: "atomic",
reason:
"This unknown already targets a single concrete detail that can be investigated directly.",
decompositionKind: null,
};
}
if (isBroadCompositeUnknownText(nodeText)) {
return {
atomicity: "composite",
reason:
"This unknown combines multiple broad candidate explanations, so it should be split into smaller dimensions first.",
decompositionKind: "broad_explanation",
};
}
return {
atomicity: "atomic",
reason:
"No deterministic composite pattern was detected, so the unknown can be investigated directly.",
decompositionKind: null,
};
}
export function formulateTieResolutionQuestion({ graph }) {
const comparability = assessComparability(graph);
if (comparability.comparabilityStatus === "uncertain") {