From bf7629691faa98ec228af24ea53652580b5c9060 Mon Sep 17 00:00:00 2001 From: robbond Date: Sun, 30 Aug 2026 10:43:57 +0100 Subject: [PATCH] fix(confidence-engine): preserve follow-up context while processing --- components/reasoning-workspace.jsx | 265 +++++++++++-------- docs/current-handoff.md | 63 +++++ tests/open-questions-vs-assumptions.test.jsx | 249 +++++++++++++++++ 3 files changed, 462 insertions(+), 115 deletions(-) diff --git a/components/reasoning-workspace.jsx b/components/reasoning-workspace.jsx index ed9ed69..efff9a7 100644 --- a/components/reasoning-workspace.jsx +++ b/components/reasoning-workspace.jsx @@ -147,14 +147,28 @@ function FocusedQuestionBody({ const hasAnswer = Boolean(focused?.answer); // A non-null result means we are still in a completed-context state even after the user selects a follow-up (which clears answer). // Without this guard, selecting a follow-up question would erase "Previously answered" + "Your response". - const hasCompletedContext = processingStep !== "active" && Boolean(focused?.result); + // Completed context: result (primary) OR prior contributions (fallback during processing/error). + // Processing and error are transient states — they must NOT collapse completed context. + const hasCompletedContext = Boolean(focused?.result) || (() => { + const pc = [...(focusedContributions || [])].reverse().find((c) => c?.question && c?.answer); + return !!pc; + })(); // ── Source of completed context: latest canonical Contribution when follow-up is active ── // After setFollowUpQuestion() mutates focused.question/answer, derive from the // latest completed Contribution so the narrative remains correct. - const hasActiveFollowUp = hasCompletedContext && !hasAnswer - && (focused.result?.possibleFollowUpQuestions || []).some((q) => q === focused?.question); - const latestCompletedContrib = [...(focusedContributions || [])].reverse().find((c) => c?.question && c?.answer); + // Active follow-up detection: primary via result (when result exists), fallback via priorContribs (error state may have null result). + const priorContribs = [...(focusedContributions || [])].reverse(); + // Follow-ups from result are primary; priorContribs is fallback when result is null. + const followUpsFromResult = focused?.result?.possibleFollowUpQuestions || []; + const hasActiveFollowUpFromResult = + !hasAnswer && followUpsFromResult.length > 0 && followUpsFromResult.some((q) => q === focused?.question); + const hasActiveFollowUpFromPrior = priorContribs.length > 0 + ? priorContribs.find((c) => (c.possibleFollowUpQuestions || []).length > 0)?.possibleFollowUpQuestions?.includes(focused?.question) ?? false + : false; + // Active follow-up requires either: a matched follow-up in result, OR priorContribs with a valid possibleFollowUp. + const hasActiveFollowUp = (hasActiveFollowUpFromResult || hasActiveFollowUpFromPrior); + const latestCompletedContrib = priorContribs.find((c) => c?.question && c?.answer); const displayedCompletedQuestion = hasActiveFollowUp ? (latestCompletedContrib?.question ?? focused?.question) @@ -222,120 +236,141 @@ function FocusedQuestionBody({ )} - {processingStep === "active" &&

{deconstructMsg}

} + {processingStep === "active" && ( +

+ + Processing: + {deconstructMsg} +

+ )} - {focused?.result && ( + {(hasActiveFollowUp || hasCompletedContext) && ( <> - {/* Prior accumulated learning removed from left pane — SecondaryPreviousLearning on the right owns historical Previous Learning exclusively */} - {/* PriorContributionsSummary was causing duplication in the two-column focused workspace */} -

What this tells us