fix(confidence-engine): scope focused presentation to active question

- FocusedQuestionBody derives thread-local contribution subset using
  targetNodeId || originatingTargetNodeId matching
- hasCompletedContext, latest completed contrib, and all effective
  presentation fallbacks use scoped collection only
- scenario-wide focusedContributions history preserved in memory
- Fresh Question B no longer bleeds Question A's content across
  every presentation surface (Previously answered, What this tells us,
  Still unclear, Questions this raises, Assumptions, Connections)
- Reopening or revisiting Question A still uses its own history
- Targeted regression: 3 new Vitest cases pass
- Handoff docs updated with v0.52 correction record
This commit is contained in:
2026-09-02 12:46:50 +01:00
parent a061428711
commit 2b2096e41d
3 changed files with 181 additions and 5 deletions
+8 -2
View File
@@ -143,6 +143,12 @@ function FocusedQuestionBody({
onUpdateFindingDisposition,
onUpdateFindingProposition,
}) {
// ── Active-thread contribution scoping (v0.52) ──
// focusedContributions is scenario-wide; present only the active node's contributions.
const threadContribs = (focusedContributions || []).filter(
(c) => c.targetNodeId === nodeId || c.originatingTargetNodeId === nodeId,
);
const hasContent = focused?.question?.trim() || formulationStep === "active" || processingStep === "active" || focused?.error;
const hasResult = Boolean(focused?.result);
const hasAnswer = Boolean(focused?.answer);
@@ -151,7 +157,7 @@ function FocusedQuestionBody({
// 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);
const pc = [...threadContribs].reverse().find((c) => c?.question && c?.answer);
return !!pc;
})();
@@ -159,7 +165,7 @@ function FocusedQuestionBody({
// After setFollowUpQuestion() mutates focused.question/answer, derive from the
// latest completed Contribution so the narrative remains correct.
// Active follow-up detection: primary via result (when result exists), fallback via priorContribs (error state may have null result).
const priorContribs = [...(focusedContributions || [])].reverse();
const priorContribs = [...threadContribs].reverse();
// Follow-ups from result are primary; priorContribs is fallback when result is null.
const followUpsFromResult = focused?.result?.possibleFollowUpQuestions || [];
const hasActiveFollowUpFromResult =