- Empty Done immediate transition now sets node.status to resolved
alongside resolvedNodeIds/doneForNowIds — same canonical parked
shape as populated Done (no server call required)
- Clarified-question Re-open removes target from doneForNowIds so
the question visibly returns to Open Questions
- Immediate graph mutation creates new node objects immutably
(React state semantics), touching only the target node
- 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
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.
Adopt executeEpisodeDone orchestration as the canonical path for
'Done for now' activity boundary: one user Done triggers exactly
prepareCompletedEpisode -> reconsiderCompletedEpisode -> applyValidatedProposal
-> Current Understanding synthesis -> leave focused workspace.
Production changes (components/scenario-form.jsx):
- Add prepareCompletedEpisode, reconsiderCompletedEpisode, applyValidatedProposal imports
- Export executeEpisodeDone({params}) with all 4 domain functions as named
parameters (defaults to module exports) for deterministic test wiring
- Rewrite handleDoneForNowPromotion(targetNodeId) as async: delegates to
executeEpisodeDone pipeline; CU synthesis installed only on success
- Add doneInProgressRef useRef(false) for exactly-once Done enforcement
- On synthesis failure: KEEP updated graph, KEEP Findings, KEEP existing CU
- Retire produceFindingInformedSummary from ScenarioForm (legacy CU writer)
- Remove legacy idempotence guard and Evidence:[] regex dedup
Test changes (tests/ui/scenario-form-episode-done.test.jsx):
- 9 tests verifying orchestration pipeline correctness:
1. Successful path order: prepare -> reconsider -> apply -> synthesis
2. Correct prepared episode input parameters
3. Structured application evidence (no answer fields in context)
4. nextGraph used for synthesis (not stale result state)
5. Reasoning failure: apply not called, CU synthesis not called
6. Application failure: CU synthesis not called, graph not replaced
7. Synthesis failure: nextGraph remains installed (no rollback)
8. Exactly-once per call for each domain function
9. Legacy Done writer retired (pipeline does not produce deterministic summary)
Architecture: after successful /api/cases/update, derive explicit nextGraph +
nextFindings, call synthesizeFromFindings exactly once, replace Current
Understanding with reconstruction result.
Key invariants:
- outcome.summary retired as final CU authority → always synthesis reconstruction
- Explicit derived state (no React-state reread) for graph and findings
- Previous CU preserved on synthesis failure (no fallback to outcome.summary)
- Graph and Findings NOT lost on synthesis failure
- saveInvestigation persistence uses currentUnderstanding, not outcome.summary
Deterministic regression: 7 tests (Cases A-E + 2 edges) covering all rules.
Files: components/scenario-form.jsx, tests/ui/scenario-form-case-update-synthesis.test.jsx