feat(confidence-engine): expose correlated findings to focused presentation
This commit is contained in:
@@ -1274,3 +1274,185 @@ describe("Previous Learning — responsive visibility in narrow and wide layouts
|
||||
expect(workspaceClasses).toContain("grid-cols-1");
|
||||
});
|
||||
});
|
||||
|
||||
// ── Correlated findings presentation seam tests ────────────────
|
||||
|
||||
describe("Correlated findings derivation chain", () => {
|
||||
it("exact Contribution resolved by correlationId from focused result", async () => {
|
||||
const corrId = "test-corr-001";
|
||||
const contributionId = "contrib-abc";
|
||||
|
||||
const contributions = [
|
||||
{ id: contributionId, targetNodeId: "q1", question: "What is the blocker?", correlationId: corrId, observations: [] },
|
||||
];
|
||||
|
||||
const focusedResult = { observations: ["A confirmed"], uncertainties: [], correlationId: corrId };
|
||||
|
||||
// Derivation: correlationId → exact Contribution
|
||||
const foundContribution = contributions.find(
|
||||
(c) => c.correlationId === focusedResult.correlationId,
|
||||
);
|
||||
|
||||
expect(foundContribution).not.toBeUndefined();
|
||||
expect(foundContribution.correlationId).toBe(corrId);
|
||||
expect(foundContribution.id).toBe(contributionId);
|
||||
});
|
||||
|
||||
it("existing Findings selected by contributionId", async () => {
|
||||
const contributionId = "contrib-abc";
|
||||
|
||||
const findings = [
|
||||
{ id: "f1", contributionId: "contrib-abc", proposition: "X is a blocker" },
|
||||
{ id: "f2", contributionId: "contrib-abc", proposition: "Y is related" },
|
||||
{ id: "f3", contributionId: "contrib-def", proposition: "Z unrelated" },
|
||||
];
|
||||
|
||||
// Derivation: contribution.id → findings.filter(f => f.contributionId === contribution.id)
|
||||
const selectedFindings = findings.filter(
|
||||
(f) => f.contributionId === contributionId,
|
||||
);
|
||||
|
||||
expect(selectedFindings).toHaveLength(2);
|
||||
expect(selectedFindings[0].id).toBe("f1");
|
||||
expect(selectedFindings[1].id).toBe("f2");
|
||||
// Prove the unrelated finding is NOT included
|
||||
expect(selectedFindings).not.toContainEqual(expect.objectContaining({ id: "f3" }));
|
||||
});
|
||||
|
||||
it("full chain: focused result correlationId → exact Contribution → existing Findings → FocusedQuestionBody receives exact objects", async () => {
|
||||
const corrId = "test-corr-002";
|
||||
const contributionId = "contrib-xyz";
|
||||
|
||||
// Fixture: contributions already persisted (owned by ScenarioForm)
|
||||
const focusedContributions = [
|
||||
{ id: contributionId, targetNodeId: "q1", question: "What blocks delivery?", correlationId: corrId },
|
||||
{ id: "contrib-other", targetNodeId: "q2", question: "How?", correlationId: "corr-diff" },
|
||||
];
|
||||
|
||||
// Fixture: findings already persisted (owned by ScenarioForm)
|
||||
const findings = [
|
||||
{ id: "find-a", contributionId: contributionId, proposition: "Team is overloaded" },
|
||||
{ id: "find-b", contributionId: contributionId, proposition: "Scope too wide" },
|
||||
{ id: "find-c", contributionId: "contrib-other", proposition: "Something else" },
|
||||
];
|
||||
|
||||
// Step 1: focused result carries correlationId
|
||||
const focusedResult = { observations: ["Derived insight"], uncertainties: [], correlationId: corrId };
|
||||
expect(focusedResult.correlationId).toBe(corrId);
|
||||
|
||||
// Step 2: exact Contribution resolved by correlationId (not array tail)
|
||||
const matchedContribution = focusedContributions.find(
|
||||
(c) => c.correlationId === focusedResult.correlationId,
|
||||
);
|
||||
expect(matchedContribution).not.toBeUndefined();
|
||||
expect(matchedContribution.id).toBe(contributionId);
|
||||
|
||||
// Prove it is NOT from array tail — the tail contribution has a different id
|
||||
const tailContribution = focusedContributions[focusedContributions.length - 1];
|
||||
expect(tailContribution.id).not.toBe(matchedContribution.id);
|
||||
|
||||
// Step 3: existing Findings selected by contributionId (no ID recreation)
|
||||
const correlatedFindings = findings.filter(
|
||||
(f) => f.contributionId === matchedContribution.id,
|
||||
);
|
||||
expect(correlatedFindings).toHaveLength(2);
|
||||
expect(correlatedFindings[0].id).toBe("find-a");
|
||||
expect(correlatedFindings[1].id).toBe("find-b");
|
||||
|
||||
// Prove findings are exact existing objects — not recreated
|
||||
expect(correlatedFindings[0]).toBe(findings.find((f) => f.id === "find-a"));
|
||||
expect(correlatedFindings[1]).toBe(findings.find((f) => f.id === "find-b"));
|
||||
|
||||
// Step 4: these are exactly what FocusedQuestionBody receives as currentFindings
|
||||
const currentFindings = correlatedFindings;
|
||||
expect(currentFindings).toHaveLength(2);
|
||||
expect(currentFindings[0].id).toBe("find-a");
|
||||
expect(currentFindings[1].id).toBe("find-b");
|
||||
});
|
||||
|
||||
it("no array-tail / latest-contribution lookup used in derivation", async () => {
|
||||
const corrId = "test-corr-003";
|
||||
|
||||
const contributions = [
|
||||
{ id: "contrib-a", correlationId: "corr-x" },
|
||||
{ id: "contrib-b", correlationId: corrId },
|
||||
{ id: "contrib-c", correlationId: "corr-z" },
|
||||
];
|
||||
|
||||
// Derivation uses find() by correlationId — NOT tail index
|
||||
const matched = contributions.find(
|
||||
(c) => c.correlationId === corrId,
|
||||
);
|
||||
|
||||
expect(matched.id).toBe("contrib-b");
|
||||
// Proves: the match is NOT the array tail (contrib-c would be tail)
|
||||
expect(matched).not.toBe(contributions[contributions.length - 1]);
|
||||
});
|
||||
|
||||
it("no recreated Finding ID — existing objects passed through", async () => {
|
||||
const originalFindings = [
|
||||
{ id: "existing-find-1", contributionId: "c1", proposition: "P1" },
|
||||
];
|
||||
|
||||
// Derivation returns filtered references to existing objects
|
||||
const derived = originalFindings.filter((f) => f.contributionId === "c1");
|
||||
|
||||
// Prove: same object reference, not recreated
|
||||
expect(derived[0]).toBe(originalFindings[0]);
|
||||
// Prove: no new ID was constructed
|
||||
expect(derived[0].id).toBe("existing-find-1");
|
||||
});
|
||||
|
||||
it("no focused result correlationId → empty currentFindings (not an error)", async () => {
|
||||
const findings = [{ id: "f1", contributionId: "c1", proposition: "P" }];
|
||||
const contributions = [];
|
||||
|
||||
// Simulate: no focused result or no correlationId
|
||||
const focusedResult = null;
|
||||
let currentFindings = [];
|
||||
|
||||
if (focusedResult?.correlationId && findings) {
|
||||
const matchedContribution = contributions.find(
|
||||
(c) => c.correlationId === focusedResult.correlationId,
|
||||
);
|
||||
if (matchedContribution) {
|
||||
currentFindings = findings.filter(
|
||||
(f) => f.contributionId === matchedContribution.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
expect(currentFindings).toEqual([]);
|
||||
});
|
||||
|
||||
it("FocusedQuestionBody receives exact existing Finding objects — rendered from observations untouched", async () => {
|
||||
const corrId = "test-corr-004";
|
||||
const contributionId = "contrib-receive-test";
|
||||
|
||||
const focusedContributions = [
|
||||
{ id: contributionId, correlationId: corrId },
|
||||
];
|
||||
const findings = [
|
||||
{ id: "find-x", contributionId: contributionId, proposition: "Core finding" },
|
||||
];
|
||||
|
||||
const focusedResult = { observations: ["Derived from LLM"], correlations: [], correlationId: corrId };
|
||||
|
||||
const matchedContribution = focusedContributions.find(
|
||||
(c) => c.correlationId === focusedResult.correlationId,
|
||||
);
|
||||
expect(matchedContribution.id).toBe(contributionId);
|
||||
|
||||
const currentFindings = findings.filter(
|
||||
(f) => f.contributionId === matchedContribution.id,
|
||||
);
|
||||
|
||||
// Prove: exact object reference
|
||||
expect(currentFindings[0]).toBe(findings[0]);
|
||||
|
||||
// Prove: "What this tells us" still renders from focused.result.observations (unchanged path)
|
||||
expect(focusedResult.observations).toEqual(["Derived from LLM"]);
|
||||
// currentFindings does NOT replace observations — it is separate presentation data
|
||||
expect(currentFindings).not.toEqual(focusedResult.observations);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user