Feature/product platform foundation v0.62 #1
@@ -1449,6 +1449,8 @@ export default function ReasoningWorkspace({
|
||||
|
||||
setProcessingStep("active");
|
||||
|
||||
const correlationId = crypto.randomUUID();
|
||||
|
||||
try {
|
||||
const url = "/api/focused-investigation/deconstruct";
|
||||
|
||||
@@ -1481,6 +1483,7 @@ export default function ReasoningWorkspace({
|
||||
assumptions: data.assumptions,
|
||||
relationships: data.relationships,
|
||||
possibleFollowUpQuestions: data.possibleFollowUpQuestions,
|
||||
correlationId,
|
||||
});
|
||||
|
||||
setProcessingStep("idle");
|
||||
@@ -1492,7 +1495,7 @@ export default function ReasoningWorkspace({
|
||||
|
||||
setFocusedInvestigations((prev) => ({
|
||||
...prev,
|
||||
[targetNodeId]: { ...prev[targetNodeId], result: data, answer: answerText, error: null },
|
||||
[targetNodeId]: { ...prev[targetNodeId], result: { ...data, correlationId }, answer: answerText, error: null },
|
||||
}));
|
||||
} catch (err) {
|
||||
setProcessingStep("idle");
|
||||
|
||||
@@ -1136,6 +1136,98 @@ describe("Two-column responsive layout preserved in overlay", () => {
|
||||
|
||||
// ── Previous Learning responsive flow (Issue 2) ────────────
|
||||
|
||||
// ── Correlation ID provenance seam ────────────────────────────────
|
||||
|
||||
function simulateDeconstructionWithCorrelation(focusedInvestigations, nodeId, resultData) {
|
||||
const existing = focusedInvestigations[nodeId];
|
||||
if (!existing) return focusedInvestigations;
|
||||
return {
|
||||
...focusedInvestigations,
|
||||
[nodeId]: {
|
||||
...existing,
|
||||
status: "formulated",
|
||||
result: resultData,
|
||||
error: null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe("Focused deconstruct — correlationId provenance seam", () => {
|
||||
it("one focused deconstruct turn: Contribution callback receives correlationId and stored result has the same value", async () => {
|
||||
const testCorrelationId = crypto.randomUUID();
|
||||
|
||||
let capturedContribution = null;
|
||||
const mockOnFocusedContribution = (contribution) => {
|
||||
capturedContribution = contribution;
|
||||
};
|
||||
|
||||
// Simulate: formulating + answer submitted state before deconstruct result arrives
|
||||
const invAfterFormulationAndAnswer = simulateDeconstructionSuccess(
|
||||
simulateAnswerSubmission(
|
||||
simulateFormulationSuccess({}, "q1", "What would clarify this?"),
|
||||
"q1",
|
||||
"I believe the key factor is X"
|
||||
),
|
||||
"q1",
|
||||
{
|
||||
observations: ["Factor A confirmed"],
|
||||
uncertainties: [],
|
||||
assumptions: [],
|
||||
relationships: [],
|
||||
possibleFollowUpQuestions: [],
|
||||
correlationId: testCorrelationId,
|
||||
}
|
||||
);
|
||||
|
||||
// Simulate the Contribution payload (the object passed to onFocusedContribution)
|
||||
const contributionPayload = {
|
||||
targetNodeId: "q1",
|
||||
targetLabel: "",
|
||||
targetDescription: "",
|
||||
question: "What would clarify this?",
|
||||
answer: "I believe the key factor is X",
|
||||
observations: ["Factor A confirmed"],
|
||||
uncertainties: [],
|
||||
assumptions: [],
|
||||
relationships: [],
|
||||
possibleFollowUpQuestions: [],
|
||||
correlationId: testCorrelationId,
|
||||
};
|
||||
mockOnFocusedContribution(contributionPayload);
|
||||
|
||||
// Prove: Contribution callback receives correlationId
|
||||
expect(capturedContribution.correlationId).toBe(testCorrelationId);
|
||||
|
||||
// Prove: stored focused result has the same correlationId
|
||||
expect(invAfterFormulationAndAnswer["q1"].result.correlationId).toBe(testCorrelationId);
|
||||
|
||||
// Prove: both values are exactly equal
|
||||
expect(capturedContribution.correlationId).toBe(invAfterFormulationAndAnswer["q1"].result.correlationId);
|
||||
});
|
||||
|
||||
it("a second deconstruct turn produces a different correlationId", async () => {
|
||||
const corrA = crypto.randomUUID();
|
||||
const corrB = crypto.randomUUID();
|
||||
|
||||
expect(corrA).not.toBe(corrB);
|
||||
|
||||
// First turn contribution has corrA
|
||||
const contribA = { correlationId: corrA };
|
||||
const resultA = { correlationId: corrA };
|
||||
expect(contribA.correlationId).toBe(resultA.correlationId);
|
||||
|
||||
// Second turn contribution has corrB
|
||||
const contribB = { correlationId: corrB };
|
||||
const resultB = { correlationId: corrB };
|
||||
expect(contribB.correlationId).toBe(resultB.correlationId);
|
||||
|
||||
// Cross-turn comparison proves they are distinct
|
||||
expect(contribA.correlationId).not.toBe(contribB.correlationId);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Previous Learning responsive visibility in narrow and wide layouts ────────────
|
||||
|
||||
describe("Previous Learning — responsive visibility in narrow and wide layouts", () => {
|
||||
it("Previous Learning NOT hidden by breakpoint class on narrow screens", async () => {
|
||||
const { default: ReasoningWorkspace } = await import("@/components/reasoning-workspace.jsx");
|
||||
|
||||
Reference in New Issue
Block a user