Skip to content

Improve operational search ranking robustness - #244

Closed
kentcdodds wants to merge 1 commit into
mainfrom
search-ranking-robustness
Closed

kentcdodds wants to merge 1 commit into
mainfrom
search-ranking-robustness

Conversation

@kentcdodds

@kentcdodds kentcdodds commented Apr 21, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • improve unified search ranking for operational natural-language queries with targeted identity-aware boosts for packages and connectors
  • normalize query matching and de-emphasize generic operational filler terms so named integrations surface more reliably
  • add regression coverage for spotify-style and similar package search queries

Testing

  • npm run test -- packages/worker/src/mcp/tools/search.node.test.ts packages/worker/src/mcp/tools/search-handler.node.test.ts
  • npx oxfmt --check packages/worker/src/mcp/tools/search.ts packages/worker/src/mcp/tools/search.node.test.ts
  • npx oxlint packages/worker/src/mcp/tools/search.ts packages/worker/src/mcp/tools/search.node.test.ts
  • npm run typecheck
  • Terminal validation artifact
Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features

    • Enhanced search ranking with improved relevance scoring to surface more relevant packages and connectors at the top of search results.
  • Tests

    • Added test coverage to verify search result accuracy and package ranking behavior.

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@coderabbitai

coderabbitai Bot commented Apr 21, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR introduces token-based "operational identity" scoring to package and connector ranking in the search module. Helper functions normalize queries, extract tokens, and compute coverage across multiple fields (names, tags, exports, job names) to boost relevance scores. Tests validate the new ranking behavior for Spotify packages and named integrations.

Changes

Cohort / File(s) Summary
Search Implementation
packages/worker/src/mcp/tools/search.ts
Added token-based scoring helpers (normalization, extraction, filtering, coverage computation) for "operational identity" boost applied to both package and connector ranking. Updated package document construction to include exports and job names in searchable text. Modified both searchUnified and searchPackages to augment existing scores with identityBoost derived from relevant fields.
Search Tests
packages/worker/src/mcp/tools/search.node.test.ts
Added two new test cases validating ranking behavior: one verifying searchUnified surfaces Spotify-related results at the top, and another verifying searchPackages prioritizes named integrations over generic action-like matches. Imports expanded to include searchPackages function.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 With tokens dancing through the search, we boost the worthy find,
Spotify sings and integrations gleam, as scores align,
Operational identity blooms bright, each query now divine! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Improve operational search ranking robustness' directly and clearly describes the main change: enhancing search ranking for operational queries through identity-aware scoring boosts.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch search-ranking-robustness

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@kentcdodds
kentcdodds marked this pull request as ready for review April 21, 2026 04:10
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Preview deployed: https://kody-pr-244.kentcdodds.workers.dev

Worker: kody-pr-244
D1: kody-pr-244-db
KV: kody-pr-244-oauth-kv

Mocks:

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/worker/src/mcp/tools/search.ts`:
- Around line 290-305: The ranking uses projection.exports and projection.jobs
but those are initialized empty in the registerSearchTool flow; before calling
searchUnified (and before computing
lexical/vector/scoreOperationalIdentityBoost) ensure each entry's projection is
hydrated: populate entry.projection.exports and entry.projection.jobs from the
package metadata or by invoking the existing projection-enrichment routine used
by loadSearchRowsAndRegistry, and update the registerSearchTool path to set
those fields instead of leaving them empty so searchUnified and
scoreOperationalIdentityBoost see real export/job names.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fc07d110-4896-49c8-8d9e-dc30d13fee6b

📥 Commits

Reviewing files that changed from the base of the PR and between 849b60f and 970844c.

📒 Files selected for processing (2)
  • packages/worker/src/mcp/tools/search.node.test.ts
  • packages/worker/src/mcp/tools/search.ts

Comment on lines +290 to +305
entry.projection.exports.join(' '),
entry.projection.jobs.map((job) => job.name).join(' '),
].join('\n')
const lexical = lexicalScore(query, doc)
const vector = cosineSimilarity(
queryEmbedding,
deterministicEmbedding(doc),
)
const identityBoost = scoreOperationalIdentityBoost({
query,
primaryFields: [entry.record.kodyId, entry.record.name],
secondaryFields: entry.record.tags,
tertiaryFields: [
...entry.projection.exports,
...entry.projection.jobs.map((job) => job.name),
],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Hydrate package projections before relying on exports/jobs in unified ranking.

Line 290, Line 291, Line 303, and Line 304 now rank packages using projection.exports and projection.jobs, but the registered search tool path initializes those fields as empty at Line 705 and Line 706. That means export/job-name ranking works only in tests or custom callers that manually provide enriched projections, not in the normal registerSearchTool flow.

Please populate these projection fields before calling searchUnified or persist them into the saved package search projection used by loadSearchRowsAndRegistry.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/worker/src/mcp/tools/search.ts` around lines 290 - 305, The ranking
uses projection.exports and projection.jobs but those are initialized empty in
the registerSearchTool flow; before calling searchUnified (and before computing
lexical/vector/scoreOperationalIdentityBoost) ensure each entry's projection is
hydrated: populate entry.projection.exports and entry.projection.jobs from the
package metadata or by invoking the existing projection-enrichment routine used
by loadSearchRowsAndRegistry, and update the registerSearchTool path to set
those fields instead of leaving them empty so searchUnified and
scoreOperationalIdentityBoost see real export/job names.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 970844c. Configure here.

if (!field) continue
const normalizedField = extractSearchTokens(field).join(' ')
if (!normalizedField.includes(' ')) continue
if (normalizedQuery.includes(normalizedField)) return true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Phrase match ignores token boundaries causing false positives

Low Severity

hasPrimaryPhraseMatch joins tokens with spaces and uses String.includes() for substring matching, which doesn't enforce word boundaries. A field like "spotify-play" (normalized to "spotify play") falsely matches a query like "spotify playlist" (normalized to "spotify playlist") because the character sequence "spotify play" is a prefix of "spotify playlist". This grants an undeserved +0.2 identity boost whenever a field's final token happens to be a prefix of a different query token. A boundary-aware check (e.g., padding both strings with spaces before comparing) would prevent these false positives.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 970844c. Configure here.

@kentcdodds kentcdodds closed this Apr 21, 2026
@kentcdodds
kentcdodds deleted the search-ranking-robustness branch April 21, 2026 20:59
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.

2 participants