Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 44 additions & 10 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,12 @@ Production queries accept the named profile selection `compatibility`, `balanced
`natural-language`. Only `compatibility` currently has matrix-derived values; the remaining names are
runtime placeholders until their configurations are selected from the benchmark matrix.

Benchmark-owned profile seeds and optimizer search live under `benchmarks/retrieval/`; current profile
values are marked `authored-seed` and are not presented as benchmark-derived weights. Production keeps
only the active router configuration and reusable fusion/evidence seams. A benchmark result is promoted
explicitly rather than making the production package responsible for discovering its own profile.
Benchmark-owned corpus checkout and preparation live under `benchmarks/retrieval/corpus/`; quality
evaluation, profile seeds, and optimizer search live under `benchmarks/retrieval/evaluation/`; native
SQLite and worker execution live under `benchmarks/retrieval/execution/`. Current profile values are
marked `authored-seed` and are not presented as benchmark-derived weights. Production keeps only the
active router configuration and reusable fusion/evidence seams. A benchmark result is promoted explicitly
rather than making the production package responsible for discovering its own profile.

### Retrieval Quality Benchmark

Expand Down Expand Up @@ -212,6 +214,11 @@ Short-profile fusion runs use Relative Score and DBSF; the full profile also run
comparisons.
Schema 14 records the router search strategy and compute-time breakdown in each artifact, including
corpus preparation, embedding, retrieval, static fusion search, and evidence-router search duration.
For runtime planning, `timings.evidenceRouterSearchDurationMs` is the relevant embedding-free wall-clock
measurement. The current `develop` calibration uses one DBSF fusion, grouped 3-fold, no repository
holdouts, one fit-all job, and three objective selections from each shared dynamic search. Its empirical
estimate is `T ~= 32.15 + 0.14 * chunks` seconds for 60 query samples; the complete job-count and
sample-scaling model is documented in ADR-0019.
Schema 15 adds deterministic one-stage proxy promotion to the evidence-router search. The benchmark evaluates the
current production router as an explicit holdout baseline, adds Recall@50, and uses one
shared Pareto search to select objective-specific candidates for direct retrieval, reranker top-20
Expand All @@ -227,10 +234,11 @@ in schema-17 artifacts. ADR-0020 promotes the validated Sparse contract to the p
fusion path and persists its IDF and postings in `.pix/index.db`.
Schema 19 removes the benchmark-owned Sparse encoder, in-process postings implementation, and separate
embedding caches. Benchmark profile fitting and optimizer search remain benchmark-owned, while the
fusion adapters and evidence signals are shared with production. Benchmarks
compose the production SparseEmbedder and IndexStore around a migrated in-memory SQLite database;
Dense and Sparse ranking therefore execute through the same adapters as product queries. Experimental
profile fitting remains benchmark-owned. Every artifact includes the authored file-qualified ground truth and
fusion adapters and evidence signals are shared with production. Benchmarks compose the production
SparseEmbedder and IndexStore around a migrated SQLite database; current schema-24 runs persist that
benchmark database and channel rankings under `benchmarks/.cache/retrieval/v1/` for warm reuse. Dense
and Sparse ranking therefore execute through the same adapters as product queries. Experimental profile
fitting remains benchmark-owned. Every artifact includes the authored file-qualified ground truth and
both the current Production router and a fixed five-channel `1/1/1/1/1` historical RRF baseline. Channel combinations
and leave-one-channel-out variants use equal weights so channel contribution is not confounded by routing.
New repositories are represented by JSON manifests in `benchmarks/corpus/`, selected with
Expand All @@ -245,9 +253,35 @@ current beam elites, so a later coordinate cannot regress the best development c
development folds and evaluated unchanged against static weights on excluded intent folds and
repositories; authored query-form labels remain informed reference strata and are not router inputs.
This router remains benchmark-only until holdouts justify a production change.
Schema 24 exposes the historical `successive-halving` router search beside the default `proxy-promotion`
mode through `PIX_BENCH_ROUTER_STRATEGY`. Successive Halving uses the original lexicographic quality
comparator and `halvingKeepFactor`; both modes share the proxy/full evaluator pools and native worker
queue. Artifacts record the selected algorithm so FastAPI and other corpus comparisons remain
reproducible.
Benchmark weight and router searches use a benchmark-only prepared evaluator: each sample and
fusion method materializes per-chunk normalized or RRF contributions once, then candidate weights reuse
that data. Public `fuseRankings` semantics and its existing ranking, normalization, and typed-array caches
remain unchanged. The evaluator still materializes and sorts the full candidate union for every
configuration; partial top-K or metric-specialized evaluation remains future work. The explicit parallel
benchmark path uses a fixed native `node:worker_threads` pool (default `max(1, availableParallelism() - 1)`),
sends the prepared snapshot once per worker, and batches candidate weight vectors. Beam, cache, archive, and
selection state stay on the main thread; async candidate evaluations let independent router jobs share one
queue without a second controller-worker layer. The search APIs are async so serial and worker execution
share the same selection algorithm; `PIX_BENCH_WORKERS=0` selects serial evaluation, `workerCount: 1`
selects the serial pool through `createCandidateEvaluationPool`, and the runner's
`PIX_BENCH_SEARCH_MODE=serial` selects that serial pool mode. `PIX_BENCH_WORKER_BATCH_SIZE` bounds worker
messages. Router search diagnostics retain candidate snapshot and pool initialization time separately from
candidate evaluation time; benchmark timings also record shared candidate-queue startup and shutdown.
Evidence-router grouped-fold, repository-holdout, and fit-all jobs run concurrently on the main thread while
candidate batches use work-stealing tasks in the shared native queue. The queue caches each prepared snapshot
once per worker and defaults to one candidate per task because the benchmark's candidate costs are uneven;
`PIX_BENCH_WORKER_BATCH_SIZE` overrides that default. Results retain planner order through `Promise.all`.
Serial mode keeps the same algorithm on the main thread for comparison. Native worker startup or task
failures are surfaced and all workers are terminated before the benchmark fails.
Repository checkouts live under ignored `benchmarks/.cache/repos/`; generated artifacts live under
ignored `benchmarks/results/`. Dense and Sparse vectors are held only in the production in-memory
SQLite adapter during a benchmark run. See `benchmarks/README.md` and `benchmarks/BASELINE.md`.
ignored `benchmarks/results/`. Benchmark Dense and Sparse vectors and channel rankings live under the
ignored `benchmarks/.cache/retrieval/v1/` cache; production indexes remain separate. See
`benchmarks/README.md` and `benchmarks/BASELINE.md`.

### Scorer

Expand Down
49 changes: 47 additions & 2 deletions benchmarks/BASELINE.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,45 @@
# Preliminary Retrieval Baseline

The schema-17 entries below are historical artifacts from the benchmark-owned Sparse implementation.
Current schema-22 runs use the production SparseEmbedder and IndexStore without benchmark vector caches.
Current schema-24 runs use the production SparseEmbedder and IndexStore without benchmark vector caches.

## Schema 24: Selectable Router Search

Schema 24 exposes the current `proxy-promotion` search and the historical `successive-halving`
algorithm from commits `1754725` and `2f92428` through `PIX_BENCH_ROUTER_STRATEGY`. The legacy path
uses the original `halvingKeepFactor: 8`, lexicographic `R@20/R@10/Context@4k/MRR` comparator, and
does not run the later random-scout baseline. Both runs below used the same MiniLM model, pinned corpus,
warm retrieval cache, DBSF fusion, and 11 native worker threads.

### FastAPI Develop Sanity Check

| Strategy | Artifact | Router time | Total time | Dynamic holdout summary (R@5/R@10/R@20/R@50/Context@4k) |
| -------------------- | ----------------------------------------- | ----------: | ---------: | ------------------------------------------------------------ |
| `successive-halving` | `retrieval-2026-08-05T01-57-19.822Z.json` | 18.56 s | 23.59 s | 51.3%/64.3%/77.3%/88.7%/58.7% |
| `proxy-promotion` | `retrieval-2026-08-05T01-59-43.049Z.json` | 114.59 s | 119.64 s | objective-specific; 54.0%/65.0%/72.3%/85.0%/58.7% for direct |

Successive Halving was 83.8% faster on this warm-cache control. Its objective-specific output is one
historical candidate repeated across the current three-objective artifact rows; Proxy Promotion selects
objective-specific candidates. This is a sanity check of behavior and queue integration, not a quality
promotion decision.

### Effect v4 Develop Sanity Check

| Strategy | Artifact | Router time | Total time | Dynamic holdout summary (R@5/R@10/R@20/R@50/Context@4k) |
| -------------------- | ----------------------------------------- | ----------: | ---------: | ------------------------------------------------------------ |
| `successive-halving` | `retrieval-2026-08-05T02-00-45.058Z.json` | 29.62 s | 46.62 s | 40.0%/52.0%/59.3%/82.0%/48.0% |
| `proxy-promotion` | `retrieval-2026-08-05T01-52-54.327Z.json` | 938.61 s | 954.79 s | objective-specific; 32.7%/42.0%/52.0%/80.0%/42.0% for direct |

Successive Halving was 96.8% faster on the larger Effect v4 control. The worker queue completed both
strategies successfully; the large runtime gap comes from candidate selection/evaluation volume, not
embedding, which was reused from the persistent cache.
Comment on lines +33 to +35

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the cache explanation for the runtime comparison.

Line 35 says that embedding was reused from a persistent cache. benchmarks/README.md Lines 105-106 state that each model run computes Dense and Sparse vectors once and has no benchmark embedding cache. If the run reused retrieval or index artifacts, name those artifacts instead of saying that the embedding was reused.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@benchmarks/BASELINE.md` around lines 33 - 35, Update the benchmark comparison
note in BASELINE.md to remove the incorrect claim that embeddings were reused
from a persistent cache. Describe the actual reused retrieval or index artifacts
if applicable, using the benchmark documentation as the source of truth;
otherwise state that no embedding cache was used.

