Skip to content

CPLAT-11801: show matched role in search results and rank user hits first - #165

Merged
gavin-jeong merged 1 commit into
masterfrom
CPLAT-11801-search-role-rank
Aug 31, 2026
Merged

gavin-jeong merged 1 commit into
masterfrom
CPLAT-11801-search-role-rank

Conversation

@gavin-jeong

Copy link
Copy Markdown
Collaborator

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:

Ordering by role in SQL keeps user hits through the LIMIT — but the ranking is global, so an old session with many user hits fills the cap and the most recent session disappears from the results entirely.

I wrote that, a test caught it, and TestLimitDoesNotStarveRecentSessions now 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*8 user blocks inside one old session could still crowd out a recent one — and sqlOverFetch documents 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)

Query Time user hits in top 500
worktree 152ms 2 → 11
the (very broad) 128ms 81 → 500
워크트리 76ms 5

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

  • Full suite green, go vet ./... clean.
  • Covers: user-before-assistant within a session, session order NOT reordered by role, user hits surviving the limit, scan/index ordering parity, roleless entries sorting last, role chip rendering, and the starvation regression.
  • Mutation-tested: removed the role tie-break and removed the over-fetch — each fails its covering test.

Note

Verified by tests and measurement, not interactively. Worth a Ctrl+S on a broad query to check the chips are legible at a glance and that user-first actually feels like the right default.

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 Kairo-Kim added the auto-review/approved Auto-approved by the Slack auto-reviewer bot label Aug 31, 2026

@jinsekim jinsekim left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM!

@upwind-code-us

upwind-code-us Bot commented Aug 31, 2026

Copy link
Copy Markdown

Upwind Upwind Code Scan - ✅ Proceed with Deployment

0 newly introduced vulnerabilities · 0 resolved · 1 total in this PR vs master

🔶 1 High

View full analysis in Upwind Console

Scan completed in 15s

Scan history (1 scan)
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

@upwind-code-us

upwind-code-us Bot commented Aug 31, 2026

Copy link
Copy Markdown

Upwind Upwind IaC Scan - ✅ Proceed with Deployment

0 newly introduced misconfigurations · 0 resolved · 0 total in this PR vs main

View full analysis in Upwind Console →

Scan completed in 2s

Scan history (1 scan)
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

@gavin-jeong
gavin-jeong merged commit d5a0511 into master Aug 31, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

auto-review/approved Auto-approved by the Slack auto-reviewer bot

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants