tooling: expose closure metadata in live harness
This commit is contained in:
@@ -343,6 +343,16 @@ async function runUpdateOnlyMode() {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Capture explicit closure metadata from accepted update ─
|
||||
const finalActiveUnknownNodeId =
|
||||
updatedGraph?.activeUnknownNodeId === undefined
|
||||
? null
|
||||
: updatedGraph.activeUnknownNodeId;
|
||||
console.log(
|
||||
`finalActiveUnknownNodeId: ${JSON.stringify(finalActiveUnknownNodeId)}`,
|
||||
);
|
||||
console.log(`finalSelectedQuestion: ${JSON.stringify(sq)}`);
|
||||
|
||||
// ── Capture persistent graph after update ───────────────
|
||||
const pNodes = updatedGraph?.nodes ?? [];
|
||||
const pEdges = updatedGraph?.edges ?? [];
|
||||
|
||||
@@ -156,6 +156,8 @@ function runPreAnchoredSimulationWithFixture(cfg) {
|
||||
proposal: proposal ? { addedNodes: proposal.addedNodes ?? [], addedEdges: proposal.addedEdges ?? [], updatedNodes: proposal.updatedNodes ?? [], resolvedUnknownNodeIds: proposal.resolvedUnknownNodeIds ?? [] } : null,
|
||||
structuralActionRequired: sar,
|
||||
selectedQuestion: sq && typeof sq === "object" ? { question: sq.question, nodeId: sq.nodeId } : null,
|
||||
finalActiveUnknownNodeId: uj.updatedSituationGraph?.activeUnknownNodeId === undefined ? null : uj.updatedSituationGraph.activeUnknownNodeId,
|
||||
finalSelectedQuestion: sq,
|
||||
graph: { nodes: uj.updatedSituationGraph?.nodes ?? [], edges: uj.updatedSituationGraph?.edges ?? [] },
|
||||
},
|
||||
capturedUpdateBody,
|
||||
@@ -477,6 +479,68 @@ describe("reproduce-multi-turn-investigation harness: one-shot semantics", () =>
|
||||
expect(graph.edges[0].relationship).toBe("depends_on");
|
||||
});
|
||||
|
||||
it("accepted Update exposes explicit null finalActiveUnknownNodeId", () => {
|
||||
const r = runSimulationWithResponseShape({
|
||||
scenario: "test_ok",
|
||||
maxUpdates: 1,
|
||||
answers: ["good answer"],
|
||||
onResponseUpdate: () => ({
|
||||
success: true,
|
||||
stage: "update_applied",
|
||||
updatedSituationGraph: { nodes: [], edges: [], activeUnknownNodeId: null },
|
||||
selectedQuestion: { question: "q2", nodeId: "n_pending" },
|
||||
updatedProposal: { addedNodes: [], addedEdges: [], updatedNodes: [], resolvedUnknownNodeIds: [] },
|
||||
}),
|
||||
});
|
||||
|
||||
expect(r.captured?.finalActiveUnknownNodeId).toBeNull();
|
||||
});
|
||||
|
||||
it("accepted Update exposes explicit null finalSelectedQuestion", () => {
|
||||
const r = runSimulationWithResponseShape({
|
||||
scenario: "test_ok",
|
||||
maxUpdates: 1,
|
||||
answers: ["good answer"],
|
||||
onResponseUpdate: () => ({
|
||||
success: true,
|
||||
stage: "update_applied",
|
||||
updatedSituationGraph: { nodes: [], edges: [], activeUnknownNodeId: null },
|
||||
selectedQuestion: null,
|
||||
updatedProposal: { addedNodes: [], addedEdges: [], updatedNodes: [], resolvedUnknownNodeIds: [] },
|
||||
}),
|
||||
});
|
||||
|
||||
expect(r.captured?.finalSelectedQuestion).toBeNull();
|
||||
});
|
||||
|
||||
it("accepted Update preserves populated closure metadata unchanged", () => {
|
||||
const r = runSimulationWithResponseShape({
|
||||
scenario: "test_ok",
|
||||
maxUpdates: 1,
|
||||
answers: ["good answer"],
|
||||
onResponseUpdate: () => ({
|
||||
success: true,
|
||||
stage: "update_applied",
|
||||
updatedSituationGraph: {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
activeUnknownNodeId: "n_example",
|
||||
},
|
||||
selectedQuestion: {
|
||||
nodeId: "n_example",
|
||||
question: "What evidence would clarify this example?",
|
||||
},
|
||||
updatedProposal: { addedNodes: [], addedEdges: [], updatedNodes: [], resolvedUnknownNodeIds: [] },
|
||||
}),
|
||||
});
|
||||
|
||||
expect(r.captured?.finalActiveUnknownNodeId).toBe("n_example");
|
||||
expect(r.captured?.finalSelectedQuestion).toEqual({
|
||||
nodeId: "n_example",
|
||||
question: "What evidence would clarify this example?",
|
||||
});
|
||||
});
|
||||
|
||||
it("rejected Update still exposes rejectedProposalSnapshot", () => {
|
||||
const r = runSimulation({ scenario: "test_ok", maxUpdates: 1, answers: ["answer_reject"] });
|
||||
expect(r.startCalls).toBe(1);
|
||||
@@ -1715,7 +1779,14 @@ function runSimulationWithResponseShape(cfg) {
|
||||
const answers = cfg.answers || [];
|
||||
const maxUpdates = typeof cfg.maxUpdates === "number" ? cfg.maxUpdates : 2;
|
||||
const limit = Math.min(maxUpdates, answers.length);
|
||||
let captured = { answerMeaning: null, proposal: null, selectedQuestion: null, graph: null };
|
||||
let captured = {
|
||||
answerMeaning: null,
|
||||
proposal: null,
|
||||
selectedQuestion: null,
|
||||
finalActiveUnknownNodeId: null,
|
||||
finalSelectedQuestion: null,
|
||||
graph: null,
|
||||
};
|
||||
|
||||
for (let i = 0; i < limit; i++) {
|
||||
const upResp = api.post("/api/cases/update", { situationGraph: graph, previousQuestion: question, answer: answers[i] });
|
||||
@@ -1747,6 +1818,10 @@ function runSimulationWithResponseShape(cfg) {
|
||||
|
||||
const sq = uj.selectedQuestion ?? null;
|
||||
if (sq && typeof sq === "object") captured.selectedQuestion = { question: sq.question, nodeId: sq.nodeId };
|
||||
captured.finalSelectedQuestion = sq;
|
||||
|
||||
captured.finalActiveUnknownNodeId =
|
||||
graph?.activeUnknownNodeId === undefined ? null : graph.activeUnknownNodeId;
|
||||
|
||||
captured.graph = { nodes: graph?.nodes ?? [], edges: graph?.edges ?? [] };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user