feat: decompose non-answerable unknowns
This commit is contained in:
@@ -569,6 +569,93 @@ function isDirectlyAnswerableObservationChildText(text) {
|
||||
);
|
||||
}
|
||||
|
||||
function countPrerequisiteConceptSignals(text) {
|
||||
let count = 0;
|
||||
|
||||
if (/\bproblem\b/.test(text)) count += 1;
|
||||
if (/\b(audience|customer|user|buyer|stakeholder|recipient)\b/.test(text))
|
||||
count += 1;
|
||||
if (/\b(demand|seek help|actively look for help)\b/.test(text)) count += 1;
|
||||
if (/\b(pay|willingness to pay|price|pricing)\b/.test(text)) count += 1;
|
||||
if (
|
||||
/\b(compare|comparison|different from|alternatives|alternative|existing alternatives|existing tools|better than)\b/.test(
|
||||
text,
|
||||
)
|
||||
)
|
||||
count += 1;
|
||||
if (/\b(value|viability|justified|business case|commercial)\b/.test(text))
|
||||
count += 1;
|
||||
if (/\b(feasibility|technical)\b/.test(text)) count += 1;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
function countIndependentAnswerDimensions(node, graph) {
|
||||
const text = normaliseText(`${node?.label || ""} ${node?.description || ""}`);
|
||||
const relatedNodes = collectRelatedNodes(node, graph);
|
||||
const unresolvedDependencies = relatedNodes.filter(
|
||||
(relatedNode) =>
|
||||
relatedNode.kind === "unknown" && relatedNode.status !== "resolved",
|
||||
).length;
|
||||
const conjunctionCount = (text.match(/\b(and|or)\b/g) || []).length;
|
||||
const prerequisiteConceptCount = countPrerequisiteConceptSignals(text);
|
||||
const implicitConclusion =
|
||||
/\b(commercially justified|commercial justification|commercial viability|business case|customer value|market demand|product validation|technical feasibility)\b/.test(
|
||||
text,
|
||||
) ||
|
||||
(/\bevidence that\b/.test(text) &&
|
||||
/\b(problem|need|demand|audience|customer|user|alternatives|better than)\b/.test(
|
||||
text,
|
||||
)) ||
|
||||
(/\bwhether\b/.test(text) &&
|
||||
/\b(addresses|solve|solves|justifies|supports|demonstrates)\b/.test(
|
||||
text,
|
||||
) &&
|
||||
/\b(problem|value|need|demand|audience|customer|user)\b/.test(text));
|
||||
|
||||
return {
|
||||
prerequisiteConceptCount,
|
||||
unresolvedDependencies,
|
||||
conjunctionCount,
|
||||
implicitConclusion,
|
||||
multipleEvidenceDimensions:
|
||||
prerequisiteConceptCount >= 2 || conjunctionCount >= 2,
|
||||
};
|
||||
}
|
||||
|
||||
export function assessUnknownAnswerability({ node, graph }) {
|
||||
const text = normaliseText(`${node?.label || ""} ${node?.description || ""}`);
|
||||
const dimensionSummary = countIndependentAnswerDimensions(node, graph);
|
||||
const independentlyAnswerable =
|
||||
!dimensionSummary.implicitConclusion &&
|
||||
!dimensionSummary.multipleEvidenceDimensions &&
|
||||
dimensionSummary.unresolvedDependencies === 0 &&
|
||||
dimensionSummary.prerequisiteConceptCount <= 1;
|
||||
|
||||
if (independentlyAnswerable) {
|
||||
return {
|
||||
independentlyAnswerable: true,
|
||||
reason:
|
||||
"This unknown can be answered directly without first resolving several prerequisite investigations.",
|
||||
prerequisiteConceptCount: dimensionSummary.prerequisiteConceptCount,
|
||||
decompositionRequired: false,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
independentlyAnswerable: false,
|
||||
reason: dimensionSummary.implicitConclusion
|
||||
? "This unknown asks for a higher-level conclusion that depends on several smaller investigations."
|
||||
: "This unknown still bundles multiple prerequisite evidence dimensions, so it should be decomposed before it becomes the selected question.",
|
||||
prerequisiteConceptCount: Math.max(
|
||||
dimensionSummary.prerequisiteConceptCount,
|
||||
dimensionSummary.unresolvedDependencies,
|
||||
dimensionSummary.conjunctionCount + 1,
|
||||
),
|
||||
decompositionRequired: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function assessUnknownAtomicity({ node, graph }) {
|
||||
const nodeText = normaliseText(
|
||||
`${node?.label || ""} ${node?.description || ""}`,
|
||||
|
||||
Reference in New Issue
Block a user