CPLAT-11801: show matched role in search results and rank user hits first - #165
Merged
Merged
Conversation
A search hit did not say whose words matched, and the ordering ignored role entirely. There is far more assistant text than user text, so on a broad query the replies crowd out the prompt you were actually trying to find — it sinks below the fold or gets cut by the result limit. Prefix each result with the app-wide role chip (usr/ast), the same one the conversation view uses, so a hit reads like a conversation row. An entry with no role gets no chip rather than an invented label. Rank user hits first WITHIN a session. Session recency still decides the order across sessions: hoisting every user hit globally would destroy the "what was I doing lately" browsing the list exists for. Making that survive the result limit took three attempts, and the two rejected ones are worth recording: - A plain SQL LIMIT truncates in rowid order, so user hits can be cut before Go ever ranks them. - Ordering by role in SQL fixes that but is global: an old session with many user hits fills the cap and the most recent session vanishes from the results entirely. A test caught this; TestLimitDoesNotStarveRecentSessions now pins it. - Fetching every hit ranks correctly but costs ~400ms on a broad query (measured on a 400 MB index). What shipped is a role-ordered over-fetch of 8x the limit: the user's words survive the cut, enough rows from enough sessions remain for the recency sort to mean something, and a broad query stays around 150ms. It bounds the problem rather than eliminating it — a query matching more than limit*8 user blocks in one old session could still crowd out a recent one — and sqlOverFetch documents that tail rather than implying a guarantee. The scan fallback uses the same ordering, so results do not reshuffle depending on whether the index could answer the query. Measured on the real corpus at limit 500: user hits in the top 500 go from 2 to 11 for "worktree" and from 81 to 500 for "the", at 128-152ms against the ~1.8s full scan this replaced.
Kairo-Kim
approved these changes
Aug 31, 2026
|
| Commit | Scanned at | New | Resolved | Net |
|---|---|---|---|---|
09d2ca4 < |
2026-08-31 01:18 UTC | 0 | 0 | 0 |
Last scanned: 09d2ca4 · 2026-08-31 01:18 UTC
|
| Commit | Scanned at | New | Resolved | Net |
|---|---|---|---|---|
09d2ca4 < |
2026-08-31 01:18 UTC | 0 | 0 | 0 |
Last scanned: 09d2ca4 · 2026-08-31 01:18 UTC
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
JIRA: https://sendbird.atlassian.net/browse/CPLAT-11801
Problem
A search hit doesn't say whose words matched. A hit in your prompt and a hit in the model's reply answer different questions, and the snippet alone rarely makes that obvious.
The ordering also ignored role entirely. There is far more assistant text than user text, so on a broad query the replies crowd out the prompt you were actually looking for — it sinks below the fold or gets cut by the result limit.
What this does
Shows the role. Each result is prefixed with the app-wide role chip (
usr/ast) — the same one the conversation view uses, so a hit reads like a conversation row. A roleless entry gets no chip rather than an invented label.Ranks user hits first, within a session. Session recency still decides the order across sessions; hoisting every user hit globally would destroy the "what was I doing lately" browsing the list exists for.
The interesting part
Making that survive the result limit took three attempts. The two rejected ones are in the commit message, but the middle one is worth flagging here:
I wrote that, a test caught it, and
TestLimitDoesNotStarveRecentSessionsnow pins the failure. Fetching every hit ranks correctly but costs ~400ms on a broad query.What shipped is a role-ordered over-fetch of 8x the limit. It's a bound, not a guarantee — a query matching more than
limit*8user blocks inside one old session could still crowd out a recent one — andsqlOverFetchdocuments that tail rather than implying it's solved.The scan fallback uses the same ordering, so results don't reshuffle depending on whether the index could answer the query.
Measured (real corpus, limit 500)
worktreethe(very broad)워크트리Slower than the 44ms this cost before (the ranking has to see more rows), still far below the ~1.8s full scan the index replaced.
Testing
go vet ./...clean.Note
Verified by tests and measurement, not interactively. Worth a
Ctrl+Son a broad query to check the chips are legible at a glance and that user-first actually feels like the right default.