Source: Path instructions


## Schema 20: Search-Priority DBSF Selection

The historical schema-20 `search-priority` full-profile `fd` run is
`benchmarks/results/retrieval-2026-08-03T23-34-02.311Z.json`. Its authored query-form objective is
`identifier/agentTask/naturalQuestion/searchPhrase = 1/2/3/4`; the profile's channel weights are
authored seeds, not benchmark-derived deployment weights. This evidence predates the current schema-22
authored seeds, not benchmark-derived deployment weights. This evidence predates the current schema-23
artifact format. Result artifacts are ignored and the referenced JSON is available only in the local
worktree that produced that promotion evidence.

Expand Down Expand Up @@ -200,6 +231,20 @@ and SQLite retrieval `2.50 s`. The fit-all router evaluated `3,693` proxy candid
candidates. The small hold-out movements are recorded rather than treated as a universal quality gain;
future optimizer changes must compare the same pinned corpora, model, folds, and static controls.

### Strategy Equivalence Check

There is a matched Schema-19 `fd` smoke comparison between
`retrieval-2026-08-02T01-31-54.105Z.json` (`successive-halving-pareto`) and
`retrieval-2026-08-03T15-09-38.902Z.json` (`proxy-promotion`). Both use the same pinned corpus, model,
folds, DBSF router, objectives, and guardrail settings. The weighted evidence-router holdout summaries
are identical at the reported precision for all three objectives. Successive Halving took `97.01 s`
of router time; proxy promotion took `111.90 s`.

This supports using Successive Halving as a faster benchmark-search mode with no observed quality loss
on this control. It is an artifact comparison, not an automated strategy A/B test, and it does not yet
establish equivalence on the larger FastAPI and Effect-TS corpora. The older Schema-14/15 comparison
also found only rounding-level holdout movement while successive halving reduced router time by `9.3%`.

## Schema 14: Router Search Strategy And Runtime Telemetry

Schema 14 records the deterministic `halton-global-scout-elitist-beam` router search strategy and
Expand Down
23 changes: 18 additions & 5 deletions benchmarks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ vp run bench:retrieval:full

`bench:retrieval` aliases `bench:retrieval:validate`. Every profile measures the same physical
rankings and retrieval variants; profiles only control matrix size, holdout coverage, and expensive
diagnostics. The selected profile is recorded in schema-22 artifacts without changing retrieval
diagnostics. The selected profile is recorded in schema-24 artifacts without changing retrieval
semantics. The full profile includes all three fusion methods; short profiles intentionally omit RRF
to keep development runs fast.

Expand All @@ -110,6 +110,7 @@ Limit an exploratory run with comma-separated environment variables:
$env:PIX_BENCH_REPOS = "fd"
$env:PIX_BENCH_MODELS = "Xenova/all-MiniLM-L6-v2"
$env:PIX_BENCH_OPTIMIZATION_PROFILE = "search-priority"
$env:PIX_BENCH_ROUTER_STRATEGY = "proxy-promotion"
vp run bench:retrieval:validate
```

Expand Down Expand Up @@ -154,6 +155,12 @@ defaulting to MiniLM. Select another with `PIX_BENCH_MODELS`. Supported values a
- `Xenova/bge-small-en-v1.5`
- `jinaai/jina-embeddings-v2-base-code`

The router search defaults to `proxy-promotion`. Set `PIX_BENCH_ROUTER_STRATEGY` to
`successive-halving` to select the historical Successive-Halving variant. It uses the original
lexicographic `R@20`, `R@10`, `Context@4k`, and MRR comparator plus its `halvingKeepFactor`.
Both strategies use the same candidate evaluator and native worker queue, so their artifacts can be
compared directly.

Comment on lines +158 to +163

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Document both candidate-evaluation modes.

The README describes native worker execution as unconditional. The queue factory selects SerialCandidateEvaluationQueue when workerCount <= 1, while worker mode uses the native queue.

  • benchmarks/README.md#L158-L163: qualify the strategy comparison with in worker mode and mention serial fallback.
  • benchmarks/README.md#L320-L326: apply the same qualification to the architecture description.
📍 Affects 1 file
  • benchmarks/README.md#L158-L163 (this comment)
  • benchmarks/README.md#L320-L326
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@benchmarks/README.md` around lines 158 - 163, Update both
benchmarks/README.md sections at lines 158-163 and 320-326 to document both
candidate-evaluation modes: qualify the strategy comparison and architecture
description as applying in worker mode, and mention that workerCount <= 1 uses
SerialCandidateEvaluationQueue as the serial fallback instead of the native
worker queue.

The Jina code model cannot embed Effect's longest 7,103-token AST chunk on the tested DML GPU even as
a single-item batch. Do not silently truncate, re-chunk only one model, or mix CPU and GPU vectors to
complete that cell: any of those choices changes the comparison. Treat the cell as unsupported until
Expand Down Expand Up @@ -310,9 +317,13 @@ Score and DBSF consume the same `ChannelRankings` interface and are evaluated wi
encoders, persistence, or scoring. `src/lib/retrieval/evidence-router.ts` is likewise shared by production
configuration and benchmark evidence evaluation.

`benchmarks/retrieval/optimization-profiles.ts` owns authored (`authored-seed`) profile seeds, and
`benchmarks/retrieval/weight-search.ts` owns candidate search. A validated benchmark result is promoted
to an explicit production configuration; production does not discover or optimize its own profile.
`benchmarks/retrieval/evaluation/optimization-profiles.ts` owns authored (`authored-seed`) profile seeds, and
`benchmarks/retrieval/evaluation/weight-search.ts` owns candidate search. Corpus checkout and preparation
live under `benchmarks/retrieval/corpus/`; native SQLite and worker execution live under
`benchmarks/retrieval/execution/`. A validated benchmark result is promoted to an explicit production
configuration; production does not discover or optimize its own profile. Router searches keep their beam
and archive state on the main thread; only candidate scoring crosses the worker seam, so independent jobs
share one queue without a second controller-worker protocol.

