fix(reasoning): reconcile evidence and preserve deterministic continuation
This commit is contained in:
@@ -4504,6 +4504,191 @@ describe("60B.43 — terminal post-mutation target is cleared after valid decisi
|
||||
};
|
||||
}
|
||||
|
||||
function makeRichReconciliationFixture() {
|
||||
const productLaunchDecision = makeNode({
|
||||
id: "n_product_launch_decision_rich",
|
||||
label: "Which option leaves us better off overall?",
|
||||
description:
|
||||
"Uncertainty about whether launching this year or waiting twelve months leaves the organisation better off overall.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
});
|
||||
const launchThisYear = makeNode({
|
||||
id: "opt_launch_this_year_rich",
|
||||
label: "Launch this year",
|
||||
description:
|
||||
"Launch this year with approximately £1.2M expected annual revenue and around £300,000 additional support and implementation cost.",
|
||||
kind: "option",
|
||||
status: "known",
|
||||
confidence: "high",
|
||||
});
|
||||
const waitTwelveMonths = makeNode({
|
||||
id: "opt_wait_twelve_months_rich",
|
||||
label: "Wait twelve months",
|
||||
description:
|
||||
"Wait twelve months to reduce immediate support cost and improve the product, but delay revenue and risk competitor movement.",
|
||||
kind: "option",
|
||||
status: "known",
|
||||
confidence: "high",
|
||||
});
|
||||
const customerSigningPrimary = makeNode({
|
||||
id: "n_enterprise_customer_signing_rich",
|
||||
label: "Prospective enterprise customer signing status",
|
||||
description:
|
||||
"Unknown whether the large enterprise customer will sign if we launch this year, because they account for approximately £700,000 of expected annual revenue.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
});
|
||||
const customerSigningSecondary = makeNode({
|
||||
id: "n_customer_contract_timeline_pending",
|
||||
label: "Enterprise customer contract status is currently unknown",
|
||||
description:
|
||||
"The status of the large enterprise customer's contract is currently unknown, including whether signing is still pending on this year's launch timeline.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
});
|
||||
const customerDecisionTimeline = makeNode({
|
||||
id: "n_customer_decision_timeline",
|
||||
label: "When will the enterprise customer make its decision?",
|
||||
description:
|
||||
"Unknown when the enterprise customer will make its decision, and the timing could still affect launch planning.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
});
|
||||
const revenueConsequence = makeNode({
|
||||
id: "n_customer_revenue_consequence",
|
||||
label: "Whether £700,000 of annual revenue from the enterprise customer will be received",
|
||||
description:
|
||||
"Unknown whether the £700,000 annual revenue linked to the enterprise customer will be received if we launch this year.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
});
|
||||
const commercialViability = makeNode({
|
||||
id: "n_launch_viability_without_anchor_customer",
|
||||
label: "Is launch commercially viable without the £700,000?",
|
||||
description:
|
||||
"Unknown whether launching this year is still commercially viable without the £700,000 enterprise-customer revenue.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
});
|
||||
const competitorRisk = makeNode({
|
||||
id: "n_competitor_move_first_risk",
|
||||
label: "Could competitors move first during a 12-month delay?",
|
||||
description:
|
||||
"Unknown whether competitors could move first if launch is delayed by twelve months, and this could still materially change the decision.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
});
|
||||
|
||||
const graph = makeGraph({
|
||||
centralStatement:
|
||||
"We need to decide whether there is enough evidence to launch this new software product this year or whether waiting twelve months is safer.",
|
||||
nodes: [
|
||||
productLaunchDecision,
|
||||
launchThisYear,
|
||||
waitTwelveMonths,
|
||||
customerSigningPrimary,
|
||||
customerSigningSecondary,
|
||||
customerDecisionTimeline,
|
||||
revenueConsequence,
|
||||
commercialViability,
|
||||
competitorRisk,
|
||||
],
|
||||
edges: [
|
||||
makeEdge({
|
||||
id: "e-rich-launch-to-decision",
|
||||
fromNodeId: launchThisYear.id,
|
||||
toNodeId: productLaunchDecision.id,
|
||||
relationship: "contained_in",
|
||||
confidence: "high",
|
||||
description: "Launch-this-year is an option in the decision.",
|
||||
}),
|
||||
makeEdge({
|
||||
id: "e-rich-wait-to-decision",
|
||||
fromNodeId: waitTwelveMonths.id,
|
||||
toNodeId: productLaunchDecision.id,
|
||||
relationship: "contained_in",
|
||||
confidence: "high",
|
||||
description: "Wait-twelve-months is an option in the decision.",
|
||||
}),
|
||||
makeEdge({
|
||||
id: "e-rich-primary-customer-to-launch",
|
||||
fromNodeId: customerSigningPrimary.id,
|
||||
toNodeId: launchThisYear.id,
|
||||
relationship: "contained_in",
|
||||
confidence: "high",
|
||||
description: "Primary customer-signing uncertainty is material to launching this year.",
|
||||
}),
|
||||
makeEdge({
|
||||
id: "e-rich-secondary-customer-to-launch",
|
||||
fromNodeId: customerSigningSecondary.id,
|
||||
toNodeId: launchThisYear.id,
|
||||
relationship: "contained_in",
|
||||
confidence: "high",
|
||||
description: "Secondary customer contract-status uncertainty is also material to launching this year.",
|
||||
}),
|
||||
makeEdge({
|
||||
id: "e-rich-timeline-to-launch",
|
||||
fromNodeId: customerDecisionTimeline.id,
|
||||
toNodeId: launchThisYear.id,
|
||||
relationship: "depends_on",
|
||||
confidence: "medium",
|
||||
description: "Launch planning depends in part on when the customer decides.",
|
||||
}),
|
||||
makeEdge({
|
||||
id: "e-rich-revenue-consequence-to-launch",
|
||||
fromNodeId: revenueConsequence.id,
|
||||
toNodeId: launchThisYear.id,
|
||||
relationship: "depends_on",
|
||||
confidence: "high",
|
||||
description: "Receiving the £700,000 revenue depends on whether the customer signs.",
|
||||
}),
|
||||
makeEdge({
|
||||
id: "e-rich-viability-to-decision",
|
||||
fromNodeId: commercialViability.id,
|
||||
toNodeId: productLaunchDecision.id,
|
||||
relationship: "depends_on",
|
||||
confidence: "high",
|
||||
description: "The decision depends on whether launch is viable without the anchor customer.",
|
||||
}),
|
||||
makeEdge({
|
||||
id: "e-rich-competitor-risk-to-wait",
|
||||
fromNodeId: competitorRisk.id,
|
||||
toNodeId: waitTwelveMonths.id,
|
||||
relationship: "contained_in",
|
||||
confidence: "high",
|
||||
description: "Competitor move-first risk is material to the wait option.",
|
||||
}),
|
||||
],
|
||||
activeUnknownNodeId: productLaunchDecision.id,
|
||||
resolvedNodeIds: [],
|
||||
currentSummary:
|
||||
"The decision is open; customer signing is unresolved; customer contract status and decision timing remain represented separately; revenue consequence, commercial viability, and competitor timing remain material uncertainties.",
|
||||
});
|
||||
|
||||
return {
|
||||
graph,
|
||||
ids: {
|
||||
productLaunchDecision: productLaunchDecision.id,
|
||||
launchThisYear: launchThisYear.id,
|
||||
waitTwelveMonths: waitTwelveMonths.id,
|
||||
customerSigningPrimary: customerSigningPrimary.id,
|
||||
customerSigningSecondary: customerSigningSecondary.id,
|
||||
customerDecisionTimeline: customerDecisionTimeline.id,
|
||||
revenueConsequence: revenueConsequence.id,
|
||||
commercialViability: commercialViability.id,
|
||||
competitorRisk: competitorRisk.id,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
it("accepts the 60B.37-shaped closure proposal, resolves the customer factor, and clears the terminal decision target after mutation", () => {
|
||||
const { graph, ids } = makeProductLaunchClosureFixture();
|
||||
|
||||
@@ -4617,6 +4802,421 @@ describe("60B.43 — terminal post-mutation target is cleared after valid decisi
|
||||
expect(result.selectedQuestion?.nodeId).not.toBe(ids.productLaunchDecision);
|
||||
});
|
||||
|
||||
it("60B.81 regression — after materially informative sufficiency answer, a specific remaining material factor should outrank repeated sufficiency confirmation", () => {
|
||||
const { graph, ids } = makeProductLaunchClosureFixture({
|
||||
includeFallbackUnknown: true,
|
||||
});
|
||||
|
||||
const previousQuestion =
|
||||
"Is there anything else material that could change which option is better?";
|
||||
const answer =
|
||||
"The enterprise customer has now confirmed in writing that they will not sign if we launch this year, so the £700,000 of expected annual revenue from them will not be received.";
|
||||
|
||||
const result = applyValidatedProposal({
|
||||
situationGraph: graph,
|
||||
proposal: {
|
||||
updatedNodes: [
|
||||
{
|
||||
nodeId: ids.enterpriseCustomerSigning,
|
||||
previousStatus: "unknown",
|
||||
newStatus: "resolved",
|
||||
previousValue: null,
|
||||
newValue: answer,
|
||||
reason:
|
||||
"The answer definitively resolves the enterprise-customer signing factor with materially new negative evidence.",
|
||||
},
|
||||
{
|
||||
nodeId: ids.launchThisYear,
|
||||
previousStatus: "known",
|
||||
newStatus: "known",
|
||||
previousValue: null,
|
||||
newValue:
|
||||
"If we launch this year, the £700,000 expected annual revenue from the enterprise customer will not be received.",
|
||||
reason:
|
||||
"Update the launch-this-year option to reflect the resolved financial consequence of the customer not signing.",
|
||||
},
|
||||
],
|
||||
addedNodes: [],
|
||||
addedEdges: [],
|
||||
removedEdgeIds: [],
|
||||
resolvedUnknownNodeIds: [ids.enterpriseCustomerSigning],
|
||||
affectedNodeIds: [ids.launchThisYear, ids.productLaunchDecision],
|
||||
selectedQuestion: {
|
||||
nodeId: ids.productLaunchDecision,
|
||||
question: previousQuestion,
|
||||
reason:
|
||||
"The prior turn asked the bounded sufficiency-confirmation question at the decision level.",
|
||||
},
|
||||
},
|
||||
previousQuestion,
|
||||
answer,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
|
||||
const resolvedCustomer = result.updatedSituationGraph.nodes.find(
|
||||
(node) => node.id === ids.enterpriseCustomerSigning,
|
||||
);
|
||||
const remainingFactor = result.updatedSituationGraph.nodes.find(
|
||||
(node) => node.id === ids.fallbackUnknown,
|
||||
);
|
||||
const launchOption = result.updatedSituationGraph.nodes.find(
|
||||
(node) => node.id === ids.launchThisYear,
|
||||
);
|
||||
|
||||
expect(resolvedCustomer?.status).toBe("resolved");
|
||||
expect(remainingFactor?.status).toBe("unknown");
|
||||
expect(result.updatedSituationGraph.resolvedNodeIds).toContain(
|
||||
ids.enterpriseCustomerSigning,
|
||||
);
|
||||
expect(result.updatedSituationGraph.resolvedNodeIds).not.toContain(
|
||||
ids.productLaunchDecision,
|
||||
);
|
||||
expect(result.newActiveUnknownNodeId).toBe(ids.fallbackUnknown);
|
||||
expect(result.selectedQuestion?.nodeId).toBe(ids.fallbackUnknown);
|
||||
expect(result.selectedQuestion?.selectedQuestionTemplate).not.toBe(
|
||||
"decision_threshold_sufficiency_confirmation",
|
||||
);
|
||||
expect(result.selectedQuestion?.question).not.toBe(previousQuestion);
|
||||
expect(launchOption?.value).toContain("£700,000");
|
||||
expect(launchOption?.value).toContain("will not be received");
|
||||
});
|
||||
|
||||
it("60B.82 richer live regression — definitive customer rejection must supersede stale overlapping customer-unknown state and must not repeat sufficiency question", () => {
|
||||
const productLaunchDecision = makeNode({
|
||||
id: "n_product_launch_decision_rich",
|
||||
label: "Which option leaves us better off overall?",
|
||||
description:
|
||||
"Uncertainty about whether launching this year or waiting twelve months leaves the organisation better off overall.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
});
|
||||
const launchThisYear = makeNode({
|
||||
id: "opt_launch_this_year_rich",
|
||||
label: "Launch this year",
|
||||
description:
|
||||
"Launch this year with approximately £1.2M expected annual revenue and around £300,000 additional support and implementation cost.",
|
||||
kind: "option",
|
||||
status: "known",
|
||||
confidence: "high",
|
||||
});
|
||||
const waitTwelveMonths = makeNode({
|
||||
id: "opt_wait_twelve_months_rich",
|
||||
label: "Wait twelve months",
|
||||
description:
|
||||
"Wait twelve months to reduce immediate support cost and improve the product, but delay revenue and risk competitor movement.",
|
||||
kind: "option",
|
||||
status: "known",
|
||||
confidence: "high",
|
||||
});
|
||||
const customerSigningPrimary = makeNode({
|
||||
id: "n_enterprise_customer_signing_rich",
|
||||
label: "Prospective enterprise customer signing status",
|
||||
description:
|
||||
"Unknown whether the large enterprise customer will sign if we launch this year, because they account for approximately £700,000 of expected annual revenue.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
});
|
||||
const customerSigningSecondary = makeNode({
|
||||
id: "n_customer_contract_timeline_pending",
|
||||
label: "Enterprise customer contract timing still pending",
|
||||
description:
|
||||
"The status of the large enterprise customer's contract is currently unknown, including whether signing is still pending on this year's launch timeline.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
});
|
||||
const delayingBenefit = makeNode({
|
||||
id: "n_delay_improvement_benefit",
|
||||
label: "Quantifiable benefit of delaying launch for product improvements versus immediate market entry",
|
||||
description:
|
||||
"Unknown how much extra value twelve months of product improvement would create relative to launching now, and this could still change which option is better.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
});
|
||||
const competitorRisk = makeNode({
|
||||
id: "n_competitor_move_first_risk",
|
||||
label: "External firms that could move first if the product is delayed",
|
||||
description:
|
||||
"Unknown which external firms could move first if launch is delayed, and the competitive impact could still materially change the decision.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
});
|
||||
|
||||
const graph = makeGraph({
|
||||
centralStatement:
|
||||
"We need to decide whether there is enough evidence to launch this new software product this year or whether waiting twelve months is safer.",
|
||||
nodes: [
|
||||
productLaunchDecision,
|
||||
launchThisYear,
|
||||
waitTwelveMonths,
|
||||
customerSigningPrimary,
|
||||
customerSigningSecondary,
|
||||
delayingBenefit,
|
||||
competitorRisk,
|
||||
],
|
||||
edges: [
|
||||
makeEdge({
|
||||
id: "e-rich-launch-to-decision",
|
||||
fromNodeId: launchThisYear.id,
|
||||
toNodeId: productLaunchDecision.id,
|
||||
relationship: "contained_in",
|
||||
confidence: "high",
|
||||
description: "Launch-this-year is an option in the decision.",
|
||||
}),
|
||||
makeEdge({
|
||||
id: "e-rich-wait-to-decision",
|
||||
fromNodeId: waitTwelveMonths.id,
|
||||
toNodeId: productLaunchDecision.id,
|
||||
relationship: "contained_in",
|
||||
confidence: "high",
|
||||
description: "Wait-twelve-months is an option in the decision.",
|
||||
}),
|
||||
makeEdge({
|
||||
id: "e-rich-primary-customer-to-launch",
|
||||
fromNodeId: customerSigningPrimary.id,
|
||||
toNodeId: launchThisYear.id,
|
||||
relationship: "contained_in",
|
||||
confidence: "high",
|
||||
description: "Primary customer-signing uncertainty is material to launching this year.",
|
||||
}),
|
||||
makeEdge({
|
||||
id: "e-rich-secondary-customer-to-launch",
|
||||
fromNodeId: customerSigningSecondary.id,
|
||||
toNodeId: launchThisYear.id,
|
||||
relationship: "contained_in",
|
||||
confidence: "high",
|
||||
description: "Secondary customer contract-status uncertainty is also material to launching this year.",
|
||||
}),
|
||||
makeEdge({
|
||||
id: "e-rich-delay-benefit-to-wait",
|
||||
fromNodeId: delayingBenefit.id,
|
||||
toNodeId: waitTwelveMonths.id,
|
||||
relationship: "contained_in",
|
||||
confidence: "high",
|
||||
description: "The quantified benefit of delay is material to the wait option.",
|
||||
}),
|
||||
makeEdge({
|
||||
id: "e-rich-competitor-risk-to-wait",
|
||||
fromNodeId: competitorRisk.id,
|
||||
toNodeId: waitTwelveMonths.id,
|
||||
relationship: "contained_in",
|
||||
confidence: "high",
|
||||
description: "Competitor move-first risk is material to the wait option.",
|
||||
}),
|
||||
],
|
||||
activeUnknownNodeId: productLaunchDecision.id,
|
||||
resolvedNodeIds: [],
|
||||
currentSummary:
|
||||
"The decision is open; customer signing is unresolved; delaying-launch benefits and competitor timing remain material uncertainties.",
|
||||
});
|
||||
|
||||
const previousQuestion =
|
||||
"Is there anything else material that could change which option is better?";
|
||||
const answer =
|
||||
"The enterprise customer has now confirmed in writing that they will not sign if we launch this year, so the £700,000 of expected annual revenue from them will not be received.";
|
||||
|
||||
const result = applyValidatedProposal({
|
||||
situationGraph: graph,
|
||||
proposal: {
|
||||
updatedNodes: [
|
||||
{
|
||||
nodeId: customerSigningPrimary.id,
|
||||
previousStatus: "unknown",
|
||||
newStatus: "resolved",
|
||||
previousValue: null,
|
||||
newValue: answer,
|
||||
reason:
|
||||
"The answer definitively resolves the primary enterprise-customer signing factor with negative evidence.",
|
||||
},
|
||||
{
|
||||
nodeId: launchThisYear.id,
|
||||
previousStatus: "known",
|
||||
newStatus: "known",
|
||||
previousValue: null,
|
||||
newValue:
|
||||
"If we launch this year, the £700,000 expected annual revenue from the enterprise customer will not be received.",
|
||||
reason:
|
||||
"Reflect the financial impact of the confirmed customer rejection in the launch-this-year option.",
|
||||
},
|
||||
],
|
||||
addedNodes: [],
|
||||
addedEdges: [],
|
||||
removedEdgeIds: [],
|
||||
resolvedUnknownNodeIds: [customerSigningPrimary.id],
|
||||
affectedNodeIds: [launchThisYear.id, productLaunchDecision.id],
|
||||
selectedQuestion: {
|
||||
nodeId: productLaunchDecision.id,
|
||||
question: previousQuestion,
|
||||
reason:
|
||||
"The previous live turn was the sufficiency confirmation question at the decision level.",
|
||||
},
|
||||
},
|
||||
previousQuestion,
|
||||
answer,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
|
||||
const primaryCustomerNode = result.updatedSituationGraph.nodes.find(
|
||||
(node) => node.id === customerSigningPrimary.id,
|
||||
);
|
||||
const staleCustomerNode = result.updatedSituationGraph.nodes.find(
|
||||
(node) => node.id === customerSigningSecondary.id,
|
||||
);
|
||||
|
||||
expect(primaryCustomerNode?.status).toBe("resolved");
|
||||
expect(primaryCustomerNode?.value).toContain("will not sign");
|
||||
|
||||
expect(staleCustomerNode?.status).not.toBe("unknown");
|
||||
expect(staleCustomerNode?.description?.toLowerCase()).not.toContain(
|
||||
"currently unknown",
|
||||
);
|
||||
expect(staleCustomerNode?.description?.toLowerCase()).not.toContain(
|
||||
"pending",
|
||||
);
|
||||
|
||||
expect(result.selectedQuestion?.selectedQuestionTemplate).not.toBe(
|
||||
"decision_threshold_sufficiency_confirmation",
|
||||
);
|
||||
expect(result.selectedQuestion?.question).not.toBe(previousQuestion);
|
||||
expect([
|
||||
delayingBenefit.id,
|
||||
competitorRisk.id,
|
||||
]).toContain(result.selectedQuestion?.nodeId);
|
||||
});
|
||||
|
||||
describe("60B.83 — reconciliation safety envelope before generic implementation", () => {
|
||||
function applyRichCustomerRejectionUpdate() {
|
||||
const { graph, ids } = makeRichReconciliationFixture();
|
||||
const previousQuestion =
|
||||
"Is there anything else material that could change which option is better?";
|
||||
const answer =
|
||||
"The enterprise customer has now confirmed in writing that they will not sign if we launch this year, so the £700,000 of expected annual revenue from them will not be received.";
|
||||
|
||||
const result = applyValidatedProposal({
|
||||
situationGraph: graph,
|
||||
proposal: {
|
||||
updatedNodes: [
|
||||
{
|
||||
nodeId: ids.customerSigningPrimary,
|
||||
previousStatus: "unknown",
|
||||
newStatus: "resolved",
|
||||
previousValue: null,
|
||||
newValue: answer,
|
||||
reason:
|
||||
"The answer definitively resolves the primary enterprise-customer signing factor with negative evidence.",
|
||||
},
|
||||
{
|
||||
nodeId: ids.launchThisYear,
|
||||
previousStatus: "known",
|
||||
newStatus: "known",
|
||||
previousValue: null,
|
||||
newValue:
|
||||
"If we launch this year, the £700,000 expected annual revenue from the enterprise customer will not be received.",
|
||||
reason:
|
||||
"Reflect the financial impact of the confirmed customer rejection in the launch-this-year option.",
|
||||
},
|
||||
],
|
||||
addedNodes: [],
|
||||
addedEdges: [],
|
||||
removedEdgeIds: [],
|
||||
resolvedUnknownNodeIds: [ids.customerSigningPrimary],
|
||||
affectedNodeIds: [ids.launchThisYear, ids.productLaunchDecision],
|
||||
selectedQuestion: {
|
||||
nodeId: ids.productLaunchDecision,
|
||||
question: previousQuestion,
|
||||
reason:
|
||||
"The previous live turn was the sufficiency confirmation question at the decision level.",
|
||||
},
|
||||
},
|
||||
previousQuestion,
|
||||
answer,
|
||||
});
|
||||
|
||||
return { result, ids, previousQuestion, answer };
|
||||
}
|
||||
|
||||
it("1 SAME_PROPOSITION — same-proposition sibling should no longer remain an active unresolved contradiction", () => {
|
||||
const { result, ids } = applyRichCustomerRejectionUpdate();
|
||||
expect(result.success).toBe(true);
|
||||
|
||||
const primaryNode = result.updatedSituationGraph.nodes.find(
|
||||
(node) => node.id === ids.customerSigningPrimary,
|
||||
);
|
||||
const siblingNode = result.updatedSituationGraph.nodes.find(
|
||||
(node) => node.id === ids.customerSigningSecondary,
|
||||
);
|
||||
|
||||
expect(primaryNode?.status).toBe("resolved");
|
||||
expect(siblingNode?.status).not.toBe("unknown");
|
||||
});
|
||||
|
||||
it("2 SAME_ENTITY_DIFFERENT_DIMENSION — timeline node remains unresolved", () => {
|
||||
const { result, ids } = applyRichCustomerRejectionUpdate();
|
||||
expect(result.success).toBe(true);
|
||||
|
||||
const timelineNode = result.updatedSituationGraph.nodes.find(
|
||||
(node) => node.id === ids.customerDecisionTimeline,
|
||||
);
|
||||
|
||||
expect(timelineNode?.status).toBe("unknown");
|
||||
});
|
||||
|
||||
it("3 DERIVED_CONSEQUENCE — structurally linked revenue consequence may be updated from explicit dependency semantics", () => {
|
||||
const { result, ids } = applyRichCustomerRejectionUpdate();
|
||||
expect(result.success).toBe(true);
|
||||
|
||||
const revenueNode = result.updatedSituationGraph.nodes.find(
|
||||
(node) => node.id === ids.revenueConsequence,
|
||||
);
|
||||
|
||||
expect(revenueNode?.status).not.toBe("unknown");
|
||||
});
|
||||
|
||||
it("4 DOWNSTREAM_DEPENDENCY — commercial viability without the £700k remains unresolved", () => {
|
||||
const { result, ids } = applyRichCustomerRejectionUpdate();
|
||||
expect(result.success).toBe(true);
|
||||
|
||||
const viabilityNode = result.updatedSituationGraph.nodes.find(
|
||||
(node) => node.id === ids.commercialViability,
|
||||
);
|
||||
|
||||
expect(viabilityNode?.status).toBe("unknown");
|
||||
});
|
||||
|
||||
it("5 UNRELATED — competitor factor remains unresolved and untouched", () => {
|
||||
const { result, ids } = applyRichCustomerRejectionUpdate();
|
||||
expect(result.success).toBe(true);
|
||||
|
||||
const competitorNode = result.updatedSituationGraph.nodes.find(
|
||||
(node) => node.id === ids.competitorRisk,
|
||||
);
|
||||
|
||||
expect(competitorNode?.status).toBe("unknown");
|
||||
expect(competitorNode?.value ?? null).toBe(null);
|
||||
});
|
||||
|
||||
it("6 QUESTION_CONTINUATION — next question should continue with a specific remaining factor, not repeated sufficiency confirmation", () => {
|
||||
const { result, ids, previousQuestion } = applyRichCustomerRejectionUpdate();
|
||||
expect(result.success).toBe(true);
|
||||
|
||||
expect([
|
||||
ids.customerDecisionTimeline,
|
||||
ids.commercialViability,
|
||||
ids.competitorRisk,
|
||||
]).toContain(result.selectedQuestion?.nodeId);
|
||||
expect(result.selectedQuestion?.selectedQuestionTemplate).not.toBe(
|
||||
"decision_threshold_sufficiency_confirmation",
|
||||
);
|
||||
expect(result.selectedQuestion?.question).not.toBe(previousQuestion);
|
||||
});
|
||||
});
|
||||
|
||||
it("returns no active target and no final question when only terminal unknowns remain after a valid mutation", () => {
|
||||
const graph = makeGraph({
|
||||
centralStatement: "Known-only terminal graph after mutation",
|
||||
|
||||
Reference in New Issue
Block a user