Skip to content

fix(app): put new sessions in Recents right away - #229

Merged
quickbeard merged 1 commit into
mainfrom
recents-query-client
Aug 14, 2026
Merged

quickbeard merged 1 commit into
mainfrom
recents-query-client

Conversation

@quickbeard

Copy link
Copy Markdown
Owner

Creating a session left the sidebar's Recents list unchanged. The session only turned up once something made that list reload — switching between Code and Work and back was the reliable way to do it.

Root cause

QueryProvider builds a new QueryClient on every render, and it was mounted twice:

  • AppBaseProviders installs one (client A)
  • AppInterface's ServerShell installed a second one (client B), nested inside it

GlobalProvider sits above ServerShell, so ServerSync — and the home session index cache it owns (context/global-sync/home-session-index.ts) — resolved A, while everything under the router, the sidebar and home screen included, resolved B.

Confirmed live by tagging both clients in a dev build:

PROBE model  client=tvzhjv  indexKey=["home","session-index","sidecar"]  total=5
PROBE apply  client=m3sfd5  state=false                                  total=36

state=false is the failure: homeSessions.apply() looks the index query up in A, where it does not exist, and returns early. Every session.created / session.updated event was written into a cache with no readers, so the list those events exist to keep fresh could only change when its own query refetched. Switching Code/Work reassigns the sidebar observer's query key, which forces exactly that refetch — hence the workaround.

Work made this obvious because there is less incidental navigation there to trigger a refetch, but Code was affected the same way. refreshIndex() in home-model.tsx kept working throughout because it runs on the reader's own client, which is why archive/rename/delete always refreshed.

Change

ServerShell renders SharedProviders directly. QueryProvider itself is untouched and still installed once, by AppBaseProviders. Both AppInterface call sites (packages/app/src/entry.tsx, packages/desktop/src/renderer/index.tsx) already wrap it in AppBaseProviders, so the nested client was always redundant.

Verification

Drove the real flow in a dev desktop build over CDP (sidebar "New session" → type → Enter) and read the sidebar DOM:

  • +1s — a New session row appears at the top of Recents
  • +3s — that row becomes Greeting, the model-generated title, live

Before the change the row never appeared. Titles now land in Recents as soon as the model picks one instead of waiting for the next reload.

bun typecheck clean in packages/app and packages/desktop; test:unit 657 pass, test:browser 69 pass. No automated regression test — the invariant is "ServerSync and the components reading its queries resolve the same QueryClient", which only a full-tree mount would catch, so this was verified by instrumenting the running app.

Divergence recorded in DIVERGENCES.md; user-facing note in CHANGELOG.md.

🤖 Generated with Claude Code

QueryProvider builds a fresh QueryClient on every render, and it was
mounted twice: once in AppBaseProviders and again inside AppInterface's
ServerShell. GlobalProvider sits above ServerShell, so ServerSync — and
the home session index cache it owns — resolved the outer client, while
everything under the router, the sidebar and home screen included,
resolved the nested one.

The index that session.created/session.updated write to therefore had no
readers, and the list those events exist to keep fresh could only change
when its own query refetched. In this fork that list is the persistent
sidebar's Recents section, so a new session stayed missing from it until
something remounted the observer — switching Code/Work and back
reassigns the observer's key, which is why that looked like the fix.
Work made it obvious because there is less incidental navigation there,
but Code was affected the same way.

Both AppInterface call sites already wrap it in AppBaseProviders, so the
nested client was always redundant. Drop it: Recents now gains the row
as the session is created, and picks up the generated title as soon as
the model chooses one instead of waiting for the next reload.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@quickbeard
quickbeard merged commit 71e2973 into main Aug 14, 2026
0 of 5 checks passed
@quickbeard
quickbeard deleted the recents-query-client branch August 14, 2026 03:52
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