The remaining architectural follow-up is a diagnostic retrieval snapshot from `IndexStore` if future
benchmark work needs to inspect persisted channel evidence through the application boundary. Current
Expand All @@ -339,7 +350,9 @@ output size without introducing an LLM or provider-specific tokenizer.

Each run writes ignored JSON and Markdown artifacts under `benchmarks/results`. JSON rows retain the
repository, revision, language, size, category, difficulty, query form, grouped fold, model, variant,
individual gold ranks, timing, and every metric. Schema 22 stores each authored query and its exact
individual gold ranks, timing, and every metric. Schema 24 adds selectable router strategies while
retaining shared candidate-queue lifecycle and
per-router candidate-pool initialization timings. Each artifact stores each authored query and its exact
file-qualified ground truth once, records productive Sparse timings, and adds
the fixed equal-weight RRF baseline. The Markdown report includes quality by query form,
marginal leave-one-channel-out contribution, cross-validation folds, Shapley values, and final fitted
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ import path from "node:path"

import { Effect } from "effect"

import type { Chunk } from "../../src/domain/chunk.js"
import { DEFAULT_CONFIG } from "../../src/domain/config.js"
import type { Identifier } from "../../src/domain/identifier.js"
import type { Bm25Index } from "../../src/domain/ports.js"
import { getExtension } from "../../src/lib/config/extension.js"
import { extractIdentifiers } from "../../src/lib/parsing/identifier-extractor.js"
import { buildExtensionRegistry } from "../../src/lib/registry.js"
import { buildBm25Index } from "../../src/lib/retrieval/bm25.js"
import { buildIdentifierIndex } from "../../src/lib/retrieval/identifier-index.js"
import { chunkTextWithConfig } from "../../src/services/chunker.js"
import { listCorpusFiles } from "./corpus.js"
import type { ChunkIdentifiers } from "./metrics.js"
import type { CorpusManifest } from "./types.js"
import type { Chunk } from "../../../src/domain/chunk.js"
import { DEFAULT_CONFIG } from "../../../src/domain/config.js"
import type { Identifier } from "../../../src/domain/identifier.js"
import type { Bm25Index } from "../../../src/domain/ports.js"
import { getExtension } from "../../../src/lib/config/extension.js"
import { extractIdentifiers } from "../../../src/lib/parsing/identifier-extractor.js"
import { buildExtensionRegistry } from "../../../src/lib/registry.js"
import { buildBm25Index } from "../../../src/lib/retrieval/bm25.js"
import { buildIdentifierIndex } from "../../../src/lib/retrieval/identifier-index.js"
import { chunkTextWithConfig } from "../../../src/services/chunker.js"
import type { ChunkIdentifiers } from "../evaluation/metrics.js"
import type { CorpusManifest } from "../evaluation/types.js"
import { listCorpusFiles } from "./repository.js"

/** Query-independent indexes and source chunks prepared once per repository. */
export interface PreparedCorpus {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { promisify } from "node:util"

import { Effect, Schema } from "effect"

import { CorpusManifestSchema, type CorpusManifest } from "./types.js"
import { CorpusManifestSchema, type CorpusManifest } from "../evaluation/types.js"

const execFilePromise = promisify(execFile)
const CACHE_ROOT = path.resolve("benchmarks/.cache/repos")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
decodeEvidenceRouterConfig,
ZERO_CHANNEL_COEFFICIENTS,
type EvidenceRouterConfig,
} from "../../src/domain/retrieval.js"
} from "../../../src/domain/retrieval.js"

/** Historical RRF configuration used only for explicit benchmark and rollback comparisons. */
export const HISTORICAL_RRF_BASELINE_CONFIG: EvidenceRouterConfig = decodeEvidenceRouterConfig({
Expand Down
Loading
Loading