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
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
Two presentation fixes (no reasoning-engine changes):
A. Follow-up answer continuity — selectFollowUpQuestion clears focused.answer,
which previously caused the completed-narrative framing ('Previously answered'
and 'Your response') to disappear mid-investigation. The guard now treats
a non-null result as sufficient evidence of a completed-context state, so the
user's verbatim answer and derived findings remain visible while a follow-up is
being formulated.
B. Prior-contributions chronology — display order in 'Previous learning' panels
has been reversed at the presentation boundary (newest → oldest). This means
users see the most recently learned evidence first, without modifying data-order
anywhere else. Applies to both PriorContributionsSummary and
SecondaryPreviousLearning.
When a reopened completed turn is displayed, distinguish it from an active question:
- 'PREVIOUSLY ANSWERED' + 'YOUR RESPONSE' headings for completed turns (hasAnswer=true)
- Bare 'QUESTION' heading preserved for active follow-ups (answer=null)
- Verbatim user answer rendered under its own heading — never conflated with Engine-derived findings
- Causal narrative: Question → Your response → What this tells us
Gate results:
- 102 tests passed (78 existing + 24 new v0.49 provenance narrative tests)
- Clean production build
- Live verification on localhost:3000 confirmed correct rendering
ThreadContributionsBadge, PriorContributionsSummary, and
SecondaryPreviousLearning all filtered contributions via
c.targetNodeId === nodeId. Multi-turn follow-up Contributions carry a
different immediate targetNodeId while the canonical origin remains on
Findings (originatingTargetNodeId).
Repaired: all contribution filters now match on EITHER
c.targetNodeId === nodeId || c.originatingTargetNodeId === nodeId.
handleDeconstructSubmit carries originatingTargetNodeId from
focusedPresentationItemId as provenance for cold-return recovery.
- ThreadContributionsBadge (rendered per-node on OpenQuestionsPanel
cards and Done-for-now cards) now shows an amber INVESTIGATING
indicator when the node has matching contributions via targetNodeId
identity match.
- UNCLEAR and INVESTIGATING cues coexist independently on the same
card — UNCLEAR is epistemic state, INVESTIGATING is activity cue.
- Deterministic test suite added: focused-investigation-history (11
tests) covering identity matching, zero-contrib edge cases,
multiple-contrib coalescing, done-for-now retention, and uncoupling
from UNCLEAR state.
- All 57 tests pass.
Verified: deriveFindingsFromContributions() exists with passing tests but
is never called in production. The contributions → findings seam is un-wired:
1. handleDeconstructSubmit() sends contribution to ScenarioForm
2. appendFocusedContribution() stores it in focusedContributions[]
3. findings state stays [] — no derivation ever runs
4. empty findings sent to /api/cases/update (which only echoes them back)
5. nothing renders from the findings surface
Fix: call deriveFindingsFromContributions after contribution is appended.
Intentional changes in this checkpoint:
- Deconstruct route: use body.targetNodeId (client identity) over raw.model-invented ID
- ThreadContributionsBadge: compact per-thread contribution indicator with expandable history
- Reopen continuation: resume from accumulated contributions instead of reformulating
- showEvidenceLimit gate: hide evidence-limit card during active investigation paths
- Evidence-limit visibility correction in rendering pipeline
- Section ordering: assumptions and connections after 'Still unclear' in focused result
- Prompt v0.3: preserve user-stated alternatives as separate unknowns; no count inflation
- 3 durable regression tests (target identity, contribution persistence, reopen state)
- evidence-limit card visibility gate test suite
Temporary residue removed:
- test-analysis.mjs (scratch diagnostic)
- 5 diagnostic console.log blocks from reasoning-workspace.jsx
- Enhance Current Understanding prominence with subtle teal/teal border
gradient, stronger heading, larger body text, more internal spacing
- Restore Situation panel as right-hand column in initial reflection view;
uses OriginalSituation when graph exists, scenario text fallback otherwise
- Stacks layout on narrow screens via grid-cols-1/gap-6/lg:grid-cols-3
- Apply teal styling to normal-state CurrentUnderstandingCard and
PlainLanguageCard (was flat gray border with bg-transparent)
- Surface assumption nodes alongside unknowns in Open Questions; add
Unclear / Plausible interpretation tags
- Wire up follow-up question buttons in deconstructed results
The successful focused deconstruct calls onFocusedContribution which
updates parent state, but never persisted the new collection to
sessionStorage. This meant an immediate reload would lose the
contribution.
Fix: add a useEffect in ReasoningWorkspace that watches the
focusedContributions prop for changes and saves via the existing
saveSession mechanism. A ref guard prevents double-save alongside the
existing updateStatus-success effect.
- Add focusedContributions state + appendFocusedContribution callback in ScenarioForm
- Contributions persist through session lifecycle (save/restore/restart)
- Pass onFocusedContribution and focusedContributions to ReasoningWorkspace
- Call onFocusedContribution on successful deconstruct with full result shape
- Test: contribution sequence, field preservation, same/different target coexistence
Add three exclusivity guards that suppress legacy surfaces during the
initial post-Analyse reflection state (postAnalyseStatus === 'success'):
- CurrentInvestigationCard: suppressed because its selectedQuestion
from startCase was leaking into the initial reflection view
- OpenQuestionsPanel: suppressed because it rendered whenever hasGraph
was true, regardless of initial reflection state
- Terminal state cards (EvidenceLimitCard / CompletionCard): suppressed
because they fired on status='success' && !hasSelectedQuestion
Transition out of initial reflection happens when user clicks a proposed
finding, which sets formulationStep='active' and triggers the existing
deactivation useEffect.
No reasoning changes. No startCase changes. No mock changes.