Files
confidence-engine/docs/experiment-60b32.md
T

149 lines
7.6 KiB
Markdown

# 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`:
```js
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):
```js
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:
```js
if (
!selectedStrategy &&
(hasCriteriaLanguage ||
(hasDecisionValueLanguage && !isWhetherPropositionMeaning(meaning)))
) {
selectedStrategy = buildInvestigationStrategy({
key: "decision_threshold",
...
});
}
```
Three conditions conspire:
1. `hasDecisionContext` is true (parent product-launch node exists)
2. `hasDecisionValueLanguage` is true ("value" in description text within decision context)
3. `!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
```js
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:
- `collectRelatedNodes` returns no ancestors with decision keywords
- `hasDecisionContext` checks only the single node + centralStatement → false (centralStatement defaults to "Decision context" which doesn't match `\b(whether to|build|launch|continue...)`)
- `actionPhrase` scans 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.