Feature/product platform foundation v0.62 #1

Merged
robbond merged 683 commits from feature/product-platform-foundation-v0.62 into feature/emergent-unknowns-v0.5 2026-09-09 07:58:20 +01:00
2 changed files with 60 additions and 51 deletions
Showing only changes of commit c4431997b1 - Show all commits
@@ -135,8 +135,11 @@ async function main() {
console.log(`stage: ${updateResult.json.stage ?? "unknown"}`); console.log(`stage: ${updateResult.json.stage ?? "unknown"}`);
console.log(`proposal/apply success: ${updateResult.json.proposal?.success ?? updateResult.json.applySuccess ?? null}`); console.log(`proposal/apply success: ${updateResult.json.proposal?.success ?? updateResult.json.applySuccess ?? null}`);
// ── Capture accepted answer meaning ───────────────────── // ── Capture accepted structural mutation fields ────────
const am = updateResult.json.answerMeaning ?? null; const proposal = updateResult.json.updatedProposal ?? updateResult.json.proposal ?? null;
// ── Capture answerMeaning and structuralActionRequired from proposal (production path) ─
const am = proposal?.answerMeaning ?? null;
if (am) { if (am) {
console.log(`answerMeaning.userSupportedMeaning: ${JSON.stringify(am.userSupportedMeaning ?? null)}`); console.log(`answerMeaning.userSupportedMeaning: ${JSON.stringify(am.userSupportedMeaning ?? null)}`);
console.log(`answerMeaning.possibleInference: ${JSON.stringify(am.possibleInference ?? null)}`); console.log(`answerMeaning.possibleInference: ${JSON.stringify(am.possibleInference ?? null)}`);
@@ -144,8 +147,6 @@ async function main() {
console.log(`answerMeaning.resolutionGuidance: ${JSON.stringify(am.resolutionGuidance ?? null)}`); console.log(`answerMeaning.resolutionGuidance: ${JSON.stringify(am.resolutionGuidance ?? null)}`);
} }
// ── Capture accepted structural mutation fields ────────
const proposal = updateResult.json.updatedProposal ?? updateResult.json.proposal ?? null;
if (proposal) { if (proposal) {
console.log(`updatedNodes: ${JSON.stringify(proposal.updatedNodes ?? [])}`); console.log(`updatedNodes: ${JSON.stringify(proposal.updatedNodes ?? [])}`);
console.log(`resolvedUnknownNodeIds: ${JSON.stringify(proposal.resolvedUnknownNodeIds ?? [])}`); console.log(`resolvedUnknownNodeIds: ${JSON.stringify(proposal.resolvedUnknownNodeIds ?? [])}`);
@@ -154,7 +155,7 @@ async function main() {
} }
// ── Capture structuralActionRequired from accepted update ─ // ── Capture structuralActionRequired from accepted update ─
const sar = updateResult.json.structuralActionRequired; const sar = proposal?.structuralActionRequired;
if (sar === undefined || sar === null) { if (sar === undefined || sar === null) {
console.log(`structuralActionRequired: null`); console.log(`structuralActionRequired: null`);
} else { } else {
@@ -272,13 +272,7 @@ describe("reproduce-multi-turn-investigation harness: one-shot semantics", () =>
stage: "update_applied", stage: "update_applied",
updatedSituationGraph: { nodes: [], edges: [] }, updatedSituationGraph: { nodes: [], edges: [] },
selectedQuestion: { question: "q2" }, selectedQuestion: { question: "q2" },
answerMeaning: { updatedProposal: { addedNodes: [], addedEdges: [], updatedNodes: [], resolvedUnknownNodeIds: [], answerMeaning: { userSupportedMeaning: "cost reduction is a primary driver", possibleInference: "if cost savings not realized, relocation weakens", supportCategory: "other", resolutionGuidance: "determine actual projected figures" } },
userSupportedMeaning: "cost reduction is a primary driver",
possibleInference: "if cost savings not realized, relocation weakens",
supportCategory: "other",
resolutionGuidance: "determine actual projected figures",
},
updatedProposal: { addedNodes: [], addedEdges: [], updatedNodes: [], resolvedUnknownNodeIds: [] },
}), }),
}); });
expect(r.updateCalls).toBe(1); expect(r.updateCalls).toBe(1);
@@ -389,8 +383,7 @@ describe("reproduce-multi-turn-investigation harness: one-shot semantics", () =>
stage: "update_applied", stage: "update_applied",
updatedSituationGraph: { nodes: [], edges: [] }, updatedSituationGraph: { nodes: [], edges: [] },
selectedQuestion: { question: "q2" }, selectedQuestion: { question: "q2" },
structuralActionRequired: true, updatedProposal: { addedNodes: [], addedEdges: [], updatedNodes: [], resolvedUnknownNodeIds: [], structuralActionRequired: true },
updatedProposal: { addedNodes: [], addedEdges: [], updatedNodes: [], resolvedUnknownNodeIds: [] },
}), }),
}); });
expect(r.startCalls).toBe(1); expect(r.startCalls).toBe(1);
@@ -408,8 +401,7 @@ describe("reproduce-multi-turn-investigation harness: one-shot semantics", () =>
stage: "update_applied", stage: "update_applied",
updatedSituationGraph: { nodes: [], edges: [] }, updatedSituationGraph: { nodes: [], edges: [] },
selectedQuestion: { question: "q2" }, selectedQuestion: { question: "q2" },
structuralActionRequired: false, updatedProposal: { addedNodes: [], addedEdges: [], updatedNodes: [], resolvedUnknownNodeIds: [], structuralActionRequired: false },
updatedProposal: { addedNodes: [], addedEdges: [], updatedNodes: [], resolvedUnknownNodeIds: [] },
}), }),
}); });
expect(r.updateCalls).toBe(1); expect(r.updateCalls).toBe(1);
@@ -444,8 +436,7 @@ describe("reproduce-multi-turn-investigation harness: one-shot semantics", () =>
stage: "update_applied", stage: "update_applied",
updatedSituationGraph: { nodes: [], edges: [] }, updatedSituationGraph: { nodes: [], edges: [] },
selectedQuestion: { question: "q2" }, selectedQuestion: { question: "q2" },
structuralActionRequired: null, updatedProposal: { addedNodes: [], addedEdges: [], updatedNodes: [], resolvedUnknownNodeIds: [], structuralActionRequired: null },
updatedProposal: { addedNodes: [], addedEdges: [], updatedNodes: [], resolvedUnknownNodeIds: [] },
}), }),
}); });
expect(r.updateCalls).toBe(1); expect(r.updateCalls).toBe(1);
@@ -561,8 +552,7 @@ describe("reproduce-multi-turn-investigation harness: one-shot semantics", () =>
stage: "update_applied", stage: "update_applied",
updatedSituationGraph: { nodes: [], edges: [] }, updatedSituationGraph: { nodes: [], edges: [] },
selectedQuestion: { question: "q" }, selectedQuestion: { question: "q" },
structuralActionRequired: true, updatedProposal: { addedNodes: [], addedEdges: [], updatedNodes: [], resolvedUnknownNodeIds: [], structuralActionRequired: true },
updatedProposal: { addedNodes: [], addedEdges: [], updatedNodes: [], resolvedUnknownNodeIds: [] },
}), }),
}); });
expect(rSuccess.updateCalls).toBe(2); expect(rSuccess.updateCalls).toBe(2);
@@ -863,18 +853,18 @@ describe("reproduce-multi-turn-investigation harness: one-shot semantics", () =>
edges: [{ fromNodeId: "n_proj_validation", toNodeId: "n_relocation_state", relationship: "depends_on" }], edges: [{ fromNodeId: "n_proj_validation", toNodeId: "n_relocation_state", relationship: "depends_on" }],
}, },
selectedQuestion: { question: "What evidence would clarify validation?", nodeId: "n_proj_validation" }, selectedQuestion: { question: "What evidence would clarify validation?", nodeId: "n_proj_validation" },
structuralActionRequired: true,
answerMeaning: {
userSupportedMeaning: "User is unsure whether projected savings are realistic.",
possibleInference: null,
supportCategory: "uncertain",
resolutionGuidance: null,
},
updatedProposal: { updatedProposal: {
addedNodes: [{ id: "n_proj_validation" }], addedNodes: [{ id: "n_proj_validation" }],
addedEdges: [], addedEdges: [],
updatedNodes: [], updatedNodes: [],
resolvedUnknownNodeIds: [], resolvedUnknownNodeIds: [],
structuralActionRequired: true,
answerMeaning: {
userSupportedMeaning: "User is unsure whether projected savings are realistic.",
possibleInference: null,
supportCategory: "uncertain",
resolutionGuidance: null,
},
}, },
}), }),
}); });
@@ -1141,18 +1131,19 @@ function runPreAnchoredSimulation(cfg) {
stage: "update_applied", stage: "update_applied",
updatedSituationGraph: graph, updatedSituationGraph: graph,
selectedQuestion: { question: "What evidence would clarify projected savings realism?", nodeId: "n_savings_realism" }, selectedQuestion: { question: "What evidence would clarify projected savings realism?", nodeId: "n_savings_realism" },
structuralActionRequired: C.updateBehavior === "no-op" ? false : true, // structuralActionRequired and answerMeaning only inside updatedProposal (production path)
answerMeaning: {
userSupportedMeaning: "User is unsure whether the projected office savings from the relocation are realistic.",
possibleInference: null,
supportCategory: "uncertain",
resolutionGuidance: null,
},
updatedProposal: { updatedProposal: {
addedNodes: C.updateBehavior === "no-op" ? [] : [{ id: "n_new_unknown", kind: "unknown", label: "test node", description: "test", confidence: "low", value: null, unit: null, evidenceIds: [], dependsOn: [], affects: [], parentId: null, childIds: [] }], addedNodes: C.updateBehavior === "no-op" ? [] : [{ id: "n_new_unknown", kind: "unknown", label: "test node", description: "test", confidence: "low", value: null, unit: null, evidenceIds: [], dependsOn: [], affects: [], parentId: null, childIds: [] }],
addedEdges: C.updateBehavior === "no-op" ? [] : [{ id: "e-new", fromNodeId: "n_new_unknown", toNodeId: "n_relocation_state", relationship: "depends_on", confidence: "medium", description: "test edge" }], addedEdges: C.updateBehavior === "no-op" ? [] : [{ id: "e-new", fromNodeId: "n_new_unknown", toNodeId: "n_relocation_state", relationship: "depends_on", confidence: "medium", description: "test edge" }],
updatedNodes: updatedNodes, updatedNodes: updatedNodes,
resolvedUnknownNodeIds: [], resolvedUnknownNodeIds: [],
structuralActionRequired: C.updateBehavior === "no-op" ? false : true,
answerMeaning: {
userSupportedMeaning: "User is unsure whether the projected office savings from the relocation are realistic.",
possibleInference: null,
supportCategory: "uncertain",
resolutionGuidance: null,
},
}, },
}), }),
}; };
@@ -1191,10 +1182,10 @@ function runPreAnchoredSimulation(cfg) {
}; };
} }
// Capture fields (mirrors harness print logic) // Capture fields mirrors harness production path (from proposal, not root)
const am = uj.answerMeaning ?? null;
let proposal = uj.updatedProposal ?? uj.proposal ?? null; let proposal = uj.updatedProposal ?? uj.proposal ?? null;
let sar = uj.structuralActionRequired; const am = proposal?.answerMeaning ?? null;
let sar = proposal?.structuralActionRequired;
if (sar === undefined || sar === null) sar = null; if (sar === undefined || sar === null) sar = null;
const sq = uj.selectedQuestion ?? null; const sq = uj.selectedQuestion ?? null;
@@ -1238,10 +1229,26 @@ function runSimulationWithResponseShape(cfg) {
: null; : null;
if (resp) { if (resp) {
return { status: resp.success ? 200 : 422, json: () => resp }; // Production exposes these inside updatedProposal; read from there.
const proposalFromResp = resp.updatedProposal ?? {};
const sar = proposalFromResp.structuralActionRequired !== undefined
? proposalFromResp.structuralActionRequired
: (resp.structuralActionRequired !== undefined ? resp.structuralActionRequired : undefined);
const am = proposalFromResp.answerMeaning ?? resp.answerMeaning ?? null;
return { status: resp.success ? 200 : 422, json: () => ({
...resp,
updatedProposal: {
addedNodes: proposalFromResp.addedNodes ?? [],
addedEdges: proposalFromResp.addedEdges ?? [],
updatedNodes: proposalFromResp.updatedNodes ?? [],
resolvedUnknownNodeIds: proposalFromResp.resolvedUnknownNodeIds ?? [],
structuralActionRequired: sar,
answerMeaning: am,
},
}) };
} }
// Fallback to default mock // Fallback to default mock (no structuralActionRequired or answerMeaning at any level)
const shouldReject = typeof body.answer === "string" && body.answer.includes("_reject"); const shouldReject = typeof body.answer === "string" && body.answer.includes("_reject");
return { return {
status: shouldReject ? 422 : 200, status: shouldReject ? 422 : 200,
@@ -1281,21 +1288,22 @@ function runSimulationWithResponseShape(cfg) {
graph = uj.updatedSituationGraph; graph = uj.updatedSituationGraph;
question = uj.selectedQuestion ? uj.selectedQuestion.question : null; question = uj.selectedQuestion ? uj.selectedQuestion.question : null;
// Mirror the harness capture (what would be printed) // Mirror the harness capture — reads from proposal (production path), NOT root.
const am = uj.answerMeaning ?? null;
if (am) captured.answerMeaning = { userSupportedMeaning: am.userSupportedMeaning, possibleInference: am.possibleInference, supportCategory: am.supportCategory, resolutionGuidance: am.resolutionGuidance };
const proposal = uj.updatedProposal ?? uj.proposal ?? null; const proposal = uj.updatedProposal ?? uj.proposal ?? null;
if (proposal) captured.proposal = { addedNodes: proposal.addedNodes ?? [], addedEdges: proposal.addedEdges ?? [], updatedNodes: proposal.updatedNodes ?? [], resolvedUnknownNodeIds: proposal.resolvedUnknownNodeIds ?? [] }; if (proposal) {
captured.proposal = { addedNodes: proposal.addedNodes ?? [], addedEdges: proposal.addedEdges ?? [], updatedNodes: proposal.updatedNodes ?? [], resolvedUnknownNodeIds: proposal.resolvedUnknownNodeIds ?? [] };
// Mirror the harness structuralActionRequired capture for accepted updates // Structural fields live inside graphUpdate schema, not at root level.
const sar = uj.structuralActionRequired; const sar = proposal.structuralActionRequired;
if (sar === undefined || sar === null) { if (sar === undefined || sar === null) {
captured.structuralActionRequired = null; captured.structuralActionRequired = null;
} else { } else {
captured.structuralActionRequired = sar; captured.structuralActionRequired = sar;
}
} }
const am = proposal?.answerMeaning ?? null;
if (am) captured.answerMeaning = { userSupportedMeaning: am.userSupportedMeaning, possibleInference: am.possibleInference, supportCategory: am.supportCategory, resolutionGuidance: am.resolutionGuidance };
const sq = uj.selectedQuestion ?? null; const sq = uj.selectedQuestion ?? null;
if (sq && typeof sq === "object") captured.selectedQuestion = { question: sq.question, nodeId: sq.nodeId }; if (sq && typeof sq === "object") captured.selectedQuestion = { question: sq.question, nodeId: sq.nodeId };