test(confidence-engine): add progressed investigation experiment fixture

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
2026-09-30 10:55:13 +01:00
co-authored by Claude Code
parent 2dc1cb0fe6
commit ef6592cd91
2 changed files with 430 additions and 0 deletions
@@ -0,0 +1,189 @@
/**
* Progressed investigation fixture apparatus verification.
*
* Proves that the canonical progressed-investigation fixture:
* 1. loads successfully as valid JSON
* 2. contains >= 1 admitted known/supported evidence item
* 3. contains >= 1 unresolved Open Question (kind=unknown, status=unknown)
* 4. the unresolved Open Question is not admitted evidence
* 5. at least one eligible Finding exists (null disposition)
* 6. unresolved Open Question is not represented as an eligible Finding
* 7. resolvedNodeIds does NOT include any unknown node id
*/
import { describe, it, expect } from "vitest";
import fixture from "../fixtures/progressed-investigation-manufacturing.json";
describe("Progressed investigation fixture apparatus", () => {
/* ── 1. Fixture loads successfully ─────────────────── */
it("loads as valid JSON with required top-level keys", () => {
expect(fixture).toHaveProperty("success", true);
expect(fixture).toHaveProperty("stage", "update_applied");
expect(fixture).toHaveProperty("situationGraph");
expect(fixture.situationGraph).toHaveProperty("nodes");
expect(fixture.situationGraph).toHaveProperty("edges");
expect(fixture.situationGraph).toHaveProperty("activeUnknownNodeId");
expect(fixture.situationGraph).toHaveProperty("resolvedNodeIds");
expect(fixture.situationGraph).toHaveProperty("centralStatement");
});
/* ── 2. Contains admitted evidence (>= 1 supported/known node) ─── */
it("contains at least one admitted known or supported evidence node", () => {
const admitted = fixture.situationGraph.nodes.filter(
(n) => n.status === "supported" || n.status === "known"
);
expect(admitted.length).toBeGreaterThan(0);
});
it("admitted nodes are not unknown kind", () => {
const admitted = fixture.situationGraph.nodes.filter(
(n) => ["supported", "known"].includes(n.status)
);
for (const node of admitted) {
expect(node.kind).not.toBe("unknown");
}
});
/* ── 3. Contains unresolved Open Question (>= 1) ─────── */
it("contains at least one unresolved Open Question node", () => {
const unresolved = fixture.situationGraph.nodes.filter(
(n) => n.kind === "unknown" && n.status === "unknown"
);
expect(unresolved.length).toBeGreaterThan(0);
});
/* ── 4. Unresolved Open Question is not admitted evidence ─── */
it("no node is simultaneously unresolved unknown AND admitted evidence", () => {
const allNodes = fixture.situationGraph.nodes;
for (const node of allNodes) {
if (node.kind === "unknown" && node.status === "unknown") {
// An unresolved unknown must not have admitted status
expect(["supported", "known"]).not.toContain(node.status);
}
}
});
it("unresolved and admitted node ID sets are disjoint", () => {
const unresolvedIds = new Set(
fixture.situationGraph.nodes
.filter((n) => n.kind === "unknown" && n.status === "unknown")
.map((n) => n.id)
);
const admittedIds = new Set(
fixture.situationGraph.nodes
.filter((n) => ["supported", "known"].includes(n.status))
.map((n) => n.id)
);
for (const id of unresolvedIds) {
expect(admittedIds.has(id)).toBe(false);
}
});
it("unresolved unknown nodes are distinct from admitted evidence", () => {
const unresolvedIds = new Set(
fixture.situationGraph.nodes
.filter((n) => n.kind === "unknown" && n.status === "unknown")
.map((n) => n.id)
);
const admittedIds = new Set(
fixture.situationGraph.nodes
.filter((n) => ["supported", "known"].includes(n.status))
.map((n) => n.id)
);
for (const id of unresolvedIds) {
expect(admittedIds.has(id)).toBe(false);
}
});
/* ── 5. Unresolved question excluded from resolvedNodeIds ─── */
it("resolvedNodeIds does not contain any unresolved unknown node id", () => {
const unresolvedIds = fixture.situationGraph.nodes
.filter((n) => n.kind === "unknown" && n.status === "unknown")
.map((n) => n.id);
for (const nodeId of unresolvedIds) {
expect(fixture.situationGraph.resolvedNodeIds).not.toContain(nodeId);
}
});
/* ── 6. Eligible Finding exists ─────────────────────── */
it("findings array exists and has eligible items", () => {
expect(fixture.findings).toBeDefined();
expect(Array.isArray(fixture.findings)).toBe(true);
const eligible = fixture.findings.filter(
(f) => f.status === "provisional" && f.userDisposition === null
);
expect(eligible.length).toBeGreaterThan(0);
});
/* ── 7. Unresolved Open Question is not represented as a Finding ─── */
it("unresolved unknown nodes are not mapped to findings propositions", () => {
const unresolvedNodeIds = new Set(
fixture.situationGraph.nodes
.filter((n) => n.kind === "unknown" && n.status === "unknown")
.map((n) => n.id)
);
// originTargetNodeId links findings to unknown nodes
// A finding whose originatingTargetNodeId is an unresolved unknown is fine
// but the proposition should not BE the same as the node label
const eligibleFindings = fixture.findings.filter(
(f) => f.status === "provisional" && f.userDisposition === null
);
const unresolvedNodes = fixture.situationGraph.nodes.filter(
(n) => n.kind === "unknown" && n.status === "unknown"
);
for (const finding of eligibleFindings) {
// The proposition is an admitted evidence item, not the unknown itself
expect(finding.proposition).not.toContain("Whether");
// Verify the originating node is NOT resolved — it remains open
const targetNode = unresolvedNodes.find(
(n) => n.id === finding.originatingTargetNodeId
);
if (targetNode) {
expect(targetNode.status).toBe("unknown");
expect(targetNode.kind).toBe("unknown");
}
}
});
/* ── 8. Canonical shape integrity ──────────────────── */
it("activeUnknownNodeId references a valid unresolved unknown node", () => {
const unresolvedIds = fixture.situationGraph.nodes
.filter((n) => n.kind === "unknown" && n.status === "unknown")
.map((n) => n.id);
expect(unresolvedIds).toContain(fixture.situationGraph.activeUnknownNodeId);
});
it("all nodes pass the canonical situationNode schema invariants", () => {
for (const node of fixture.situationGraph.nodes) {
expect(node).toHaveProperty("id");
expect(node).toHaveProperty("label");
expect(node).toHaveProperty("description");
expect(["observation","reported_claim","metric","state","transition","relationship","assumption","unknown","conclusion","option"]).toContain(node.kind);
expect(["known","unknown","provisional","supported","weakened","contradicted","resolved"]).toContain(node.status);
expect(["low","medium","high"]).toContain(node.confidence);
expect(Array.isArray(node.evidenceIds)).toBe(true);
expect(Array.isArray(node.dependsOn)).toBe(true);
expect(Array.isArray(node.affects)).toBe(true);
}
});
it("all edges reference valid nodes", () => {
const nodeIds = new Set(fixture.situationGraph.nodes.map((n) => n.id));
for (const edge of fixture.situationGraph.edges) {
expect(edge).toHaveProperty("id");
expect(nodeIds.has(edge.fromNodeId)).toBe(true);
expect(nodeIds.has(edge.toNodeId)).toBe(true);
expect(edge).toHaveProperty("relationship");
expect(edge).toHaveProperty("confidence");
}
});
});
@@ -0,0 +1,241 @@
{
"success": true,
"stage": "update_applied",
"situationGraph": {
"centralStatement": "Complaint counts increased while production also increased.",
"nodes": [
{
"id": "n-obs-1",
"label": "Customer complaints rose by 35% over the last six months",
"description": "Customer complaints rose by 35% over the last six months",
"kind": "observation",
"status": "supported",
"confidence": "high",
"value": null,
"unit": null,
"evidenceIds": [
"complaint-data"
],
"dependsOn": [],
"affects": [],
"parentId": null,
"childIds": []
},
{
"id": "n-obs-2",
"label": "Production volume increased by 40% in the same period",
"description": "Production volume increased by 40% in the same period",
"kind": "observation",
"status": "supported",
"confidence": "high",
"value": null,
"unit": null,
"evidenceIds": [
"production-data"
],
"dependsOn": [],
"affects": [],
"parentId": null,
"childIds": []
},
{
"id": "n-obs-3",
"label": "A new CRM tagging system was introduced three months ago",
"description": "A new CRM tagging system was introduced three months ago, coinciding with the complaint category changes",
"kind": "observation",
"status": "known",
"confidence": "high",
"value": null,
"unit": null,
"evidenceIds": [
"crm-audit"
],
"dependsOn": [],
"affects": [],
"parentId": null,
"childIds": []
},
{
"id": "n-unknown-1",
"label": "Whether complaints increased per-unit or proportionally with production volume",
"description": "Need to know the complaint rate normalized by production volume to determine if quality deteriorated or if growth explains the rise",
"kind": "unknown",
"status": "unknown",
"confidence": "high",
"value": null,
"unit": null,
"evidenceIds": [],
"dependsOn": [],
"affects": [
"n-conclusion"
],
"parentId": null,
"childIds": []
},
{
"id": "n-unknown-2",
"label": "Whether the new CRM tagging system changed what gets counted as a complaint",
"description": "The complaint category changes may be an artifact of the new CRM system rather than genuine quality change",
"kind": "unknown",
"status": "unknown",
"confidence": "medium",
"value": null,
"unit": null,
"evidenceIds": [],
"dependsOn": [],
"affects": [
"n-unknown-1"
],
"parentId": null,
"childIds": []
},
{
"id": "n-conclusion",
"label": "Whether automated quality inspection investment is justified",
"description": "Decision node for whether to spend about £120,000 on automated quality inspection",
"kind": "state",
"status": "provisional",
"confidence": "medium",
"value": null,
"unit": null,
"evidenceIds": [],
"dependsOn": [
"n-unknown-1"
],
"affects": [],
"parentId": null,
"childIds": []
},
{
"id": "nz92pkx-summary",
"label": "Complaint counts increased while production also increased.",
"description": "Summary of the situation from the scenario text",
"kind": "state",
"status": "provisional",
"confidence": "medium",
"value": null,
"unit": null,
"evidenceIds": [],
"dependsOn": [
"n-obs-1",
"n-obs-2",
"n-obs-3"
],
"affects": [
"n-conclusion"
],
"parentId": null,
"childIds": []
}
],
"edges": [
{
"id": "e-evidence-n-obs-1",
"fromNodeId": "n-obs-1",
"toNodeId": "nz92pkx-summary",
"relationship": "supports",
"confidence": "high",
"description": "Customer complaints rose by 35% over the last six months supports the summary"
},
{
"id": "e-evidence-n-obs-2",
"fromNodeId": "n-obs-2",
"toNodeId": "nz92pkx-summary",
"relationship": "supports",
"confidence": "high",
"description": "Production volume increased by 40% in the same period supports the summary"
},
{
"id": "e-evidence-n-obs-3",
"fromNodeId": "n-obs-3",
"toNodeId": "nz92pkx-summary",
"relationship": "supports",
"confidence": "high",
"description": "A new CRM tagging system was introduced three months ago supports the summary"
},
{
"id": "e-unk-n-unknown-1",
"fromNodeId": "n-unknown-1",
"toNodeId": "nz92pkx-summary",
"relationship": "depends_on",
"confidence": "high",
"description": "Whether complaints increased per-unit or proportionally with production volume is an unresolved factor for this situation"
},
{
"id": "e-unk-n-unknown-2",
"fromNodeId": "n-unknown-2",
"toNodeId": "nz92pkx-summary",
"relationship": "depends_on",
"confidence": "medium",
"description": "Whether the new CRM tagging system changed what gets counted as a complaint is an unresolved factor for this situation"
},
{
"id": "e-unk-conclusion",
"fromNodeId": "n-unknown-1",
"toNodeId": "n-conclusion",
"relationship": "depends_on",
"confidence": "medium",
"description": "Whether complaints increased per-unit or proportionally with production volume affects the investment decision"
}
],
"activeUnknownNodeId": "n-unknown-1",
"resolvedNodeIds": [],
"currentSummary": "Nodes: 4 observation, 2 unknown, 2 state | Edges: 6 total | Unknowns: 2 unresolved"
},
"findings": [
{
"id": "finding-complaint-rise",
"proposition": "Customer complaints have increased by 35% over the last six months while production volume grew by 40%.",
"status": "provisional",
"userDisposition": null,
"originatingTargetNodeId": "n-unknown-1",
"contributionId": "contrib-admission-1",
"sourceObservation": "user_input"
},
{
"id": "finding-crm-change",
"proposition": "A new CRM tagging system was introduced three months ago, coinciding with complaint category changes.",
"status": "provisional",
"userDisposition": null,
"originatingTargetNodeId": "n-unknown-2",
"contributionId": "contrib-admission-2",
"sourceObservation": "user_input"
}
],
"selectedQuestion": {
"nodeId": "n-unknown-1",
"question": "What is the complaint rate normalized by production volume, and has it increased per unit?",
"reason": "The admitted evidence shows absolute increases but does not establish whether quality deteriorated or growth explains the rise.",
"strategy": "evidence_gathering",
"investigationStrategy": {
"key": "evidence_gathering",
"reason": "Resolving the unknown requires normalized rate data.",
"nodeId": "n-unknown-1",
"nodeLabel": "Whether complaints increased per-unit or proportionally with production volume",
"meaning": "whether complaints increased per-unit or proportionally with production volume",
"actionPhrase": "deciding whether to spend about £120,000 on automated quality inspection now",
"relatedNodeIds": [
"n-conclusion"
],
"centralStatement": "Complaint counts increased while production also increased."
},
"reasoningPattern": "decision",
"reasoningPatternReason": "Selected decision because the active unknown sits inside an investment justification context."
},
"diagnostics": {
"promptVersion": "v0.4",
"modelName": "configured-model",
"validationStatus": "valid",
"nodeCount": 6,
"edgeCount": 6,
"graphReferenceValidation": {
"valid": true,
"errors": []
},
"unknownSelectionExplanation": {
"selectedNodeId": "n-unknown-1",
"status": "selected",
"tieType": "none"
}
}
}