Skip to content

fix: prevent session sidebar from going stale#255

Merged
mattleaverton merged 3 commits intodanshapiro:mainfrom
mattleaverton:worktree-fix-session-staleness
Mar 30, 2026
Merged

fix: prevent session sidebar from going stale#255
mattleaverton merged 3 commits intodanshapiro:mainfrom
mattleaverton:worktree-fix-session-staleness

Conversation

@mattleaverton
Copy link
Copy Markdown
Collaborator

Summary

  • Default queueActiveSessionWindowRefresh() to 'sidebar' when activeSurface is undefined — fixes a race condition where sessions.changed WS messages arriving before bootstrap silently dropped all sidebar refreshes permanently
  • Mark wsSnapshotReceived = true on any successful HTTP session fetch, not just during the 30-second bootstrap window — prevents WS patches from being silently discarded after reconnect
  • Apply the same 'sidebar' default to refreshActiveSessionWindow() for consistency
  • Add persistedAt timestamp to persisted tab state and log a warning when restoring state older than 24 hours

Context

Two Freshell instances on different ports showed divergent session lists — one kept updating while the other froze. Root cause was activeSurface being undefined when early sessions.changed messages arrived, causing the refresh thunk to exit silently. A secondary issue: wsSnapshotReceived was only promoted during a narrow 30s bootstrap window, so any reconnect outside that window permanently blocked WS patches.

Test plan

  • All existing unit tests pass (sessionsSlice, sessionsThunks, persistMiddleware, persistedState — 91 tests)
  • TypeScript compiles cleanly
  • Built and ran test server on port 3380 — sidebar loaded sessions correctly with live-updating timestamps
  • Verify on a real multi-instance setup that both instances stay in sync

🤖 Generated with Claude Code

…nset

The session sidebar could permanently stop updating due to a race condition:
when sessions.changed WS messages arrived before bootstrap set activeSurface,
queueActiveSessionWindowRefresh() silently exited and never queued a refresh.
All subsequent session updates were also silently dropped.

Three fixes:
- Default to 'sidebar' when activeSurface is undefined in refresh thunks
- Mark wsSnapshotReceived=true on any successful HTTP fetch (not just during
  the 30-second bootstrap window), so WS patches aren't silently discarded
- Add persistedAt timestamp to tab state in localStorage and log a warning
  when restoring state older than 24 hours

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 837a3b23f9

ℹ️ 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".


if (tabsDirty) {
const tabsPayload = {
persistedAt: Date.now(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop forcing tab payload churn with volatile persistedAt

Adding persistedAt: Date.now() makes every tabs flush produce a brand-new raw string even when the persisted tab content is unchanged (for example tabs/updateTab updates only lastInputAt, which is stripped before persistence). Cross-tab sync dedupes by exact raw value in installCrossTabSync (tryDedupeAndMark), so this change defeats that dedupe and causes other clients to hydrate tabs repeatedly during normal typing activity, creating unnecessary cross-tab churn and avoidable UI/perf noise.

Useful? React with 👍 / 👎.

mattleaverton and others added 2 commits March 30, 2026 15:33
…-resolve

# Conflicts:
#	src/store/persistMiddleware.ts
#	src/store/sessionsThunks.ts
#	src/store/tabsSlice.ts
Set persistedAt once per session instead of on every flush, so the
serialized payload stays identical when tab content hasn't changed.
This preserves the raw-string dedup in cross-tab sync.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mattleaverton mattleaverton merged commit 50b10af into danshapiro:main Mar 30, 2026
1 check passed
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.

1 participant