reasoning: suppress explanation question without relationship structure

This commit is contained in:
2026-08-10 10:17:35 +01:00
parent 1a31949a48
commit 4c5666dfb3
3 changed files with 184 additions and 1 deletions
+3 -1
View File
@@ -436,7 +436,9 @@ function classifyObservationRelationshipWhenComparable(graph) {
reason:
"There is not enough structure to classify the relationship safely.",
contradictionReasoningAllowed: false,
questionRequired: true,
questionRequired: false,
questionSuppressedReason:
"No meaningful relationship structure was established; uncertainty is preserved without fabricating an explanation problem.",
};
}
@@ -128,6 +128,69 @@ describe("comparability assessment", () => {
]);
});
it("returns questionRequired=false for fallback (no meaningful relationship structure)", () => {
// Two non-measurement-like observations with no shared concept and no known direction.
// They pass comparability (confirmed) but fall through all classification conditions:
// - not duplicate, no available+unavailable, no revenue/cash pair, no all-known-directions
// → hits the fallback which now returns questionRequired=false
const graph = {
centralStatement:
"Temperature was measured. Ice was observed.",
nodes: [
{
id: "obs-temperature",
label: "Temperature was measured.",
description: "Temperature was measured.",
kind: "observation",
status: "supported",
confidence: "high",
value: null,
unit: null,
evidenceIds: [],
dependsOn: [],
affects: [],
parentId: null,
childIds: [],
},
{
id: "obs-ice",
label: "Ice was observed.",
description: "Ice was observed.",
kind: "observation",
status: "supported",
confidence: "high",
value: null,
unit: null,
evidenceIds: [],
dependsOn: [],
affects: [],
parentId: null,
childIds: [],
},
],
edges: [],
activeUnknownNodeId: null,
resolvedNodeIds: [],
currentSummary: "Fallback regression fixture",
};
const relationship = classifyObservationRelationship(graph);
expect(relationship.relationshipStatus).toBe("insufficient_information");
expect(relationship.questionRequired).toBe(false);
// Verify reasoning stages exist and have correct status
const stages = relationship.reasoningStages;
expect(stages.length).toBe(2);
expect(stages[0].stage).toBe("comparability");
expect(stages[0].status).toBe("confirmed");
expect(stages[1].stage).toBe("relationship");
expect(stages[1].status).toBe("insufficient_information");
// questionRequired=false means buildEmergentReasoningUnknown returns early,
// so no "Explanation for why..." unknown is created — the engine preserves
// uncertainty rather than fabricating an explanation problem.
});
it("allows contradiction reasoning only for genuine contradictions", () => {
const serviceGraph = {
centralStatement:
@@ -279,4 +279,122 @@ describe("reasoning-pattern validation", () => {
expect(result.selectedQuestion?.nodeId).not.toBe("n-incompatible-child");
expect(result.selectedQuestion?.reasoningPattern).toBe("decision");
});
it("preserves potentially_related with questionRequired=true for meaningful shared structure", () => {
// Two observations sharing "revenue" concept, both with known direction ("up").
// With confirmed comparability (via stored state), condition 2 triggers:
// sharedConcepts.size > 0 && all directions known → potentially_related
const graph = makeGraph({
centralStatement: "Revenue increased. Revenue doubled.",
nodes: [
makeNode({
id: "n-revenue-1",
label: "Revenue increased.",
description: "Revenue increased.",
kind: "observation",
status: "supported",
confidence: "high",
}),
makeNode({
id: "n-revenue-2",
label: "Revenue doubled.",
description: "Revenue doubled.",
kind: "observation",
status: "supported",
confidence: "high",
}),
],
edges: [],
activeUnknownNodeId: null,
resolvedNodeIds: [],
currentSummary: "Potentially related regression fixture",
reasoningState: {
comparabilityStatus: "confirmed",
comparabilityReason: "Stored from earlier assessment.",
comparabilityEvidence: [],
relationshipStatus: "potentially_related",
relationshipReason: "Observations concern the same subject but do not assert a direct contradiction.",
relationshipAssessed: true,
contradictionReasoningAllowed: false,
reasoningStages: [
{ stage: "comparability", status: "confirmed", outcome: "Stored." },
],
},
});
const relationship = classifyObservationRelationship(graph);
expect(relationship.relationshipStatus).toBe("potentially_related");
expect(relationship.questionRequired).toBe(true);
const result = applyValidatedProposal({
situationGraph: graph,
proposal: {
addedNodes: [],
updatedNodes: [
{
nodeId: "n-revenue-2",
previousStatus: "supported",
newStatus: "resolved",
previousValue: null,
newValue: "Revenue doubled confirmed by accounts.",
reason: "Regression test update.",
},
],
addedEdges: [],
removedEdgeIds: [],
resolvedUnknownNodeIds: [],
affectedNodeIds: [],
selectedQuestion: null,
},
});
expect(result.success).toBe(true);
});
it("preserves contradiction with questionRequired=true for genuinely opposing observations", () => {
const graph = makeGraph({
centralStatement:
"The service was reported as available throughout the hour and unavailable throughout the same hour.",
nodes: [
makeNode({
id: "n-available",
label: "The service was available throughout the hour.",
description: "The service was available throughout the hour.",
kind: "observation",
status: "supported",
confidence: "high",
}),
makeNode({
id: "n-unavailable",
label: "The service was unavailable throughout the same hour.",
description: "The service was unavailable throughout the same hour.",
kind: "observation",
status: "supported",
confidence: "high",
}),
],
edges: [],
activeUnknownNodeId: null,
resolvedNodeIds: [],
currentSummary: "Contradiction regression fixture",
reasoningState: {
comparabilityStatus: "confirmed",
comparabilityReason: "Stored from earlier assessment.",
comparabilityEvidence: [],
relationshipStatus: "contradictory",
relationshipReason: "The observations assert mutually incompatible states about the same subject.",
relationshipAssessed: true,
contradictionReasoningAllowed: true,
reasoningStages: [
{ stage: "comparability", status: "confirmed", outcome: "Stored." },
],
},
});
const relationship = classifyObservationRelationship(graph);
expect(relationship.relationshipStatus).toBe("contradictory");
expect(relationship.questionRequired).toBe(true);
});
});