feat: surface new unknowns after graph updates

This commit is contained in:
2026-08-02 10:30:59 +01:00
parent 904aec7616
commit 72ef175971
16 changed files with 910 additions and 41 deletions
+85 -15
View File
@@ -161,21 +161,56 @@ describe("graphUpdateSchema", () => {
it("validates a complete update", () => {
const node = makeNode({ id: "n2", label: "New Node" });
const edge = makeEdge({ fromNodeId: "n1", toNodeId: "n2" });
const result = graphUpdateSchema.safeParse({
addedNodes: [node],
updatedNodes: [{ nodeId: "n1", newStatus: "resolved", previousStatus: "unknown", reason: "Question answered" }],
updatedNodes: [
{
nodeId: "n1",
newStatus: "resolved",
previousStatus: "unknown",
reason: "Question answered",
},
],
addedEdges: [edge],
removedEdgeIds: ["e-old"],
resolvedUnknownNodeIds: ["n2"],
affectedNodeIds: ["n3"],
selectedQuestion: {
nodeId: "n2",
question: "What does this new node mean?",
reason: "A follow-up unknown remains.",
},
});
expect(result.success).toBe(true);
});
it("allows null selectedQuestion", () => {
const result = graphUpdateSchema.safeParse({
selectedQuestion: null,
});
expect(result.success).toBe(true);
});
it("rejects update with invalid node kind in addedNodes", () => {
const invalid = graphUpdateSchema.safeParse({
addedNodes: [{ id: "x", label: "Test", kind: "invalid_kind", description: "test", status: "unknown", confidence: "medium", value: null, unit: null, evidenceIds: [], dependsOn: [], affects: [], parentId: null, childIds: [] }],
addedNodes: [
{
id: "x",
label: "Test",
kind: "invalid_kind",
description: "test",
status: "unknown",
confidence: "medium",
value: null,
unit: null,
evidenceIds: [],
dependsOn: [],
affects: [],
parentId: null,
childIds: [],
},
],
});
expect(invalid.success).toBe(false);
});
@@ -184,7 +219,9 @@ describe("graphUpdateSchema", () => {
describe("API request schemas", () => {
describe("startCaseRequestSchema", () => {
it("validates scenario field", () => {
const result = startCaseRequestSchema.safeParse({ scenario: "Test scenario" });
const result = startCaseRequestSchema.safeParse({
scenario: "Test scenario",
});
expect(result.success).toBe(true);
});
@@ -195,14 +232,16 @@ describe("API request schemas", () => {
it("rejects scenario over 10000 chars", () => {
const longScenario = "a".repeat(10001);
const result = startCaseRequestSchema.safeParse({ scenario: longScenario });
const result = startCaseRequestSchema.safeParse({
scenario: longScenario,
});
expect(result.success).toBe(false);
});
it("accepts optional promptVersion", () => {
const result = startCaseRequestSchema.safeParse({
scenario: "Test",
promptVersion: "v0.3"
const result = startCaseRequestSchema.safeParse({
scenario: "Test",
promptVersion: "v0.3",
});
expect(result.success).toBe(true);
});
@@ -213,7 +252,7 @@ describe("API request schemas", () => {
const graph = makeGraph({
centralStatement: "Test scenario",
nodes: [makeNode({ id: "n1", label: "N" })],
currentSummary: "Current state of situation"
currentSummary: "Current state of situation",
});
const result = updateCaseRequestSchema.safeParse({
situationGraph: graph,
@@ -235,7 +274,7 @@ describe("API request schemas", () => {
const graph = makeGraph({
centralStatement: "Test",
nodes: [makeNode({ id: "n1", label: "N" })],
currentSummary: "Test summary"
currentSummary: "Test summary",
});
const result = updateCaseRequestSchema.safeParse({
situationGraph: graph,
@@ -261,7 +300,9 @@ describe("deterministic ID generation", () => {
});
it("IDs are prefixed with 'n' and short", () => {
const id = makeNodeId("A very long label that would produce a longer hash if not truncated");
const id = makeNodeId(
"A very long label that would produce a longer hash if not truncated",
);
expect(id.startsWith("n")).toBe(true);
expect(id.length).toBeLessThan(15);
});
@@ -325,7 +366,7 @@ describe("helper functions", () => {
const graph = makeGraph({
centralStatement: "Test",
currentSummary: "Default summary",
nodes: [makeNode({ id: "n1", label: "Placeholder" })]
nodes: [makeNode({ id: "n1", label: "Placeholder" })],
});
const result = situationGraphSchema.safeParse(graph);
expect(result.success).toBe(true);
@@ -346,19 +387,48 @@ describe("helper functions", () => {
describe("enum values completeness", () => {
it("SituationKind has all expected values", () => {
const expected = ["observation", "reported_claim", "metric", "state", "transition", "relationship", "assumption", "unknown", "conclusion"];
const expected = [
"observation",
"reported_claim",
"metric",
"state",
"transition",
"relationship",
"assumption",
"unknown",
"conclusion",
];
const actual = Object.values(SituationKind);
expect(actual).toEqual(expect.arrayContaining(expected));
});
it("SituationStatus has all expected values", () => {
const expected = ["known", "unknown", "provisional", "supported", "weakened", "contradicted", "resolved"];
const expected = [
"known",
"unknown",
"provisional",
"supported",
"weakened",
"contradicted",
"resolved",
];
const actual = Object.values(SituationStatus);
expect(actual).toEqual(expect.arrayContaining(expected));
});
it("SituationRelationship has all expected values", () => {
const expected = ["supports", "weakens", "contradicts", "depends_on", "causes", "may_cause", "measures", "compares_with", "updates", "other"];
const expected = [
"supports",
"weakens",
"contradicts",
"depends_on",
"causes",
"may_cause",
"measures",
"compares_with",
"updates",
"other",
];
const actual = Object.values(SituationRelationship);
expect(actual).toEqual(expect.arrayContaining(expected));
});