/** * RTO.A4 — Real captured fixture apparatus verification. * * Proves the bounded projection boundary: * 1. source fixture is the real captured JSON * 2. projected situationGraph is the captured updatedSituationGraph * 3. exactly 3 unresolved unknowns are derived from real graph state * 4. their IDs/labels match the captured nodes * 5. recommended item derives from selectedQuestion.nodeId * 6. diagnostics recommendation agrees for this fixture * 7. no synthetic availableThreads/recommendedThreadId data is required * 8. any presentation selection remains outside situationGraph * 9. active/reasoning ownership fields are not mutated */ import { describe, it, expect } from "vitest"; import rawFixture from "../fixtures/live-product-launch-update-response.json"; import { buildScenarioFixture } from "@/lib/mocks/scenarios.js"; const projected = buildScenarioFixture("rto-real-product-launch", 0); describe("RTO.A4 real captured fixture apparatus", () => { /* ── 1. Source fixture is the real captured JSON ─────────── */ it("uses the live-product-launch-update-response.json as source", () => { expect(projected._sourceFixturePath).toBe("../../tests/fixtures/live-product-launch-update-response.json"); }); /* ── 2. Projected situationGraph is the captured updatedSituationGraph ─── */ it("projects situationGraph from fixture.updatedSituationGraph", () => { // projected.situationGraph is the SAME object as projected.updatedSituationGraph (alias) expect(projected.situationGraph).toBe(projected.updatedSituationGraph); }); it("preserves centralStatement from captured graph", () => { expect(projected.situationGraph.centralStatement.length).toBeGreaterThan(100); }); /* ── 3. Exactly 3 unresolved unknowns derived from real graph state ─── */ it("derives exactly 3 unresolved unknowns", () => { const unresolved = projected.situationGraph.nodes.filter( (n) => n.kind === "unknown" && n.status !== "resolved" ); expect(unresolved.length).toBe(3); }); /* ── 4. IDs/labels match the captured nodes ──────────────── */ it("resolves to the three known unresolved node IDs", () => { const resolved = JSON.stringify(rawFixture.updatedSituationGraph.nodes .filter(n => n.kind === "unknown" && n.status !== "resolved") .map(n => n.id) .sort()); expect(resolved).toBe(JSON.stringify(["nR4vL9w", "ntpt9ki", "nxmeiab"].sort())); }); it("labels come directly from captured nodes, not synthesized", () => { const unresolved = projected.situationGraph.nodes.filter( (n) => n.kind === "unknown" && n.status !== "resolved" ); const labels = unresolved.map((n) => n.label); expect(labels).toContain( "Probability or current status of the large enterprise customer signing their contract before launch or within the year" ); expect(labels).toContain( "Whether competitors are actively developing similar products and how soon they might release them" ); expect(labels).toContain( "Sufficiency of committed or highly probable revenue from other customers to cover £300k cost and yield acceptable return" ); }); /* ── 5. Recommended item derives from selectedQuestion.nodeId ─── */ it("derives recommendation from selectedQuestion.nodeId", () => { expect(projected.selectedQuestion.nodeId).toBe("nR4vL9w"); }); it("recommendation matches one of the unresolved graph nodes", () => { const resolved = projected.situationGraph.nodes.find( (n) => n.id === projected.selectedQuestion.nodeId && n.kind === "unknown" ); expect(resolved).toBeDefined(); expect(resolved.label).toContain("Sufficiency of committed or highly probable revenue"); }); /* ── 6. Diagnostics recommendation agrees for this fixture ── */ it("diagnostics.unknownSelectionExplanation.selectedNodeId agrees with selectedQuestion", () => { expect(projected.diagnostics.unknownSelectionExplanation.selectedNodeId).toBe("nR4vL9w"); expect(projected.selectedQuestion.nodeId).toBe( projected.diagnostics.unknownSelectionExplanation.selectedNodeId ); }); /* ── 7. No synthetic availableThreads/recommendedThreadId required ─── */ it("does not require synthetic reasoning fields to establish the open set", () => { // The open set comes from situationGraph.nodes, not from synthetic data const graphUnknowns = projected.situationGraph.nodes.filter( (n) => n.kind === "unknown" && n.status !== "resolved" ); expect(graphUnknowns.length).toBe(3); // No _experimental or synthetic thread fields on situationGraph expect(projected.situationGraph._experimental).toBeUndefined(); }); it("does not introduce synthetic recommendationOrder, questionFrame, thread confidence, or priority", () => { const unresolved = projected.situationGraph.nodes.filter( (n) => n.kind === "unknown" && n.status !== "resolved" ); for (const node of unresolved) { expect(node).not.toHaveProperty("recommendationOrder"); expect(node).not.toHaveProperty("questionFrame"); } }); /* ── 8. Presentation selection remains outside situationGraph ── */ it("keeps presentation-only state separate from situationGraph", () => { expect(projected.situationGraph.selectedPresentationItemId).toBeUndefined(); }); it("activeUnknownNodeId is not mutated by the projection", () => { expect(projected.situationGraph.activeUnknownNodeId).toBe( rawFixture.updatedSituationGraph.activeUnknownNodeId ); }); it("selectedQuestion is preserved as captured, not rewritten", () => { // selectedQuestion comes directly from the fixture — check key fields expect(projected.selectedQuestion.nodeId).toBe(rawFixture.selectedQuestion.nodeId); expect(projected.selectedQuestion.question).toBe(rawFixture.selectedQuestion.question); }); /* ── 9. No production reasoning or ownership mutation ─────── */ it("does not modify activeUnknownNodeId for presentation selection", () => { const resolved = projected.situationGraph.resolvedNodeIds || []; expect(resolved).toEqual([]); }); it("resolvedNodeIds matches captured state (empty)", () => { const resolved = projected.situationGraph.resolvedNodeIds || []; expect(resolved.length).toBe(0); }); /* ── Additional boundary checks ─────────────────────────── */ it("preserves success and stage from fixture", () => { expect(projected.success).toBe(true); expect(projected.stage).toBe("update_applied"); }); it("newlySurfacedNodeIds derives from proposal.addedNodes (unknown kind)", () => { const expected = rawFixture.proposal.addedNodes .filter(n => n.kind === "unknown") .map(n => n.id); expect(projected.newlySurfacedNodeIds).toEqual(expected); }); it("selectedQuestion matches captured field exactly", () => { expect(projected.selectedQuestion.nodeId).toBe(rawFixture.selectedQuestion.nodeId); expect(projected.selectedQuestion.question).toBe(rawFixture.selectedQuestion.question); }); it("diagnostics object matches captured diagnostics key fields", () => { expect(projected.diagnostics.promptVersion).toBe(rawFixture.diagnostics.promptVersion); expect(projected.diagnostics.modelName).toBe(rawFixture.diagnostics.modelName); expect(projected.diagnostics.validationStatus).toBe(rawFixture.diagnostics.validationStatus); expect(projected.diagnostics.nodeCount).toBe(rawFixture.diagnostics.nodeCount); }); });