fix(confidence-engine): restore finding controls on reopen
This commit is contained in:
@@ -989,3 +989,154 @@ describe("Focused investigation history cue", () => {
|
||||
// some_other_node would show INVESTIGATING, but it's NOT oq-originating
|
||||
});
|
||||
});
|
||||
|
||||
// ── Reopened Finding resolution (v0.49 repair) ─────────────────────
|
||||
describe("Reopened Finding resolution via Contribution.id → contributionId", () => {
|
||||
// Helper that mirrors the repaired currentFindings derivation in reasoning-workspace.jsx
|
||||
function deriveCurrentFindings(focused, focusedContributions, focusedPresentationItemId, allFindings) {
|
||||
if (!allFindings?.length) return [];
|
||||
|
||||
const hasCorrelationId = !!focused?.result?.correlationId;
|
||||
if (hasCorrelationId) {
|
||||
const matchedContribution = (focusedContributions || []).find(
|
||||
(c) => c.correlationId === focused.result.correlationId,
|
||||
);
|
||||
if (matchedContribution) {
|
||||
return allFindings.filter((f) => f.contributionId === matchedContribution.id);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
// REOPEN PATH
|
||||
const target = focusedPresentationItemId;
|
||||
if (!target || !focusedContributions?.length) return [];
|
||||
|
||||
const threadContribs = focusedContributions.filter(
|
||||
(c) => c.targetNodeId === target || c.originatingTargetNodeId === target,
|
||||
);
|
||||
if (!threadContribs.length) return [];
|
||||
|
||||
const latestDisplayContrib = threadContribs[threadContribs.length - 1];
|
||||
return allFindings.filter((f) => f.contributionId === latestDisplayContrib.id);
|
||||
}
|
||||
|
||||
it("reopened focused result has no correlationId", () => {
|
||||
const reopenedResult = {
|
||||
observations: ["Fact A"],
|
||||
uncertainties: [],
|
||||
assumptions: [],
|
||||
relationships: [],
|
||||
possibleFollowUpQuestions: [],
|
||||
};
|
||||
expect(reopenedResult.correlationId).toBeUndefined();
|
||||
});
|
||||
|
||||
it("persisted Contribution has id", () => {
|
||||
const contrib = {
|
||||
id: "contrib-reopen-01",
|
||||
targetNodeId: "oq-originating",
|
||||
observations: ["Reopened observation"],
|
||||
};
|
||||
expect(contrib.id).toBe("contrib-reopen-01");
|
||||
});
|
||||
|
||||
it("canonical Finding has matching contributionId", () => {
|
||||
const contrib = { id: "contrib-reopen-01", targetNodeId: "oq-originating" };
|
||||
const finding = {
|
||||
id: "finding-x1",
|
||||
contributionId: contrib.id,
|
||||
proposition: "The system scales horizontally.",
|
||||
userDisposition: "agreed",
|
||||
};
|
||||
expect(finding.contributionId).toBe(contrib.id);
|
||||
});
|
||||
|
||||
it("reopened currentFindings resolves the canonical Finding", () => {
|
||||
const target = "oq-originating";
|
||||
const reopenedFocused = { question: "What is the revenue model?", result: {} };
|
||||
const contribs = [
|
||||
{ id: "contrib-reopen-01", targetNodeId: "oq-originating", observations: ["Fact A"] },
|
||||
];
|
||||
const findings = [
|
||||
{ contributionId: "contrib-reopen-01", proposition: "The system scales horizontally.", userDisposition: "agreed" },
|
||||
];
|
||||
|
||||
const result = deriveCurrentFindings(reopenedFocused, contribs, target, findings);
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].proposition).toBe("The system scales horizontally.");
|
||||
});
|
||||
|
||||
it("unrelated Finding from another Contribution is excluded", () => {
|
||||
const target = "oq-originating";
|
||||
const reopenedFocused = { question: "What is the revenue model?", result: {} };
|
||||
const contribs = [
|
||||
{ id: "contrib-reopen-01", targetNodeId: "oq-originating" },
|
||||
];
|
||||
const findings = [
|
||||
{ contributionId: "contrib-reopen-01", proposition: "Correct finding", userDisposition: "agreed" },
|
||||
{ contributionId: "contrib-other-x", proposition: "Unrelated finding", userDisposition: "dismissed" },
|
||||
];
|
||||
|
||||
const result = deriveCurrentFindings(reopenedFocused, contribs, target, findings);
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].proposition).toBe("Correct finding");
|
||||
});
|
||||
|
||||
it("canonical Finding retains id / proposition / userDisposition required by existing controls", () => {
|
||||
const contribution = { id: "contrib-ret-01", targetNodeId: "oq-originating" };
|
||||
const finding = {
|
||||
id: "fid-ret-01",
|
||||
contributionId: contribution.id,
|
||||
proposition: "Revenue via subscription.",
|
||||
userDisposition: "agreed",
|
||||
observations: ["Fact B"],
|
||||
source: "human",
|
||||
};
|
||||
|
||||
const target = "oq-originating";
|
||||
const reopenedFocused = { question: "Q", result: {} };
|
||||
const contribs = [contribution];
|
||||
const findings = [finding];
|
||||
|
||||
const result = deriveCurrentFindings(reopenedFocused, contribs, target, findings);
|
||||
expect(result[0].id).toBe("fid-ret-01");
|
||||
expect(result[0].proposition).toBe("Revenue via subscription.");
|
||||
expect(result[0].userDisposition).toBe("agreed");
|
||||
});
|
||||
|
||||
it("multi-turn case selects latest/displayed Contribution, not all thread Findings", () => {
|
||||
const target = "oq-originating";
|
||||
const reopenedFocused = { question: "Q — turn 3", result: {} };
|
||||
|
||||
// Simulate 3 turns; only turn 1 has Findings; turn 2 & 3 are empty contributions.
|
||||
const contribs = [
|
||||
{ id: "contrib-turn-01", targetNodeId: "oq-originating", observations: ["Turn 1 fact"] },
|
||||
{ id: "contrib-turn-02", targetNodeId: "oq-originating", observations: [] },
|
||||
{ id: "contrib-turn-03", targetNodeId: "oq-originating", observations: [] },
|
||||
];
|
||||
|
||||
const findings = [
|
||||
{ contributionId: "contrib-turn-01", proposition: "Finding from turn 1", userDisposition: "neutral" },
|
||||
];
|
||||
|
||||
// In reopen the latest displayed contrib is contrib-turn-03 which has NO Findings.
|
||||
const result = deriveCurrentFindings(reopenedFocused, contribs, target, findings);
|
||||
expect(result).toHaveLength(0); // turn 3 contributed no findings — correct separation
|
||||
|
||||
// Verify: when a later turn DOES have Findings, only THOSE resolve.
|
||||
const contribsWithTurn2Findings = [
|
||||
...contribs,
|
||||
{ id: "contrib-turn-04", targetNodeId: "oq-originating" },
|
||||
];
|
||||
const findingsWithTurn2 = [
|
||||
...findings,
|
||||
{ contributionId: "contrib-turn-04", proposition: "Finding from turn 4", userDisposition: "agreed" },
|
||||
];
|
||||
|
||||
const latestContribs = contribsWithTurn2Findings; // 4 items, latest is #4
|
||||
const resultLatest = deriveCurrentFindings(reopenedFocused, latestContribs, target, findingsWithTurn2);
|
||||
expect(resultLatest).toHaveLength(1);
|
||||
expect(resultLatest[0].proposition).toBe("Finding from turn 4");
|
||||
// Turn 1 Finding NOT merged into latest view.
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user