feat(confidence-engine): place zero-Open-Questions milestone at Open Questions position

Move the milestone invitation from after Clarified Questions to occupy
the same spatial position as Open Questions — between Current Understanding
and Questions we have clarified. Uses ternary: openUnknowns > 0 ? OpenQuestionsUI : milestoneAllowed ? MilestoneInvitation : null, followed by ClarifiedQuestionsUI unconditionally. No duplication of clarified cards or Re-open controls.
This commit is contained in:
2026-09-02 12:02:55 +01:00
parent 043ba5f264
commit a061428711
3 changed files with 211 additions and 8 deletions
+49
View File
@@ -232,3 +232,52 @@ Finding↔SituationGraph reasoning contract → captured in authoritative graph
```
These architectural decisions are preserved as settled current state, not as historical narrative. Detailed experiment evidence remains in `docs/archive/experiments/`.
### Open Questions filtering surfaces (durable production fact)
Two separate surfaces render Open Questions from the same graph data but use different filter rules:
- **Inline Open Questions** (`ReasoningWorkspace`, line ~1841): filters by `n.kind === "unknown" && !resolvedIds.has(n.id)` — uses `resolvedNodeIds` **only**.
- **OpenQuestionsPanel** (same file, line ~1193): filters by `n.kind === "unknown" && !resolvedIds.has(n.id) && !doneForNowIds.includes(n.id)` — uses **both** `resolvedNodeIds` and `doneForNowIds`.
**Immediate Done behavior:** Both surfaces remove the question simultaneously (React batches both `onImmediateGraphChange``setResult` and `setDoneForNowIds` in the same render cycle, so both see the updated state on the next paint).
**Post-server-confirmation inconsistency (open): conditional structural risk.** If the server returns a graph that does **not** include the node in `resolvedNodeIds`, OpenQuestionsPanel (pre-v0.51) still hides it (because `doneForNowIds` persisted), but the inline surface shows it as open again.
---
### Zero Open Questions invitation — v0.51 correction (verified)
**Live evidence that motivated this correction:**
A real completed investigation reached zero Open Questions, clarified questions were visible, CU refreshed correctly, but the invitation was absent because its prerequisite was tied to transient `doneForNowIds` rather than canonical graph state.
**Correction applied — eligibility:** Invitation uses canonical resolved-node graph state (same derivation as inline "Questions we have clarified"), not local `doneForNowIds`:
- `openUnknowns.length === 0 AND clarifiedQuestions.length > 0 AND !cuSynthesisLoading`
- Clarification count = unknown nodes in `resolvedNodeIds` (same source as inline section)
- Re-open behavior preserved (still uses `setDoneForNowIds` for local toggle)
- "Review current understanding" reveals existing CU via `cu-scroll-target` without triggering synthesis or new LLM call
- Invitation hidden while `cuSynthesisLoading === true`; appears after CU refresh completes
- Zero Open Questions carries no readiness/completion judgement — it is a milestone invitation, not a decision
**Exact working copy:** "You've now worked through all of the questions we surfaced. Would you like to see an overview of what we understand so far?"
**Action:** "Review current understanding" (scrolls to CU section)
**Placement correction (v0.51 placement):**
- The milestone invitation occupies the same conceptual/spatial position previously occupied by Open Questions — between Current Understanding and Questions we have clarified.
- When Open Questions still exist: Current Understanding → Open Questions → Questions we have clarified
- When zero Open Questions: Current Understanding → Milestone invitation → Questions we have clarified
- This is a ternary in the inline ReasoningWorkspace section: `openUnknowns.length > 0 ? <OpenQuestionsUI> : milestoneAllowed ? <MilestoneInvitation> : null`, followed by `<ClarifiedQuestionsUI />` unconditionally.
**Verification:**
- Rob manually verified the milestone renders correctly on persisted investigation at `http://localhost:3000` (before placement correction)
- This session corrected placement via Edit only; deterministic verification via targeted Vitest (145 tests) and build — deliberately did not repeat Playwright
- All 145 tests pass; production build compiles successfully
**Open defects (unchanged):**
- Focused-investigation state bleed: newly selected Open Question can show stale previous-question material — separate future increment
- Empty Done `no_episodic_content`: choosing Done without episodic content can produce `{ success: false, stage: "preparation", error: "no_episodic_content" }` — separate future increment
**Next restart point:** Place milestone at Open Questions position when reverting placement correction. See v0.51 placement section above for the exact ternary pattern.