import { describe, expect, it } from "vitest"; // ── Simulated rendering logic extracted from reasoning-workspace.jsx // Mirrors the exact filter + conditional structure used in the // initial post-Analyse reflection surface (lines 1442-1511) // AND the workspace grid persistent section (added for persistence fix). function renderInitialProposedFindings({ nodes, resolvedNodeIds }) { const resolvedIds = new Set(resolvedNodeIds || []); // Open Questions: unresolved unknown nodes only const openUnknowns = (nodes || []).filter( (n) => n.kind === "unknown" && n.status !== "resolved" && !resolvedIds.has(n.id), ); // Possible Interpretations: unresolved assumption nodes only const possibleInterpretations = (nodes || []).filter( (n) => n.kind === "assumption" && n.status !== "resolved" && !resolvedIds.has(n.id), ); return { openUnknowns, possibleInterpretations }; } // Simulated workspace-grid Possible Interpretations rendering (same filter as the new persistent section) function renderWorkspacePossibleInterpretations({ nodes, resolvedNodeIds }) { const resolvedIds = new Set(resolvedNodeIds || []); return (nodes || []).filter( (n) => n.kind === "assumption" && n.status !== "resolved" && !resolvedIds.has(n.id), ); } describe("Open Questions vs Possible Interpretations separation", () => { const graph = { nodes: [ { id: "u1", kind: "unknown", status: "unclear", label: "What is the revenue model?" }, { id: "u2", kind: "unknown", status: "unclear", label: "Who is the primary customer?" }, { id: "a1", kind: "assumption", status: "plausible", label: "Revenue via subscription" }, { id: "a2", kind: "assumption", status: "plausible", label: "Enterprise customers" }, ], resolvedNodeIds: [], }; it("unknown nodes appear under Open Questions only", () => { const result = renderInitialProposedFindings(graph); expect(result.openUnknowns).toHaveLength(2); expect(result.openUnknowns.map((n) => n.id)).toEqual(["u1", "u2"]); result.openUnknowns.forEach((n) => { expect(n.kind).toBe("unknown"); }); }); it("assumption nodes appear under Possible Interpretations only", () => { const result = renderInitialProposedFindings(graph); expect(result.possibleInterpretations).toHaveLength(2); expect(result.possibleInterpretations.map((n) => n.id)).toEqual(["a1", "a2"]); result.possibleInterpretations.forEach((n) => { expect(n.kind).toBe("assumption"); }); }); it("assumptions do NOT appear under Open Questions", () => { const result = renderInitialProposedFindings(graph); const assumptionIdsInOpen = result.openUnknowns.filter( (n) => n.kind === "assumption", ); expect(assumptionIdsInOpen).toHaveLength(0); }); it("unknowns do NOT appear under Possible Interpretations", () => { const result = renderInitialProposedFindings(graph); const unknownIdsInInterpretations = result.possibleInterpretations.filter( (n) => n.kind === "unknown", ); expect(unknownIdsInInterpretations).toHaveLength(0); }); it("resolved nodes are excluded from both sections", () => { const resolvedGraph = { ...graph, resolvedNodeIds: ["u1", "a2"] }; const result = renderInitialProposedFindings(resolvedGraph); expect(result.openUnknowns).toHaveLength(1); expect(result.openUnknowns[0].id).toBe("u2"); expect(result.possibleInterpretations).toHaveLength(1); expect(result.possibleInterpretations[0].id).toBe("a1"); }); it("only unknown kind is eligible for Open Questions", () => { const mixedGraph = { nodes: [ { id: "u1", kind: "unknown", status: "unclear", label: "U1" }, { id: "o1", kind: "observation", status: "active", label: "O1" }, { id: "s1", kind: "state", status: "active", label: "S1" }, { id: "c1", kind: "conclusion", status: "active", label: "C1" }, ], resolvedNodeIds: [], }; const result = renderInitialProposedFindings(mixedGraph); expect(result.openUnknowns).toHaveLength(1); expect(result.openUnknowns[0].id).toBe("u1"); }); it("only assumption kind is eligible for Possible Interpretations", () => { const mixedGraph = { nodes: [ { id: "a1", kind: "assumption", status: "plausible", label: "A1" }, { id: "o1", kind: "observation", status: "active", label: "O1" }, { id: "u1", kind: "unknown", status: "unclear", label: "U1" }, ], resolvedNodeIds: [], }; const result = renderInitialProposedFindings(mixedGraph); expect(result.possibleInterpretations).toHaveLength(1); expect(result.possibleInterpretations[0].id).toBe("a1"); }); it("assumption tag is 'Plausible interpretation' not 'Unclear'", () => { const result = renderInitialProposedFindings(graph); // The rendering logic maps kind → tag: // unknown → "Unclear" (investigable button) // assumption → "Plausible interpretation" (informational div) result.possibleInterpretations.forEach((n) => { expect(n.kind).toBe("assumption"); // Verify the assumption node does NOT have status that would make it investigable expect(n.status).not.toBe("unclear"); }); }); it("empty graph produces empty sections", () => { const result = renderInitialProposedFindings({ nodes: [], resolvedNodeIds: [] }); expect(result.openUnknowns).toHaveLength(0); expect(result.possibleInterpretations).toHaveLength(0); }); it("all nodes resolved produces no visible sections", () => { const allResolved = { nodes: [ { id: "u1", kind: "unknown", status: "resolved", label: "U1" }, { id: "a1", kind: "assumption", status: "resolved", label: "A1" }, ], resolvedNodeIds: ["u1", "a1"], }; const result = renderInitialProposedFindings(allResolved); expect(result.openUnknowns).toHaveLength(0); expect(result.possibleInterpretations).toHaveLength(0); }); }); // ── Focused investigation behaviour invariant (clickability) ──── describe("focused investigation behaviour", () => { it("unknown nodes retain their investigable button interface pattern", () => { // The rendering produces