From bc35e0525305732472130bae51930ee8dc1dd99b Mon Sep 17 00:00:00 2001 From: robbond Date: Thu, 27 Aug 2026 15:44:58 +0100 Subject: [PATCH] refactor(confidence-engine): migrate scenario persistence to storage provider --- components/scenario-form.jsx | 35 +- .../storage/scenario-form-persistence.test.js | 331 ++++++++++++++++++ 2 files changed, 339 insertions(+), 27 deletions(-) create mode 100644 tests/storage/scenario-form-persistence.test.js diff --git a/components/scenario-form.jsx b/components/scenario-form.jsx index 379a7f1..56d8314 100644 --- a/components/scenario-form.jsx +++ b/components/scenario-form.jsx @@ -6,6 +6,7 @@ import DiagnosticsView from "@/components/diagnostics-view"; import ReasoningWorkspace, { LoadingOverlay, ContinueLaterBanner } from "@/components/reasoning-workspace"; import { mockFetch, AVAILABLE_SCENARIOS } from "@/lib/mocks/confidence-engine/mock-client"; import { deriveFindingsFromContributions, normalizeFindings } from "@/lib/graph/finding-helpers"; +import { loadInvestigation, saveInvestigation, clearInvestigation } from "@/lib/storage/investigation-storage"; /* Compile-time env resolution — NEXT_PUBLIC_ vars are injected by Next.js at build */ const MOCK_ENABLED = process.env.NEXT_PUBLIC_CONFIDENCE_ENGINE_MOCKS === "true"; @@ -192,26 +193,6 @@ export function UpdateErrorPanel({ updateError }) { export { INITIAL_MESSAGES, UPDATE_MESSAGES, useLoadingStatus }; -// ── Session key ──────────────────────────────────────────────── -const SESSION_KEY = "confidence-engine-session"; - -function getSession() { - if (typeof sessionStorage === "undefined") return null; - try { - const raw = sessionStorage.getItem(SESSION_KEY); - return raw ? JSON.parse(raw) : null; - } catch (_) { return null; } -} - -function saveSession(state) { - if (typeof sessionStorage === "undefined") return; - try { sessionStorage.setItem(SESSION_KEY, JSON.stringify(state)); } catch (_) {} -} - -function clearSession() { - if (typeof sessionStorage === "undefined") return; - try { sessionStorage.removeItem(SESSION_KEY); } catch (_) {} -} /** * Derives whether the current component state represents a valid investigation @@ -310,10 +291,10 @@ export default function ScenarioForm() { // Delegated to the exported utility below. const validCtx = hasValidInvestigationContext(result, status, scenario); - /* Restore persisted session on mount (Phase 3) ─────────── */ + /* Restore persisted session on mount ─────────── */ useEffect(() => { if (typeof window === "undefined") return; - const saved = getSession(); + const saved = loadInvestigation(); if (!saved) return; const hasGraph = Boolean(saved.situationGraph); @@ -407,7 +388,7 @@ export default function ScenarioForm() { setCurrentUnderstanding(data.summary ?? null); const normalised = normaliseStartResult(data); setResult(normalised); - saveSession({ scenario, situationGraph: normalised.situationGraph, selectedQuestion: normalised.selectedQuestion, summary: data.summary ?? null, updatedAt: new Date().toISOString(), focusedContributions, findings: [] }); + saveInvestigation({ scenario, situationGraph: normalised.situationGraph, selectedQuestion: normalised.selectedQuestion, summary: data.summary ?? null, updatedAt: new Date().toISOString(), focusedContributions, findings: [] }); } else { setStatus("error"); setCurrentUnderstanding(data.summary ?? null); @@ -480,7 +461,7 @@ export default function ScenarioForm() { })); setAnswer(""); // Persist after successful update turn — include findings - saveSession({ scenario, situationGraph: outcome.updatedSituationGraph, selectedQuestion: normaliseUpdateSelectedQuestion(outcome.selectedQuestion), summary: outcome.summary ?? currentUnderstanding, updatedAt: new Date().toISOString(), focusedContributions, findings: newFindings }); + saveInvestigation({ scenario, situationGraph: outcome.updatedSituationGraph, selectedQuestion: normaliseUpdateSelectedQuestion(outcome.selectedQuestion), summary: outcome.summary ?? currentUnderstanding, updatedAt: new Date().toISOString(), focusedContributions, findings: newFindings }); } else { setUpdateStatus("error"); setUpdateError(outcome); @@ -642,7 +623,7 @@ export default function ScenarioForm() { onUpdateFindingDisposition={updateFindingDisposition} onUpdateFindingProposition={updateFindingProposition} onRestart={() => { - clearSession(); + clearInvestigation(); setStatus("idle"); setResult(null); setAnswer(""); @@ -662,7 +643,7 @@ export default function ScenarioForm() { {/* ── Continue later banner when session was restored ── */} {status === "success" && result?.updatedAt && ( - { clearSession(); setStatus("idle"); setResult(null); setAnswer(""); setUpdateStatus("idle"); setCurrentUnderstanding(null); setFocusedContributions([]); setFindings([]); }} /> + { clearInvestigation(); setStatus("idle"); setResult(null); setAnswer(""); setUpdateStatus("idle"); setCurrentUnderstanding(null); setFocusedContributions([]); setFindings([]); }} /> )} {/* Reset button after successful analysis */} @@ -670,7 +651,7 @@ export default function ScenarioForm() {