fix(chat): keep empty soft-swap centered until first turn - #140
Conversation
After /change, mid-switch visibility could still dock the composer before remount reset finished, killing first-message flight origins. Prefer cache emptiness in syncComposerPhase, treat known-empty mid- switch as not visible, and reset session flight flags in $effect.pre.
tomtom-bot40
left a comment
There was a problem hiding this comment.
Standards
Tight follow-up to #139. $effect.pre + emptiness-wins in syncComposerPhase are clear and well-commented. No blocking smells.
Spec
Matches the post-#139 Spec:
- Remount reset runs in
$effect.preso it wins the race against mid-switchsyncComposerPhasedock - Mid-switch known-empty (
getCachedItemCount === 0) is not treated as visible - Empty + !awaiting → force center (emptiness beats stale
hasVisibleConversation) - Empty session stays centered; content docks
Pre-PR two-axis sign-off stands.
Standards: pass · Spec: pass
/change AppendSystemMessage becomes a status row; raw item count made empty forks look non-empty, docking the composer and skipping FirstTurnFlight. Gate first-turn and composer phase on user/assistant turns via hasCachedConversationTurns.
tomtom-bot40
left a comment
There was a problem hiding this comment.
Re-review: 39023d9 ignore fork status for first-turn dock
Spec — pass
Validated remaining center→dock: fork AppendSystemMessage → status ChatItem → raw count > 0 killed firstTurn. This commit gates firstTurn / bindSession / syncComposerPhase / soft-swap remount on hasCachedConversationTurns (user|assistant only). Status-only fork stays centered + FirstTurnFlight. Test covers it.
Standards — pass
Clear helper + comments. Non-blocking residual: when store is bound, hasVisibleConversation still uses items.length > 0 (status can hide EmptyChatState while composer stays centered) — acceptable for this fix.
Together with 24dfd1b, #140 Spec is complete for the user’s remaining repro.
Standards: pass · Spec: pass
svelte-check requires TranscriptResponse.session_id; CI failed on the fork status-only controller test mocks.
tomtom-bot40
left a comment
There was a problem hiding this comment.
Re-review: d1a43aa EmptyChatState on status-only fork
Spec — pass
Closes the residual: bound-session hasVisibleConversation now uses hasCachedConversationTurns (and snapshot user|assistant), so fork status notes no longer unmount EmptyChatState/avatar while composer stays centered.
Standards — pass
One predicate end-to-end with dock/firstTurn. Clean.
Standards: pass · Spec: pass · tip signed off
Summary
Follow-up to #139: after
/change, mid-switch visibility could still dock an empty fork before remount reset finished, so the first-message fly-in lost hero origins.$effect.pre(beforesyncComposerPhase)getCachedItemCount === 0) is not visiblesyncComposerPhase: empty + not awaiting → force center (emptiness wins)Product rule: empty session stays centered; dock only once there is real content (or first-turn prepare).
Test plan
/change→ composer stays centered (no center→dock)vitestconversation-controller (incl. empty+visible→center)Reviewed two-axis by tomtom.bot40 @
24dfd1b.