feat: enforce one-concept questions
This commit is contained in:
+111
-11
@@ -1385,10 +1385,74 @@ function describeObservationFocus(context, which) {
|
||||
}
|
||||
|
||||
function buildDecompositionTemplates(parentNode, graph, depth = 0) {
|
||||
const parentText = normaliseText(
|
||||
`${parentNode?.label || ""} ${parentNode?.description || ""}`,
|
||||
);
|
||||
const context = buildDecompositionContext(graph);
|
||||
const firstFocus = describeObservationFocus(context, "first");
|
||||
const secondFocus = describeObservationFocus(context, "second");
|
||||
|
||||
if (
|
||||
/\b(genuine problem|commercially justified|commercial justification|people would value|pay for it|justified confidence|decision support methods|willingness to pay|seek help)\b/.test(
|
||||
parentText,
|
||||
)
|
||||
) {
|
||||
return [
|
||||
{
|
||||
label: "Who experiences this problem",
|
||||
description:
|
||||
"Need to know who experiences this problem, because that must be clear before deciding whether it is commercially justified.",
|
||||
dependsOnLabels: [],
|
||||
},
|
||||
{
|
||||
label: "What happens when this problem is not resolved",
|
||||
description:
|
||||
"Need to know what happens when this problem is not resolved, because that is needed before judging whether the problem matters.",
|
||||
dependsOnLabels: ["Who experiences this problem"],
|
||||
},
|
||||
{
|
||||
label: "How often this problem happens",
|
||||
description:
|
||||
"Need to know how often this problem happens, because that helps judge whether it is a real recurring problem.",
|
||||
dependsOnLabels: ["Who experiences this problem"],
|
||||
},
|
||||
{
|
||||
label: "How people deal with this problem today",
|
||||
description:
|
||||
"Need to know how people deal with this problem today, because that is needed before comparing alternatives or value.",
|
||||
dependsOnLabels: [
|
||||
"Who experiences this problem",
|
||||
"What happens when this problem is not resolved",
|
||||
"How often this problem happens",
|
||||
],
|
||||
},
|
||||
depth === 0
|
||||
? {
|
||||
label: "Whether people actively look for help with this problem",
|
||||
description:
|
||||
"Need to know whether people actively look for help with this problem, because that is needed before judging demand or willingness to pay.",
|
||||
dependsOnLabels: [
|
||||
"Who experiences this problem",
|
||||
"What happens when this problem is not resolved",
|
||||
"How often this problem happens",
|
||||
"How people deal with this problem today",
|
||||
],
|
||||
}
|
||||
: {
|
||||
label: "Whether people would pay to solve this problem",
|
||||
description:
|
||||
"Need to know whether people would pay to solve this problem, because that can only be judged after the problem itself is established.",
|
||||
dependsOnLabels: [
|
||||
"Who experiences this problem",
|
||||
"What happens when this problem is not resolved",
|
||||
"How often this problem happens",
|
||||
"How people deal with this problem today",
|
||||
"Whether people actively look for help with this problem",
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (/\btiming or measurement basis\b/i.test(parentNode.label)) {
|
||||
return [
|
||||
{
|
||||
@@ -1434,14 +1498,25 @@ function buildDecompositionTemplates(parentNode, graph, depth = 0) {
|
||||
function buildCompositeUnknownChildren(parentNode, graph, depth = 0) {
|
||||
const templates = buildDecompositionTemplates(parentNode, graph, depth);
|
||||
|
||||
const candidateNodes = templates.map(
|
||||
(template) =>
|
||||
findEquivalentDecompositionChild(
|
||||
graph,
|
||||
parentNode.id,
|
||||
template.label,
|
||||
template.description,
|
||||
) || {
|
||||
const labelToId = new Map(
|
||||
templates.map((template) => [
|
||||
template.label,
|
||||
buildDecompositionChildId(parentNode.id, template.label),
|
||||
]),
|
||||
);
|
||||
const candidateNodes = templates.map((template) => {
|
||||
const existing = findEquivalentDecompositionChild(
|
||||
graph,
|
||||
parentNode.id,
|
||||
template.label,
|
||||
template.description,
|
||||
);
|
||||
const dependsOn = (template.dependsOnLabels || [])
|
||||
.map((label) => labelToId.get(label))
|
||||
.filter(Boolean);
|
||||
|
||||
return (
|
||||
existing || {
|
||||
id: buildDecompositionChildId(parentNode.id, template.label),
|
||||
label: template.label,
|
||||
description: template.description,
|
||||
@@ -1451,12 +1526,13 @@ function buildCompositeUnknownChildren(parentNode, graph, depth = 0) {
|
||||
value: null,
|
||||
unit: null,
|
||||
evidenceIds: [],
|
||||
dependsOn: [],
|
||||
dependsOn,
|
||||
affects: [],
|
||||
parentId: parentNode.id,
|
||||
childIds: [],
|
||||
},
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
const childNodes = [];
|
||||
const childEdges = [];
|
||||
const childNodeIds = [];
|
||||
@@ -1566,9 +1642,14 @@ function runDeterministicDecomposition({
|
||||
let proposedChildCount = 0;
|
||||
let acceptedChildCount = 0;
|
||||
let selectedChildNodeId = null;
|
||||
let selectedUnknownBefore =
|
||||
deterministicSelection?.status === "selected"
|
||||
? deterministicSelection.nodeId
|
||||
: null;
|
||||
let decompositionStoppedReason = null;
|
||||
let rejectedChildren = [];
|
||||
let childQualitySummary = [];
|
||||
let decompositionTriggeredByQuestionComplexity = false;
|
||||
|
||||
while (workingSelection?.status === "selected" && workingSelection?.nodeId) {
|
||||
const selectedNode = findNodeById(workingGraph, workingSelection.nodeId);
|
||||
@@ -1699,6 +1780,8 @@ function runDeterministicDecomposition({
|
||||
rejectedChildren,
|
||||
childQualitySummary,
|
||||
selectedChildNodeId,
|
||||
selectedUnknownBefore,
|
||||
decompositionTriggeredByQuestionComplexity,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2121,6 +2204,10 @@ export function applyValidatedProposal({
|
||||
})
|
||||
: null;
|
||||
|
||||
const questionComplexity = formulatedQuestion?.questionComplexity ?? null;
|
||||
const plainLanguageNormalisations =
|
||||
formulatedQuestion?.plainLanguageNormalisations ?? [];
|
||||
|
||||
const finalSelectedQuestion =
|
||||
deterministicSelection?.status === "ambiguous"
|
||||
? {
|
||||
@@ -2137,6 +2224,8 @@ export function applyValidatedProposal({
|
||||
reason: formulatedQuestion?.reason || deterministicSelection.reason,
|
||||
strategy: formulatedQuestion?.strategy,
|
||||
investigationStrategy: formulatedQuestion?.investigationStrategy,
|
||||
questionComplexity,
|
||||
plainLanguageNormalisations,
|
||||
}
|
||||
: null;
|
||||
|
||||
@@ -2202,6 +2291,17 @@ export function applyValidatedProposal({
|
||||
rejectedChildren,
|
||||
selectedChildNodeId,
|
||||
childQualitySummary,
|
||||
selectedUnknownBefore: decompositionResult.selectedUnknownBefore,
|
||||
selectedUnknownAfter: deterministicSelection?.nodeId ?? null,
|
||||
questionComplexityAccepted: questionComplexity?.acceptable ?? null,
|
||||
primaryConceptCount: questionComplexity?.primaryConceptCount ?? null,
|
||||
cognitiveLoad: questionComplexity?.cognitiveLoad ?? null,
|
||||
complexityReasons: questionComplexity?.reasons ?? [],
|
||||
decompositionTriggeredByQuestionComplexity:
|
||||
decompositionResult.decompositionTriggeredByQuestionComplexity ?? false,
|
||||
previousQuestion,
|
||||
finalQuestion: finalSelectedQuestion?.question ?? null,
|
||||
plainLanguageNormalisations,
|
||||
propagationPerformed,
|
||||
resolvedChildNodeId,
|
||||
parentNodeId,
|
||||
|
||||
Reference in New Issue
Block a user