feat(confidence-engine): use server investigation persistence
This commit is contained in:
@@ -6,7 +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, restartInvestigation, clearInvestigation } from "@/lib/storage/investigation-storage";
|
||||
import { loadInvestigation, saveInvestigation, restartInvestigation } 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";
|
||||
@@ -303,6 +303,16 @@ export default function ScenarioForm({ investigationId, onNavigateToReport }) {
|
||||
|
||||
/* ── v2 findings from focused contributions ─────────────── */
|
||||
const [findings, setFindings] = useState([]);
|
||||
const [hydrated, setHydrated] = useState(false);
|
||||
|
||||
function persist(snapshot) {
|
||||
void saveInvestigation(snapshot).catch((error) => console.error("Investigation autosave failed", error));
|
||||
}
|
||||
|
||||
function restartPersistedInvestigation() {
|
||||
void restartInvestigation(investigationId)
|
||||
.catch((error) => console.error("Investigation restart failed", error));
|
||||
}
|
||||
|
||||
function appendFinding(finding) {
|
||||
setFindings((prev) => {
|
||||
@@ -544,8 +554,11 @@ export default function ScenarioForm({ investigationId, onNavigateToReport }) {
|
||||
/* Restore persisted session on mount ─────────── */
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined") return;
|
||||
const saved = investigationId ? loadInvestigation(investigationId) : null;
|
||||
if (!saved) return;
|
||||
let active = true;
|
||||
(async () => {
|
||||
try {
|
||||
const saved = investigationId ? await loadInvestigation(investigationId) : null;
|
||||
if (!active || !saved) return;
|
||||
|
||||
const hasGraph = Boolean(saved.situationGraph);
|
||||
|
||||
@@ -569,7 +582,12 @@ export default function ScenarioForm({ investigationId, onNavigateToReport }) {
|
||||
if (hasGraph) {
|
||||
setStatus("success");
|
||||
}
|
||||
}, []);
|
||||
} finally {
|
||||
if (active) setHydrated(true);
|
||||
}
|
||||
})();
|
||||
return () => { active = false; };
|
||||
}, [investigationId]);
|
||||
|
||||
/* ── Canonical autosave — persist whenever state changes (Phase 2) ── */
|
||||
|
||||
@@ -578,9 +596,9 @@ export default function ScenarioForm({ investigationId, onNavigateToReport }) {
|
||||
// Guard: no valid investigation yet → skip autosave during idle/start flows.
|
||||
// Also prevents overwriting an existing saved investigation with the initial
|
||||
// empty state of a fresh ScenarioForm instance (hydration race guard).
|
||||
if (!result?.situationGraph) return;
|
||||
if (!hydrated || !result?.situationGraph) return;
|
||||
|
||||
void saveInvestigation({
|
||||
persist({
|
||||
id: investigationId,
|
||||
scenario,
|
||||
situationGraph: result.situationGraph,
|
||||
@@ -600,7 +618,7 @@ export default function ScenarioForm({ investigationId, onNavigateToReport }) {
|
||||
focusedContributions,
|
||||
findings,
|
||||
investigationReport,
|
||||
investigationRevision,
|
||||
investigationRevision, hydrated,
|
||||
]);
|
||||
|
||||
/* Restore facilitator dismiss preference (Experiment 05) ─── */
|
||||
@@ -680,7 +698,7 @@ export default function ScenarioForm({ investigationId, onNavigateToReport }) {
|
||||
setResult(normalised);
|
||||
/* ── v0.59a — provenance: first meaningful change sets revision to 1 ── */
|
||||
setInvestigationRevision(1);
|
||||
saveInvestigation({ id: investigationId, scenario, situationGraph: normalised.situationGraph, selectedQuestion: normalised.selectedQuestion, summary: data.summary ?? null, updatedAt: new Date().toISOString(), focusedContributions, findings: [], investigationReport, investigationRevision: 1 });
|
||||
persist({ id: investigationId, scenario, situationGraph: normalised.situationGraph, selectedQuestion: normalised.selectedQuestion, summary: data.summary ?? null, updatedAt: new Date().toISOString(), focusedContributions, findings: [], investigationReport, investigationRevision: 1 });
|
||||
} else {
|
||||
setStatus("error");
|
||||
setCurrentUnderstanding(data.summary ?? null);
|
||||
@@ -767,7 +785,7 @@ export default function ScenarioForm({ investigationId, onNavigateToReport }) {
|
||||
/* ── v0.59a — provenance: meaningful change advances revision ── */
|
||||
const nextRev = (investigationRevision ?? 0) + 1;
|
||||
setInvestigationRevision(nextRev);
|
||||
saveInvestigation({ id: investigationId, scenario, situationGraph: nextGraph, selectedQuestion: normaliseUpdateSelectedQuestion(outcome.selectedQuestion), summary: currentUnderstanding, updatedAt: new Date().toISOString(), focusedContributions, findings: nextFindings, investigationReport, investigationRevision: nextRev });
|
||||
persist({ id: investigationId, scenario, situationGraph: nextGraph, selectedQuestion: normaliseUpdateSelectedQuestion(outcome.selectedQuestion), summary: currentUnderstanding, updatedAt: new Date().toISOString(), focusedContributions, findings: nextFindings, investigationReport, investigationRevision: nextRev });
|
||||
} else {
|
||||
setUpdateStatus("error");
|
||||
setUpdateError(outcome);
|
||||
@@ -947,7 +965,7 @@ export default function ScenarioForm({ investigationId, onNavigateToReport }) {
|
||||
setResult((prev) => ({ ...(prev ?? {}), situationGraph: nextGraph }));
|
||||
}}
|
||||
onRestart={() => {
|
||||
restartInvestigation(investigationId);
|
||||
restartPersistedInvestigation();
|
||||
setInvestigationRevision(0);
|
||||
setStatus("idle");
|
||||
setResult(null);
|
||||
@@ -968,7 +986,7 @@ export default function ScenarioForm({ investigationId, onNavigateToReport }) {
|
||||
|
||||
{/* ── Continue later banner when session was restored ── */}
|
||||
{status === "success" && result?.updatedAt && (
|
||||
<ContinueLaterBanner onRestart={() => { restartInvestigation(investigationId); setInvestigationRevision(0); setStatus("idle"); setResult(null); setAnswer(""); setUpdateStatus("idle"); setCurrentUnderstanding(null); setFocusedContributions([]); setFindings([]); }} />
|
||||
<ContinueLaterBanner onRestart={() => { restartPersistedInvestigation(); setInvestigationRevision(0); setStatus("idle"); setResult(null); setAnswer(""); setUpdateStatus("idle"); setCurrentUnderstanding(null); setFocusedContributions([]); setFindings([]); }} />
|
||||
)}
|
||||
|
||||
{/* Reset button after successful analysis */}
|
||||
@@ -976,7 +994,7 @@ export default function ScenarioForm({ investigationId, onNavigateToReport }) {
|
||||
<div className="text-center">
|
||||
<button
|
||||
onClick={() => {
|
||||
restartInvestigation(investigationId);
|
||||
restartPersistedInvestigation();
|
||||
setInvestigationRevision(0);
|
||||
setScenario("");
|
||||
setStatus("idle");
|
||||
|
||||
Reference in New Issue
Block a user