feat(confidence-engine): promote focused learning on done

This commit is contained in:
2026-08-28 10:48:45 +01:00
parent e221bd3bf8
commit f1bd91faf8
4 changed files with 352 additions and 1 deletions
+40 -1
View File
@@ -5,7 +5,7 @@ import { useState, useRef, useMemo } from "react";
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 { deriveFindingsFromContributions, normalizeFindings, produceFindingInformedSummary } 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 */
@@ -267,6 +267,43 @@ export default function ScenarioForm() {
);
}
/**
* v0.49 promotion seam — deterministic Current Understanding update
* triggered by "Done for now" activity boundary (no case/update, no LLM).
*/
function handleDoneForNowPromotion(targetNodeId) {
if (!targetNodeId || !findings?.length) return;
// Filter eligible findings for this specific target only.
const eligible = findings.filter(
(f) => f.originatingTargetNodeId === targetNodeId && (f.userDisposition === null || f.userDisposition === "agree"),
);
if (eligible.length === 0) return;
// Determine the base: use currentUnderstanding if available, else empty string.
const baseSummary = currentUnderstanding ?? "";
// Deterministic producer — no LLM, no API.
const newSummary = produceFindingInformedSummary(baseSummary, eligible);
// Idempotence guard: skip if summary is unchanged (no new eligible findings
// beyond what's already in the current Evidence block).
if (newSummary === baseSummary) return;
// Avoid duplicate evidence propositions from repeated promotion.
const existingEvidenceMatch = baseSummary.match(/Evidence:\s*\[([^\]]+)\]/);
let isDuplicate = false;
if (existingEvidenceMatch) {
const existingTexts = existingEvidenceMatch[1].split("; ").map((t) => t.trim());
isDuplicate = eligible.every((f) => existingTexts.includes(f.proposition));
}
if (isDuplicate) return;
// Mutate the SAME summary/state that autosave already persists.
setCurrentUnderstanding(newSummary);
}
function appendFocusedContribution(contribution) {
// Derive a single stored contribution object and use it for BOTH
// contribution storage AND Finding derivation so the same identity
@@ -649,6 +686,8 @@ export default function ScenarioForm() {
findings={findings}
onUpdateFindingDisposition={updateFindingDisposition}
onUpdateFindingProposition={updateFindingProposition}
/* v0.49 done-for-now promotion seam */
onSummaryUpdate={handleDoneForNowPromotion}
onRestart={() => {
clearInvestigation();
setStatus("idle");