checkpoint: preserve semantic decomposition investigation state

This commit is contained in:
2026-08-22 08:21:29 +01:00
parent 68be2344c6
commit 517d780e2c
4 changed files with 319 additions and 24 deletions
+89 -5
View File
@@ -3,7 +3,10 @@ import {
assessUnknownAnswerability,
assessUnknownAtomicity,
} from "@/lib/graph/question-formulator.js";
import { applyValidatedProposal } from "@/lib/graph/apply-proposal.js";
import {
applyValidatedProposal,
determineGraphBackedQuestion,
} from "@/lib/graph/apply-proposal.js";
import { makeGraph, makeNode } from "@/lib/graph/schema.js";
const COMMERCIAL_SCENARIO =
@@ -95,10 +98,10 @@ describe("assessUnknownAnswerability", () => {
});
describe("answerability-triggered decomposition", () => {
it("decomposes a non-answerable parent into independently answerable child investigations", () => {
it("decomposes a non-answerable parent into independently answerable child investigations", async () => {
const graph = makeCommercialContainerGraph();
const result = applyValidatedProposal({
const result = await applyValidatedProposal({
situationGraph: graph,
proposal: makeMeaningfulNoOpProposal(),
});
@@ -116,10 +119,10 @@ describe("answerability-triggered decomposition", () => {
);
});
it("keeps the parent unresolved while selecting a child unknown", () => {
it("keeps the parent unresolved while selecting a child unknown", async () => {
const graph = makeCommercialContainerGraph();
const result = applyValidatedProposal({
const result = await applyValidatedProposal({
situationGraph: graph,
proposal: makeMeaningfulNoOpProposal(),
});
@@ -135,4 +138,85 @@ describe("answerability-triggered decomposition", () => {
expect(selectedChild.parentId).toBe(parentNode.id);
expect(selectedChild.status).toBe("unknown");
});
it("invokes bounded semantic fallback when no deterministic family exists and accepts valid children without direct LLM graph mutation", async () => {
const parent = makeNode({
id: "n-dependency-parent",
label: "Which factor is causing the dependency",
description:
"Need to know whether team capability, customer or client dependency, responsibility distribution, or insufficient deliberate delegation or stepping away is causing the dependency.",
kind: "unknown",
status: "unknown",
confidence: "medium",
});
const graph = makeGraph({
centralStatement:
"A dependency persists, but it is unclear whether the cause is team capability, customer dependency, responsibility distribution, or insufficient deliberate delegation.",
nodes: [parent],
edges: [],
activeUnknownNodeId: parent.id,
resolvedNodeIds: [],
currentSummary: "Dependency-factor decomposition fixture",
});
const atomicity = assessUnknownAtomicity({ node: parent, graph });
const answerability = assessUnknownAnswerability({ node: parent, graph });
const provider = {
generateReconstruction: async () => ({
parentNodeId: parent.id,
proposedChildren: [
{
label: "Whether team capability is causing the dependency",
description:
"Need to know whether team capability is causing the dependency, because that would narrow the source of the dependency.",
},
{
label: "Whether customer dependency is causing the dependency",
description:
"Need to know whether customer dependency is causing the dependency, because that would narrow the source of the dependency.",
},
],
}),
};
const result = await determineGraphBackedQuestion({
situationGraph: graph,
provider,
modelName: "mock-ollama",
});
expect(atomicity.atomicity).toBe("atomic");
expect(answerability.independentlyAnswerable).toBe(false);
expect(answerability.decompositionRequired).toBe(true);
expect(result.success).toBe(true);
expect(result.decompositionAttempted).toBe(true);
expect(result.decompositionTriggeredByAnswerability).toBe(true);
expect(result.decompositionAccepted).toBe(true);
expect(result.proposedChildCount).toBe(2);
expect(result.acceptedChildCount).toBe(2);
expect(result.childUnknownCount).toBe(2);
expect(result.selectedContainerUnknown).toBe(parent.id);
expect(result.selectedChildUnknown).not.toBe(parent.id);
expect(result.selectedQuestion).toBeTruthy();
});
it("preserves deterministic family path without invoking semantic fallback", async () => {
const graph = makeCommercialContainerGraph();
let providerCalled = false;
const provider = {
generateReconstruction: async () => {
providerCalled = true;
return { parentNodeId: "n-commercial-parent", proposedChildren: [] };
},
};
const result = await determineGraphBackedQuestion({
situationGraph: graph,
provider,
modelName: "mock-ollama",
});
expect(result.success).toBe(true);
expect(result.decompositionPerformed).toBe(true);
expect(providerCalled).toBe(false);
});
});