feat(site/src): reintroduce chat search cache invalidation - #27892
Merged
Conversation
DanielleMaywood
force-pushed
the
feat/chat-search-invalidation
branch
from
August 5, 2026 20:12
510ce6a to
683335d
Compare
DanielleMaywood
force-pushed
the
feat/chat-search-invalidation
branch
from
August 5, 2026 20:23
683335d to
d420ea1
Compare
DanielleMaywood
force-pushed
the
feat/chat-search-invalidation
branch
from
August 6, 2026 08:22
d420ea1 to
fe41cbe
Compare
Contributor
Author
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fe41cbe176
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
DanielleMaywood
force-pushed
the
feat/chat-search-invalidation
branch
from
August 6, 2026 08:42
fe41cbe to
dbd41cb
Compare
DanielleMaywood
marked this pull request as ready for review
August 6, 2026 09:01
ibetitsmike
approved these changes
Aug 6, 2026
DanielleMaywood
added a commit
that referenced
this pull request
Aug 6, 2026
… watch paths (#27901) Implements Phase 2 item 5 of the chats query architecture: by-workspace cache reconciliation. Stacked on #27892; base branch `feat/chat-search-invalidation`. ## Problem `chatsByWorkspace` mappings (flat `Record<workspaceId, chatId>`, IDs only, archived chats filtered server-side) go stale on archive/unarchive, workspace binding changes, and watch events (FINDINGS 2.3). Because the client cannot re-derive archived state from the cached map, the only correct repair for a stale mapping is synchronous removal plus family invalidation. ## Fix - New `removeChatFromChatsByWorkspace(queryClient, chatId)` in `site/src/api/queries/chats.ts`: value-match removal across the by-workspace family, reference-preserving when nothing is removed (mirrors the `patchChatMessages` no-op pattern). - `archiveChat.onSuccess` and the AgentsPageLayout archive-and-delete `onSuccess` synchronously remove the mapping; the existing `onSettled`/explicit invalidations then reconverge. - Watch handler: `deleted` branch removes then invalidates (remove-before-invalidate ordering); `created` root branch invalidates; merge path invalidates behind a new `shouldInvalidateChatsByWorkspace` predicate (`status_change`, `action_required` only; `created`/`deleted` have their own branches, title/summary/diff/context events do not move `updated_at` ordering); `onOpen` reconnect invalidates for convergence. - `useChatToolInvalidations`: workspace-binding tool completion (`create_workspace`) also invalidates by-workspace; this is the only reconciliation path on the embed route. ## Confirmed exclusions - No optimistic patch in `updateChatWorkspace.onMutate`; the awaited `onSettled` invalidation already converges. - No cancellation guard for the by-workspace family (Phase 2 item 9 territory). - No `createChat` change (already invalidates); no ACL or pin changes. ## Known constraints - Cascade archives remove only the event's own chat ID from the mapping; per-family-member `deleted` events plus family invalidation repair the rest. - The REST `patchChat` workspace-rebind branch publishes no watch event server-side, so cross-session manual rebinds converge only via the acting session's `onSettled` or a WorkspacesPage remount; not fixable client-side. ## Testing - `chats.test.ts`: removal scoping and reference-preservation tests; `it.each` wiring tests (archiveChat/unarchiveChat/updateChatWorkspace onSettled, createChat onSuccess invalidate by-workspace); synchronous-removal assertion for `archiveChat.onSuccess`; negative assertions for `updateChatTitle` and `createChatMessage`. - `AgentsPageLayout.test.ts`: exhaustive `ChatWatchEventKind` table for `shouldInvalidateChatsByWorkspace`. - `useChatToolInvalidations.test.tsx`: create_workspace regression test extended with a seeded by-workspace bystander. PR generated by Coder Agents.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on
feat/chat-cache-semantic-ops.Bug
Chat search results (
chatSearchqueries) were never invalidated, so the search dialog served stale results after archives, renames, deletions, new chats, message edits, and watch-driven status changes.Fix
Reintroduces
invalidateChatSearches, a prefix invalidation over the module-privatechatSearchFamilyKey, and wires it into:chats.ts:archiveChat.onSettled,unarchiveChat.onSettled,updateChatTitle.onSettled,editChatMessage.onSettled,createChat.onSuccessuseChatStore.ts:upsertCacheMessages(unconditional; assistant message bodies are indexed too) andreplaceCacheMessagesAgentsPageLayout.tsx: thedeletedand rootcreatedwatch branches, the merge watch branch (gated by a new exportedshouldInvalidateChatSearcheshelper), thehas_unreadclearing effect, theonOpenreconnect convergence, andarchiveAndDeleteMutation.onSuccessThe merge-branch gate only invalidates for search-affecting event kinds (
title_change,status_change,diff_status_change,action_required).summary_change,chat_summary_change, andcontext_dirtyare excluded: stalelast_turn_summarysubtitles are accepted until reconciliation lands.Backend constraint
Message bodies only enter full-text search via the dbpurge backfill (
search_tsvstarts NULL and is populated every 10 minutes). Frontend invalidation fixes removals, ordering, and rendered fields immediately, but a chat that newly matches on message body will not appear until the next backfill. This is a server-side eventual-consistency limit we accept.Scope decisions (confirmed)
createChatMessage.onSuccess: the send path already routes throughuseChatStore.upsertCacheMessages; adding both would double-invalidate every send.pinChat/unpinChat/reorderPinnedChat(ordering-only, self-heals).Tests
qparams invalidated, bystanders (list, by-workspace, entity, messages, cost tree) untouched.archiveChat,unarchiveChat,updateChatTitle,editChatMessage, andcreateChatinvalidates a seeded search key;createChatMessageasserted NOT to.shouldInvalidateChatSearchesunit-tested over allChatWatchEventKindvalues.PR generated by Coder Agents.