diff --git a/scripts/reproduce-multi-turn-investigation.mjs b/scripts/reproduce-multi-turn-investigation.mjs index f69e4d0..5132eb7 100644 --- a/scripts/reproduce-multi-turn-investigation.mjs +++ b/scripts/reproduce-multi-turn-investigation.mjs @@ -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 ?? []; diff --git a/tests/reproduce-multi-turn-investigation.harness.test.js b/tests/reproduce-multi-turn-investigation.harness.test.js index 0c05431..aecaa06 100644 --- a/tests/reproduce-multi-turn-investigation.harness.test.js +++ b/tests/reproduce-multi-turn-investigation.harness.test.js @@ -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 ?? [] }; }