7.6 KiB
Experiment 60B.32 — Runtime Question Formulation Path Diagnosis
Branch: feature/proposition-prefix-over-v0.32
Starting HEAD: clean (after 60B.31)
Date: 2026-08-14
Status: COMPLETE — Classification: D — ACTIVE CONTEXT DRIFT
Objective
Answer exactly:
Where does the full runtime diverge from the deterministic question-formulator path, causing the correct selected node to end with a generic decision-justification question?
Fixed Input (60B.31)
The revenue and launch-cost estimates are good enough for the decision. The remaining issue is one prospective enterprise customer. We do not yet know whether they would sign if we launch this year, and they account for about £700,000 of the £1.2 million expected annual revenue.
Live node added:
- id:
n_enterprise_customer_signing - label:
Enterprise customer signing decision(or variant with "decision" at end) - description:
Whether the prospective enterprise customer will commit this year, because resolving this uncertainty is needed to decide if launching this year provides superior net value over waiting twelve months.
60B.32 Findings
Root Cause: extractMeaning proposition detection depends on label keywords
In question-formulator.js line 120-127 of extractMeaning:
if (
/\b(status|likelihood|probability|chance|risk|uncertainty)\b/i.test(
String(node?.label || ""),
) &&
/^whether\s+/i.test(strippedDescription)
) {
return sentenceCase(extractWhetherProposition(strippedDescription));
}
The proposition-extraction path requires the label to contain one of: status, likelihood, probability, chance, risk, uncertainty.
The focused test (line 896-914) uses label "Supplier renewal likelihood" — contains "likelihood" ✓ → meaning starts with "Whether..." → isWhetherPropositionMeaning(meaning) = true.
The live 60B.31 node uses label "Enterprise customer signing decision" — contains none of those keywords ✗ → falls through to line 129-136 which strips "Whether" → meaning does NOT start with "Whether..." → isWhetherPropositionMeaning(meaning) = false.
Root Cause: Parent context bleeds into child formulation via extractActionPhrase
In selectInvestigationStrategy (line 1593):
const actionPhrase = extractActionPhrase([
...resolvedValues,
...relatedNodes.map((relatedNode) => relatedNode.value),
...relatedNodes.map((relatedNode) => relatedNode.label),
...relatedNodes.map((relatedNode) => relatedNode.description),
graph?.centralStatement,
]);
extractActionPhrase iterates over ALL related nodes including the parent n_product_launch_decision. The regex \b(build|launch|adopt|buy|continue|proceed|invest in|fund)\s+([^.,;:]+)/i matches words like "launch" in the parent's label/description, returning an action phrase from the parent node.
This means the child node's question text embeds the parent's decision vocabulary ("launching"), not the child's own proposition.
Root Cause: hasDecisionValueLanguage wins over proposition semantics
At line 1680-1695:
if (
!selectedStrategy &&
(hasCriteriaLanguage ||
(hasDecisionValueLanguage && !isWhetherPropositionMeaning(meaning)))
) {
selectedStrategy = buildInvestigationStrategy({
key: "decision_threshold",
...
});
}
Three conditions conspire:
hasDecisionContextis true (parent product-launch node exists)hasDecisionValueLanguageis true ("value" in description text within decision context)!isWhetherPropositionMeaning(meaning)is true (extractMeaning stripped "Whether")
All three are true → selects decision_threshold strategy over evidence gathering.
Generic question origin
Function: buildQuestionFromStrategy at line 1759 of question-formulator.js
Pattern: "decision_threshold"
Family: "decision_threshold"
Template: Uses strategy.actionPhrase from parent node's "launch" keyword
Trigger: actionPhrase != null (from parent context) → interpolates gerund form
return strategy.actionPhrase
? `What outcome would demonstrate enough value to justify ${toGerundPhrase(strategy.actionPhrase)}?`
: "What outcome would be sufficient to justify this decision?";
Why it wins: The decision_threshold condition (line 1680-1695) fires before evidence_gathering conditions (line 1726-1742). hasDecisionValueLanguage combines with !isWhetherPropositionMeaning(meaning) as a gate — and because extractMeaning didn't produce a "Whether..." meaning for this node, the gate passes.
Critical distinction between test and live
The focused test's graph (via makeGraphFor) contains ONLY the single unknown node. No parent nodes exist. Therefore:
collectRelatedNodesreturns no ancestors with decision keywordshasDecisionContextchecks only the single node + centralStatement → false (centralStatement defaults to "Decision context" which doesn't match\b(whether to|build|launch|continue...))actionPhrasescans nothing relevant → null
The live graph contains: parent product-launch decision node + child customer-signing unknown node. The parent provides both hasDecisionContext and actionPhrase via collectRelatedNodes.
Context comparison
| Input | Focused deterministic test | Live runtime (60B.31) |
|---|---|---|
| node label | "Supplier renewal likelihood" | "Enterprise customer signing decision" |
| node description | "Whether the supplier will renew the contract." | "Whether the prospective enterprise customer will commit this year, because resolving this uncertainty is needed to decide if launching this year provides superior net value over waiting twelve months." |
| label keywords match | YES ("likelihood") | NO (none of status/likelihood/probability/chance/risk/uncertainty) |
| extracted meaning starts with "Whether" | YES | NO |
| isWhetherPropositionMeaning | true | false |
| parent node exists | NO | YES (n_product_launch_decision) |
| hasDecisionContext | false | true |
| decisionContext flag in selectInvestigationStrategy | false | true |
| actionPhrase source | null (nothing to scan) | parent node's "launch" keyword |
| hasDecisionValueLanguage | false ("value"/"justify" not in text) | true (description contains "value", context is decision) |
| selectedStrategy key | evidence_gathering | decision_threshold |
| reasoningPattern | diagnosis (default, no decision context) | decision (parent triggers it) |
Minimum corrective boundary
Choice: D — REMOVE/CHANGE POST-FORMULATION OVERRIDE (more precisely: make proposition semantics override decision-context heuristics)
The fix must ensure that when a node description starts with "Whether..." (bare proposition), the proposition extraction in extractMeaning does NOT depend on label keywords. The description-level "Whether" itself is sufficient evidence of an unresolved proposition.
Specifically, line 120-127 of question-formulator.js should be augmented:
- Either remove the label keyword requirement when description starts with "Whether..."
- Or add a separate extraction path that checks bare "Whether..." in description regardless of label
Would this preserve generic decision questions when the decision node itself is selected?
YES — because only nodes whose description starts with "Whether" (not just any node with "decision" in its label) would get the proposition extraction boost. A product launch decision node has a different description format.
Would it preserve 60B.20 direct interrogative behaviour?
LIKELY — because isDirectInterrogativeMeaning is checked at line 92 first, before any "Whether" handling. Direct interrogatives already bypass all the Whether-stripping logic.