checkpoint: preserve semantic decomposition investigation state
This commit is contained in:
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
assessChildUnknownQuality,
|
||||
applyValidatedProposal,
|
||||
determineGraphBackedQuestion,
|
||||
MAX_DECOMPOSITION_DEPTH,
|
||||
} from "@/lib/graph/apply-proposal.js";
|
||||
import { makeGraph, makeNode } from "@/lib/graph/schema.js";
|
||||
@@ -193,7 +194,7 @@ describe("decomposition stopping conditions", () => {
|
||||
};
|
||||
}
|
||||
|
||||
it("does not decompose an atomic selected unknown", () => {
|
||||
it("does not decompose an atomic selected unknown", async () => {
|
||||
const atomic = makeNode({
|
||||
id: "n-atomic",
|
||||
label: "Were both figures measured over the same accounting period?",
|
||||
@@ -213,7 +214,7 @@ describe("decomposition stopping conditions", () => {
|
||||
currentSummary: "Atomic selected node graph",
|
||||
});
|
||||
|
||||
const result = applyValidatedProposal({
|
||||
const result = await applyValidatedProposal({
|
||||
situationGraph: graph,
|
||||
proposal: makeMeaningfulNoOpProposal(),
|
||||
});
|
||||
@@ -225,7 +226,7 @@ describe("decomposition stopping conditions", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("does not manufacture comparison children for a generic explanatory parent", () => {
|
||||
it("does not manufacture comparison children for a generic explanatory parent", async () => {
|
||||
const { parent, graph } = makeParentGraph({
|
||||
centralStatement: "Traffic increased, but sales stayed flat.",
|
||||
parentLabel:
|
||||
@@ -252,7 +253,7 @@ describe("decomposition stopping conditions", () => {
|
||||
],
|
||||
});
|
||||
|
||||
const result = applyValidatedProposal({
|
||||
const result = await applyValidatedProposal({
|
||||
situationGraph: graph,
|
||||
proposal: makeMeaningfulNoOpProposal(),
|
||||
});
|
||||
@@ -286,7 +287,7 @@ describe("decomposition stopping conditions", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("still decomposes a genuine comparison/measurement parent into valid comparison children", () => {
|
||||
it("still decomposes a genuine comparison/measurement parent into valid comparison children", async () => {
|
||||
const { graph } = makeParentGraph({
|
||||
centralStatement:
|
||||
"Revenue increased by 18%, but cash in the bank fell over the same period.",
|
||||
@@ -313,7 +314,7 @@ describe("decomposition stopping conditions", () => {
|
||||
],
|
||||
});
|
||||
|
||||
const result = applyValidatedProposal({
|
||||
const result = await applyValidatedProposal({
|
||||
situationGraph: graph,
|
||||
proposal: makeMeaningfulNoOpProposal(),
|
||||
});
|
||||
@@ -342,4 +343,45 @@ describe("decomposition stopping conditions", () => {
|
||||
expect(MAX_DECOMPOSITION_DEPTH).toBeGreaterThanOrEqual(2);
|
||||
expect(MAX_DECOMPOSITION_DEPTH).toBeLessThanOrEqual(3);
|
||||
});
|
||||
|
||||
it("rejects invalid semantic fallback children through existing quality validation", async () => {
|
||||
const { parent, graph } = makeParentGraph({
|
||||
centralStatement:
|
||||
"A dependency persists, but the cause is unclear.",
|
||||
parentLabel: "Which factor is causing the dependency",
|
||||
parentDescription:
|
||||
"Need to know whether team capability, customer dependency, responsibility distribution, or insufficient deliberate delegation is causing the dependency.",
|
||||
});
|
||||
const provider = {
|
||||
generateReconstruction: async () => ({
|
||||
parentNodeId: parent.id,
|
||||
proposedChildren: [
|
||||
{
|
||||
label: "Team capability or customer dependency",
|
||||
description:
|
||||
"Need to know whether team capability or customer dependency is causing the dependency.",
|
||||
},
|
||||
{
|
||||
label: "Responsibility distribution or insufficient deliberate delegation",
|
||||
description:
|
||||
"Need to know whether responsibility distribution or insufficient deliberate delegation is causing the dependency.",
|
||||
},
|
||||
],
|
||||
}),
|
||||
};
|
||||
|
||||
const result = await determineGraphBackedQuestion({
|
||||
situationGraph: graph,
|
||||
provider,
|
||||
modelName: "mock-ollama",
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.decompositionAttempted).toBe(true);
|
||||
expect(result.decompositionAccepted).toBe(false);
|
||||
expect(result.proposedChildCount).toBe(2);
|
||||
expect(result.acceptedChildCount).toBe(0);
|
||||
expect(result.childUnknownCount).toBe(0);
|
||||
expect(result.rejectedChildren).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user