From ae1201bb2776d365d7ff309f73f69b5e44d1c222 Mon Sep 17 00:00:00 2001 From: robbond Date: Sun, 30 Aug 2026 08:51:18 +0100 Subject: [PATCH] fix(confidence-engine): simplify active follow-up presentation --- components/reasoning-workspace.jsx | 50 +++++++++++------- docs/current-handoff.md | 36 +++++++++++++ tests/open-questions-vs-assumptions.test.jsx | 54 ++++++++++++++++++++ 3 files changed, 121 insertions(+), 19 deletions(-) diff --git a/components/reasoning-workspace.jsx b/components/reasoning-workspace.jsx index c227b70..ed9ed69 100644 --- a/components/reasoning-workspace.jsx +++ b/components/reasoning-workspace.jsx @@ -274,25 +274,37 @@ function FocusedQuestionBody({

Questions this raises

{(focused.result.possibleFollowUpQuestions || []).length > 0 ? (
- {focused.result.possibleFollowUpQuestions.map((q, i) => { - const isCurrentQuestion = q === focused?.question; - return ( - - ); - })} + {hasActiveFollowUp + ? focused.result.possibleFollowUpQuestions.filter((q) => q !== focused.question).map((q, i) => ( + + )) + : focused.result.possibleFollowUpQuestions.map((q, i) => { + const isCurrentQuestion = q === focused?.question; + return ( + + ); + })}
) : (

None yet

diff --git a/docs/current-handoff.md b/docs/current-handoff.md index d2edd9b..d53f173 100644 --- a/docs/current-handoff.md +++ b/docs/current-handoff.md @@ -2158,3 +2158,39 @@ Those are distinct presentation responsibilities. Separating them exposes a dupl - Previous Learning single-owner presentation: CLOSED - Previous Learning newest-first: CLOSED - Post-answer promotion: PRESERVED + +## v0.49 — ACTIVE FOLLOW-UP PRESENTATION SIMPLIFICATION + +### Defect resolved + +Selected follow-up candidate rendered twice inside "QUESTIONS THIS RAISES": once as a disabled `(current question)` row and again in the active continuation block above the textarea. Both rows contained the identical question text, creating visual redundancy. + +### Fix summary + +In `components/reasoning-workspace.jsx`, when `hasActiveFollowUp` is true (a follow-up has been selected), unselected candidates are still rendered with their existing selectable form (`→ pick this question`), but the candidate matching `focused.question` is filtered out from the candidate list entirely. The active continuation block already renders the selected question plus textarea + submit — no second rendering needed. + +- Selected follow-up candidate now transforms into the active response block (single rendering). +- Duplicate `(current question)` / second-question rendering removed. +- Exactly one selected-question presentation. +- One textarea, Submit response visible. +- Completed Q/A provenance preserved. +- Previous Learning single-owner/newest-first preserved. +- Post-answer promotion preserved. + +### Implementation detail + +Production file changed: `components/reasoning-workspace.jsx` (candidate render boundary — lines ~275-300). No state added, no identity changes, no submit mechanics altered. + +### Vitest config hygiene (from 8bded90) + +vitest.config.js change classification: **A** — intentional and necessary (adds `environment: "jsdom"` required for React component testing in this repo). + +### Tests + +- Command: `npx vitest run tests/open-questions-vs-assumptions.test.jsx` +- Actual tests passed: **117** (up from 116 — one new regression test added) +- New regression assertion: verifies that after selecting a follow-up, the question text appears exactly once and no `(current question)` label is rendered. + +### Build + +- Result: PASS diff --git a/tests/open-questions-vs-assumptions.test.jsx b/tests/open-questions-vs-assumptions.test.jsx index 6a2d20c..700fb4c 100644 --- a/tests/open-questions-vs-assumptions.test.jsx +++ b/tests/open-questions-vs-assumptions.test.jsx @@ -2195,5 +2195,59 @@ describe("v0.49 RENDERED — in-place follow-up context ownership", () => { const prevLearningHeadings = screen.queryAllByText(/previous learning/i); expect(prevLearningHeadings).toHaveLength(1); }); + + // ── v0.49 REGRESSION: selected follow-up must not render twice ── + it("v0.49 regression: selected follow-up renders exactly once, no '(current question)' duplicate", () => { + const turn1Q = "Completed question?"; + const turn1A = "Complete answer."; + + // Q3 completed with raised follow-up Q4 + const contribs = [ + makeContrib(turn1Q, turn1A, 1), + ]; + + // After setFollowUpQuestion: focused.question === "Q4", possibleFollowUpQuestions = ["Q4"], hasActiveFollowUp = true + render( +
+ {}} + handleDeconstructSubmit={() => {}} + retryFormulation={() => {}} + setFollowUpQuestion={() => {}} + focused={ + { + question: "Q4", + answer: null, + status: "formulated", + result: { + observations: ["finding"], + uncertainties: [], + possibleFollowUpQuestions: ["Q4"], + assumptions: [], + relationships: [], + }, + error: null, + } + } + focusedContributions={contribs} + /> +
, + ); + + // The selected follow-up text should appear exactly once (in the active block), not duplicated in candidate row. + const q4Text = screen.getAllByText("Q4"); + expect(q4Text).toHaveLength(1); + + // After selection, "(current question)" label must NOT be rendered — the active form communicates selection unambiguously. + const currentQuestionLabels = screen.queryAllByText(/\(current question\)/i); + expect(currentQuestionLabels).toHaveLength(0); + }); }); }); \ No newline at end of file