Repository navigation
Conversation
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a focused server bug fix that makes thread search use readable citation text while preserving existing behavior for ordinary messages. The additive migration, fallback logic, and targeted projection/replay tests keep the change bounded and backward-compatible. You can add or adjust custom eligibility rules. Learn more. |
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Add and index projected search text apps/server/src/orchestration-v2/messageSearchIndex.ts, apps/server/src/persistence/Migrations/077_ProjectionMessageSearchText.ts, apps/server/src/persistence/Migrations/077_ProjectionMessageSearchText.test.ts, apps/server/src/persistence/Migrations.ts, apps/server/src/persistence/reconcileV2PreviewMigration.test.ts |
Migration 77 adds search_text and backfills eligible citation messages in batches. Search triggers and index rebuilds use the schema-appropriate text expression. Migration tests cover backfill and indexed search. |
Maintain search text on projected messages apps/server/src/orchestration-v2/ProjectionStore.ts |
Projection inserts and conflict updates store readable search text derived from message text. |
Search readable citation text apps/server/src/orchestration-v2/ThreadSearch.ts, apps/server/src/orchestration-v2/ThreadSearch.test.ts |
Thread search uses the shared message-search text expression for scans, indexed queries, and substring filtering. Tests cover citation matches, edits, index rebuilds, and projection replay. |
Estimated code review effort: 3 (Moderate) | ~25 minutes
Change: Bug fix
Possibly related issues
- Thread search can't find phrases inside quotes and quote comments, and shows raw
t3-citationlinks in results #22304 — The issue describes missing citation phrase matches and encoded citation snippets; this change adds readable citation text to search and snippets.
Suggested reviewers: maria-rcks, juliusmarminge
Merge Risk: 
Minimal · up to 7c15a
-
No identified issue remains that should block merging after normal checks.
Architecture risk: 
Low · up to bb269
-
The change affects 1 system.
Changed systems:
apps/serverArchitecture concerns
No architecture-level concerns identified.Review details
-
Systems and components
- observed — apps/server (service) was modified; 7 changed files map to changed impact.
Before / after behavior
- observed — Modified behavior in apps/server/src/orchestration-v2/ProjectionStore.ts: Added the
assistantCitationsToPlainTextimport used to derive searchable message text. - observed — Modified behavior in apps/server/src/orchestration-v2/ProjectionStore.ts:
message.updatednow derives citation-stripped plain text from the message text for search indexing. - observed — Modified behavior in apps/server/src/orchestration-v2/ProjectionStore.ts: The projected messages insert now includes the
search_textcolumn. - observed — Modified behavior in apps/server/src/orchestration-v2/ProjectionStore.ts: Message inserts store normalized citation text, using
NULLwhen it matches the original message text.
-
Pre-merge checks: 
3 · 
1
-
Failed checks: 1 warning Passed checks: 3 passed-
Full details: Title check
-
Explanation
The title "fix(server): thread search finds phrases inside quotes and their comments" matches the main change. The PR stores readable citation text in a new search_text column, backfills it via migration 071, and makes ThreadSearch match COALESCE(search_text, text). It is a conventional commit title in plain language, concise, and specific. The 73-character length is within normal limits.
-
Full details: Description check
-
Explanation
The description includes Problem, Change, Scope and approval, and Verification sections. It gives detailed behavior, implementation scope, test results, manual checks, limitations, screenshots, and agent attribution. However, the Scope and approval section does not include an explicit maintainer approval comment or explain why the focused-fix exemption applies.
Resolution
Add a link to the triaged issue or discussion with the maintainer's explicit approval of the direction and scope, including the approval comment. If no approval is required, explain why this qualifies as a focused fix of an obvious bug under the repository requirements.
-
Finishing Touches
-
Generate unit tests (beta)
-
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
…ents A quote is stored in message text as a serialized t3-citation link, so its text and comment are percent-encoded: a phrase with spaces never matched, and accented, CJK and apostrophe words in a quote never matched through the index. Projected messages now keep a readable search_text, made with assistantCitationsToPlainText, whenever it differs from the stored text. Migration 077 adds the column, recreates the search triggers and backfills messages that hold a citation link; the update trigger re-indexes each one it changes. createMessageSearchTriggers and the doctor rebuild index search_text once the column exists, so migrations 69 and 74, which run before it, keep indexing the stored text, and a later migration that recreates the triggers keeps quote indexing. Thread search matches and builds snippets from the same text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… rule - recreateMessageSearchTriggers replaces the drop-and-recreate block that migrations 74 and 77 each wrote out. - readableSearchText is the one rule for what search_text holds, used by the projection and the backfill. - The update trigger compares the text the index stores, so it has one shape before and after migration 77 and still re-indexes the backfill. - The backfill checks the raw payload for a citation link before parsing its JSON. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Migration 074 called the trigger helper directly; only migration 77 needs the shared recreateMessageSearchTriggers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem
Thread search can't find a phrase inside a quote or its comment (#22304). A quote is stored in the message text as an
[Assistant quote](t3-citation://v1/…)link, andURLSearchParamsencodes the quote and comment inside it:what are you talking aboutis stored aswhat+are+you+talking+about. Thread search matches that stored text, both through the message index and the fallback scan, so phrases with spaces never match, and accented, CJK and apostrophe words in a quote never match through the index. Single words do, including words from the quote's hiddenprefixandsuffixselection context, so a thread can show up for a word its reader never saw in it.Change
ProjectionStoreprojects a message, it now storessearch_textonorchestration_v2_projection_messages:assistantCitationsToPlainText(text), the helper the queued-message and stash previews already use. A quote becomes its text plusComment: …, without the link or its hidden context. The column isNULLwhen that equals the stored text, so plain messages aren't stored twice. Everymessage.updatedupsert rewrites it, so edits and projection rebuilds keep it current.t3-citation://, 500 at a time bymessage_id, and writessearch_textonly where the readable text differs; the update trigger re-indexes each message it changes.createMessageSearchTriggers, which now indexessearch_textonce the column exists. Migrations 069 and 074 are unchanged and, running before 077, keep indexing the stored text; a later migration that recreates the triggers through the helper keeps quote indexing instead of reverting it. Migration 077 recreates them through a newrecreateMessageSearchTriggers.rebuildMessageSearch(t3 doctor,migrate-dev-db) indexes the same text.COALESCE(search_text, text)and builds the snippet from the same value, through onemessageSearchTexthelper, on the same reader-worker query.Scope and approval
Refs #22304. This fixes its matching half. @EtanHey reported it, traced both halves and suggested a derived search-text column written at projection time; this builds that.
Snippets come from the matched text, so a quoted match also shows readable text instead of the encoded link.
Verification
Linux (Ubuntu 26.04), in
apps/server:ThreadSearch.test.tscases project a quote with hidden prefix and suffix words, find its accented, CJK and apostrophe words through the index, rebuild the index, and replay the quote from events throughProjectionMaintenance.rebuild. Quote and comment phrases match and the hidden words don't. After a secondmessage.updateddrops the quote, the comment phrase stops matching.077_ProjectionMessageSearchText.test.tsmigrates to 076, inserts a quoted message, a plain one, an invalid citation link, a non-string text, an accented quote and 501 quoted rows to cross a page, then runs 077 and checkssearch_textand the index. A second case recreates the triggers throughrecreateMessageSearchTriggers, as a later migration would, and checks that a new quote is still indexed as it reads.54c5f25249, the three newThreadSearchcases fail (3 failed, 26 passed). On this branch,ThreadSearch, 077, 069,MigrationsandreconcileV2PreviewMigrationpass, 49 of 49.vp run check:changed: format, lint, knip, typecheck and tests pass.Real app, on every client: each run starts on main with an isolated state of test threads, where I selected a line of an agent reply (from the dev Replay provider), chose Cite, added the comment
what are you talking about, moved the quote into a new thread withquiet silver lantern, and sent it. Then I stopped main and started this branch on the same state. It ran only migration 077, which filledsearch_textfor that quoted message. Searching for the comment finds nothing on main; after the upgrade it finds the quoting thread with a readable snippet.Web (dev servers, Chromium):
A quote sent after the upgrade goes through the projection path and is found by its comment.
https://gist.githubusercontent.com/only21mil/9900a9292b7d0ac07e57580b0abdd405/raw/cb5ed48448bac8b79beee211fcf74e22cf06c5df/pr22553r3-branch-new-quote.png
Desktop (Linux Electron under Xvfb,
vp run dev:desktopof main and of this branch):Mobile (T3 Code Dev client on an Android 16 emulator and an iOS 26.5 simulator, against main's and this branch's dev servers):
Not checked:
Prior PRs: #22391 (open) fixes the snippet only. No open or closed PR changes the matching. #16858 (open) edits the same query for qualifiers and may need a small rebase.
Generated with Claude Opus 5.5 in T3 Code Nightly