feat(wing): score a wing by its best page, not summed hits; add wingbacktest command (#205 S1)

A first backtest showed BM25 summed over a wing's top hits favours big wings
(0/4 agreement). Best-page scoring is volume-robust; pinned by a test that
failed before the change.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-20 06:04:45 +02:00
co-authored by Claude Sonnet 5.5
parent 365130459f
commit 62de8ac2e6
3 changed files with 194 additions and 12 deletions
+5 -12
View File
@@ -11,7 +11,6 @@ import (
const (
maxQueryTerms = 12
minTermLen = 5
hitsPerWing = 3
)
// BM25Scorer scores a note against each wing by running the brain's own BM25
@@ -38,24 +37,18 @@ func (b *BM25Scorer) Score(text string) map[string]float64 {
return out
}
for _, w := range b.wings {
hits, err := search.Query(b.brainDir, search.QueryOptions{Query: query, Wing: w, Limit: hitsPerWing + 1})
hits, err := search.Query(b.brainDir, search.QueryOptions{Query: query, Wing: w, Limit: 2})
if err != nil {
continue
}
var sum float64
n := 0
for _, h := range hits {
if b.exclude != "" && h.Path == b.exclude {
continue
}
if n == hitsPerWing {
break
}
sum += float64(h.Score)
n++
}
if sum > 0 {
out[w] = sum
// Hits come best-first: a wing is scored by its single best
// page, so a big wing cannot win on volume alone.
out[w] = float64(h.Score)
break
}
}
return out