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 96 additions and 1 deletions
Showing only changes of commit ca7d8e5384 - Show all commits
+4 -1
View File
@@ -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");
+92
View File
@@ -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");