From b97237e92382c20126d8a84cae8cccc6b2ce96b7 Mon Sep 17 00:00:00 2001 From: dvcdsys Date: Wed, 19 Aug 2026 19:00:52 +0100 Subject: [PATCH 1/6] perf(chunksfts): rank the whole workspace in one FTS5 query MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stage 2 of the search-perf plan, and the change stage 1's timings pointed at: BM25 was 78-80% of the workspace fan-out's work. The reason is structural. FTS5 drives the query: it evaluates MATCH over the WHOLE chunks_fts table — every project on the server — joins each hit to chunks_meta, and only then discards the rows belonging to other projects. So a per-project BM25 query costs about the same whichever project it names, and a 43-project workspace paid for the same global match 43 times. On top of that the 43 queries contended over one index: a repo's BM25 measured 326-542 ms standalone but up to 7,000 ms inside the fan-out. chunksfts.SearchProjects replaces them with one statement that ranks within each project via ROW_NUMBER() OVER (PARTITION BY project_path ORDER BY bm, rowid) and keeps each project's top rows. The IN list is batched at 500 paths to stay under SQLite's 999-variable ceiling, the same batch size the vector store already uses. In the handler the query runs in the fan-out's errgroup alongside the dense scans rather than before them, so nothing serialises; fusion moves out of the per-project goroutines because it now needs both sides, and it was under a millisecond across the whole fan-out anyway. Measured on the 45-repo fixture (1.9M chunks, 43-project workspace, 10 queries, medians). Both builds were run back to back against the same already-warm page cache — a process restart does not evict it — with one warm-up pass discarded each time, because a first comparison across a cold restart credited this change with twice the improvement it earned: phase develop stage 2 wall 10,235 4,650 2.2x fan-out 9,961 4,376 2.3x BM25 111,210 4,375 25.4x (summed over 43 -> one query) dense sum 15,166 11,136 1.4x dense max 2,318 1,123 2.1x The dense rows are the ones worth pausing on: nothing in the dense path changed. Removing 43 concurrent FTS queries gave the vector scans back the CPU and I/O they were contending for, which is worth 1.4x on the work and 2.1x on the project anyone actually waits for. BM25 is no longer the dominant term: at 4,375 ms it now sits level with the fan-out's own wall time, so the single FTS query IS the critical path. Whatever comes next should start there rather than from the old 78-80% figure. Correctness, on the fixture, 50 queries, full response captured per query: the BM25 signal is IDENTICAL in all 500 panel rows, and the project panel order is identical for all 50 queries. Dense scores wobble by <=0.0015 in a few percent of rows — but the same binary compared against ITSELF wobbles at least as much (30 rows vs 38), so that is a pre-existing property of the fixture, not this change. Its cause is not established; single-project search repeats bit-identically, and three consecutive workspace queries repeat bit-identically, so it correlates with machine load rather than with the query. Both orderings are (bm ASC, rowid ASC). The rowid is defensive rather than a fix: bm25 ties are the norm in a trigram index — 14 of 16 hits in the package's own test corpus share a score — and SQLite happens to return tied rows in rowid order for both the LIMIT and the window form today, so they agree without being told to. That is unspecified sorter behaviour, and naming the tiebreak makes the agreement a property of the queries instead of a coincidence. bm25_sum_ms and bm25_max_ms collapse into bm25_ms. The split existed to separate "work done" from "waited for" across N queries; with one query they are the same number, and keeping both would imply a fan-out that no longer happens. The blast radius grew and the tests say so: BM25 used to fail per project, and now one failing query costs every project its sparse signal at once. The fallback is the one a pre-FTS install already lives with — dense-only results, no failed_repos, no 500 — and TestWorkspaceSearch_SurvivesBM25Failure drops chunks_fts outright to prove it. Tests, each mutation-checked against the bug it describes: - SearchProjects matches SearchProject per project, same hits, same order, same scores, over four queries x two limits x four projects on a tie-heavy corpus; - the IN list does not prefix-match (project paths routinely share prefixes: "local:host:/x" vs "local:host:/x/y"); - the map survives the batch boundary (searchProjectsBatch + 7 projects); - BM25 hits stay in their own project end-to-end through the handler; - a total BM25 failure still returns dense results. Co-Authored-By: Claude Opus 5 --- doc/openapi.yaml | 14 +- server/internal/chunksfts/chunksfts.go | 155 +- server/internal/chunksfts/chunksfts_test.go | 207 +++ .../internal/httpapi/openapi/openapi.gen.go | 1440 +++++++++-------- server/internal/httpapi/searchtimings.go | 15 +- server/internal/httpapi/workspacesearch.go | 128 +- .../internal/httpapi/workspacesearch_test.go | 154 +- 7 files changed, 1317 insertions(+), 796 deletions(-) diff --git a/doc/openapi.yaml b/doc/openapi.yaml index 6a1dff13..6243d5a3 100644 --- a/doc/openapi.yaml +++ b/doc/openapi.yaml @@ -6606,14 +6606,16 @@ components: The slowest single project's dense search. May belong to a project whose query failed; the fan-out logs a warning of its own for those. - bm25_sum_ms: + bm25_ms: type: integer description: | - FTS5/BM25 search summed across projects, on the same terms as - dense_sum_ms. - bm25_max_ms: - type: integer - description: The slowest single project's BM25 search. + The workspace's BM25 search. One FTS5 statement covering every + project, partitioned per project by a window function — not a + sum over projects, which is why it has no matching `_max` + field. `MATCH` is evaluated over the whole server's index + whatever the scope, so asking once per project repeated the + same global work N times and the N queries contended over one + index on top of that. fuse_ms: type: integer description: Normalisation, candidacy blending and thresholding. diff --git a/server/internal/chunksfts/chunksfts.go b/server/internal/chunksfts/chunksfts.go index 73da2b6f..429735eb 100644 --- a/server/internal/chunksfts/chunksfts.go +++ b/server/internal/chunksfts/chunksfts.go @@ -199,6 +199,10 @@ func DeleteByProject(ctx context.Context, db *sql.DB, projectPath string) error // // Empty or all-tokens-too-short queries return a nil slice without // hitting the DB — there is nothing to match. +// +// For more than one project use SearchProjects: this query costs about +// the same whichever project it is restricted to, so running it once per +// project is the expensive way to ask. func SearchProject(ctx context.Context, db *sql.DB, projectPath, query string, limit int) ([]Hit, error) { if limit <= 0 { limit = 20 @@ -214,7 +218,7 @@ func SearchProject(ctx context.Context, db *sql.DB, projectPath, query string, l FROM chunks_fts cf JOIN chunks_meta cm ON cm.rowid = cf.rowid WHERE chunks_fts MATCH ? AND cm.project_path = ? - ORDER BY bm ASC + ORDER BY bm ASC, cm.rowid ASC LIMIT ?`, fts5Q, projectPath, limit, ) @@ -224,29 +228,162 @@ func SearchProject(ctx context.Context, db *sql.DB, projectPath, query string, l defer rows.Close() var out []Hit for rows.Next() { + h, err := scanHit(rows) + if err != nil { + return nil, err + } + out = append(out, h) + } + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("iterate chunks_fts: %w", err) + } + return out, nil +} + +// searchProjectsBatch caps how many project_path values go into one IN +// list. SQLite's default bound-variable ceiling is 999; 500 leaves room +// for the query and limit parameters and matches the batch size the +// vector store already uses for its own IN lists. +const searchProjectsBatch = 500 + +// SearchProjects answers the same question as SearchProject for many +// projects at once, returning each project's top `perProject` hits keyed +// by project_path. Projects with no match are absent from the map rather +// than present with an empty slice — a caller distinguishing "nothing +// matched" from "not asked about" gets that for free, and "BM25 found +// nothing here" is a signal this package exists to produce. +// +// Why this is not a loop over SearchProject: FTS5 drives the query. It +// evaluates MATCH over the WHOLE chunks_fts table — every project on the +// server — joins each hit to chunks_meta, and only then discards the rows +// belonging to other projects. So the per-project cost barely depends on +// the project's size, and asking N times does the same global work N +// times. Measured on a 43-project workspace, BM25 was 78-80% of the +// fan-out's total work, and each query slowed from ~400 ms standalone to +// as much as 7 s when 43 of them ran against the index at once. +// +// The window function does the partitioning SQLite would otherwise make +// us do with N queries: rank within each project, keep the top rows of +// each. +// +// Both forms order by (bm ASC, rowid ASC). The rowid is defensive, not a +// fix for an observed bug: bm25 ties are the norm rather than the +// exception in a trigram index over real code — in this package's own +// test corpus 14 of 16 hits share a score with another hit — and today +// SQLite happens to return tied rows in rowid order for both the LIMIT +// and the window form, so they agree without being told to. That is +// unspecified behaviour of the sorter. Naming the tiebreak makes the +// agreement a property of the queries instead of a coincidence that a +// future planner is free to break. +func SearchProjects(ctx context.Context, db *sql.DB, projectPaths []string, query string, perProject int) (map[string][]Hit, error) { + if perProject <= 0 { + perProject = 20 + } + fts5Q := buildFTS5Query(query) + if fts5Q == "" || len(projectPaths) == 0 { + return nil, nil + } + + out := make(map[string][]Hit, len(projectPaths)) + for start := 0; start < len(projectPaths); start += searchProjectsBatch { + end := start + searchProjectsBatch + if end > len(projectPaths) { + end = len(projectPaths) + } + if err := searchProjectsBatchInto(ctx, db, projectPaths[start:end], fts5Q, perProject, out); err != nil { + return nil, err + } + } + return out, nil +} + +func searchProjectsBatchInto(ctx context.Context, db *sql.DB, projectPaths []string, + fts5Q string, perProject int, dst map[string][]Hit) error { + + args := make([]any, 0, len(projectPaths)+2) + args = append(args, fts5Q) + for _, pp := range projectPaths { + args = append(args, pp) + } + args = append(args, perProject) + + q := fmt.Sprintf(` + WITH hits AS ( + SELECT cm.project_path AS pp, cm.rowid AS rid, + cm.file_path, cm.start_line, cm.end_line, + cm.chunk_type, cm.symbol_name, cm.language, + cf.content, bm25(chunks_fts) AS bm + FROM chunks_fts cf + JOIN chunks_meta cm ON cm.rowid = cf.rowid + WHERE chunks_fts MATCH ? AND cm.project_path IN (%s) + ) + SELECT pp, file_path, start_line, end_line, + chunk_type, symbol_name, language, content, bm + FROM (SELECT *, ROW_NUMBER() OVER ( + PARTITION BY pp ORDER BY bm ASC, rid ASC) AS rn + FROM hits) + WHERE rn <= ? + ORDER BY pp, rn`, placeholders(len(projectPaths))) + + rows, err := db.QueryContext(ctx, q, args...) + if err != nil { + return fmt.Errorf("chunks_fts workspace search: %w", err) + } + defer rows.Close() + for rows.Next() { + var ( + pp string + h Hit + ) var ( - h Hit chunkT sql.NullString symName sql.NullString language sql.NullString bm float64 ) - if err := rows.Scan(&h.FilePath, &h.StartLine, &h.EndLine, + if err := rows.Scan(&pp, &h.FilePath, &h.StartLine, &h.EndLine, &chunkT, &symName, &language, &h.Content, &bm); err != nil { - return nil, fmt.Errorf("scan chunks_fts row: %w", err) + return fmt.Errorf("scan chunks_fts row: %w", err) } h.ChunkType = chunkT.String h.SymbolName = symName.String h.Language = language.String - // SQLite returns more-negative bm25 for better matches. Flip so - // callers can blend with cosine-style "higher is better" scores. h.Score = -bm - out = append(out, h) + dst[pp] = append(dst[pp], h) } if err := rows.Err(); err != nil { - return nil, fmt.Errorf("iterate chunks_fts: %w", err) + return fmt.Errorf("iterate chunks_fts: %w", err) } - return out, nil + return nil +} + +func placeholders(n int) string { + if n <= 0 { + return "NULL" + } + return strings.TrimSuffix(strings.Repeat("?,", n), ",") +} + +// scanHit reads one single-project ranking row. +func scanHit(rows *sql.Rows) (Hit, error) { + var ( + h Hit + chunkT sql.NullString + symName sql.NullString + language sql.NullString + bm float64 + ) + if err := rows.Scan(&h.FilePath, &h.StartLine, &h.EndLine, + &chunkT, &symName, &language, &h.Content, &bm); err != nil { + return Hit{}, fmt.Errorf("scan chunks_fts row: %w", err) + } + h.ChunkType = chunkT.String + h.SymbolName = symName.String + h.Language = language.String + // SQLite returns more-negative bm25 for better matches. Flip so + // callers can blend with cosine-style "higher is better" scores. + h.Score = -bm + return h, nil } // buildFTS5Query turns a free-text query into a safe FTS5 expression: diff --git a/server/internal/chunksfts/chunksfts_test.go b/server/internal/chunksfts/chunksfts_test.go index ab9e4de1..17500325 100644 --- a/server/internal/chunksfts/chunksfts_test.go +++ b/server/internal/chunksfts/chunksfts_test.go @@ -3,6 +3,7 @@ package chunksfts import ( "context" "database/sql" + "fmt" "strings" "testing" @@ -270,3 +271,209 @@ func TestBuildFTS5Query(t *testing.T) { } } } + +// hitKey identifies a hit for comparison. Content is included because two +// chunks of the same file can share a line span only if something upstream +// is wrong, and if that ever happens the comparison should notice. +func hitKey(h Hit) string { + return fmt.Sprintf("%s:%d-%d|%s|%.6f", h.FilePath, h.StartLine, h.EndLine, h.SymbolName, h.Score) +} + +func keysOf(hits []Hit) []string { + out := make([]string, 0, len(hits)) + for _, h := range hits { + out = append(out, hitKey(h)) + } + return out +} + +// seedCorpus fills several projects with overlapping vocabulary, so BM25 has +// something to rank rather than a single obvious winner per project. +func seedCorpus(t *testing.T, d *sql.DB, projects []string) { + t.Helper() + bodies := []string{ + "func retryWithBackoff(ctx context.Context) error { return retry(ctx) }", + "// retry policy: exponential backoff with jitter, capped at one minute", + "func backoffDuration(attempt int) time.Duration { return base << attempt }", + "type RetryPolicy struct { MaxAttempts int; Backoff time.Duration }", + "// no mention of the interesting words at all, just filler content here", + "func retry(ctx context.Context) error { for { if err := do(); err == nil { return nil } } }", + } + for pi, p := range projects { + for bi, body := range bodies { + // Vary how many copies each project gets so BM25 scores differ + // across projects rather than tying everywhere. + copies := 1 + (pi+bi)%3 + chunks := make([]Chunk, 0, copies) + for c := 0; c < copies; c++ { + chunks = append(chunks, Chunk{ + Content: body, + FilePath: fmt.Sprintf("src/f%02d.go", bi), + StartLine: 1 + c*10, + EndLine: 5 + c*10, + SymbolName: fmt.Sprintf("S%02d", bi), + Language: "go", + }) + } + upsert(t, d, p, fmt.Sprintf("src/f%02d.go", bi), chunks) + } + // Deliberate BM25 ties: byte-identical chunks in different files + // score identically, so any limit below their count forces the + // engine to pick some of them arbitrarily. Without an explicit + // tiebreak the per-project query and the partitioned one are free + // to pick differently, and the equivalence this test asserts would + // hold only by luck. Ties are not exotic here — a trigram index + // over real code is full of near-duplicate boilerplate. + // file_path and symbol_name are indexed columns, so a tie needs all + // three to match: same file, same symbol, same content. Only the + // line span differs, and line numbers are not part of the index. + tied := make([]Chunk, 0, 6) + for i := 0; i < 6; i++ { + tied = append(tied, Chunk{ + Content: "func retryWithBackoff(ctx context.Context) error { return retry(ctx) }", + FilePath: "src/tied.go", + StartLine: 1 + i*10, + EndLine: 5 + i*10, + SymbolName: "Tie", + Language: "go", + }) + } + upsert(t, d, p, "src/tied.go", tied) + } +} + +// TestSearchProjects_MatchesPerProjectQueries is the equivalence test for the +// workspace-wide query: for every project, the partitioned result must be what +// the per-project query returns — same hits, same order, same scores. +// +// This is the whole safety argument for replacing N queries with one. The +// per-project BM25 signal feeds project candidacy in workspace search, so a +// partitioned result that merely contains the right rows in a different order +// would silently re-rank projects. +func TestSearchProjects_MatchesPerProjectQueries(t *testing.T) { + d := openTestDB(t) + ctx := context.Background() + projects := []string{"proj-a", "proj-b", "proj-c", "proj-d"} + seedCorpus(t, d, projects) + + for _, query := range []string{ + "retry backoff", + "retry", + "exponential backoff with jitter", + "nothing matches this string xyzzy", + } { + for _, limit := range []int{3, 50} { + batched, err := SearchProjects(ctx, d, projects, query, limit) + if err != nil { + t.Fatalf("SearchProjects(%q, %d): %v", query, limit, err) + } + for _, p := range projects { + want, err := SearchProject(ctx, d, p, query, limit) + if err != nil { + t.Fatalf("SearchProject(%q, %q): %v", p, query, err) + } + got := batched[p] + if len(want) == 0 { + if _, present := batched[p]; present { + t.Errorf("%q/%q limit=%d: project with no hits is present in the map", + query, p, limit) + } + continue + } + gk, wk := keysOf(got), keysOf(want) + if len(gk) != len(wk) { + t.Errorf("%q/%q limit=%d: got %d hits, per-project query returns %d", + query, p, limit, len(gk), len(wk)) + continue + } + for i := range wk { + if gk[i] != wk[i] { + t.Errorf("%q/%q limit=%d: rank %d differs\n got %s\n want %s", + query, p, limit, i, gk[i], wk[i]) + break + } + } + } + } + } +} + +// TestSearchProjects_DoesNotPrefixMatchProjectPaths guards the IN list. Project +// paths are namespaced strings that routinely share prefixes — "local:host:/x" +// and "local:host:/x/y" are different projects — so an implementation that +// filtered with LIKE, or that built the list by concatenation, would leak one +// project's chunks into another's slice. +func TestSearchProjects_DoesNotPrefixMatchProjectPaths(t *testing.T) { + d := openTestDB(t) + ctx := context.Background() + + upsert(t, d, "proj", "a.go", []Chunk{ + {Content: "func retryWithBackoff() {}", FilePath: "a.go", StartLine: 1, EndLine: 2, Language: "go"}, + }) + upsert(t, d, "proj-extended", "b.go", []Chunk{ + {Content: "func retryWithBackoff() {}", FilePath: "b.go", StartLine: 1, EndLine: 2, Language: "go"}, + }) + + got, err := SearchProjects(ctx, d, []string{"proj"}, "retry", 50) + if err != nil { + t.Fatalf("SearchProjects: %v", err) + } + if len(got) != 1 { + t.Fatalf("asked about one project, got slices for %d: %v", len(got), got) + } + for _, h := range got["proj"] { + if h.FilePath != "a.go" { + t.Errorf("hit from another project leaked in: %+v", h) + } + } + if _, present := got["proj-extended"]; present { + t.Error("a project that was not asked about appears in the result") + } +} + +// TestSearchProjects_SpansTheBatchBoundary checks the IN-list batching. The +// map is filled across several statements, so a batch that overwrote instead +// of appending, or that dropped its last slice, would only show up above the +// batch size. +func TestSearchProjects_SpansTheBatchBoundary(t *testing.T) { + d := openTestDB(t) + ctx := context.Background() + + const n = searchProjectsBatch + 7 + projects := make([]string, 0, n) + for i := 0; i < n; i++ { + p := fmt.Sprintf("p%04d", i) + projects = append(projects, p) + upsert(t, d, p, "a.go", []Chunk{ + {Content: "func retryWithBackoff() {}", FilePath: "a.go", StartLine: 1, EndLine: 2, Language: "go"}, + }) + } + + got, err := SearchProjects(ctx, d, projects, "retry", 50) + if err != nil { + t.Fatalf("SearchProjects: %v", err) + } + if len(got) != n { + t.Errorf("got hits for %d projects, want %d — a batch was lost or overwritten", len(got), n) + } + for _, p := range projects { + if len(got[p]) != 1 { + t.Errorf("%s: got %d hits, want 1", p, len(got[p])) + } + } +} + +// TestSearchProjects_EmptyInputs pins the two no-op paths, both of which must +// avoid touching the DB: nothing to match, and nobody to match it for. +func TestSearchProjects_EmptyInputs(t *testing.T) { + d := openTestDB(t) + ctx := context.Background() + seedCorpus(t, d, []string{"proj-a"}) + + if got, err := SearchProjects(ctx, d, []string{"proj-a"}, " x ", 50); err != nil || got != nil { + t.Errorf("all-tokens-too-short query: got %v, %v; want nil, nil", got, err) + } + if got, err := SearchProjects(ctx, d, nil, "retry", 50); err != nil || got != nil { + t.Errorf("no projects: got %v, %v; want nil, nil", got, err) + } +} diff --git a/server/internal/httpapi/openapi/openapi.gen.go b/server/internal/httpapi/openapi/openapi.gen.go index 9335610f..c8d8a047 100644 --- a/server/internal/httpapi/openapi/openapi.gen.go +++ b/server/internal/httpapi/openapi/openapi.gen.go @@ -3332,12 +3332,14 @@ type WorkspaceSearchStaleFTSRepo struct { // round-robin interleave and writing the response — all in memory and, // on the load-test fixture, ~19 ms of ~9,900 ms. type WorkspaceSearchTimings struct { - // Bm25MaxMs The slowest single project's BM25 search. - Bm25MaxMs *int `json:"bm25_max_ms,omitempty"` - - // Bm25SumMs FTS5/BM25 search summed across projects, on the same terms as - // dense_sum_ms. - Bm25SumMs *int `json:"bm25_sum_ms,omitempty"` + // Bm25Ms The workspace's BM25 search. One FTS5 statement covering every + // project, partitioned per project by a window function — not a + // sum over projects, which is why it has no matching `_max` + // field. `MATCH` is evaluated over the whole server's index + // whatever the scope, so asking once per project repeated the + // same global work N times and the N queries contended over one + // index on top of that. + Bm25Ms *int `json:"bm25_ms,omitempty"` // DenseMaxMs The slowest single project's dense search. May belong to a // project whose query failed; the fan-out logs a warning of its @@ -8169,718 +8171,720 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl // const string: with thousands of chunks the chained `+` fold is several // times slower for the Go compiler than parsing a slice literal. var swaggerSpec = []string{ - "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", - "9Ln/9pff/r8AAAD//w==", + "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", + "fwEAAP//", } // decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, diff --git a/server/internal/httpapi/searchtimings.go b/server/internal/httpapi/searchtimings.go index a79f6b9a..fc8dde8d 100644 --- a/server/internal/httpapi/searchtimings.go +++ b/server/internal/httpapi/searchtimings.go @@ -61,10 +61,15 @@ type searchPhases struct { fanOut time.Duration fuse time.Duration + // bm25 is a single duration, not a sum and a max, because there is a + // single query: one workspace-wide FTS5 statement partitioned per + // project. It ran per project once, and the sum/max split existed to + // separate "work done" from "waited for" across those. With one query + // they are the same number. + bm25 time.Duration + denseSum atomic.Int64 // nanoseconds denseMax atomic.Int64 - bm25Sum atomic.Int64 - bm25Max atomic.Int64 } // addDense records one project's dense-side latency. Includes the vector @@ -78,9 +83,6 @@ type searchPhases struct { // the fan-out logs its own warning per failed project. func (p *searchPhases) addDense(d time.Duration) { addSumMax(&p.denseSum, &p.denseMax, d) } -// addBM25 records one project's BM25-side latency. -func (p *searchPhases) addBM25(d time.Duration) { addSumMax(&p.bm25Sum, &p.bm25Max, d) } - func addSumMax(sum, max *atomic.Int64, d time.Duration) { n := d.Nanoseconds() sum.Add(n) @@ -117,8 +119,7 @@ func (p *searchPhases) payload(wall time.Duration, scanned, returned, panel int) "fanout_ms": ms(p.fanOut), "dense_sum_ms": msn(p.denseSum.Load()), "dense_max_ms": msn(p.denseMax.Load()), - "bm25_sum_ms": msn(p.bm25Sum.Load()), - "bm25_max_ms": msn(p.bm25Max.Load()), + "bm25_ms": ms(p.bm25), "fuse_ms": ms(p.fuse), "projects_scanned": scanned, "projects_returned": returned, diff --git a/server/internal/httpapi/workspacesearch.go b/server/internal/httpapi/workspacesearch.go index 0e28f648..b8e33f40 100644 --- a/server/internal/httpapi/workspacesearch.go +++ b/server/internal/httpapi/workspacesearch.go @@ -480,7 +480,7 @@ func (s *Server) reportSearchTimings(workspaceID, query string, p *searchPhases, // line up when read by eye, which is the only way anyone reads them. var timingFields = []string{ "wall_ms", "embed_ms", "resolve_ms", "stale_fts_ms", "fanout_ms", - "dense_sum_ms", "dense_max_ms", "bm25_sum_ms", "bm25_max_ms", + "dense_sum_ms", "dense_max_ms", "bm25_ms", "fuse_ms", "projects_scanned", "projects_returned", "projects_in_panel", } @@ -612,17 +612,27 @@ func (s *Server) detectStaleFTSRepos(ctx context.Context, projectPaths []string) return out } -// fanOutHybrid runs dense + BM25 in parallel per project, fuses each -// project's two ranked lists via RRF, and returns the per-project -// aggregates the candidacy step needs. Bounded by NumCPU goroutines -// across the workspace; each project is one slot regardless of -// whether it issues one or two sub-queries. +// fanOutHybrid runs the dense scan per project and BM25 once for the +// whole workspace, fuses each project's two ranked lists via RRF, and +// returns the per-project aggregates the candidacy step needs. Bounded +// by NumCPU goroutines; the BM25 query takes one of those slots and runs +// alongside the dense scans rather than before them. // -// Per-project failures: a BM25-side error is logged but does not mark -// the project as failed (FTS5 might not be populated yet for a -// pre-existing install; dense still works). A dense-side error is -// surfaced via failed_repos and dense_signal is left at 0 — the -// project can still be retained if BM25 alone is strong. +// BM25 used to be per-project too, which is the expensive way to ask. +// FTS5 evaluates MATCH over the whole server's chunks_fts and filters by +// project afterwards, so N projects meant N evaluations of the same +// global match — and, measured on a 43-project workspace, 78-80% of the +// fan-out's total work plus an order-of-magnitude slowdown from the +// queries contending with each other over one index. See +// chunksfts.SearchProjects. +// +// Failures: a BM25-side error is logged but fails nothing (FTS5 might +// not be populated yet for a pre-existing install; dense still works). +// It is now one error for the workspace rather than one per project — +// the blast radius grew, which is the cost of asking once, and the +// fallback is the same one a pre-FTS install already relies on. A +// dense-side error is surfaced via failed_repos and dense_signal is left +// at 0 — the project can still be retained if BM25 alone is strong. func (s *Server) fanOutHybrid( ctx context.Context, workspaceID string, @@ -643,14 +653,36 @@ func (s *Server) fanOutHybrid( results := make([]projectHits, len(projectPaths)) failures := make([]workspaceSearchFailedRepoPayload, len(projectPaths)) failed := make([]bool, len(projectPaths)) + denseHits := make([][]workspaceSearchChunkPayload, len(projectPaths)) var mu sync.Mutex + // One BM25 query for the whole workspace, concurrent with the dense + // scans. Errors are swallowed on purpose: bm25ByProject stays nil and + // every project falls back to dense-only, which is what a pre-FTS + // install does anyway. + var bm25ByProject map[string][]chunksfts.Hit + g.Go(func() error { + bm25Start := time.Now() + hits, berr := chunksfts.SearchProjects(gctx, s.Deps.DB, projectPaths, rawQuery, workspaceSearchBM25Limit) + phases.bm25 = time.Since(bm25Start) + if berr != nil { + s.Deps.Logger.Warn("workspaces search: bm25 query failed", + "workspace_id", workspaceID, + "projects", len(projectPaths), + "err", berr) + return nil + } + mu.Lock() + bm25ByProject = hits + mu.Unlock() + return nil + }) + for i, pp := range projectPaths { i, pp := i, pp g.Go(func() error { var ( denseRes []workspaceSearchChunkPayload - bm25Res []workspaceSearchChunkPayload denseErr error ) @@ -682,39 +714,6 @@ func (s *Server) fanOutHybrid( } } - bm25Start := time.Now() - rawBM25, berr := chunksfts.SearchProject(gctx, s.Deps.DB, pp, rawQuery, workspaceSearchBM25Limit) - phases.addBM25(time.Since(bm25Start)) - if berr != nil { - s.Deps.Logger.Warn("workspaces search: bm25 query failed", - "workspace_id", workspaceID, - "project_path", pp, - "err", berr) - } else { - bm25Res = make([]workspaceSearchChunkPayload, 0, len(rawBM25)) - for _, h := range rawBM25 { - bm25Res = append(bm25Res, workspaceSearchChunkPayload{ - ProjectPath: pp, - FilePath: h.FilePath, - StartLine: h.StartLine, - EndLine: h.EndLine, - SymbolName: h.SymbolName, - Language: h.Language, - // Score field carries the dense cosine for the - // merged chunk; for BM25-only hits we leave it - // at 0 (BM25 score is on a different scale and - // would mislead a client reading "score" as - // cosine). - Score: 0, - Content: h.Content, - }) - } - } - - fused := fuseRRF(denseRes, bm25Res) - denseSig := meanTopN(denseScoresOf(denseRes), workspaceSearchTopNPerProject) - bm25Sig := meanTopN(bm25ScoresOf(rawBM25), workspaceSearchTopNPerProject) - mu.Lock() if denseErr != nil { failures[i] = workspaceSearchFailedRepoPayload{ @@ -723,12 +722,7 @@ func (s *Server) fanOutHybrid( } failed[i] = true } - results[i] = projectHits{ - ProjectPath: pp, - FusedChunks: fused, - DenseSignal: float32(denseSig), - BM25Signal: float32(bm25Sig), - } + denseHits[i] = denseRes mu.Unlock() return nil }) @@ -736,6 +730,38 @@ func (s *Server) fanOutHybrid( if err := g.Wait(); err != nil { return nil, nil, err } + + // Fusion moved out of the goroutines with the BM25 query: it needs both + // sides, and it is arithmetic over at most a hundred chunks per project. + // Measured across the whole 43-project fan-out it was under a + // millisecond, so there is nothing to gain by parallelising it and a + // simpler read to be had by not. + for i, pp := range projectPaths { + rawBM25 := bm25ByProject[pp] + bm25Res := make([]workspaceSearchChunkPayload, 0, len(rawBM25)) + for _, h := range rawBM25 { + bm25Res = append(bm25Res, workspaceSearchChunkPayload{ + ProjectPath: pp, + FilePath: h.FilePath, + StartLine: h.StartLine, + EndLine: h.EndLine, + SymbolName: h.SymbolName, + Language: h.Language, + // Score field carries the dense cosine for the merged + // chunk; for BM25-only hits we leave it at 0 (BM25 score + // is on a different scale and would mislead a client + // reading "score" as cosine). + Score: 0, + Content: h.Content, + }) + } + results[i] = projectHits{ + ProjectPath: pp, + FusedChunks: fuseRRF(denseHits[i], bm25Res), + DenseSignal: float32(meanTopN(denseScoresOf(denseHits[i]), workspaceSearchTopNPerProject)), + BM25Signal: float32(meanTopN(bm25ScoresOf(rawBM25), workspaceSearchTopNPerProject)), + } + } failedOut := make([]workspaceSearchFailedRepoPayload, 0) for i, f := range failed { if f { diff --git a/server/internal/httpapi/workspacesearch_test.go b/server/internal/httpapi/workspacesearch_test.go index 32fffab0..567d9994 100644 --- a/server/internal/httpapi/workspacesearch_test.go +++ b/server/internal/httpapi/workspacesearch_test.go @@ -1192,11 +1192,11 @@ func TestWorkspaceSearch_ReportsPhaseTimings(t *testing.T) { // The max of a phase cannot exceed its sum, whatever the machine was // doing at the time. This is the one relationship worth pinning: it // catches a sum and a max wired to the wrong accumulator, which would - // otherwise look plausible in every log line. - for _, phase := range []string{"dense", "bm25"} { - if sum, max := num(phase+"_sum_ms"), num(phase+"_max_ms"); max > sum { - t.Errorf("%s_max_ms (%d) exceeds %s_sum_ms (%d)", phase, max, phase, sum) - } + // otherwise look plausible in every log line. Only dense is split this + // way — BM25 is a single workspace-wide query, so it reports one + // number. + if sum, max := num("dense_sum_ms"), num("dense_max_ms"); max > sum { + t.Errorf("dense_max_ms (%d) exceeds dense_sum_ms (%d)", max, sum) } assertCounterOrder(t, num) } @@ -1468,3 +1468,147 @@ func TestWorkspaceSearch_LogsSlowQueriesThatSearchedNothing(t *testing.T) { t.Errorf("a search that never ran reported timings: %v", raw["timings"]) } } + +// TestWorkspaceSearch_BM25HitsStayInTheirOwnProject is the handler-level guard +// on the partition. BM25 is now one workspace-wide query whose rows are split +// back out per project; a mis-keyed split would hand one repo another repo's +// hits, and the symptom would be a plausible-looking result set rather than an +// error. The dense side cannot mask it here: only one repo is near the query +// vector, so any BM25-driven repo in the panel had to come from the split. +func TestWorkspaceSearch_BM25HitsStayInTheirOwnProject(t *testing.T) { + d, err := dbOpenMemory(t) + if err != nil { + t.Fatalf("open db: %v", err) + } + vs := openTestVectorStore(t) + query := l2([]float32{1, 0, 0, 0}) + router := newSearchRouter(t, d, vs, fixedEmbedder{q: query}) + wsID := createWS(t, router, "partition") + + // Three repos, each with a literal token only it contains, so BM25's + // answer per repo is unambiguous and checkable. + repos := []struct{ path, token, file string }{ + {"github.com/o/alpha@main", "ZZALPHAZZ", "a.go"}, + {"github.com/o/beta@main", "ZZBETAZZ", "b.go"}, + {"github.com/o/gamma@main", "ZZGAMMAZZ", "c.go"}, + } + for i, r := range repos { + seedRepoWithChunks(t, d, vs, wsID, r.path, + []vectorstore.Chunk{ + {Content: "func handle() { /* " + r.token + " */ }", FilePath: r.file, + StartLine: 1, EndLine: 9, ChunkType: "function", + SymbolName: "handle", Language: "go"}, + }, + // Only alpha is anywhere near the query vector. + [][]float32{l2([]float32{1.0, float32(i), 0.0, 0.0})}, + ) + } + + // Ask for beta's token. Beta must be the repo carrying the hit. + rr := doJSON(t, router, http.MethodGet, + "/api/v1/workspaces/"+wsID+"/search?q=ZZBETAZZ&min_score=0", nil) + if rr.Code != http.StatusOK { + t.Fatalf("expected 200, got %d (%s)", rr.Code, rr.Body.String()) + } + var body struct { + Projects []struct { + ProjectPath string `json:"project_path"` + BM25Score float64 `json:"bm25_score"` + } `json:"projects"` + Chunks []struct { + ProjectPath string `json:"project_path"` + FilePath string `json:"file_path"` + } `json:"chunks"` + } + if err := json.Unmarshal(rr.Body.Bytes(), &body); err != nil { + t.Fatalf("decode: %v", err) + } + + var betaBM25, otherBM25 float64 + for _, p := range body.Projects { + if p.ProjectPath == "github.com/o/beta@main" { + betaBM25 = p.BM25Score + continue + } + if p.BM25Score > otherBM25 { + otherBM25 = p.BM25Score + } + } + if betaBM25 <= 0 { + t.Errorf("beta has bm25_score %v — its own token did not reach it: %+v", betaBM25, body.Projects) + } + if otherBM25 != 0 { + t.Errorf("a repo that contains none of the query's tokens has bm25_score %v — "+ + "the partition leaked: %+v", otherBM25, body.Projects) + } + for _, c := range body.Chunks { + if c.FilePath == "b.go" && c.ProjectPath != "github.com/o/beta@main" { + t.Errorf("beta's chunk is attributed to %s", c.ProjectPath) + } + } +} + +// TestWorkspaceSearch_SurvivesBM25Failure covers the blast radius this change +// creates. BM25 used to fail per project; now one failing query costs every +// project its sparse signal at once. The fallback has to be the same one a +// pre-FTS install already lives with — dense-only results, no failed_repos, +// no 500 — because the alternative is that one broken table takes down +// workspace search entirely. +func TestWorkspaceSearch_SurvivesBM25Failure(t *testing.T) { + d, err := dbOpenMemory(t) + if err != nil { + t.Fatalf("open db: %v", err) + } + vs := openTestVectorStore(t) + query := l2([]float32{1, 0, 0, 0}) + router := newSearchRouter(t, d, vs, fixedEmbedder{q: query}) + wsID := createWS(t, router, "nofts") + seedRepoWithChunks(t, d, vs, wsID, "github.com/o/near@main", + []vectorstore.Chunk{ + {Content: "func handle() {}", FilePath: "n.go", StartLine: 1, EndLine: 9, + ChunkType: "function", SymbolName: "handle", Language: "go"}, + }, + [][]float32{l2([]float32{1.0, 0.0, 0.0, 0.0})}, + ) + + // Break only the FTS side. chunks_meta survives, so the stale-FTS probe + // still answers and the repo is not reported as needing a reindex — the + // failure is the query, not the data. + if _, err := d.Exec(`DROP TABLE chunks_fts`); err != nil { + t.Fatalf("drop chunks_fts: %v", err) + } + + rr := doJSON(t, router, http.MethodGet, + "/api/v1/workspaces/"+wsID+"/search?q=handle", nil) + if rr.Code != http.StatusOK { + t.Fatalf("expected 200 despite the BM25 failure, got %d (%s)", rr.Code, rr.Body.String()) + } + var body struct { + Status string `json:"status"` + Projects []struct { + ProjectPath string `json:"project_path"` + BM25Score float64 `json:"bm25_score"` + DenseScore float64 `json:"dense_score"` + } `json:"projects"` + Chunks []map[string]any `json:"chunks"` + FailedRepos []map[string]any `json:"failed_repos"` + } + if err := json.Unmarshal(rr.Body.Bytes(), &body); err != nil { + t.Fatalf("decode: %v", err) + } + if len(body.Chunks) == 0 { + t.Errorf("no chunks — dense should still answer with BM25 broken: %s", rr.Body.String()) + } + if len(body.FailedRepos) != 0 { + t.Errorf("a BM25 failure marked repos as failed: %v", body.FailedRepos) + } + if len(body.Projects) != 1 { + t.Fatalf("expected the one repo in the panel, got %+v", body.Projects) + } + if body.Projects[0].BM25Score != 0 { + t.Errorf("bm25_score is %v with no FTS table at all", body.Projects[0].BM25Score) + } + if body.Projects[0].DenseScore <= 0 { + t.Errorf("dense_score is %v — the dense side should be unaffected", body.Projects[0].DenseScore) + } +} From 6a47df7501c0c25dfb9d87a13ac3fd550ae29d53 Mon Sep 17 00:00:00 2001 From: dvcdsys Date: Thu, 20 Aug 2026 13:55:03 +0100 Subject: [PATCH 2/6] perf(chunksfts): rank on rowids, fetch the payload after the trim MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found by clicking the fixture dashboard: "google authentication login form" took 18.6 s, against a 4.6 s median. The slow-query log said the whole 18.6 s was the single BM25 statement, so the timings from stage 1 pointed straight at it. The cause was not the partitioning, it was what the CTE carried. Selecting file_path, content and the rest inside `hits` makes SQLite materialise all of it for EVERY matched row before ROW_NUMBER trims to perProject per project — and the match set is the whole server's index, because that is how FTS5 evaluates MATCH. That query matched 263,515 rows to return 2,300. Ranking on (project_path, rowid, bm) alone and joining chunks_meta and chunks_fts back for the survivors costs one rowid round-trip per returned row and nothing per discarded row. Measured with loadtests/bench/ftsab (the server's own driver, serial, so the old shape's sum is not hidden by the fan-out's concurrency): query match set 46 queries payload rank in CTE first google authentication login form 263,515 26,085 15,070 2,798 parse JWT token and validate sig. 623,913 27,587 29,566 5,492 rate limiter middleware 166,347 16,904 7,692 993 websocket upgrade handshake 21,049 3,505 1,011 156 graceful shutdown on SIGTERM 14,987 3,244 854 424 The JWT row is the one that matters: with a 624k-row match set the query as shipped was SLOWER than the 46 per-project queries it replaced. The gain scaled inversely with the match set — exactly backwards — and the ten-query bench set hid it because the old shape ran concurrently in the fan-out while these numbers are serial. End to end on the fixture, same warm cache, 10 queries, medians: phase develop prev commit this commit vs develop wall 10,235 4,650 3,048 3.4x BM25 111,210 4,375 2,782 40.0x dense sum 15,166 11,136 11,649 1.3x dense max 2,318 1,123 1,138 2.0x And the query that started this: 18,623 ms -> 2,711 ms. No test guards this. It is a property of the query plan, not of the result, and the equivalence tests pass against both forms — they did, and that is the point: correctness tests cannot see this class of bug. What guards it is loadtests/bench/ftsab, which times all three shapes against the real corpus, and the comment on the query saying why the obvious form is wrong. Co-Authored-By: Claude Opus 5 --- server/internal/chunksfts/chunksfts.go | 33 +++++++++++++++++--------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/server/internal/chunksfts/chunksfts.go b/server/internal/chunksfts/chunksfts.go index 429735eb..8e92326d 100644 --- a/server/internal/chunksfts/chunksfts.go +++ b/server/internal/chunksfts/chunksfts.go @@ -307,23 +307,34 @@ func searchProjectsBatchInto(ctx context.Context, db *sql.DB, projectPaths []str } args = append(args, perProject) + // Rank on the cheap columns, then fetch the payload for the rows that + // survived. The obvious form — selecting file_path, content and the rest + // inside the CTE — makes SQLite materialise all of it for EVERY matched + // row before ROW_NUMBER trims to `perProject` per project, and the match + // set is the whole server's index. On the load-test fixture a four-common- + // word query matched 263,515 rows to return 2,300: carrying the payload + // through cost 15.1 s against 2.8 s for this form, and a 624k-row match + // cost 29.6 s — worse than the 46 per-project queries this replaced. The + // rowid round-trip is the cheap half of the trade. q := fmt.Sprintf(` WITH hits AS ( - SELECT cm.project_path AS pp, cm.rowid AS rid, - cm.file_path, cm.start_line, cm.end_line, - cm.chunk_type, cm.symbol_name, cm.language, - cf.content, bm25(chunks_fts) AS bm + SELECT cm.project_path AS pp, cm.rowid AS rid, bm25(chunks_fts) AS bm FROM chunks_fts cf JOIN chunks_meta cm ON cm.rowid = cf.rowid WHERE chunks_fts MATCH ? AND cm.project_path IN (%s) + ), + ranked AS ( + SELECT pp, rid, bm, + ROW_NUMBER() OVER (PARTITION BY pp ORDER BY bm ASC, rid ASC) AS rn + FROM hits ) - SELECT pp, file_path, start_line, end_line, - chunk_type, symbol_name, language, content, bm - FROM (SELECT *, ROW_NUMBER() OVER ( - PARTITION BY pp ORDER BY bm ASC, rid ASC) AS rn - FROM hits) - WHERE rn <= ? - ORDER BY pp, rn`, placeholders(len(projectPaths))) + SELECT r.pp, cm.file_path, cm.start_line, cm.end_line, + cm.chunk_type, cm.symbol_name, cm.language, cf.content, r.bm + FROM ranked r + JOIN chunks_meta cm ON cm.rowid = r.rid + JOIN chunks_fts cf ON cf.rowid = r.rid + WHERE r.rn <= ? + ORDER BY r.pp, r.rn`, placeholders(len(projectPaths))) rows, err := db.QueryContext(ctx, q, args...) if err != nil { From c495f0b2df33a763904b707a77f943c5d82938b3 Mon Sep 17 00:00:00 2001 From: dvcdsys Date: Thu, 20 Aug 2026 14:12:18 +0100 Subject: [PATCH 3/6] =?UTF-8?q?fix(chunksfts):=20review=20findings=20?= =?UTF-8?q?=E2=80=94=20plan=20guard,=20shared=20scanner,=20timing=20invari?= =?UTF-8?q?ant?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit N1, the one worth doing: 6a47df7's commit message named loadtests/bench/ftsab as the guard against the payload-in-CTE regression returning. /loadtests/ is gitignored, so that guard exists on one machine and the reference is worse than none in a repo whose commit messages are written for the next agent. The in-repo guard asserts the query PLAN, because that is the only place this bug lives: every equivalence test in the package passes against the slow form too — verified, not assumed — since both forms return the same rows. FTS5 reports a rowid lookup as "0:=" and a MATCH scan as "0:M..."; the rank-first form does both, the payload-in-CTE form only scans. workspaceRankQuery is extracted so the test builds the string production builds rather than a copy free to drift. Its mutation check lives in the tree rather than in a shell history: TestExplainDistinguishesTheTwoQueryShapes builds the slow form and asserts the plan assertion REJECTS it. Without that, a change in how SQLite reports plans could quietly turn the guard into a tautology. Confirmed by mutation: moving the payload back into the CTE fails the plan test while the equivalence tests still pass. Also, and where the reasoning differs from the review: - F2, bm25_ms had no invariant left after sum/max collapsed. Added bm25_ms <= fanout_ms. Stated plainly in the comment is what it does NOT catch: a dropped assignment, since 0 <= fanout holds. "> 0" is not available because an in-memory corpus rounds to 0 ms, and a flaky guard is worse than an honest partial one. - F3, the batch loop hand-copied scanHit's eleven lines and lost the sign-flip comment. Both paths now share scanRankedHit, which takes an optional leading project_path. This mattered more than it looks: the equivalence test compares the two paths against EACH OTHER, so a mistake made symmetrically in both would have passed. - F4, the mutex around bm25ByProject guarded nothing — single writer, readers after g.Wait(). Dropped, with a comment saying why, because a lock that protects nothing reads like protection to whoever next needs those hits inside the fan-out. - F5, denseHits[i] is released as the fusion loop consumes it. Fusion used to free its inputs per goroutine; without this, every project's dense hits, BM25 hits and fused copies stay live at once. - F7, placeholders' n<=0 branch returned "NULL", which matches nothing and is indistinguishable from "nothing matched". Removed: IN () is a syntax error, which is loud and points at the wrong caller. - F9, the searchPhases header still said "the fan-out phases keep a SUM and a MAX" after this PR left only dense with that shape. F6/F8 — collapsing SearchProject into SearchProjects([]string{p}) — NOT done, deliberately. It would make TestSearchProjects_MatchesPerProjectQueries compare a function to itself, and that test is the only independent check that the partitioned ranking matches the known-good per-project one. The per-project BM25 signal feeds project candidacy, so a divergence re-ranks the projects panel with no error and no failed_repos. Structural agreement is worth less here than an oracle. SearchProject's doc comment now says it has no production caller, why it is kept, and not to delete it as unused — which is the real fix for F8. go test ./... green (46 packages), -race green on both changed packages, go vet and gofmt clean. Co-Authored-By: Claude Opus 5 --- server/internal/chunksfts/chunksfts.go | 136 +++++++++++------- server/internal/chunksfts/chunksfts_test.go | 84 +++++++++++ server/internal/httpapi/searchtimings.go | 5 +- server/internal/httpapi/workspacesearch.go | 14 +- .../internal/httpapi/workspacesearch_test.go | 14 ++ 5 files changed, 199 insertions(+), 54 deletions(-) diff --git a/server/internal/chunksfts/chunksfts.go b/server/internal/chunksfts/chunksfts.go index 8e92326d..72075df9 100644 --- a/server/internal/chunksfts/chunksfts.go +++ b/server/internal/chunksfts/chunksfts.go @@ -200,9 +200,24 @@ func DeleteByProject(ctx context.Context, db *sql.DB, projectPath string) error // Empty or all-tokens-too-short queries return a nil slice without // hitting the DB — there is nothing to match. // -// For more than one project use SearchProjects: this query costs about -// the same whichever project it is restricted to, so running it once per -// project is the expensive way to ask. +// NOTE: nothing in production calls this any more — workspace search asks +// SearchProjects for every project at once, and a single-project workspace +// goes through the same path. It is kept for two reasons, both worth more +// than the ~40 lines it costs: +// +// 1. it is the independent oracle for SearchProjects. The two are +// structurally different statements that must return byte-identical +// rankings, because the per-project BM25 signal feeds project candidacy +// in workspace search — a divergence would silently re-rank the projects +// panel with no error and no failed_repos. +// TestSearchProjects_MatchesPerProjectQueries is that check, and it is +// only worth anything while this stays a separate implementation. +// Collapsing it into SearchProjects([]string{p}) would make the test +// compare a function to itself; +// 2. it is the fallback if a single-project regression ever shows up in the +// partitioned form. +// +// Do not delete it as unused. func SearchProject(ctx context.Context, db *sql.DB, projectPath, query string, limit int) ([]Hit, error) { if limit <= 0 { limit = 20 @@ -307,16 +322,49 @@ func searchProjectsBatchInto(ctx context.Context, db *sql.DB, projectPaths []str } args = append(args, perProject) - // Rank on the cheap columns, then fetch the payload for the rows that - // survived. The obvious form — selecting file_path, content and the rest - // inside the CTE — makes SQLite materialise all of it for EVERY matched - // row before ROW_NUMBER trims to `perProject` per project, and the match - // set is the whole server's index. On the load-test fixture a four-common- - // word query matched 263,515 rows to return 2,300: carrying the payload - // through cost 15.1 s against 2.8 s for this form, and a 624k-row match - // cost 29.6 s — worse than the 46 per-project queries this replaced. The - // rowid round-trip is the cheap half of the trade. - q := fmt.Sprintf(` + rows, err := db.QueryContext(ctx, workspaceRankQuery(placeholders(len(projectPaths))), args...) + if err != nil { + return fmt.Errorf("chunks_fts workspace search: %w", err) + } + defer rows.Close() + for rows.Next() { + var pp string + h, err := scanRankedHit(rows, &pp) + if err != nil { + return err + } + dst[pp] = append(dst[pp], h) + } + if err := rows.Err(); err != nil { + return fmt.Errorf("iterate chunks_fts: %w", err) + } + return nil +} + +// workspaceRankQuery builds the partitioned statement for a placeholder list. +// +// Split out so the test that pins the query PLAN builds the same string this +// does. A copy in the test would drift, and drifting is the entire failure +// mode being guarded against. +// +// Rank on the cheap columns, then fetch the payload for the rows that +// survived. The obvious form — selecting file_path, content and the rest +// inside the CTE — makes SQLite materialise all of it for EVERY matched +// row before ROW_NUMBER trims to `perProject` per project, and the match +// set is the whole server's index. On the load-test fixture a four-common- +// word query matched 263,515 rows to return 2,300: carrying the payload +// through cost 15.1 s against 2.8 s for this form, and a 624k-row match +// cost 29.6 s — worse than the 46 per-project queries this replaced. The +// rowid round-trip is the cheap half of the trade. +// +// Those numbers came from a bench harness that is NOT in this repository: +// /loadtests/ is gitignored, corpus and tools alike. To recreate it, time +// this statement against a corpus with a large match set alongside the +// same statement with the payload columns moved into `hits` — the gap only +// appears when the match set is orders of magnitude larger than the result. +// TestSearchProjects_FetchesPayloadAfterTheTrim is the in-repo guard. +func workspaceRankQuery(ph string) string { + return fmt.Sprintf(` WITH hits AS ( SELECT cm.project_path AS pp, cm.rowid AS rid, bm25(chunks_fts) AS bm FROM chunks_fts cf @@ -334,49 +382,31 @@ func searchProjectsBatchInto(ctx context.Context, db *sql.DB, projectPaths []str JOIN chunks_meta cm ON cm.rowid = r.rid JOIN chunks_fts cf ON cf.rowid = r.rid WHERE r.rn <= ? - ORDER BY r.pp, r.rn`, placeholders(len(projectPaths))) - - rows, err := db.QueryContext(ctx, q, args...) - if err != nil { - return fmt.Errorf("chunks_fts workspace search: %w", err) - } - defer rows.Close() - for rows.Next() { - var ( - pp string - h Hit - ) - var ( - chunkT sql.NullString - symName sql.NullString - language sql.NullString - bm float64 - ) - if err := rows.Scan(&pp, &h.FilePath, &h.StartLine, &h.EndLine, - &chunkT, &symName, &language, &h.Content, &bm); err != nil { - return fmt.Errorf("scan chunks_fts row: %w", err) - } - h.ChunkType = chunkT.String - h.SymbolName = symName.String - h.Language = language.String - h.Score = -bm - dst[pp] = append(dst[pp], h) - } - if err := rows.Err(); err != nil { - return fmt.Errorf("iterate chunks_fts: %w", err) - } - return nil + ORDER BY r.pp, r.rn`, ph) } +// placeholders builds "?,?,?" for an IN list. n is always >= 1: SearchProjects +// returns early on an empty slice and the batching loop never produces an empty +// batch. There is deliberately no n == 0 branch — an empty list used to render +// as IN (NULL), which matches nothing and is indistinguishable from "nothing +// matched". A syntax error from IN () is the better failure: it is loud, and it +// happens at the call that is wrong. func placeholders(n int) string { - if n <= 0 { - return "NULL" - } return strings.TrimSuffix(strings.Repeat("?,", n), ",") } // scanHit reads one single-project ranking row. -func scanHit(rows *sql.Rows) (Hit, error) { +func scanHit(rows *sql.Rows) (Hit, error) { return scanRankedHit(rows, nil) } + +// scanRankedHit reads one ranking row, optionally preceded by a project_path +// column (pass nil for the single-project query, which does not select one). +// +// Both queries share this because they must produce byte-identical Hits and +// their only structural difference is that leading column. Two hand-written +// scans agreeing is exactly the kind of thing that stays true until it +// quietly does not — and the equivalence test compares the two paths against +// EACH OTHER, so a mistake made symmetrically in both would pass. +func scanRankedHit(rows *sql.Rows, pp *string) (Hit, error) { var ( h Hit chunkT sql.NullString @@ -384,8 +414,12 @@ func scanHit(rows *sql.Rows) (Hit, error) { language sql.NullString bm float64 ) - if err := rows.Scan(&h.FilePath, &h.StartLine, &h.EndLine, - &chunkT, &symName, &language, &h.Content, &bm); err != nil { + dest := []any{&h.FilePath, &h.StartLine, &h.EndLine, + &chunkT, &symName, &language, &h.Content, &bm} + if pp != nil { + dest = append([]any{pp}, dest...) + } + if err := rows.Scan(dest...); err != nil { return Hit{}, fmt.Errorf("scan chunks_fts row: %w", err) } h.ChunkType = chunkT.String diff --git a/server/internal/chunksfts/chunksfts_test.go b/server/internal/chunksfts/chunksfts_test.go index 17500325..33beeba4 100644 --- a/server/internal/chunksfts/chunksfts_test.go +++ b/server/internal/chunksfts/chunksfts_test.go @@ -477,3 +477,87 @@ func TestSearchProjects_EmptyInputs(t *testing.T) { t.Errorf("no projects: got %v, %v; want nil, nil", got, err) } } + +// TestSearchProjects_FetchesPayloadAfterTheTrim pins the query PLAN, which is +// the only place this bug can live: every equivalence test in this file passes +// against the slow form too, because the two forms return the same rows. +// +// Carrying file_path/content through the CTE makes SQLite materialise them for +// every globally-matched row before ROW_NUMBER trims — and FTS5 evaluates MATCH +// over the whole server's index, so the match set has nothing to do with how +// many rows come back. Measured on the load-test corpus: 15.1 s against 2.8 s +// on a 263k-row match, and 29.6 s on a 624k-row one, which was slower than the +// per-project queries the partitioned form replaced. +// +// FTS5 reports a rowid-equality lookup as "0:=" and a MATCH scan as "0:M...". +// The rank-first form does both — scan to match, point lookups for survivors. +// The payload-in-CTE form only ever scans. +func TestSearchProjects_FetchesPayloadAfterTheTrim(t *testing.T) { + d := openTestDB(t) + ctx := context.Background() + seedCorpus(t, d, []string{"p1", "p2"}) + + plan := explain(t, ctx, d, workspaceRankQuery(placeholders(2)), + `"retry" OR "backoff"`, "p1", "p2", 3) + if !strings.Contains(plan, "VIRTUAL TABLE INDEX 0:=") { + t.Errorf("chunks_fts is not looked up by rowid — the payload is being "+ + "carried through the window sorter again:\n%s", plan) + } +} + +// TestExplainDistinguishesTheTwoQueryShapes is the mutation check for the test +// above, kept in the tree rather than run by hand: it builds the slow form and +// asserts the plan assertion would REJECT it. Without this, a change to how +// SQLite reports plans could turn the guard into a tautology that passes on +// everything, and nothing would say so. +func TestExplainDistinguishesTheTwoQueryShapes(t *testing.T) { + d := openTestDB(t) + ctx := context.Background() + seedCorpus(t, d, []string{"p1", "p2"}) + + slow := ` + WITH hits AS ( + SELECT cm.project_path AS pp, cm.rowid AS rid, + cm.file_path, cm.start_line, cm.end_line, + cm.chunk_type, cm.symbol_name, cm.language, + cf.content, bm25(chunks_fts) AS bm + FROM chunks_fts cf + JOIN chunks_meta cm ON cm.rowid = cf.rowid + WHERE chunks_fts MATCH ? AND cm.project_path IN (?,?) + ) + SELECT pp, file_path, start_line, end_line, + chunk_type, symbol_name, language, content, bm + FROM (SELECT *, ROW_NUMBER() OVER ( + PARTITION BY pp ORDER BY bm ASC, rid ASC) AS rn + FROM hits) + WHERE rn <= ? + ORDER BY pp, rn` + + plan := explain(t, ctx, d, slow, `"retry" OR "backoff"`, "p1", "p2", 3) + if strings.Contains(plan, "VIRTUAL TABLE INDEX 0:=") { + t.Errorf("the payload-in-CTE form reports a rowid lookup, so the plan "+ + "assertion no longer distinguishes the two shapes:\n%s", plan) + } +} + +func explain(t *testing.T, ctx context.Context, d *sql.DB, query string, args ...any) string { + t.Helper() + rows, err := d.QueryContext(ctx, "EXPLAIN QUERY PLAN "+query, args...) + if err != nil { + t.Fatalf("explain: %v", err) + } + defer rows.Close() + var plan strings.Builder + for rows.Next() { + var a, b, c int + var detail string + if err := rows.Scan(&a, &b, &c, &detail); err != nil { + t.Fatalf("scan plan row: %v", err) + } + plan.WriteString(detail + "\n") + } + if err := rows.Err(); err != nil { + t.Fatalf("iterate plan: %v", err) + } + return plan.String() +} diff --git a/server/internal/httpapi/searchtimings.go b/server/internal/httpapi/searchtimings.go index fc8dde8d..08ecfc2a 100644 --- a/server/internal/httpapi/searchtimings.go +++ b/server/internal/httpapi/searchtimings.go @@ -48,12 +48,15 @@ var slowWorkspaceQuery = 2 * time.Second // searchPhases accumulates one workspace query's timings. // -// The fan-out phases keep a SUM and a MAX, and both are needed: the sum is how +// The DENSE phase keeps a SUM and a MAX, and both are needed: the sum is how // much work the query did, the max is how long the user waited for the slowest // project. With perfect parallelism the wall time is the max; with none it is // the sum. Measured on the fixture, eight concurrent project searches ran 3.4x // faster than the same eight in sequence — so the truth is between the two // numbers, and reporting only one of them hides which. +// +// BM25 no longer has that shape: it is one workspace-wide statement, so it +// reports a single duration. See the bm25 field below. type searchPhases struct { embed time.Duration resolve time.Duration diff --git a/server/internal/httpapi/workspacesearch.go b/server/internal/httpapi/workspacesearch.go index b8e33f40..08c5c5f5 100644 --- a/server/internal/httpapi/workspacesearch.go +++ b/server/internal/httpapi/workspacesearch.go @@ -672,9 +672,12 @@ func (s *Server) fanOutHybrid( "err", berr) return nil } - mu.Lock() + // No lock: this is the only writer, and every reader runs after + // g.Wait(), which is the happens-before. A mutex here would be + // decoration that reads like protection — the next person needing + // these hits INSIDE the fan-out would see a locked write, assume the + // map was safe to touch concurrently, and add a real race. bm25ByProject = hits - mu.Unlock() return nil }) @@ -761,6 +764,13 @@ func (s *Server) fanOutHybrid( DenseSignal: float32(meanTopN(denseScoresOf(denseHits[i]), workspaceSearchTopNPerProject)), BM25Signal: float32(meanTopN(bm25ScoresOf(rawBM25), workspaceSearchTopNPerProject)), } + // Release this project's dense half now. Fusion used to happen inside + // each goroutine, which freed both inputs as it went; doing it here + // would otherwise keep every project's dense hits, every project's + // BM25 hits and every project's fused copies alive at once — three + // sets of chunk payloads for the whole workspace rather than one + // project's worth at a time. + denseHits[i] = nil } failedOut := make([]workspaceSearchFailedRepoPayload, 0) for i, f := range failed { diff --git a/server/internal/httpapi/workspacesearch_test.go b/server/internal/httpapi/workspacesearch_test.go index 567d9994..0cbfa520 100644 --- a/server/internal/httpapi/workspacesearch_test.go +++ b/server/internal/httpapi/workspacesearch_test.go @@ -1198,6 +1198,20 @@ func TestWorkspaceSearch_ReportsPhaseTimings(t *testing.T) { if sum, max := num("dense_sum_ms"), num("dense_max_ms"); max > sum { t.Errorf("dense_max_ms (%d) exceeds dense_sum_ms (%d)", max, sum) } + // bm25_ms is a plain field assigned in exactly one place. Drop that + // assignment and payload() still emits "bm25_ms": 0, the presence loop + // above still passes, and every log line reads as though BM25 were free — + // which is the number that pointed at an 18.6 s query the day this landed. + // + // Be clear about what this does and does not buy: a phase inside the + // fan-out cannot outlast it, so a timer around the wrong span is caught. + // A DROPPED assignment is not — 0 <= fanout holds — and it cannot be, + // because an in-memory corpus legitimately rounds to 0 ms and "> 0" would + // be flaky. That gap is real; the alternative is a flaky test, which is + // worse than an honest partial one. + if bm, fan := num("bm25_ms"), num("fanout_ms"); bm > fan { + t.Errorf("bm25_ms (%d) exceeds fanout_ms (%d)", bm, fan) + } assertCounterOrder(t, num) } From 202d9794e0bc240632d78fd0a0e31eb1c194fae2 Mon Sep 17 00:00:00 2001 From: dvcdsys Date: Thu, 20 Aug 2026 16:45:28 +0100 Subject: [PATCH 4/6] fix(workspacesearch): give RRF fusion a total order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found by chasing "why is the answer different each time". Not caused by this branch — main has it too — but it is the larger half of the answer, so it lands here rather than waiting. fuseRRF built its output slice by ranging over a map. Go randomises map iteration deliberately, and sort.SliceStable then preserved that randomness for every pair of chunks with equal RRF. Equal RRF is not a corner case: a chunk found only by dense at rank r and a chunk found only by BM25 at the same rank r score identically by construction, which happens in most queries. The symptom was invisible from the projects panel — project scores do not depend on chunk order — so the panel looked stable while the chunk list underneath it moved. On the fixture, the same query on the same process and binary returned a different chunk at rank 0 between consecutive calls. Sorting by (rrf desc, chunk key asc) gives a total order. Measured on the 43-project fixture, two full 50-query sweeps of one build against itself: chunk lists differing 25/50 -> 5/50 The five that remain are not ours. The provider returns a different vector for a byte-identical request often enough to matter: logging the exact request body alongside a checksum of the vector it produced, over two sweeps, 4 of 50 queries got two distinct vectors from identical bodies (sha of the marshalled request equal, sha of the float32 vector not). When a query drifts it drifts in ALL ten panel projects at once, which is the signature of the query vector moving rather than of any per-collection scan. dense_score shifts by <=0.002 and occasionally flips a rank. Nothing in cix can make that deterministic; a query-embedding cache keyed on the text would, and would cut provider spend too, but that is a separate change with its own trade-offs. The test asserts across 20 repeats, because with N tied entries a single run has a 1/N! chance of looking ordered by accident. It also pins that RRF still dominates the key: a chunk present in both lists outranks single-list chunks whatever its key sorts like. Mutation-checked — restoring the SliceStable-without-tiebreak form fails it on run 0. Co-Authored-By: Claude Opus 5 --- server/internal/httpapi/workspacesearch.go | 20 ++++++- .../internal/httpapi/workspacesearch_test.go | 56 +++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/server/internal/httpapi/workspacesearch.go b/server/internal/httpapi/workspacesearch.go index 08c5c5f5..10e5f071 100644 --- a/server/internal/httpapi/workspacesearch.go +++ b/server/internal/httpapi/workspacesearch.go @@ -817,8 +817,24 @@ func fuseRRF(dense, bm25 []workspaceSearchChunkPayload) []workspaceSearchChunkPa for _, e := range byKey { out = append(out, *e) } - sort.SliceStable(out, func(i, j int) bool { - return out[i].rrf > out[j].rrf + // The tiebreak is load-bearing, not tidiness. `out` is built by ranging + // over a map, and Go randomises map iteration order deliberately — so + // without a total order here, chunks with equal RRF come back in a + // different order on every call, and SliceStable faithfully preserves that + // randomness. Equal RRF is not a corner case: a chunk found only by dense + // at rank r and a chunk found only by BM25 at the same rank r score + // identically by construction, which happens in most queries. + // + // Observed on the load-test fixture before this line existed: the same + // query, same process, same binary, returned a different chunk at rank 0 + // between consecutive calls. Project scores were unaffected — they do not + // depend on chunk order — so the panel looked stable while the results + // underneath it moved. + sort.Slice(out, func(i, j int) bool { + if out[i].rrf != out[j].rrf { + return out[i].rrf > out[j].rrf + } + return key(out[i].c) < key(out[j].c) }) chunks := make([]workspaceSearchChunkPayload, len(out)) for i, e := range out { diff --git a/server/internal/httpapi/workspacesearch_test.go b/server/internal/httpapi/workspacesearch_test.go index 0cbfa520..c1cf4f6d 100644 --- a/server/internal/httpapi/workspacesearch_test.go +++ b/server/internal/httpapi/workspacesearch_test.go @@ -1626,3 +1626,59 @@ func TestWorkspaceSearch_SurvivesBM25Failure(t *testing.T) { t.Errorf("dense_score is %v — the dense side should be unaffected", body.Projects[0].DenseScore) } } + +// TestFuseRRF_IsDeterministicAcrossTiedChunks pins the total order in fuseRRF. +// +// The function builds its output by ranging over a map, and Go randomises map +// iteration on purpose. Sorting by RRF alone leaves chunks with equal scores in +// whatever order the map happened to yield, and sort.SliceStable then preserves +// that randomness faithfully. Equal RRF is the common case, not a corner one: a +// chunk found only by dense at rank r and a chunk found only by BM25 at rank r +// score identically by construction. +// +// The symptom was invisible from the projects panel — project scores do not +// depend on chunk order, so the panel looked stable while rank 0 of the chunk +// list changed between consecutive calls on the same process and binary. +// +// Repeats matter here: with N tied entries a single run has a 1/N! chance of +// looking sorted by accident, so one call proves nothing. +func TestFuseRRF_IsDeterministicAcrossTiedChunks(t *testing.T) { + mk := func(project, file string, line int) workspaceSearchChunkPayload { + return workspaceSearchChunkPayload{ + ProjectPath: project, FilePath: file, + StartLine: line, EndLine: line + 5, + } + } + // Disjoint lists of the same length: every dense chunk at rank r ties + // exactly with the BM25 chunk at rank r, so every pair is a tie. + var dense, bm25 []workspaceSearchChunkPayload + for i := 0; i < 12; i++ { + dense = append(dense, mk("p", fmt.Sprintf("d%02d.go", i), 1+i*10)) + bm25 = append(bm25, mk("p", fmt.Sprintf("b%02d.go", i), 1+i*10)) + } + + first := fuseRRF(dense, bm25) + if len(first) != len(dense)+len(bm25) { + t.Fatalf("expected %d fused chunks, got %d", len(dense)+len(bm25), len(first)) + } + for run := 0; run < 20; run++ { + got := fuseRRF(dense, bm25) + for i := range got { + if got[i] != first[i] { + t.Fatalf("run %d differs at rank %d: %s/%d vs %s/%d — fusion is "+ + "not deterministic across tied chunks", + run, i, got[i].FilePath, got[i].StartLine, + first[i].FilePath, first[i].StartLine) + } + } + } + + // And the ordering must still be by RRF first: a chunk in BOTH lists + // outranks any chunk in only one, whatever its key sorts like. + both := mk("p", "zzz_last_alphabetically.go", 999) + withShared := fuseRRF(append([]workspaceSearchChunkPayload{both}, dense...), + append([]workspaceSearchChunkPayload{both}, bm25...)) + if withShared[0] != both { + t.Errorf("the chunk present in both lists is not first: got %s", withShared[0].FilePath) + } +} From 7e030414805decc0a3531a2aaca579eba8f0357a Mon Sep 17 00:00:00 2001 From: dvcdsys Date: Thu, 20 Aug 2026 19:17:53 +0100 Subject: [PATCH 5/6] fix(workspacesearch): order the projects panel on the raw score, not the rounded one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Third review pass. N2 is the same defect 202d979 fixed, one level up. The panel sorted on ProjectScore — the round4 copy that ships in the JSON — and then truncated to top_projects on the next line. Rounding manufactures ties: 0.71234 and 0.71236 both become 0.7123, and the tie then decided which of two repos the caller saw AT ALL, resolved by whatever order the projects arrived in, which is workspace membership order (added_at DESC). The panel was a function of insertion history. Now sortPanel orders on the raw candidacy with ProjectPath as the tiebreak, so it is a total order and a function of the query. Extracted into its own function so the property is testable on a constructed slice: through the handler it would depend on added_at timestamps a test cannot control, and that test would be flaky rather than wrong. Two tests, both mutation-checked. The rounding one uses a 5e-6 gap — below round4's resolution — with the input in reverse order and the STRONGER project named last alphabetically, which is what lets it tell "sorted on the raw value" apart from "fell back to the path tiebreak": both alternatives would put "aaa" first. Restoring the rounded sort fails it; removing only the path tiebreak fails the other one. Writing this the obvious way first also re-broke F1: truncating `surviving` in place capped projects_returned at top_projects again, exactly the bug fixed in d511513. TestWorkspaceSearch_ReturnedCountIgnoresThePanelCap caught it immediately. `panel` now reslices instead, and the comment says why. That also removed the set-membership filter that rebuilt the panel for the interleave — `panel` already is that set, in that order. N3: fuseRRF's tiebreak called key() on both sides of every comparison, rebuilding a four-part concatenation that had already been computed as the map key and thrown away. At ~100 chunks per project that is tens of thousands of throwaway strings per query, on the path this branch exists to speed up. The key is now carried on the entry. The expression itself also existed twice — fuseRRF's `key` and interleaveByRank's `dedupKey` — and since 202d979 it decides ORDER, not just identity, so the two drifting apart would make fusion and dedup disagree with no error. One package-level chunkKey now. N4: scanRankedHit built an 8-element []any and then prepended to it, allocating and copying a second slice for every row of the workspace query — ~2,150 rows on the fixture. Built once with the right capacity. Also corrects a comment I wrote in c495f0b: denseHits[i] = nil does not avoid keeping "three sets of chunk payloads for the whole workspace" alive. fuseRRF returns the UNION of both lists, so the Content strings stay reachable through results[i].FusedChunks either way. What it frees is ~50 payload structs per project — a few KB, not the chunk text. The line is still right; the claim was inflated. go test ./... green (46 packages), -race green on both changed packages, make openapi-check in sync, go vet and gofmt clean. Co-Authored-By: Claude Opus 5 --- server/internal/chunksfts/chunksfts.go | 11 +- server/internal/httpapi/workspacesearch.go | 112 +++++++++++------- .../internal/httpapi/workspacesearch_test.go | 62 ++++++++++ 3 files changed, 137 insertions(+), 48 deletions(-) diff --git a/server/internal/chunksfts/chunksfts.go b/server/internal/chunksfts/chunksfts.go index 72075df9..618d6595 100644 --- a/server/internal/chunksfts/chunksfts.go +++ b/server/internal/chunksfts/chunksfts.go @@ -414,11 +414,16 @@ func scanRankedHit(rows *sql.Rows, pp *string) (Hit, error) { language sql.NullString bm float64 ) - dest := []any{&h.FilePath, &h.StartLine, &h.EndLine, - &chunkT, &symName, &language, &h.Content, &bm} + // Built in one allocation rather than prepending to a finished slice: the + // workspace query returns up to len(projects) * perProject rows — ~2,150 + // on the 43-project load-test fixture — and this runs per row, inside the + // path the rest of this change exists to speed up. + dest := make([]any, 0, 9) if pp != nil { - dest = append([]any{pp}, dest...) + dest = append(dest, pp) } + dest = append(dest, &h.FilePath, &h.StartLine, &h.EndLine, + &chunkT, &symName, &language, &h.Content, &bm) if err := rows.Scan(dest...); err != nil { return Hit{}, fmt.Errorf("scan chunks_fts row: %w", err) } diff --git a/server/internal/httpapi/workspacesearch.go b/server/internal/httpapi/workspacesearch.go index 10e5f071..c380c31b 100644 --- a/server/internal/httpapi/workspacesearch.go +++ b/server/internal/httpapi/workspacesearch.go @@ -387,8 +387,18 @@ func (s *Server) WorkspaceSearch(w http.ResponseWriter, r *http.Request, id stri } } - projectPayloads := make([]workspaceSearchProjectPayload, 0, len(surviving)) - for _, ph := range surviving { + sortPanel(surviving) + // `panel` reslices, it does not shrink `surviving` — projects_returned + // counts what cleared the relevance threshold, and capping that at + // top_projects is exactly the bug fixed in d511513. The test for it caught + // this line the first time it was written the other way round. + panel := surviving + if len(panel) > topProjects { + panel = panel[:topProjects] + } + + projectPayloads := make([]workspaceSearchProjectPayload, 0, len(panel)) + for _, ph := range panel { projectPayloads = append(projectPayloads, workspaceSearchProjectPayload{ ProjectPath: ph.ProjectPath, Label: projectLabel(ph.ProjectPath), @@ -399,29 +409,12 @@ func (s *Server) WorkspaceSearch(w http.ResponseWriter, r *http.Request, id stri }) } - sort.SliceStable(projectPayloads, func(i, j int) bool { - return projectPayloads[i].ProjectScore > projectPayloads[j].ProjectScore - }) - if len(projectPayloads) > topProjects { - projectPayloads = projectPayloads[:topProjects] - } - - // Restrict the interleave to projects that survived the panel - // truncation. Otherwise a workspace with > top_projects surviving - // repos can surface chunks whose project_path is absent from - // projects[] — agents lose access to bm25_score/dense_score and - // the response looks inconsistent. Filter to the panel before - // round-robin. - panelSet := make(map[string]struct{}, len(projectPayloads)) - for _, p := range projectPayloads { - panelSet[p.ProjectPath] = struct{}{} - } - panelSurviving := make([]projectHits, 0, len(projectPayloads)) - for _, ph := range surviving { - if _, ok := panelSet[ph.ProjectPath]; ok { - panelSurviving = append(panelSurviving, ph) - } - } + // The interleave is restricted to the panel. Otherwise a workspace with + // more than top_projects surviving repos can surface chunks whose + // project_path is absent from projects[] — agents lose access to + // bm25_score/dense_score and the response looks inconsistent. `panel` is + // already exactly that set, in the same order, so the set-membership + // filter this used to do is no longer needed. // Round-robin across surviving projects so rank-1 from each // project lands in the first N slots, then rank-2, etc. This @@ -429,7 +422,7 @@ func (s *Server) WorkspaceSearch(w http.ResponseWriter, r *http.Request, id stri // before any repo's tail entries appear — matches the project- // picker use case where the user wants to see each project's // most-relevant hit before diving into the dominant repo's tail. - merged := interleaveByRank(panelSurviving, topChunks) + merged := interleaveByRank(panel, topChunks) status := "ok" if len(merged) == 0 { @@ -484,6 +477,28 @@ var timingFields = []string{ "fuse_ms", "projects_scanned", "projects_returned", "projects_in_panel", } +// sortPanel orders the projects panel, best first. +// +// On the RAW candidacy, not on the rounded copy that goes out in the JSON. +// round4 manufactures ties — 0.71234 and 0.71236 both become 0.7123 — and the +// caller truncates to top_projects immediately afterwards, so a tie decides +// which of two repos the caller sees at all. Sorting the rounded value handed +// that decision to whatever order the projects happened to arrive in, which is +// workspace membership order (added_at DESC): the panel became a function of +// insertion history rather than of the query. +// +// ProjectPath breaks a genuine tie, so the result is a total order. Split out +// from the handler so the property can be tested on a constructed slice +// instead of through timestamps that a test cannot control. +func sortPanel(surviving []projectHits) { + sort.Slice(surviving, func(i, j int) bool { + if surviving[i].Candidacy != surviving[j].Candidacy { + return surviving[i].Candidacy > surviving[j].Candidacy + } + return surviving[i].ProjectPath < surviving[j].ProjectPath + }) +} + // interleaveByRank returns up to `limit` chunks by walking the surviving // projects round-robin — rank-1 from every project before any rank-2, // then rank-2, and so on. Projects are visited in candidacy-desc order @@ -505,10 +520,6 @@ func interleaveByRank(projects []projectHits, limit int) []workspaceSearchChunkP }) out := make([]workspaceSearchChunkPayload, 0, limit) - dedupKey := func(c workspaceSearchChunkPayload) string { - return c.ProjectPath + "|" + c.FilePath + "|" + - strconv.Itoa(c.StartLine) + "-" + strconv.Itoa(c.EndLine) - } seen := make(map[string]struct{}, limit) // rank index walks 0,1,2,... ; we stop when no project has a // chunk at this rank (every list exhausted). @@ -522,7 +533,7 @@ func interleaveByRank(projects []projectHits, limit int) []workspaceSearchChunkP if c.ProjectPath == "" { c.ProjectPath = p.ProjectPath } - k := dedupKey(c) + k := chunkKey(c) if _, ok := seen[k]; ok { continue } @@ -764,12 +775,14 @@ func (s *Server) fanOutHybrid( DenseSignal: float32(meanTopN(denseScoresOf(denseHits[i]), workspaceSearchTopNPerProject)), BM25Signal: float32(meanTopN(bm25ScoresOf(rawBM25), workspaceSearchTopNPerProject)), } - // Release this project's dense half now. Fusion used to happen inside - // each goroutine, which freed both inputs as it went; doing it here - // would otherwise keep every project's dense hits, every project's - // BM25 hits and every project's fused copies alive at once — three - // sets of chunk payloads for the whole workspace rather than one - // project's worth at a time. + // Release this project's dense slice now that both fuseRRF and + // denseScoresOf have consumed it. To be accurate about what this + // buys: fuseRRF returns the UNION of both lists, so the Content + // strings stay reachable through results[i].FusedChunks either way — + // what goes is the backing array of ~50 payload structs per project, + // a few KB, not the chunk text. Free, correctly ordered, and worth + // keeping; just not the workspace-wide retention fix an earlier + // version of this comment claimed. denseHits[i] = nil } failedOut := make([]workspaceSearchFailedRepoPayload, 0) @@ -781,6 +794,17 @@ func (s *Server) fanOutHybrid( return results, failedOut, nil } +// chunkKey is chunk identity: the same span of the same file in the same +// project. Shared by fusion and by the round-robin interleave's dedup, and +// since fusion started using it as a sort tiebreak it decides ORDER as well as +// identity — so the two callers agreeing is no longer merely tidy. Two copies +// of this expression drifting would make the fusion tiebreak and the interleave +// dedup disagree with no error anywhere. +func chunkKey(c workspaceSearchChunkPayload) string { + return c.ProjectPath + "|" + c.FilePath + "|" + + strconv.Itoa(c.StartLine) + "-" + strconv.Itoa(c.EndLine) +} + // fuseRRF returns chunks ranked by Reciprocal Rank Fusion over the two // per-project lists. RRF score per chunk is sum(1/(k+rank_i)) across // the lists where it appears. Chunks present in both lists naturally @@ -793,19 +817,16 @@ func (s *Server) fanOutHybrid( func fuseRRF(dense, bm25 []workspaceSearchChunkPayload) []workspaceSearchChunkPayload { type entry struct { c workspaceSearchChunkPayload + k string rrf float64 } - key := func(c workspaceSearchChunkPayload) string { - return c.ProjectPath + "|" + c.FilePath + "|" + - strconv.Itoa(c.StartLine) + "-" + strconv.Itoa(c.EndLine) - } byKey := make(map[string]*entry) for rank, c := range dense { - k := key(c) + k := chunkKey(c) byKey[k] = &entry{c: c, rrf: 1.0 / float64(rrfK+rank+1)} } for rank, c := range bm25 { - k := key(c) + k := chunkKey(c) add := 1.0 / float64(rrfK+rank+1) if e, ok := byKey[k]; ok { e.rrf += add @@ -814,7 +835,8 @@ func fuseRRF(dense, bm25 []workspaceSearchChunkPayload) []workspaceSearchChunkPa byKey[k] = &entry{c: c, rrf: add} } out := make([]entry, 0, len(byKey)) - for _, e := range byKey { + for k, e := range byKey { + e.k = k out = append(out, *e) } // The tiebreak is load-bearing, not tidiness. `out` is built by ranging @@ -834,7 +856,7 @@ func fuseRRF(dense, bm25 []workspaceSearchChunkPayload) []workspaceSearchChunkPa if out[i].rrf != out[j].rrf { return out[i].rrf > out[j].rrf } - return key(out[i].c) < key(out[j].c) + return out[i].k < out[j].k }) chunks := make([]workspaceSearchChunkPayload, len(out)) for i, e := range out { diff --git a/server/internal/httpapi/workspacesearch_test.go b/server/internal/httpapi/workspacesearch_test.go index c1cf4f6d..d3327358 100644 --- a/server/internal/httpapi/workspacesearch_test.go +++ b/server/internal/httpapi/workspacesearch_test.go @@ -1682,3 +1682,65 @@ func TestFuseRRF_IsDeterministicAcrossTiedChunks(t *testing.T) { t.Errorf("the chunk present in both lists is not first: got %s", withShared[0].FilePath) } } + +// TestSortPanel_OrdersOnRawCandidacyNotTheRoundedCopy covers the last place in +// this handler where an arbitrary tiebreak decided user-visible output. +// +// project_score ships rounded to four decimals. Sorting THAT value invents ties +// between projects that are not actually tied, and the caller truncates to +// top_projects right afterwards — so the invented tie decides which repo is +// shown and which is dropped entirely. Before this, the winner was whichever +// project came first in workspace membership order (added_at DESC), i.e. +// insertion history. +// +// The two projects here differ by 5e-6 in candidacy: far below round4's +// resolution, so both display as 0.5000, and the input order is deliberately +// the reverse of the correct one. Naming the stronger project last +// alphabetically is what makes this test able to tell "sorted on the raw value" +// apart from "fell back to the path tiebreak" — both alternatives would put +// "aaa" first. +func TestSortPanel_OrdersOnRawCandidacyNotTheRoundedCopy(t *testing.T) { + surviving := []projectHits{ + {ProjectPath: "github.com/o/aaa@main", Candidacy: 0.4999950}, + {ProjectPath: "github.com/o/zzz@main", Candidacy: 0.5000000}, + } + if round4(surviving[0].Candidacy) != round4(surviving[1].Candidacy) { + t.Fatalf("test premise broken: %v and %v do not round to the same value", + round4(surviving[0].Candidacy), round4(surviving[1].Candidacy)) + } + + sortPanel(surviving) + if surviving[0].ProjectPath != "github.com/o/zzz@main" { + t.Errorf("panel led with %s — the stronger project lost to a tie that "+ + "only exists after rounding", surviving[0].ProjectPath) + } +} + +// TestSortPanel_BreaksGenuineTiesByPath is the other half: when the candidacy +// really is equal, the order still has to be a function of the query rather +// than of the order projects happened to arrive in. +func TestSortPanel_BreaksGenuineTiesByPath(t *testing.T) { + mk := func(paths ...string) []projectHits { + out := make([]projectHits, 0, len(paths)) + for _, p := range paths { + out = append(out, projectHits{ProjectPath: p, Candidacy: 0.5}) + } + return out + } + want := []string{"github.com/o/aaa@main", "github.com/o/mmm@main", "github.com/o/zzz@main"} + for _, input := range [][]string{ + {"github.com/o/zzz@main", "github.com/o/mmm@main", "github.com/o/aaa@main"}, + {"github.com/o/mmm@main", "github.com/o/aaa@main", "github.com/o/zzz@main"}, + {"github.com/o/aaa@main", "github.com/o/mmm@main", "github.com/o/zzz@main"}, + } { + got := mk(input...) + sortPanel(got) + for i := range want { + if got[i].ProjectPath != want[i] { + t.Errorf("input %v -> position %d is %s, want %s", + input, i, got[i].ProjectPath, want[i]) + break + } + } + } +} From 7cc70a25eee931fc6e29ae88a2d27aa6979f37f9 Mon Sep 17 00:00:00 2001 From: dvcdsys Date: Thu, 20 Aug 2026 23:42:29 +0100 Subject: [PATCH 6/6] docs(workspacesearch): the panel-build comment contradicted the code under it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fourth review pass, comment only. "the projects panel sees every surviving project" was written when payloads were built for everything and truncated afterwards. 7e03041 moved the truncation above the loop, so the sentence now describes the opposite of the code three lines below it. Worth a commit of its own because of WHICH reader it misleads: this is the comment someone consults when reasoning about projects_in_panel versus projects_returned, and telling them those are the same number is exactly how d511513 gets re-broken. It has already been re-broken once, while refactoring these very lines — so the replacement says so, and says which of the two must never be capped. The num_hits half of the original sentence was correct and is kept. Co-Authored-By: Claude Opus 5 --- server/internal/httpapi/workspacesearch.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/server/internal/httpapi/workspacesearch.go b/server/internal/httpapi/workspacesearch.go index c380c31b..e3ca4d35 100644 --- a/server/internal/httpapi/workspacesearch.go +++ b/server/internal/httpapi/workspacesearch.go @@ -375,12 +375,17 @@ func (s *Server) WorkspaceSearch(w http.ResponseWriter, r *http.Request, id stri return } - // Build the projects panel + the flat chunk list. Per-project cap - // is applied to each project's fused chunk list so one dominant - // repo can't take every slot in the round-robin interleave below; - // the projects panel sees every surviving project (its num_hits - // reflects the post-cap count so the UI doesn't dangle a "10 - // hits" badge against a chunk list with 5 entries). + // Build the projects panel + the flat chunk list. The per-project cap is + // applied to each project's fused chunk list so one dominant repo can't + // take every slot in the round-robin interleave below. num_hits reflects + // the post-cap count, so the UI doesn't dangle a "10 hits" badge against a + // chunk list with 5 entries. + // + // The panel itself is capped at top_projects further down. + // projects_returned is NOT, and must not be — it counts what cleared the + // relevance threshold, and capping it turns the scanned:returned ratio + // into a measurement of a request parameter. That has been re-broken once + // already while refactoring these very lines. for i := range surviving { if len(surviving[i].FusedChunks) > workspaceSearchPerProjChunkCap { surviving[i].FusedChunks = surviving[i].FusedChunks[:workspaceSearchPerProjChunkCap]