fix(web): user-scope the prechat resume sessionStorage key (LUM-1763 follow-up)#31431
Conversation
Follow-up to LUM-1763 (#31416 merged) addressing the Codex P2 comment on that PR: > Reading prechat_native_screen unconditionally from > sessionStorage means a stale value from a prior onboarding > attempt can skip the name step for a different login in the > same webview session (user A reaches vibe step, logs out, then > user B logs in and lands directly on vibe). Fix: key the persisted screen by userId. Read happens in a `useLayoutEffect` once userId is known (vs the previous lazy useState initializer that didn't have access to it), so we restore before paint without risking a flash to the wrong step. Write/clear sites now use the same user-scoped key. If the prior session never logged out cleanly, user B reads "their" key which doesn't exist, and starts fresh at the name step — no leak. Verified locally: lint, typecheck clean. https://claude.ai/code/session_01JPGu4yGPTL4JoLaPuqpEW2
6d9bfe7 to
38629ce
Compare
There was a problem hiding this comment.
✦ APPROVE
Clean follow-up to the Codex P2 from #31416. Also picks up the goToVibeStep de-dup I flagged as non-blocking there.
User-scoped key ✅
screenStorageKey = userId ? \prechat_native_screen:${userId}` : nullcorrectly scopes the key to the active user and evaluates tonullwhile auth is still loading. All four storage sites (restore,goToVibeStep, finishNativePreChat, vibe-step back) guard on screenStorageKey` being non-null before touching sessionStorage. The logout→login bleed scenario Codex flagged is cleanly closed.
useLayoutEffect over lazy useState initializer ✅
Right call. useLayoutEffect fires synchronously before paint, so the screen correction happens before the user sees anything — no flash to the wrong step if restore runs async. The deps array is [screenStorageKey] with screen intentionally omitted: re-restore should only trigger when the active user identity changes (mount or logout→login), not on every screen transition. Correct.
No cleanup needed on the effect ✅
There's no subscription, timeout, or async operation — the effect is a single synchronous sessionStorage read. No cleanup function required.
goToVibeStep extraction ✅
The de-dup lands here as a side-effect — onContinue and onSkip now share the same closure, so the user-scoped key is written in one place instead of two. Less drift risk going forward.
CI — 7/7 green. ✅
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 38629ce4fa
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const saved = sessionStorage.getItem(screenStorageKey); | ||
| if (saved === "1") setScreen(1); |
There was a problem hiding this comment.
Reset screen for users without a saved native prechat step
When screenStorageKey changes, this effect only updates state if the new key stores "1". If user A was on screen === 1 and user B logs in without a saved key, screen stays at 1 in memory, so user B can still land on the vibe step despite having no persisted progress. This can happen whenever the component remains mounted across an auth identity swap (the exact scenario this change is targeting). The restore path should explicitly set screen back to 0 when no saved value exists for the active user key.
Useful? React with 👍 / 👎.
Follow-up to LUM-1763 (#31416, merged), addressing the unresolved Codex P2 thread on that PR.
Why
Codex flagged that the
prechat_native_screensessionStorage key wasn't user-scoped. Concrete scenario:The lazy useState initializer was reading the key unconditionally on mount, before any user identity was known.
What
screenStorageKey = userId ? \prechat_native_screen:${userId}` : null` (null while auth is still loading).useLayoutEffectkeyed onscreenStorageKey, so the restore runs onceuserIdis available, before paint — no flash to the wrong step.goToVibeStep,finishNativePreChat, vibe-step back button) use the user-scoped key.If user B logs in after user A left the key behind, user B reads their own key (which doesn't exist) and starts at the name step. No leak.
Verified locally
bun run lint— 0 errors.bun run typecheck— clean.Test plan
https://claude.ai/code/session_01JPGu4yGPTL4JoLaPuqpEW2
Generated by Claude Code