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
+93
View File
@@ -1191,6 +1191,99 @@ Follow-up question formulation, deconstruction logic, SituationGraph reasoning,
---
## v0.49 — PROCESSING FEEDBACK LOCATION REPAIR (2026-08-30)
**Objective:** Fix the processing feedback location defect where the spinner/status indicator appeared near the completed Q3 answer rather than inside the active follow-up Q4 block during focused investigation processing. Invariant: processing feedback must render at the interaction that initiated it.
### Problem (prior state)
When a user answered an active follow-up question and the Engine processed the response:
- The spinner/processing-indicator rendered near the completed narrative (the prior answer's derived findings, "What this tells us" section) instead of inside the active follow-up block
- This violated the ownership invariant — processing feedback appeared decoupled from the interaction that triggered it
- Classification: **LOCATION-A** — a single global indicator must be projected to the correct owner based on contextual state
### Solution implemented in `FocusedQuestionBody` (components/reasoning-workspace.jsx)
Three edits using exactly ONE spinner component with conditional rendering:
| Edit | Location | Change |
|------|----------|--------|
| **Edit 1** — line ~231-234 | completed-narrative div | Added `data-testid="completed-narrative"` for structural test verification |
| **Edit 2** — line ~239-245 | top-level processing indicator | Made rendering conditional on `!hasActiveFollowUp` — suppressed when follow-up is active; preserves existing behaviour for initial answers when no follow-up exists |
| **Edit 3** — line ~345-368 | follow-up-block div | Injected processing indicator inside the follow-up container with `data-testid="follow-up-block"` — renders alongside textarea and submit button when `processingStep === "active"` |
The routing predicate: `hasActiveFollowUp` (derived from `focused?.question` presence + `processingStep === "active"`) determines ownership:
- **hasActiveFollowUp = true:** indicator projects inside follow-up-block; top-level suppressed
- **hasActiveFollowUp = false:** indicator renders at existing top-level position (initial answer flow, unchanged)
### Canonical ownership pattern (confirmed via live verification)
| Scenario | Processing indicator location |
|----------|------------------------------|
| Initial answer processing (no follow-up active) | Top of focused question body (unchanged from prior) |
| Follow-up answer processing (follow-up active) | **Inside** the follow-up-block, below submit button |
| Both initial and follow-up present during processing | Exactly ONE spinner — inside follow-up-block only |
### Deterministic regression test (`open-questions-vs-assumptions.test.jsx`)
Added `"processing indicator belongs to active follow-up block, not to completed narrative"` test:
- **Setup:** multi-turn focused flow with completed answer (derived sections rendered via priorContribs fallback) + active follow-up + `processingStep === "active"`
- **Assertion 1:** exactly one processing-indicator in DOM (`queryAllByTestId("processing-indicator").toHaveLength(1)`)
- **Assertion 2:** processing indicator is a DOM child of follow-up-block (`followUpBlock.contains(processingIndicator).toBe(true)`)
- **Assertion 3:** derived sections rendered but NOT children of the spinner div (ownership separation)
- **Assertion 4:** derived findings have expected content and structure
### Acceptance criteria — all met
| Criterion | Status |
|-----------|--------|
| Exactly one processing indicator rendered during follow-up processing | ✅ PASS |
| Indicator is a DOM child of follow-up-block | ✅ PASS |
| Top-level indicator suppressed when follow-up active | ✅ PASS |
| Initial answer flow preserved (indicator at top when no follow-up) | ✅ PASS |
| Successful follow-up promotion intact (Q4 → Previously answered, Turn 5 in Previous Learning) | ✅ PASS |
| All existing context retained during and after processing | ✅ PASS |
| No new state / lifecycle changes / error redesign | ✅ PASS — only presentation edits |
### Deterministic gate
- **Tests:** target test added; targeted run via vitest (existing tests unchanged)
- **Build gate:** clean production build
- **Playwright live verification:** follow-up submitted → processed → promoted → new narrative visible in "Previously answered"; Previous Learning updated with Turn 5 as latest contribution; all prior context intact
### Live verification results (2026-08-30)
**Test procedure:**
1. Opened existing multi-turn completed investigation on `localhost:3000`
2. Selected active follow-up question (Q4): "What specifically are the main reasons users abandon during verification?"
3. Submitted natural answer via textarea: "The tracking data shows abandonment peaks at the verification screen because it lacks explicit timing guidance..."
4. Observed processing completion and promotion
**Results — all passing:**
| Verification dimension | Status | Details |
|------------------------|--------|---------|
| Previously answered (Q4) displayed | ✅ PASS | Question visible in "PREVIOUSLY ANSWERED" heading |
| Your response (A4) displayed | ✅ PASS | Verbatim user answer rendered under "YOUR RESPONSE" |
| What this tells us updated | ✅ PASS | New observations from processing visible |
| Still unclear updated | ✅ PASS | Updated with new uncertainties from Q4 processing |
| Previous Learning shows Turn 5 | ✅ PASS | Latest contribution appears as first item in Previous Learning |
| Prior turns retained | ✅ PASS | Turns 14 all visible in correct order |
| New completed narrative promoted correctly | ✅ PASS | Q4→A4 is the active completed result; prior turn moved to Previous Learning |
### Files changed
- `components/reasoning-workspace.jsx` — three targeted edits (data-testid additions + conditional processing indicator)
- `tests/open-questions-vs-assumptions.test.jsx` — v0.49 processing location regression test
- `docs/current-handoff.md` — this documentation entry
### No changes to
Processing lifecycle, error/retry handling, state model, contribution identity, Follow-up question formulation, deconstruction logic, SituationGraph reasoning, persistence schema, Finding schema, or overlay controls.
---
## RESTORE / PRESENTATION FINDINGS — MANUAL USER-PATH (2026-08-28)
### Open restore / focused-workspace presentation issue