feat: Phase 2-5 UX enhancements — recovery cards, session persistence, summary panel, contract backlog
Phase 2: Recovery state components (ProviderUnavailableCard, MalformedResponseCard, UnexpectedStateCard, ContinueLaterBanner) with automatic error detection for provider/network/malformed/unexpected states. Phase 3: Session persistence via sessionStorage — save after each successful turn, restore on mount, clear on restart/reset. Continuelater banner shown when session is restored. Phase 4: InvestigationSummaryPanel component displaying current status, understanding summary, questions answered/remaining, investigation timestamps. Phase 5: docs/reasoning-contract-backlog.md documenting all mocked fields (60+ rows across 7 categories) with feature/UI need/mock/desired output/stage/notes columns. Also: wired onRestart through ReasoningWorkspace → ScenarioForm, fixed getErrorType scope issues, removed broken window.__restartInvestigation.
This commit is contained in:
@@ -4,6 +4,8 @@
|
||||
* Pure ESM + browser-compatible (no require(), no Node-only APIs).
|
||||
*/
|
||||
|
||||
import { buildScenarioFixture, AVAILABLE_SCENARIOS } from "@/lib/mocks/scenarios.js";
|
||||
|
||||
/* ── helpers ─────────────────────────────────────────────── */
|
||||
|
||||
function getMockFlag() {
|
||||
@@ -23,9 +25,9 @@ function getScenario() {
|
||||
return s || "";
|
||||
}
|
||||
|
||||
/* ── node / edge factories ───────────────────────────────── */
|
||||
/* ── node / edge factories (re-exported for scenario files) ─ */
|
||||
|
||||
function mkNode(id, label, opts) {
|
||||
export function mkNode(id, label, opts) {
|
||||
var kind = (opts && opts.kind) || "unknown";
|
||||
var status = (opts && opts.status) || (kind === "unknown" ? "unknown" : "known");
|
||||
var confidence = (opts && opts.confidence) || "low";
|
||||
@@ -37,115 +39,17 @@ function mkNode(id, label, opts) {
|
||||
};
|
||||
}
|
||||
|
||||
function mkEdge(id, a, b, rel) {
|
||||
export function mkEdge(id, a, b, rel) {
|
||||
var r = rel || "supports";
|
||||
return { id:id, fromNodeId:a, toNodeId:b, relationship:r, confidence:"medium", description:a+" -> "+b };
|
||||
}
|
||||
|
||||
/* ── turn descriptors ────────────────────────────────────── */
|
||||
|
||||
var T0_nodes = [
|
||||
mkNode("obs-1","Complaints increased by 35%",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("obs-2","Production increased by 40%",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("state-1","Current situation",{kind:"state",status:"provisional",confidence:"medium"}),
|
||||
mkNode("rel-1","Complaint and production trends are related",{kind:"relationship",status:"known",confidence:"medium"}),
|
||||
mkNode("u-1","Whether the two figures cover the same period"),
|
||||
mkNode("u-2","Whether the percentage changes use comparable baselines"),
|
||||
mkNode("u-3","Whether complaints increased faster than production on a per-unit basis")
|
||||
];
|
||||
var T0_edges = [mkEdge("e-1","obs-1","state-1"),mkEdge("e-2","obs-2","state-1"),mkEdge("e-3","rel-1","u-1")];
|
||||
|
||||
var T1_nodes = [
|
||||
mkNode("obs-1","Complaints increased by 35%",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("obs-2","Production increased by 40%",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("obs-3","Both figures cover the same three-month period",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("state-1","Current situation",{kind:"state",status:"provisional",confidence:"medium"}),
|
||||
mkNode("rel-1","Complaint and production trends are related",{kind:"relationship",status:"known",confidence:"medium"}),
|
||||
mkNode("u-1","Whether the two figures cover the same period",{status:"resolved",confidence:"high"}),
|
||||
mkNode("u-2","Whether the percentage changes use comparable baselines"),
|
||||
mkNode("u-4","Whether reporting practices changed")
|
||||
];
|
||||
var T1_edges = [mkEdge("e-1","obs-1","state-1"),mkEdge("e-2","obs-2","state-1"),mkEdge("e-3","rel-1","u-1"),mkEdge("e-4","obs-3","u-1"),mkEdge("e-5","rel-1","u-2")];
|
||||
|
||||
var T2_nodes = [
|
||||
mkNode("obs-1","Complaints increased by 35%",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("obs-2","Production increased by 40%",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("obs-3","Both figures cover the same three-month period",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("obs-4","Complaints rose from 100 to 135; production rose from 1,000 to 1,400 units",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("state-1","Current situation",{kind:"state",status:"provisional",confidence:"medium"}),
|
||||
mkNode("rel-1","Complaint and production trends are related",{kind:"relationship",status:"known",confidence:"medium"}),
|
||||
mkNode("u-1","Whether the two figures cover the same period",{status:"resolved",confidence:"high"}),
|
||||
mkNode("u-2","Whether the percentage changes use comparable baselines",{status:"resolved",confidence:"medium"}),
|
||||
mkNode("u-3","Whether complaints increased faster than production on a per-unit basis"),
|
||||
mkNode("u-4","Whether reporting practices changed")
|
||||
];
|
||||
var T2_edges = [mkEdge("e-1","obs-1","state-1"),mkEdge("e-2","obs-2","state-1"),mkEdge("e-3","rel-1","u-1"),mkEdge("e-4","obs-3","u-1"),mkEdge("e-5","rel-1","u-2"),mkEdge("e-6","obs-4","u-2"),mkEdge("e-7","obs-4","u-3")];
|
||||
|
||||
var T3_nodes = [
|
||||
mkNode("obs-1","Complaints increased by 35%",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("obs-2","Production increased by 40%",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("obs-3","Both figures cover the same three-month period",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("obs-4","Complaints rose from 100 to 135; production rose from 1,000 to 1,400 units",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("obs-5","The complaint rate fell from 10 per 1,000 to about 9.6 per 1,000",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("state-1","Current situation",{kind:"state",status:"provisional",confidence:"medium"}),
|
||||
mkNode("rel-1","Complaint and production trends are related",{kind:"relationship",status:"known",confidence:"medium"}),
|
||||
mkNode("u-1","Whether the two figures cover the same period",{status:"resolved",confidence:"high"}),
|
||||
mkNode("u-2","Whether the percentage changes use comparable baselines",{status:"resolved",confidence:"medium"}),
|
||||
mkNode("u-3","Whether complaints increased faster than production on a per-unit basis",{status:"resolved",confidence:"high"}),
|
||||
mkNode("u-4","Whether reporting practices changed")
|
||||
];
|
||||
var T3_edges = [mkEdge("e-1","obs-1","state-1"),mkEdge("e-2","obs-2","state-1"),mkEdge("e-3","rel-1","u-1"),mkEdge("e-4","obs-3","u-1"),mkEdge("e-5","rel-1","u-2"),mkEdge("e-6","obs-4","u-2"),mkEdge("e-7","obs-4","u-3"),mkEdge("e-8","obs-5","u-3")];
|
||||
|
||||
var T4_nodes = [
|
||||
mkNode("obs-1","Complaints increased by 35%",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("obs-2","Production increased by 40%",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("obs-3","Both figures cover the same three-month period",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("obs-4","Complaints rose from 100 to 135; production rose from 1,000 to 1,400 units",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("obs-5","The complaint rate fell from 10 per 1,000 to about 9.6 per 1,000",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("obs-6","Same complaint categories and reporting rules were used throughout",{kind:"observation",status:"known",confidence:"high"}),
|
||||
mkNode("state-1","Current situation",{kind:"state",status:"provisional",confidence:"medium"}),
|
||||
mkNode("rel-1","Complaint and production trends are related",{kind:"relationship",status:"known",confidence:"medium"}),
|
||||
mkNode("u-1","Whether the two figures cover the same period",{status:"resolved",confidence:"high"}),
|
||||
mkNode("u-2","Whether the percentage changes use comparable baselines",{status:"resolved",confidence:"medium"}),
|
||||
mkNode("u-3","Whether complaints increased faster than production on a per-unit basis",{status:"resolved",confidence:"high"}),
|
||||
mkNode("u-4","Whether reporting practices changed",{status:"resolved",confidence:"high"})
|
||||
];
|
||||
var T4_edges = [mkEdge("e-1","obs-1","state-1"),mkEdge("e-2","obs-2","state-1"),mkEdge("e-3","rel-1","u-1"),mkEdge("e-4","obs-3","u-1"),mkEdge("e-5","rel-1","u-2"),mkEdge("e-6","obs-4","u-2"),mkEdge("e-7","obs-4","u-3"),mkEdge("e-8","obs-5","u-3"),mkEdge("e-9","rel-1","u-4"),mkEdge("e-10","obs-6","u-4")];
|
||||
|
||||
var TURNS = [
|
||||
{ nodes:T0_nodes, edges:T0_edges, resolved:[], active:"u-1", question:{ nodeId:"u-1", question:"Were the complaint and production figures measured over the same period?", reason:"If the figures cover different periods, comparing their movement could be misleading.", reasoningPattern:"comparability_check" }, noQReason:null, summary:"Two changes have been reported, but we do not yet know whether the figures are directly comparable." },
|
||||
{ nodes:T1_nodes, edges:T1_edges, resolved:["u-1"], active:"u-2", question:{ nodeId:"u-2", question:"Were both percentages calculated from comparable baseline counts?", reason:"Establishing the reference point for both figures is essential before evaluating their relationship.", reasoningPattern:"baseline_comparability" }, noQReason:null, summary:"The timing basis is now clear." },
|
||||
{ nodes:T2_nodes, edges:T2_edges, resolved:["u-1","u-2"], active:"u-3", question:{ nodeId:"u-3", question:"Did the complaint rate per unit produced improve or worsen?", reason:"Absolute changes in complaints and production are known; the relative rate determines whether the situation improved.", reasoningPattern:"rate_comparison" }, noQReason:null, summary:"The absolute baselines are now known." },
|
||||
{ nodes:T3_nodes, edges:T3_edges, resolved:["u-1","u-2","u-3"], active:"u-4", question:{ nodeId:"u-4", question:"Was there any change in how complaints were recorded during the period?", reason:"The per-unit rate changed; we need to rule out recording artifacts before concluding a genuine shift.", reasoningPattern:"artifact_exclusion" }, noQReason:null, summary:"The per-unit complaint rate improved slightly." },
|
||||
{ nodes:T4_nodes, edges:T4_edges, resolved:["u-1","u-2","u-3","u-4"], active:null, question:null, noQReason:"All required investigation areas are resolved.", summary:"The figures cover the same period, use comparable baselines, show an improved complaint rate, and were recorded consistently." }
|
||||
];
|
||||
|
||||
/* ── fixture builders ─────────────────────────────────────── */
|
||||
|
||||
function buildDefaultFixture(idx) {
|
||||
var d = TURNS[Math.min(idx, TURNS.length - 1)];
|
||||
return {
|
||||
success:true,
|
||||
situationGraph: { centralStatement:"Complaints increased by 35% while production increased by 40%.", currentSummary:d.summary, nodes:d.nodes, edges:d.edges, activeUnknownNodeId:d.active, resolvedNodeIds:d.resolved },
|
||||
selectedQuestion: d.question || null,
|
||||
noQuestionReason: d.noQReason,
|
||||
newlySurfacedNodeIds: [],
|
||||
diagnostics: { promptVersion:"v0.4", modelName:"mock-ollama", responseDurationMs:0, validationStatus:"valid", nodeCount:d.nodes.length, edgeCount:d.edges.length, unknownSelectionExplanation: d.active ? { status:"single_candidate" } : null }
|
||||
};
|
||||
}
|
||||
|
||||
function buildErrorFixture() {
|
||||
return { success:false, situationGraph:null, selectedQuestion:null, noQuestionReason:null, newlySurfacedNodeIds:[], error:"Mock provider error: structured response unavailable.", diagnostics:null };
|
||||
}
|
||||
|
||||
function buildUpdateFixture(scenarioName, idx) {
|
||||
if (scenarioName === "error") {
|
||||
return { success:false, stage:"provider", error:"Mock provider error: structured response unavailable.", providerErrors:["Mock provider error: structured response unavailable."], updatedSituationGraph:null, selectedQuestion:null, affectedNodeIds:[], resolvedUnknownNodeIds:[], changesApplied:null, summary:null, diagnostics:{ promptVersion:"v0.4", modelName:"mock-ollama", responseDurationMs:0 } };
|
||||
}
|
||||
var f = scenarioName === "complete" ? buildDefaultFixture(4) : buildDefaultFixture(idx);
|
||||
return { success:true, stage:"update_applied", updatedSituationGraph:f.situationGraph, selectedQuestion:f.selectedQuestion, affectedNodeIds:[], resolvedUnknownNodeIds:(f.situationGraph.resolvedNodeIds||[]).slice(), changesApplied:{ addedNodeCount:0, updatedNodeCount:0, addedEdgeCount:0, removedEdgeCount:0 }, summary:f.situationGraph.currentSummary||null, diagnostics:f.diagnostics };
|
||||
}
|
||||
|
||||
/* ── delay shim (browser only) ──────────────────────────── */
|
||||
|
||||
function delay(ms) {
|
||||
@@ -163,21 +67,51 @@ function handleStartCase(scenario) {
|
||||
_turnIndex = 0;
|
||||
var scenarioName = getScenario();
|
||||
if (scenarioName === "error") return Promise.resolve({ success:true, data:buildErrorFixture() });
|
||||
return Promise.resolve({ success:true, data:buildDefaultFixture(0) });
|
||||
return Promise.resolve({ success:true, data: buildScenarioFixture(scenarioName, 0) || buildDefaultFallback(0) });
|
||||
}
|
||||
|
||||
function handleUpdateCase(data) {
|
||||
var scenarioName = getScenario();
|
||||
if (scenarioName === "error") return delay(getDelay()).then(function() {
|
||||
return Promise.resolve({ success:true, data:buildUpdateFixture("error",0) });
|
||||
return Promise.resolve({ success:true, data:{ success:false, stage:"provider", error:"Mock provider error: structured response unavailable.", providerErrors:["Mock provider error: structured response unavailable."], updatedSituationGraph:null, selectedQuestion:null, affectedNodeIds:[], resolvedUnknownNodeIds:[], changesApplied:null, summary:null, diagnostics:{ promptVersion:"v0.4", modelName:"mock-ollama", responseDurationMs:0 } } });
|
||||
});
|
||||
_turnIndex++;
|
||||
var idx = scenarioName === "complete" ? 4 : Math.min(_turnIndex, 4);
|
||||
return delay(getDelay()).then(function() {
|
||||
return Promise.resolve({ success:true, data:buildUpdateFixture(scenarioName, idx) });
|
||||
var fixture = buildScenarioFixture(scenarioName, _turnIndex);
|
||||
if (fixture) {
|
||||
return Promise.resolve({
|
||||
success:true, stage:"update_applied",
|
||||
updatedSituationGraph:fixture.situationGraph,
|
||||
selectedQuestion:fixture.selectedQuestion,
|
||||
affectedNodeIds:[],
|
||||
resolvedUnknownNodeIds:(fixture.situationGraph.resolvedNodeIds||[]).slice(),
|
||||
changesApplied:{ addedNodeCount:0, updatedNodeCount:0, addedEdgeCount:0, removedEdgeCount:0 },
|
||||
summary:fixture.situationGraph.currentSummary||null,
|
||||
diagnostics:fixture.diagnostics
|
||||
});
|
||||
}
|
||||
// Fallback to original default if scenario not found
|
||||
return Promise.resolve({ success:true, data:buildUpdateFallback(scenarioName) });
|
||||
});
|
||||
}
|
||||
|
||||
/* ── fallback for when scenarios.js is not available ─────── */
|
||||
|
||||
var _fallbackTurns = [
|
||||
{ nodes:[mkNode("obs-1","Complaints increased by 35%",{kind:"observation",status:"known",confidence:"high"}),mkNode("obs-2","Production increased by 40%",{kind:"observation",status:"known",confidence:"high"}),mkNode("state-1","Current situation",{kind:"state",status:"provisional",confidence:"medium"})], edges:[mkEdge("e-1","obs-1","state-1"),mkEdge("e-2","obs-2","state-1")], resolved:[], active:"u-1", question:{ nodeId:"u-1", question:"Were the complaint and production figures measured over the same period?", reason:"If different periods, comparing movement could be misleading.", reasoningPattern:"comparability_check" }, noQReason:null, summary:"Two changes have been reported, but we do not yet know whether the figures are directly comparable." },
|
||||
{ nodes:[mkNode("obs-1","Complaints increased by 35%",{kind:"observation",status:"known",confidence:"high"}),mkNode("obs-2","Production increased by 40%",{kind:"observation",status:"known",confidence:"high"}),mkNode("obs-3","Both figures cover the same three-month period",{kind:"observation",status:"known",confidence:"high"}),mkNode("state-1","Current situation",{kind:"state",status:"provisional",confidence:"medium"})], edges:[mkEdge("e-1","obs-1","state-1"),mkEdge("e-2","obs-2","state-1"),mkEdge("e-3","obs-3","u-1")], resolved:["u-1"], active:"u-2", question:{ nodeId:"u-2", question:"Were both percentages calculated from comparable baseline counts?", reason:"Establishing the reference point is essential.", reasoningPattern:"baseline_comparability" }, noQReason:null, summary:"The timing basis is now clear." }
|
||||
];
|
||||
|
||||
function buildDefaultFallback(idx) {
|
||||
var d = _fallbackTurns[Math.min(idx, _fallbackTurns.length - 1)];
|
||||
return { success:true, situationGraph:{ centralStatement:"Complaints increased by 35% while production increased by 40%.", currentSummary:d.summary, nodes:d.nodes, edges:d.edges, activeUnknownNodeId:d.active, resolvedNodeIds:d.resolved }, selectedQuestion:d.question||null, noQuestionReason:d.noQReason, newlySurfacedNodeIds:[], diagnostics:{ promptVersion:"v0.4", modelName:"mock-ollama", responseDurationMs:0, validationStatus:"valid", nodeCount:d.nodes.length, edgeCount:d.edges.length } };
|
||||
}
|
||||
|
||||
function buildUpdateFallback(scenarioName) {
|
||||
var f = buildDefaultFallback(Math.min(_turnIndex, _fallbackTurns.length - 1));
|
||||
return { success:true, stage:"update_applied", updatedSituationGraph:f.situationGraph, selectedQuestion:f.selectedQuestion, affectedNodeIds:[], resolvedUnknownNodeIds:(f.situationGraph.resolvedNodeIds||[]).slice(), changesApplied:{ addedNodeCount:0, updatedNodeCount:0, addedEdgeCount:0, removedEdgeCount:0 }, summary:f.situationGraph.currentSummary||null, diagnostics:f.diagnostics };
|
||||
}
|
||||
|
||||
/* ── public intercept function ──────────────────────────── */
|
||||
|
||||
export async function mockFetch(url, options) {
|
||||
@@ -198,3 +132,5 @@ export async function mockFetch(url, options) {
|
||||
|
||||
return fetch(url, options);
|
||||
}
|
||||
|
||||
export { AVAILABLE_SCENARIOS };
|
||||
|
||||
Reference in New Issue
Block a user