fix(reasoning): preserve investigation ownership across selection and question rejection

This commit is contained in:
2026-08-17 17:58:45 +01:00
parent d908f3746d
commit 772ae495c6
7 changed files with 850 additions and 4429 deletions
+131 -11
View File
@@ -621,7 +621,7 @@ describe("selectActiveUnknownCandidate", () => {
expect(parentScore.score).toBeGreaterThan(childScore.score);
});
it("returns ambiguous for a complete unresolved tie instead of label-based winner", () => {
it("returns a deterministic winner for a complete unresolved tie", () => {
const unknownA = makeNode({
id: "tie-a",
label: "Magnitude and nature of cash outflows",
@@ -651,16 +651,12 @@ describe("selectActiveUnknownCandidate", () => {
});
const result = selectActiveUnknownCandidate(graph, []);
expect(result).toMatchObject({
selectedNode: null,
status: "ambiguous",
tieType: "complete_unresolved_tie",
tiedCandidateIds: ["tie-a", "tie-b"],
});
expect(result.nodeId).toBeUndefined();
expect(result.status).toBe("selected");
expect(result.tieType).toBe("complete_unresolved_tie");
expect(result.nodeId).toBeTruthy();
});
it("alphabetical renaming does not resolve a complete tie", () => {
it("stable ordering resolves a complete tie deterministically", () => {
const unknownA = makeNode({
id: "tie-a",
label: "Unknown B",
@@ -683,8 +679,132 @@ describe("selectActiveUnknownCandidate", () => {
});
const result = selectActiveUnknownCandidate(graph, []);
expect(result.status).toBe("ambiguous");
expect(result.tiedCandidateIds.sort()).toEqual(["tie-a", "tie-b"]);
expect(result.status).toBe("selected");
expect(result.nodeId).toBeTruthy();
});
it("preserves the active candidate when it remains eligible and substantively tied", () => {
const unknownA = makeNode({
id: "tie-a",
label: "Unknown B",
description: "Unknown factor one.",
kind: "unknown",
status: "unknown",
confidence: "high",
});
const unknownB = makeNode({
id: "tie-b",
label: "Unknown A",
description: "Unknown factor two.",
kind: "unknown",
status: "unknown",
confidence: "high",
});
const graph = makeGraph({
centralStatement: "Two conflicting signals remain unresolved.",
nodes: [unknownA, unknownB],
edges: [],
activeUnknownNodeId: "tie-a",
resolvedNodeIds: [],
currentSummary: "Tie case",
});
const result = selectActiveUnknownCandidate(graph, []);
expect(result.status).toBe("selected");
expect(result.nodeId).toBe("tie-a");
});
it("transfers ownership when the active candidate substantively loses on score", () => {
const loser = makeNode({
id: "active-loser",
label: "Unknown branch",
description: "Speculative future branch.",
kind: "unknown",
status: "unknown",
confidence: "high",
});
const winner = makeNode({
id: "clear-winner",
label: "Customer value definition",
description: "Need customer value definition.",
kind: "unknown",
status: "unknown",
confidence: "high",
});
const graph = makeGraph({
centralStatement: "Decision context",
nodes: [loser, winner],
edges: [],
activeUnknownNodeId: loser.id,
resolvedNodeIds: [],
currentSummary: "Score loss case",
});
const result = selectActiveUnknownCandidate(graph, []);
expect(result.status).toBe("selected");
expect(result.nodeId).toBe(winner.id);
});
it("transfers ownership when the active candidate is resolved or ineligible", () => {
const resolvedActive = makeNode({
id: "resolved-active",
label: "Resolved unknown",
description: "Resolved unknown.",
kind: "unknown",
status: "resolved",
confidence: "high",
});
const remaining = makeNode({
id: "remaining-unknown",
label: "Customer value definition",
description: "Need customer value definition.",
kind: "unknown",
status: "unknown",
confidence: "high",
});
const graph = makeGraph({
centralStatement: "Decision context",
nodes: [resolvedActive, remaining],
edges: [],
activeUnknownNodeId: resolvedActive.id,
resolvedNodeIds: [resolvedActive.id],
currentSummary: "Resolved active case",
});
const result = selectActiveUnknownCandidate(graph, graph.resolvedNodeIds);
expect(result.status).toBe("selected");
expect(result.nodeId).toBe(remaining.id);
});
it("keeps existing deterministic fallback when there is no active candidate", () => {
const unknownA = makeNode({
id: "fallback-a",
label: "Unknown B",
description: "Unknown factor one.",
kind: "unknown",
status: "unknown",
confidence: "high",
});
const unknownB = makeNode({
id: "fallback-b",
label: "Unknown A",
description: "Unknown factor two.",
kind: "unknown",
status: "unknown",
confidence: "high",
});
const graph = makeGraph({
centralStatement: "Two conflicting signals remain unresolved.",
nodes: [unknownA, unknownB],
edges: [],
activeUnknownNodeId: null,
resolvedNodeIds: [],
currentSummary: "Tie case",
});
const result = selectActiveUnknownCandidate(graph, []);
expect(result.status).toBe("selected");
expect(result.nodeId).toBeTruthy();
});
});