fix: handle unjustified unknown selection ties

This commit is contained in:
2026-08-02 16:09:00 +01:00
parent a1f6d0c2b9
commit 51ce356218
8 changed files with 731 additions and 145 deletions
@@ -1,7 +1,13 @@
import { describe, expect, it } from "vitest";
import { formulateQuestion } from "@/lib/graph/question-formulator.js";
import {
formulateQuestion,
formulateTieResolutionQuestion,
} from "@/lib/graph/question-formulator.js";
import { makeEdge, makeGraph, makeNode } from "@/lib/graph/schema.js";
import { explainUnknownSelection } from "@/lib/graph/utils.js";
import {
explainUnknownSelection,
selectActiveUnknownCandidate,
} from "@/lib/graph/utils.js";
function buildLiveShapedGraph() {
const summary = makeNode({
@@ -184,37 +190,57 @@ function neutraliseUnknownWording(graph) {
}
describe("selection influence diagnostic", () => {
it("records ordering changes for live-shaped, structure-only, and wording-neutralised fixtures", () => {
it("records ambiguous ordering changes for live-shaped, structure-only, and wording-neutralised fixtures", () => {
const liveGraph = buildLiveShapedGraph();
const liveExplanation = explainUnknownSelection(liveGraph, []);
const liveWinner = liveGraph.nodes.find(
(node) => node.id === liveExplanation.selectedNodeId,
);
const liveQuestion = formulateQuestion({
node: liveWinner,
graph: liveGraph,
});
const liveSelection = selectActiveUnknownCandidate(liveGraph, []);
const tieQuestion = formulateTieResolutionQuestion({ graph: liveGraph });
const noLinksExplanation = explainUnknownSelection(
removeDependencyLinks(liveGraph),
[],
);
const noLinksSelection = selectActiveUnknownCandidate(
removeDependencyLinks(liveGraph),
[],
);
const neutralWordingExplanation = explainUnknownSelection(
neutraliseUnknownWording(liveGraph),
[],
);
const neutralSelection = selectActiveUnknownCandidate(
neutraliseUnknownWording(liveGraph),
[],
);
const fallbackQuestion = formulateQuestion({
node: liveGraph.nodes.find((node) => node.id === "nqdzobz"),
graph: liveGraph,
});
const diagnosticRecord = {
liveStatus: liveExplanation.status,
liveShapedCandidateOrdering: orderCandidates(liveExplanation),
liveTiedCandidateIds: liveExplanation.tiedCandidateIds,
noLinksCandidateOrdering: orderCandidates(noLinksExplanation),
noLinksStatus: noLinksExplanation.status,
neutralWordingCandidateOrdering: orderCandidates(
neutralWordingExplanation,
),
selectedExplanationContributions:
liveExplanation.selected?.contributions ?? [],
selectedInvestigationStrategy: liveQuestion.strategy,
neutralStatus: neutralWordingExplanation.status,
selectedExplanationContributions: liveExplanation.selected?.contributions,
tieQuestion: tieQuestion.question,
liveSelection,
noLinksSelection,
neutralSelection,
fallbackQuestion,
};
expect(diagnosticRecord.liveStatus).toBe("ambiguous");
expect(diagnosticRecord.liveTiedCandidateIds).toEqual([
"nqdzobz",
"niewza",
]);
expect(diagnosticRecord.liveShapedCandidateOrdering).toEqual([
{
nodeId: "nqdzobz",
@@ -233,9 +259,17 @@ describe("selection influence diagnostic", () => {
unresolvedParentUnknownCount: 0,
},
]);
expect(diagnosticRecord.liveSelection).toMatchObject({
selectedNode: null,
status: "ambiguous",
tieType: "complete_unresolved_tie",
tiedCandidateIds: ["nqdzobz", "niewza"],
});
expect(diagnosticRecord.noLinksCandidateOrdering).toEqual(
diagnosticRecord.liveShapedCandidateOrdering,
);
expect(diagnosticRecord.noLinksStatus).toBe("ambiguous");
expect(diagnosticRecord.noLinksSelection.status).toBe("ambiguous");
expect(diagnosticRecord.neutralWordingCandidateOrdering).toEqual([
{
nodeId: "niewza",
@@ -252,14 +286,18 @@ describe("selection influence diagnostic", () => {
unresolvedParentUnknownCount: 0,
},
]);
expect(diagnosticRecord.selectedExplanationContributions).toEqual([
{
rule: "downstream_dependencies",
value: 0,
weight: 4,
delta: 0,
},
]);
expect(diagnosticRecord.selectedInvestigationStrategy).toBe("definition");
expect(diagnosticRecord.neutralStatus).toBe("ambiguous");
expect(diagnosticRecord.neutralSelection.status).toBe("ambiguous");
expect(diagnosticRecord.selectedExplanationContributions).toBeUndefined();
expect(diagnosticRecord.tieQuestion).toBe(
"What changed during the period that could explain why Revenue increased by 18%, but cash in the bank fell over the same period?",
);
expect(diagnosticRecord.tieQuestion.toLowerCase()).not.toMatch(
/accounts receivable|capex|debt repayments|working capital/,
);
expect(diagnosticRecord.fallbackQuestion.strategy).toBeNull();
expect(diagnosticRecord.fallbackQuestion.question).toBe(
"What would clarify magnitude and nature of cash outflows (operating expenses, debt repayments, capex, or working capital shifts) in this situation?",
);
});
});