Skip to content

Refresh session state after router form mutations so logout updates the nav - #776

Merged
kody-bot merged 1 commit into
mainfrom
cursor/fix-stale-nav-after-logout-34e9
Jul 17, 2026
Merged

kody-bot merged 1 commit into
mainfrom
cursor/fix-stale-nav-after-logout-34e9

Conversation

@kentcdodds

@kentcdodds kentcdodds commented Jul 17, 2026 •

Copy link
Copy Markdown
Owner

Problem

Logging out left the top nav showing the logged-in state (username, Admin link, Log out button) on the /login page.

Root cause

Not the logout handler β€” the server destroys the cookie correctly. The gap is in the client router + shell session cache interaction:

  1. The client router (packages/worker/client/client-router.tsx) intercepts every form POST (including the nav's /logout form), follows the redirect, and SPA-navigates to /login without a document reload.
  2. The shell (packages/worker/client/app.tsx) renders the nav from an in-memory session refreshed on navigation events β€” but navigation-triggered refreshes were throttled to 30s (PR Performance: package app loading, MCP execute isolate reuse, and hot-path cachingΒ #605). The comment claimed logout bypassed the throttle via setSessionRefreshHandler, but nothing ever wired that up.
  3. So any auth-changing form POST within 30s of the last /session fetch (which hydration always performs) SPA-navigated with the refresh skipped, leaving stale logged-in nav.

This was fundamental to the router, not specific to logout: the router had no concept of mutations, so listeners caching server-derived state could never know a form POST invalidated it.

Fix

  • The router now dispatches a mutation event on routerEvents after every form POST it submits, exposed as listenToRouterMutations alongside listenToRouterNavigation.
  • The shell listens and marks its session state stale, so the follow-up redirect navigation refreshes /session regardless of the throttle. (The refresh must not start in the mutation listener itself: the redirect navigation re-renders the shell, which aborts in-flight queueTask work and would drop the refresh β€” this is why the first-cut "just call queueSessionRefresh()" approach failed in testing.)
  • Extended the smoke E2E journey to log out through the nav and assert the nav flips to Login/Signup (reproduced the bug before the fix; passes after).
  • Updated the stale comment in app.tsx and the client-session-refresh section of docs/contributing/architecture/authentication.md.

Testing

  • New smoke E2E logout assertions fail on main (stale nav reproduced) and pass with this fix.
  • npm run validate green: format, lint, typecheck, 861 unit tests, 15 Playwright E2E, MCP E2E.
  • Manual GUI test against npm run dev with the seeded admin account:

logout_nav_updates_immediately.mp4

Before logout After logout
Logged-in nav with Admin, kody, Log out Logged-out nav with Login and Signup
System recap β€” extends a primitive (medium risk)

Mode: recap Β· Base: main @ 82af4acd Β· Head: cursor/fix-stale-nav-after-logout-34e9

Classification: extends β€” the browser app's client router gains a mutation event and the shell's session refresh contract changes; no primitives added.

Primitives touched

Primitive Group Impact
app-ui surfaces extends β€” client router emits mutation events; shell session refresh bypasses its throttle after mutations
app-sessions auth composes β€” /logout and /session handlers unchanged; the client now revalidates against them correctly

System map

Logout flows from the nav form through the client router's new mutation event into an unthrottled /session revalidation.

Legend: green = composes (wiring only) Β· amber = extended by this PR Β· red = new primitive Β· gray = context (unchanged, included only when an edge crosses it).

flowchart LR
	appUi["app-ui<br/>Browser app (Remix 3)"]:::extended
	appSessions["app-sessions<br/>Browser sessions"]:::touched
	appUi -->|"form POST /logout, then unthrottled GET /session after 'mutation' event"| appSessions
	classDef touched fill:#1a7f37,color:#fff
	classDef extended fill:#9a6700,color:#fff
	classDef added fill:#cf222e,color:#fff
	classDef untouched fill:#57606a,color:#fff
Loading

Change flow

sequenceDiagram
	participant Nav as Top nav (app.tsx)
	participant Router as client-router.tsx
	participant Server as Worker (/logout, /session)
	Nav->>Router: submit logout form (intercepted)
	Router->>Server: POST /logout (cookie destroyed)
	Router-->>Nav: dispatch "mutation" event β†’ session marked stale
	Router->>Server: run /login route loader (SPA redirect)
	Router-->>Nav: dispatch "navigate" event
	Nav->>Server: GET /session (throttle bypassed: stale flag)
	Nav-->>Nav: render logged-out nav
Loading
Open in WebΒ Open in CursorΒ 

Summary by CodeRabbit

  • Bug Fixes

    • Improved session handling after actions such as logout and form submissions, ensuring updated authentication state is reflected promptly.
    • Prevented interrupted session refreshes from incorrectly clearing the last known valid session.
    • Improved logout flow reliability so users are redirected to the login page and signed-in controls are removed immediately.
  • Tests

    • Updated end-to-end coverage to verify logout behavior and session-related navigation.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. πŸŽ‰

ℹ️ Recent review info
βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5dffaf10-406e-4cd9-a618-7fbf0361566b

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 82af4ac and 76594aa.

πŸ“’ Files selected for processing (4)
  • docs/contributing/architecture/authentication.md
  • e2e/smoke.spec.ts
  • packages/worker/client/app.tsx
  • packages/worker/client/client-router.tsx

πŸ“ Walkthrough

Walkthrough

Router mutations now notify the app, allowing subsequent navigation-triggered session refreshes to bypass throttling. Authentication documentation and the smoke test are updated to describe and verify logout behavior.

Changes

Session refresh synchronization

Layer / File(s) Summary
Router mutation event contract
packages/worker/client/client-router.tsx
Router form submissions dispatch mutation events after responses, and listenToRouterMutations exposes subscriptions to those events.
Stale session refresh and logout validation
packages/worker/client/app.tsx, docs/contributing/architecture/authentication.md, e2e/smoke.spec.ts
The app tracks potentially stale sessions and bypasses navigation throttling after mutations; documentation and smoke coverage describe the refresh and logout behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RouterForm
  participant routerEvents
  participant App
  participant SessionRefresh
  RouterForm->>routerEvents: Dispatch mutation after form response
  routerEvents->>App: Mark sessionMaybeStale
  App->>SessionRefresh: Refresh on subsequent navigation without throttle
Loading
πŸš₯ Pre-merge checks | βœ… 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
βœ… Passed checks (4 passed)
Check name Status Explanation
Description Check βœ… Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check βœ… Passed The title clearly summarizes the main change: refreshing session state after router form mutations so logout updates navigation.
Linked Issues check βœ… Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check βœ… Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
πŸ“ Generate docstrings
  • Create stacked PR
  • Commit on current branch
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/fix-stale-nav-after-logout-34e9

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ Share

Comment @coderabbitai help to get the list of available commands.

@kody-bot
kody-bot marked this pull request as ready for review July 17, 2026 13:36
@github-actions

Copy link
Copy Markdown
Contributor

πŸ”Ž Preview deployed: https://kody-pr-776.kody-a99.workers.dev

Worker: kody-pr-776
D1: kody-pr-776-db
KV: kody-pr-776-oauth-kv

Mocks:

@kody-bot
kody-bot merged commit cae1752 into main Jul 17, 2026
7 checks passed
@kody-bot
kody-bot deleted the cursor/fix-stale-nav-after-logout-34e9 branch July 17, 2026 13:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants