feat(confidence-engine): clarify understanding during Done refresh
This commit is contained in:
@@ -394,7 +394,7 @@ function FocusedQuestionBody({
|
||||
|
||||
// ── Persistent navigation controls (overlay-level, outside content grid) ──
|
||||
|
||||
function FocusedWorkspaceNavigation({ nodeId, doneForNow, isDoneForNowActive, isProcessing }) {
|
||||
function FocusedWorkspaceNavigation({ nodeId, doneForNow, isDoneForNowActive, isProcessing, onImmediateGraphChange }) {
|
||||
const canDoneForNow = Boolean(isDoneForNowActive) && !isProcessing;
|
||||
return (
|
||||
<div className="mt-6 flex items-center justify-end border-t border-gray-200 pt-5">
|
||||
@@ -1303,6 +1303,7 @@ export default function ReasoningWorkspace({
|
||||
scenario,
|
||||
status,
|
||||
updateStatus,
|
||||
cuSynthesisLoading,
|
||||
currentUnderstanding: propUnderstanding,
|
||||
result,
|
||||
answer,
|
||||
@@ -1317,8 +1318,12 @@ export default function ReasoningWorkspace({
|
||||
onUpdateFindingProposition,
|
||||
/* ── v0.49 — done-for-now promotion callback ───────── */
|
||||
onSummaryUpdate,
|
||||
/* ── immediate graph transition (Done acknowledged before async) ── */
|
||||
onImmediateGraphChange,
|
||||
/* ── canonical graph-replacement seam (future Re-open) ── */
|
||||
onSituationGraphChange,
|
||||
/* ── test init seam (no effect → immediate state) ───────── */
|
||||
initialPostAnalyseStatus,
|
||||
}) {
|
||||
const [investigationHistory, setInvestigationHistory] = useState([]);
|
||||
const turnCounter = useRef(0);
|
||||
@@ -1338,7 +1343,7 @@ export default function ReasoningWorkspace({
|
||||
|
||||
// ── RTO.29D — post-Analyse initial reflection surface ─────────
|
||||
const [initialReflectionActive, setInitialReflectionActive] = useState(false);
|
||||
const [postAnalyseStatus, setPostAnalyseStatus] = useState(null);
|
||||
const [postAnalyseStatus, setPostAnalyseStatus] = useState(initialPostAnalyseStatus ?? null);
|
||||
|
||||
// Capture the current selected question at submit time (not from a stale ref)
|
||||
const capturePendingTurn = (selectedQuestion, answerText) => {
|
||||
@@ -1790,11 +1795,21 @@ export default function ReasoningWorkspace({
|
||||
{/* Current Understanding + Situation — independent vertical flow */}
|
||||
<div className="flex gap-3 items-start flex-wrap">
|
||||
{/* Current Understanding — prominent orienting surface */}
|
||||
<div className={`rounded-xl border-[2.5px] border-teal-300/70 bg-gradient-to-b from-teal-50/60 to-white px-8 pt-7 pb-8 shadow-sm flex-1 min-w-0 ${hasGraph ? 'lg:max-w-2xl' : ''}`}>
|
||||
<div className={`rounded-xl border-[2.5px] border-teal-300/70 bg-gradient-to-b from-teal-50/60 to-white px-8 pt-7 pb-8 shadow-sm flex-1 min-w-0 relative ${hasGraph ? 'lg:max-w-2xl' : ''}`}>
|
||||
<h2 className="mb-4 text-[11px] font-bold tracking-[.18em] uppercase text-teal-700/60">
|
||||
Current Understanding
|
||||
</h2>
|
||||
<p className="text-lg leading-relaxed text-gray-800">{propUnderstanding}</p>
|
||||
{cuSynthesisLoading && (
|
||||
<div className="absolute inset-0 cu-skeleton-overlay rounded-xl overflow-hidden flex flex-col items-center justify-center bg-gray-50" role="status" aria-live="polite">
|
||||
<p className="text-sm font-semibold text-teal-800 mb-6 relative z-10 tracking-wide">Clarifying your current understanding…</p>
|
||||
<div className="cu-skeleton-lines w-full max-w-lg px-8 pb-8" aria-hidden="true">
|
||||
{Array.from({ length: 7 }, (_, i) => (
|
||||
<div key={i} className={`cu-skeleton-line mb-3 ${['w-[94%]', 'w-[87%]', 'w-[96%]', 'w-[72%]', 'w-[90%]', 'w-[82%]', 'w-[64%]'][i % 7]}`} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Situation panel during initial reflection */}
|
||||
@@ -1990,7 +2005,7 @@ export default function ReasoningWorkspace({
|
||||
|
||||
{/* Current Understanding — independent row, full-width of left area (cols 1-2) */}
|
||||
{propUnderstanding && hasCurrentSummaryCondition && postAnalyseStatus !== "success" && (
|
||||
<div className="lg:row-start-1 lg:col-span-full rounded-xl border-[2.5px] border-teal-300/70 bg-gradient-to-b from-teal-50/60 to-white px-8 pt-7 pb-8 shadow-sm">
|
||||
<div className="lg:row-start-1 lg:col-span-full rounded-xl border-[2.5px] border-teal-300/70 bg-gradient-to-b from-teal-50/60 to-white px-8 pt-7 pb-8 shadow-sm relative">
|
||||
<CurrentUnderstandingCard currentSummary={graph?.currentSummary || result?.updatedSituationGraph?.currentSummary} plainLanguage={propUnderstanding} />
|
||||
</div>
|
||||
)}
|
||||
@@ -2211,7 +2226,18 @@ export default function ReasoningWorkspace({
|
||||
nodeId={focusedPresentationItemId}
|
||||
doneForNow={() => {
|
||||
if (processingStep === "active") return;
|
||||
/* ── v0.49 — promote eligible focused findings into Current Understanding ─── */
|
||||
/* ── Immediate transition: resolve target node BEFORE async — cuSynthesisLoading also set ─── */
|
||||
if (onImmediateGraphChange && focusedPresentationItemId) {
|
||||
const currentGraph = result?.situationGraph;
|
||||
if (currentGraph) {
|
||||
const resolvedIds = new Set(currentGraph.resolvedNodeIds || []);
|
||||
resolvedIds.add(focusedPresentationItemId);
|
||||
onImmediateGraphChange({
|
||||
...currentGraph,
|
||||
resolvedNodeIds: Array.from(resolvedIds),
|
||||
});
|
||||
}
|
||||
}
|
||||
onSummaryUpdate?.(focusedPresentationItemId);
|
||||
setDoneForNowIds((prev) => [...prev, focusedPresentationItemId]);
|
||||
setFocusedAnswer("");
|
||||
@@ -2221,6 +2247,7 @@ export default function ReasoningWorkspace({
|
||||
}}
|
||||
isDoneForNowActive={Boolean(getFocusedInvestigation()?.question?.trim())}
|
||||
isProcessing={processingStep === "active"}
|
||||
onImmediateGraphChange={onImmediateGraphChange}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -281,6 +281,7 @@ export default function ScenarioForm() {
|
||||
const [updateResult, setUpdateResult] = useState(null);
|
||||
const [lastSubmittedAnswer, setLastSubmittedAnswer] = useState("");
|
||||
const [currentUnderstanding, setCurrentUnderstanding] = useState(null);
|
||||
const [cuSynthesisLoading, setCuSynthesisLoading] = useState(false);
|
||||
const [mockScenario, setMockScenario] = useState("");
|
||||
const [hideFacilitatorOnLanding, setHideFacilitatorOnLanding] = useState(false);
|
||||
|
||||
@@ -357,16 +358,29 @@ export default function ScenarioForm() {
|
||||
* Authoritative graph reconsideration triggered by "Done for now"
|
||||
* activity boundary. Delegates to the exported executeEpisodeDone pipeline.
|
||||
*/
|
||||
async function handleDoneForNowPromotion(targetNodeId) {
|
||||
async function handleDoneForNowPromotion(targetNodeId, onImmediateGraphUpdate) {
|
||||
if (!targetNodeId || !findings?.length) return;
|
||||
|
||||
// In-flight guard: exactly-once enforcement
|
||||
if (doneInProgressRef.current) return;
|
||||
doneInProgressRef.current = true;
|
||||
|
||||
/* ── Immediate client transition — before awaiting async work ── */
|
||||
const preDoneGraph = result?.situationGraph;
|
||||
if (preDoneGraph && onImmediateGraphUpdate) {
|
||||
const immediateResolvedIds = new Set(preDoneGraph.resolvedNodeIds || []);
|
||||
immediateResolvedIds.add(targetNodeId);
|
||||
const immediateGraph = {
|
||||
...preDoneGraph,
|
||||
resolvedNodeIds: Array.from(immediateResolvedIds),
|
||||
};
|
||||
onImmediateGraphUpdate(immediateGraph);
|
||||
}
|
||||
|
||||
setCuSynthesisLoading(true);
|
||||
try {
|
||||
const doneResult = await executeEpisodeDone({
|
||||
resultSituationGraph: result?.situationGraph,
|
||||
resultSituationGraph: preDoneGraph,
|
||||
targetNodeId,
|
||||
focusedContributions: focusedContributions ?? [],
|
||||
findings,
|
||||
@@ -388,6 +402,7 @@ export default function ScenarioForm() {
|
||||
|
||||
} finally {
|
||||
doneInProgressRef.current = false;
|
||||
setCuSynthesisLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,14 +437,9 @@ export default function ScenarioForm() {
|
||||
const currentGraph = result?.situationGraph;
|
||||
if (!currentGraph) return;
|
||||
|
||||
const completeNextFindings = normalizeFindings([
|
||||
...(findings ?? []),
|
||||
...newFindingsDelta,
|
||||
]);
|
||||
|
||||
void synthesizeFromFindings(fetch, {
|
||||
situationGraph: currentGraph,
|
||||
findings: completeNextFindings,
|
||||
findings: normalizeFindings([...(findings ?? []), ...newFindingsDelta]),
|
||||
}).then((res) => {
|
||||
if (res.ok && res.data?.currentUnderstanding) {
|
||||
setCurrentUnderstanding(res.data.currentUnderstanding);
|
||||
@@ -794,6 +804,7 @@ export default function ScenarioForm() {
|
||||
scenario={scenario}
|
||||
status={status}
|
||||
updateStatus={updateStatus}
|
||||
cuSynthesisLoading={cuSynthesisLoading}
|
||||
currentUnderstanding={currentUnderstanding}
|
||||
result={{
|
||||
...(result || {}),
|
||||
@@ -814,6 +825,8 @@ export default function ScenarioForm() {
|
||||
onUpdateFindingProposition={updateFindingProposition}
|
||||
/* ── v0.49 — done-for-now promotion seam ─────────── */
|
||||
onSummaryUpdate={handleDoneForNowPromotion}
|
||||
/* ── immediate graph transition (Done acknowledged before async) ── */
|
||||
onImmediateGraphChange={(nextGraph) => setResult((prev) => ({ ...(prev ?? {}), situationGraph: nextGraph }))}
|
||||
/* ── canonical graph-replacement seam (future Re-open) ── */
|
||||
onSituationGraphChange={(nextGraph) => setResult((prev) => ({ ...(prev ?? {}), situationGraph: nextGraph }))}
|
||||
onRestart={() => {
|
||||
|
||||
Reference in New Issue
Block a user