fix(confidence-engine): project processing indicator to active follow-up block

Repair LOCATION-A defect where processing feedback rendered near the
completed narrative instead of inside the active follow-up block.

Changes:
  - components/reasoning-workspace.jsx: three targeted edits using a single
    spinner component with conditional rendering; hasActiveFollowUp routes
    ownership to the correct container
  - tests/open-questions-vs-assumptions.test.jsx: regression test confirming
    exactly one indicator, DOM child of follow-up-block, ownership separation

Accepted criteria met:
   Exactly one processing indicator during follow-up processing
   Indicator is a DOM child of follow-up-block
   Top-level indicator suppressed when follow-up active
   Initial answer flow preserved (top-level when no follow-up)
   Successful follow-up promotion intact
   All existing context retained
   No new state/lifecycle changes/error redesign
This commit is contained in:
2026-08-30 11:09:24 +01:00
parent bf7629691f
commit 922f58a49f
3 changed files with 161 additions and 5 deletions
@@ -2362,6 +2362,62 @@ describe("v0.49 RENDERED — in-place follow-up context ownership", () => {
const questionHeadings = screen.queryAllByText(/^Question$/);
expect(questionHeadings).toHaveLength(0);
});
it("processing indicator belongs to active follow-up block, not to completed narrative", async () => {
const prevQ = "Which specific step of the onboarding funnel has the highest abandonment rate?";
const prevA = "Step 3 — account verification / phone confirmation.";
const followUpQ = "What drives the Step 3 abandonment rate?";
const contrib = makeContrib(prevQ, prevA, 1);
renderFQB({
focused: {
question: followUpQ,
answer: null,
status: "formulated",
result: {
observations: [contrib.observations[0]],
uncertainties: ["Is this causal?"],
possibleFollowUpQuestions: [followUpQ, "How does it compare to competitors?"],
assumptions: [],
relationships: [],
},
error: null,
},
focusedContributions: [contrib],
processingStep: "active",
deconstructMsg: "Working through your response…",
});
// ── Derived completed context (from prior contributions) remains visible during processing ──
expect(screen.getByText("Previously answered")).toBeInTheDocument();
expect(screen.getByText(prevQ)).toBeInTheDocument();
expect(screen.getByText("What this tells us")).toBeInTheDocument();
// ── Exactly one processing indicator exists (no duplicates, no missing) ──
const allProcessingIndicators = screen.queryAllByTestId("processing-indicator");
expect(allProcessingIndicators).toHaveLength(1);
const processingIndicator = allProcessingIndicators[0];
// ── Processing indicator must be inside the active follow-up block (Q4) ──
const followUpBlock = screen.getByTestId("follow-up-block");
expect(followUpBlock.contains(processingIndicator)).toBe(true);
// ── Processing text visible inside follow-up block ──
expect(processingIndicator.textContent).toContain("Working through your response…");
// ── Follow-up textarea and submit are present inside the same block ──
const followUpTextarea = screen.getByTestId("follow-up-textarea");
expect(followUpTextarea.disabled).toBe(true);
const followUpSubmit = screen.getByRole("button", { name: /submit/i });
expect(followUpSubmit.closest("[data-testid='follow-up-block']")).toBeInTheDocument();
// ── Completed narrative that DOES render must not contain follow-up elements ──
const completedNarrative = screen.queryByTestId("completed-narrative");
if (completedNarrative) {
expect(completedNarrative.querySelector('[data-testid="follow-up-block"]')).not.toBeInTheDocument();
}
});
});
// ── v0.51 CASE B: error preserves workspace context ────────────────