import { describe, expect, it } from "vitest"; import { buildGraphUpdatePrompt } from "@/lib/graph/prompt-builder.js"; import { makeEdge, makeGraph, makeNode } from "@/lib/graph/schema.js"; function makeContext() { const unknown = makeNode({ id: "n-unknown", label: "Complaint rate denominator", description: "Need the denominator to compare complaint rates", kind: "unknown", status: "unknown", confidence: "high", }); const observation = makeNode({ id: "n-obs", label: "Complaints up 35%", description: "Complaints increased by 35%", kind: "observation", status: "supported", confidence: "high", }); return { situationGraph: makeGraph({ centralStatement: "Complaints increased while production increased.", nodes: [unknown, observation], edges: [ makeEdge({ id: "e1", fromNodeId: observation.id, toNodeId: unknown.id, relationship: "supports", confidence: "high", description: "Observation informs the unknown", }), ], activeUnknownNodeId: unknown.id, resolvedNodeIds: [], currentSummary: "Nodes: 1 observation, 1 unknown | Edges: 1 total | Unknowns: 1 unresolved", }), previousQuestion: "What denominator is being used for the complaint rate?", answer: "The complaint rate fell from 2.0 complaints per 100 units to 1.9 complaints per 100 units.", }; } describe("buildGraphUpdatePrompt", () => { it("includes the current graph", () => { const prompt = buildGraphUpdatePrompt(makeContext()); expect(prompt).toContain( "Complaints increased while production increased.", ); expect(prompt).toContain("Complaint rate denominator"); }); it("includes previous question and answer", () => { const prompt = buildGraphUpdatePrompt(makeContext()); expect(prompt).toContain( "What denominator is being used for the complaint rate?", ); expect(prompt).toContain( "The complaint rate fell from 2.0 complaints per 100 units to 1.9 complaints per 100 units.", ); }); it("contains exact schema keys", () => { const prompt = buildGraphUpdatePrompt(makeContext()); expect(prompt).toContain("addedNodes"); expect(prompt).toContain("updatedNodes"); expect(prompt).toContain("addedEdges"); expect(prompt).toContain("removedEdgeIds"); expect(prompt).toContain("resolvedUnknownNodeIds"); expect(prompt).toContain("affectedNodeIds"); expect(prompt).toContain("selectedQuestion"); }); it("lists enum values", () => { const prompt = buildGraphUpdatePrompt(makeContext()); expect(prompt).toContain( "observation | reported_claim | metric | state | transition | relationship | assumption | unknown | conclusion", ); expect(prompt).toContain( "known | unknown | provisional | supported | weakened | contradicted | resolved", ); expect(prompt).toContain( "supports | weakens | contradicts | depends_on | causes | may_cause | measures | compares_with | updates | other", ); }); it("forbids full-graph replacement", () => { const prompt = buildGraphUpdatePrompt(makeContext()); expect(prompt).toContain("Never return a replacement graph"); expect(prompt).toContain("Propose changes only"); }); it("requires JSON only", () => { const prompt = buildGraphUpdatePrompt(makeContext()); expect(prompt).toContain("Return JSON only"); expect(prompt).toContain("Return one JSON object only"); }); it("describes controlled emergent unknown rules", () => { const prompt = buildGraphUpdatePrompt(makeContext()); expect(prompt).toContain("Add at most 3 new unknown nodes"); expect(prompt).toContain("Resolve the answered unknown first"); expect(prompt).toContain( "selectedQuestion.question must be one narrow non-compound question", ); expect(prompt).toContain( "the engine will deterministically choose final priority after validation", ); }); });