(
query: string,
limit: number,
results: {
semantic: Map<string, { chunk: CodeChunk; ranks: Array<{ rank: number; weight: number }> }>;
keyword: Map<string, { chunk: CodeChunk; ranks: Array<{ rank: number; weight: number }> }>;
},
profile: SearchIntentProfile,
intent: QueryIntent,
totalVariantWeight: number
)
| 513 | } |
| 514 | |
| 515 | private scoreAndSortResults( |
| 516 | query: string, |
| 517 | limit: number, |
| 518 | results: { |
| 519 | semantic: Map<string, { chunk: CodeChunk; ranks: Array<{ rank: number; weight: number }> }>; |
| 520 | keyword: Map<string, { chunk: CodeChunk; ranks: Array<{ rank: number; weight: number }> }>; |
| 521 | }, |
| 522 | profile: SearchIntentProfile, |
| 523 | intent: QueryIntent, |
| 524 | totalVariantWeight: number |
| 525 | ): SearchResult[] { |
| 526 | const likelyWiringQuery = this.isLikelyWiringOrFlowQuery(query); |
| 527 | const actionQuery = this.isActionOrHowQuery(query); |
| 528 | |
| 529 | // RRF: k=60 is the standard parameter (proven robust in Elasticsearch + TOSS paper arXiv:2208.11274) |
| 530 | const RRF_K = 60; |
| 531 | |
| 532 | // Collect all unique chunks from both retrieval channels |
| 533 | const allChunks = new Map<string, CodeChunk>(); |
| 534 | const rrfScores = new Map<string, number>(); |
| 535 | |
| 536 | // Gather all chunks |
| 537 | for (const [id, entry] of results.semantic) { |
| 538 | allChunks.set(id, entry.chunk); |
| 539 | } |
| 540 | for (const [id, entry] of results.keyword) { |
| 541 | if (!allChunks.has(id)) { |
| 542 | allChunks.set(id, entry.chunk); |
| 543 | } |
| 544 | } |
| 545 | |
| 546 | // Calculate RRF scores: RRF(d) = SUM(weight_i / (k + rank_i)) |
| 547 | for (const [id] of allChunks) { |
| 548 | let rrfScore = 0; |
| 549 | |
| 550 | // Add contributions from semantic ranks |
| 551 | const semanticEntry = results.semantic.get(id); |
| 552 | if (semanticEntry) { |
| 553 | for (const { rank, weight } of semanticEntry.ranks) { |
| 554 | rrfScore += weight / (RRF_K + rank); |
| 555 | } |
| 556 | } |
| 557 | |
| 558 | // Add contributions from keyword ranks |
| 559 | const keywordEntry = results.keyword.get(id); |
| 560 | if (keywordEntry) { |
| 561 | for (const { rank, weight } of keywordEntry.ranks) { |
| 562 | rrfScore += weight / (RRF_K + rank); |
| 563 | } |
| 564 | } |
| 565 | |
| 566 | rrfScores.set(id, rrfScore); |
| 567 | } |
| 568 | |
| 569 | // Normalize by theoretical maximum (rank-0 in every list), NOT by actual max. |
| 570 | // Using actual max makes top result always 1.0, breaking quality confidence gating. |
| 571 | const theoreticalMaxRrf = totalVariantWeight / (RRF_K + 0); |
| 572 | const maxRrfScore = Math.max(theoreticalMaxRrf, 0.01); |
no test coverage detected