tooling: supply anchored previous question in update-only mode

This commit is contained in:
2026-08-12 10:41:02 +01:00
parent 70db093cb1
commit 8526aa4b69
2 changed files with 53 additions and 3 deletions
@@ -239,7 +239,10 @@ async function runUpdateOnlyMode() {
// Deep-copy so mutation doesn't corrupt the original fixture.
const situationGraph = JSON.parse(JSON.stringify(initialGraph));
let selectedQuestion = null;
// Derive previousQuestion from the committed savings-realism anchor so that
// update-only mode can reach the production Update path without requiring Start.
let selectedQuestion = fixtureData.unresolved_question ?? savingsNode.label;
// ── Exactly one Update through production route ────────
const updateNum = 1;
@@ -651,15 +651,53 @@ describe("reproduce-multi-turn-investigation harness: one-shot semantics", () =>
expect(r.startCalls).toBe(0);
expect(r.updateCalls).toBe(1);
// Direct assertion: exact fixture graph transmitted (57J.81)
const updateBody = r.capturedUpdateBody;
expect(updateBody.situationGraph).toBeDefined();
const fixtureGraph = PRE_ANCHORED_FIXTURE.graph;
expect(JSON.parse(JSON.stringify(updateBody.situationGraph.nodes))).toEqual(fixtureGraph.nodes);
expect(JSON.parse(JSON.stringify(updateBody.situationGraph.edges))).toEqual(fixtureGraph.edges);
// Verify the captured graph matches what was sent
const { nodes, edges } = r;
const fixtureGraph = PRE_ANCHORED_FIXTURE.graph;
expect(nodes.length).toBe(fixtureGraph.nodes.length);
expect(edges.length).toBe(fixtureGraph.edges.length);
expect(nodes[0].id).toBe(fixtureGraph.nodes[0].id);
expect(nodes[1].id).toBe(fixtureGraph.nodes[1].id);
});
it("57J.81 update-only previousQuestion is non-empty string and matches the committed savings-realism anchor", () => {
const r = runPreAnchoredSimulation();
expect(r.startCalls).toBe(0);
expect(r.updateCalls).toBe(1);
const prevQ = r.capturedUpdateBody.previousQuestion;
expect(typeof prevQ).toBe("string");
expect(prevQ.length).toBeGreaterThan(0);
// Must correspond exactly to the anchored savings-realism uncertainty
expect(prevQ).toBe(PRE_ANCHORED_FIXTURE.unresolvedQuestion);
expect(prevQ).toBe("Are the projected office savings from relocation realistic?");
});
it("57J.81 update-only exact ANSWER_2 sent as request answer", () => {
const customAnswer = "The projected savings are based on the current London lease and business rates.";
const r = runPreAnchoredSimulation({ answer: customAnswer });
expect(r.startCalls).toBe(0);
expect(r.updateCalls).toBe(1);
// Direct assertion of exact ANSWER_2 in request body
expect(r.capturedUpdateBody.answer).toBe(customAnswer);
});
it("57J.81 missing ANSWER_2 blocks before any HTTP call", () => {
const r = runPreAnchoredSimulationWithBlock();
expect(r.startCalls).toBe(0);
expect(r.updateCalls).toBe(0);
expect(r.type).toBe("blocked_no_answer");
expect(r.blockedMessage).toContain("missing ANSWER_2");
expect(r.apiLog.length).toBe(0);
});
it("pre-anchored update-only mode does not call Start", () => {
const r = runPreAnchoredSimulation();
expect(r.startCalls).toBe(0);
@@ -1061,7 +1099,14 @@ function runPreAnchoredSimulation(cfg) {
// Pre-anchored: no Start call — graph is supplied directly.
let graph = JSON.parse(JSON.stringify(initialGraph));
let question = null; // will be set by the update response (or mock)
// Derive previousQuestion from the committed savings-realism anchor
// (mirrors the harness fix in reproduce-multi-turn-investigation.mjs).
const unresolvedQuestion = PRE_ANCHORED_FIXTURE.unresolvedQuestion;
let question = unresolvedQuestion; // fixture always has this field for pre-anchored mode
// Track the exact Update request body for direct assertions.
let capturedUpdateBody = null;
const api = {
post(path, body) {
@@ -1073,6 +1118,7 @@ function runPreAnchoredSimulation(cfg) {
if (path === "/api/cases/update") {
apiLog.push({ step: "update", answer: body.answer });
capturedUpdateBody = body; // capture for direct request-body assertions
updateCalls++;
const resp = typeof C.onResponseUpdate === "function"
@@ -1161,6 +1207,7 @@ function runPreAnchoredSimulation(cfg) {
selectedQuestion: sq && typeof sq === "object" ? { question: sq.question, nodeId: sq.nodeId } : null,
graph: { nodes: uj.updatedSituationGraph?.nodes ?? [], edges: uj.updatedSituationGraph?.edges ?? [] },
},
capturedUpdateBody,
};
}