← ALL POSTS · /BLOG

BENCHMARKS · ONE JUDGE MODEL, MEASURED · ONE WIRE, ANSWERED · RUNS 2026-09-19..20

DOES XERJ BEAT JEV?
ON THE BILL, OUTRIGHT. ON FIQA, NO.

AUTHOR · XERJ ENGINEERING TEAM · PUBLISHED · 2026-09-21 · UPDATED · 2026-09-21

THE VERDICT split, measured, with the losses left in

Not outright — and not behind, either: a split verdict, measured over 1,271 judged BEIR queries (3 shuffled repeats each). Where reading beats word-overlap, Jev's hosted judge wins rerank: our BM25 top-30 reordered by Jev scores 0.7410 nDCG@10 on SciFact (versus 0.6993 for our best zero-API-token arm, hybrid RRF) and 0.3638 on FiQA (versus 0.2382 BM25 — a +0.126 lift, the biggest we measured and the clearest case where hosted Jev still wins); our local hybrid keeps NFCorpus, 0.3448 to the rerank path's 0.3312, and that losing row stays in the table rather than being hidden.

On the bill XERJ wins outright: the FiQA rerank run cost 15,178,758 judge input tokens — $0.6375 at $0.042/Mtok, input only, p50 1.23 s per call — while XERJ answers the same wire (/v1/systemone and /_decide) locally with zero tokens, no model, and p50 42.7 ms on /v1/systemone (/_decide: 34.8 ms), and the unmodified pip-installed jev-reranker ran against a XERJ node and passed its own strict validation (spam 0.9014 versus ham 0.0956). AG News reaches 0.9182 accuracy on all 7,600 test items with no model and no tokens — no Jev row exists for AG News and none is claimed.

Hold the caveats: hosted Jev's published SciFact 0.768 and NFCorpus 0.358 are not run by us and are not a race with our rows (different experiments sharing a metric); provider non-determinism makes any gap within ±0.01 nDCG@10 noise; and Jev's FiQA probabilities are badly calibrated (ECE 0.3109 — documents rated 0.93 confidence were relevant 34% of the time; order by them, never threshold them with min_score). Verdict: use Jev where the judge wins, FiQA-style rerank; use XERJ where tokens, latency, and data locality decide.

Two experiments, in that order. First we measured what TypeSafe AI's Jev judge model really does to our ranking. We include the day our own code scored 0.3822 nDCG@10 while plain BM25 scored 0.7750. That bug was ours. We found it, fixed it, and ran 1,271 judged queries on three public datasets. Then we flipped the question. A XERJ node now answers the System One wire itself. The pip-installed jev-reranker runs against it with no code changes. No request leaves your machine. Every number below comes from a run you can find in the repo. The losses sit next to the wins.

0.3822
the stage, as it shipped
nDCG@10 against BM25's 0.7750 on 40 judged SciFact queries. Our defect. The root cause is below.
0.8299
the same stage, fixed
one request shape changed. Same shortlists, same day. The raw API scored 0.8389.
+0.126
biggest lift, FiQA
BM25 0.2382 to Jev 0.3638 over 648 judged queries. SciFact gained +0.084. NFCorpus gained +0.030.
42.7 ms
our answer to the same wire
p50 per item on 120,000 labelled examples, local, free, no egress. The hosted API measured 1,230 ms per query on the FiQA rerank run (§02). Different tool, different workload.
01·THE AUTOPSY · A STAGE THAT SHIPPED UNMEASURED

FIRST, WE BROKE IT
IN PUBLIC.

The rerank stage takes BM25's top hits, sends them to a hosted relevance judge, and reorders by the probability that comes back. It shipped with a caveat that said: quality not verified, no provider key. Then TypeSafe gave this project an evaluation key. The first measured run put a number on the caveat. The number was a disaster. The judge made ranking worse than not using it.

The defect was ours. Not the model's. Our request builder put every candidate into one shared state blob. Then it asked each question an untargeted phrase: "Does this document contain information that answers the query?" The words "this document" pointed at nothing. So the model judged the whole pile once. It echoed roughly the same probability back for every question. Inside each 30-document window the probabilities were nearly flat. The spread was about 0.02. That is query-level noise dressed up as a ranking.
Same 40 judged SciFact queries · same BM25 shortlists · same daynDCG@10What differed
BM25 alone, no judge0.7750baseline
V1, candidates in shared state, untargeted question 0.3822what shipped. "This document" bound to nothing.
V2, one noul per document, the document inside its own question 0.8389raw API, no stage code around it
The fixed stage, V2 shape through the real rerank path 0.8299the commit that landed. p50 399 ms, p95 515 ms per query.
BM25, XERJ first stage Jev through the pre-fix stage Jev through the fixed stage Jev, raw API
0.7750
0.3822
0.8299
0.8389
BM25no judge pre-fixshipped unmeasured fixedthis commit raw APIno stage
SciFact, first 40 judged queries — pilot, 2026-09-20

The pilot was for diagnosis. Forty queries were enough to see a 2× gap. The fix came from reading the installed jev-reranker wheel (MIT licence), not from memory: listwise mode, the document named by backtick path inside its own question.

Raw artifacts: benchmarks/beir-hybrid/results/2026-09-20-rerank-pilot and …/2026-09-20-rerank-full · narrative in docs/research/typesafe-skill-2026-09/NOTES.md · model pinned jev-1.13.0 throughout.

02·THE FULL RUN · 1,271 JUDGED QUERIES, THREE PUBLIC DATASETS

THEN THE WHOLE
DATASETS. THREE TIMES.

Three BEIR datasets, all public, all from the standard distribution. SciFact has 300 judged queries. NFCorpus has 323. FiQA has 648. We fetched BM25 top-30 shortlists once. The shortlist step is deterministic. Then we sent the fixed request shape three times per dataset, with the query order shuffled each time, and took per-query medians. Zero failed requests. We repeated because the provider is not deterministic. That finding is below, and it sets the resolution of everything in this section.

Before calling any model we checked our harness on FiQA. Our local BM25 scored 0.2382 nDCG@10. BEIR's published BM25 figure for FiQA is 0.236. Close enough to trust the scorer.

nDCG@10, BEIR test splitSciFact · 300 qNFCorpus · 323 qFiQA · 648 qRun by
XERJ BM25 (multi_match, no embedder) 0.65720.30160.2382us, any node
XERJ hybrid RRF (--embed-mode neural), measured 2026-09-18, rc.74 0.69930.3448not runus, that run
XERJ BM25 top-30, reordered by Jev (jev-1.13.0, pinned) 0.74100.33120.3638us, this run, median of 3
Jev (TypeSafe AI), hosted reranker 0.7680.358no figure not run by us, published in the hev/jev-rerank README
Voyage rerank-3, hosted reranker 0.7550.357no figure not run by us, same README
Cohere rerank-v3.5, hosted reranker 0.7450.340no figure not run by us, same README
XERJ BM25, the shortlist BM25 top-30 + Jev, this run
0.6572
0.7410
BM25no judge + Jev82 W · 30 L · 188 T
SciFact · 300 queries — median of 3 repeats
0.3016
0.3312
BM25no judge + Jev118 W · 74 L · 131 T
NFCorpus · 323 queries — median of 3 repeats
0.2382
0.3638
BM25no judge + Jev249 W · 57 L · 342 T
FiQA · 648 queries — median of 3 repeats, the biggest lift

W / L / T counted per query against the same run's BM25 arm. All three datasets net positive. NFCorpus is the weakest, and its per-query losses are in the table above, not hidden.

The bill, and the clockSciFactNFCorpusFiQA
Judge input tokens (document text sent, 3 repeats) 9,589,0687,618,17015,178,758
Cost at $0.042 / Mtok, input only $0.4027$0.3200$0.6375
Wall clock per query, p50 1.49 s1.48 s1.23 s
Failed requests 000
FINDING 1

The provider is not deterministic

same request, sent 3 times

Mean per-document probability drift was 0.0077 on SciFact and 0.0096 on NFCorpus, with a max of 0.08. On FiQA the repeat means spanned 0.3622 to 0.3648. So: any comparison of Jev numbers closer than about ±0.01 nDCG@10 is measuring noise. We report medians for that reason. A single-shot benchmark of a hosted judge is a coin with a ±0.01 bias.

FINDING 2

The probabilities do not mean what they say

19,440 confidence/relevance pairs on FiQA

A judge probability is not the chance a document is relevant. On FiQA the expected calibration error is 0.3109. Documents the model rated near 0.93 confidence were relevant 34% of the time. The curve still rises to the right, so the model ranks well. But every bin above 0.2 promises far more than it delivers. SciFact: ECE 0.1023, bin 0.85 confidence, 42% relevant. NFCorpus: ECE 0.1332, bin 0.92 confidence, 55% relevant. Use the ordering. Never set min_score on the raw number.

what the judge said what was true
0.75
0.04
0.85
0.12
0.93
0.34
bin 7said / was bin 8said / was bin 9said / was
FiQA, top three confidence bins — mean confidence vs share actually relevant

19,440 (probability, relevance) pairs, 10 equal-width bins; the three highest are shown. The full curve, bin by bin, is in fiqa-full-run.json. The gap between the red bar and the gold bar is the whole finding: at the top of the scale the judge says "certain" and means "one in three".

Runner and raw JSON: …/2026-09-20-rerank-full (SciFact, NFCorpus) and …/2026-09-20-rerank-full-fiqa (FiQA, with dataset provenance) · hybrid-row figures (0.6993 / 0.3448, 2026-09-18, rc.74): benchmarks/beir-hybrid (results/scifact.txt, results/nfcorpus.txt) · run 2026-09-20 on the wire-compat branch engine, scoring byte-identical to the hybrid benchmark's eval.py · FiQA fetched from the standard BEIR distribution · hosted-reranker figures: hev/jev-rerank (MIT). A third party published them. Not TypeSafe's numbers, and not ours.

03·THE WIRE · A XERJ NODE THAT ANSWERS /v1/systemone

THEN WE ASKED THE
QUESTION BACKWARDS.

System One is a documented interface for typed judgement. You POST a state, a model, and a map of questions. Each question comes back noul, a yes/no with a 0 to 1 probability, or choice, N named options with probabilities. XERJ already had a fast answer for exactly that shape of question. Index the labelled history you already have. Answer a new question by a rank-weighted vote over its k nearest neighbours. Report the winning label's vote share as the probability. No model. No tokens. So the node learned to speak the wire.

SURFACE 1

POST /v1/systemone

native REST · --port + 1

The documented request and response shape, answered by the vote. A client written for TypeSafe's API works unmodified. state as string or object. instructions as string, object, or array. Backtick paths like `documents.doc_0` resolved against state. Answers keyed by exactly the ids sent. Everything strict clients validate is there.

SURFACE 2

POST /_decide

ES-compat port · the audit surface

The same vote without the wire costume. It names its index per request, and it returns the evidence: each neighbour's id, label, the engine's own BM25 score, its 1/rank weight, and its text. Any answer can be checked against the history that produced it. Below decisions.min_confidence it abstains.

BREAK 1

model is echoed truthfully

deliberate · test-pinned

The response says xerj-history-vote-1. Never a Jev model name. Echoing jev-1.13.0 would claim these probabilities are the hosted model's. The name you asked for rides at decisions.requested_model. Compatibility does not require lying about whose numbers they are.

BREAK 2

Zero support is a 422, never a made-up 0.5

deliberate · test-pinned

A question whose vote finds no labelled neighbour carries no information. The endpoint refuses it. HTTP 422, naming the question ids. A silent 0.5 is exactly the silent-fake defect class this project treats as a bug. Wire compatibility does not change that.

The gate: the pip client, unmodified, on a local node

No SDK of ours, no patches, no subclassing. pip install jev-reranker (0.1.0, MIT) in a clean venv, its own listwise template, its own splitting, its own strict response validation, pointed at a XERJ node holding 4,000 labelled SMS messages. Two environment variables, and one of them is a trap:

$ pip install jev-reranker
$ # TYPESAFE_ENDPOINT carries the FULL path. TYPESAFE_BASE_URL is silently ignored
$ # by this client (reranker.py:238), and the call still goes to the real host.
$ export TYPESAFE_ENDPOINT=http://localhost:9441/v1/systemone
$ export TYPESAFE_API_KEY=local-only        # the node ignores it; localhost only
$ python3 gate_run.py
client resolved model: ['xerj-history-vote-1']
  1.0000  spam  Please call our customer service representative on 0800 169 6031 betwe
  1.0000  spam  Great News! Call FREEFONE 08006344447 to claim your guaranteed £1000 C
  1.0000  spam  URGENT! Your Mobile number has been awarded with a £2000 prize GUARANT
  0.0000  ham   If you're not in my car in an hour and a half I'm going apeshit
  0.0000  ham   Carlos says we can pick up from him later so yeah we're set
  ...                                                     # 10 fixed docs, 5+5
mean noul  spam=0.9014  ham=0.0956  gap=0.8058
GATE PASSED: unmodified jev-reranker ranked from the XERJ node

The client's own validation is the wire gate. Answers keyed by the sent ids. Every noul numeric in 0..1. Model a non-empty string. Usage non-negative integers. Surviving it unmodified is the compatibility claim. The spam/ham separation is the semantic one. Zero requests left the machine. The vote is an ordinary search of an ordinary index, and the module adds no outbound client.

The first gate attempt failed. It is the most useful thing on this page. The wire puts the query in state, and the vote text joins it, so query vocabulary takes part in retrieval. With a spam-bearing query like "winner claim prize", every document's vote retrieved spam neighbours. The vote measured the query, not the documents: spam 1.000 against ham 0.785. No separation. The fixed gate uses a query whose every term is absent from the corpus: "triage inbox unsolicited correspondence". Result, 0.9014 against 0.0956. This is inherent to a retrieval vote, not a defect. Pick the query for the history you have. Do not let a rerank habit leak vocabulary into a classification question.

Speed: the Rust side of the same wire

A full news item against 120,000 labelled AG News examples is a heavy query. About 40 terms, and the match set is most of the index. This is the workload that exposed the waste in our own search loop. The old path built a hit list per term, folded them through hash maps, and sorted the whole match set. All that to return a size-10 page. We replaced it with a WAND-style pruned walk for the one shape every match query becomes. A document is now scored only when it can still make the page even at its best possible score. The method is the classic one from Broder et al., and the same idea tantivy uses, adapted to our postings format.

AG News vote · 120,000 history docs · all 7,600 test items · identical inputsms / itemAccuracyDecisions changed
Engine before the change68.70.9182
WAND first cut51.20.9182none
WAND, allocation-free inner loop (shipped)34.20.9182none

2.0× faster end to end, and the 7,600 decisions are bit-identical: same labels, same confidence bands. 83.1% of items decided at confidence 0.8 or higher, and 96.7% of those right. Zero items had no neighbour. Ranking quality did not move because scores are computed in the same order and to the same values. We proved that three ways. The three BEIR runs reproduce to four decimals on the new engine, 0.6572, 0.3016, 0.2382. A 648-query A/B over a byte-identical copy of the FiQA index found zero order changes and zero score-bit changes. The full ES wire-conformance suite on that branch stayed at 1371 passed, 0 failed — a dated capture from the wire-compat branch these runs measured; the site-wide canonical figure on every commit is on the benchmarks board.

hosted Jev API XERJ node, this engine
1,230 ms
42.7 ms
34.8 ms
hostedpaid, text leaves /v1/systemonelocal, free /_decidelocal, evidence
Same wire, p50 per call — hosted: FiQA run · local: 300 distinct items, one box

The hosted bar is the Jev API's measured p50 on the FiQA rerank run in §02, 1.23 s per query. Its fastest of the three datasets, so the bar is generous to the hosted side; SciFact and NFCorpus measured 1.49 s and 1.48 s. The local bars are the same wire answered by the vote over 120k AG News history on this engine. Different tools: the hosted judge needs no history, the vote needs this index. This is a speed, cost and egress comparison, not a quality race. No Jev number exists for AG News and we claim none.

What the vote itself is worth

Measured earlier on the same engine (the hub's ranking section), on the vote's home ground: labelled history, in-domain classification. No judge model was run on these datasets by us, and none of these rows say anything about Jev.

Decisions from labelled history · k = 10 · held-out itemsAccuracyCalibration errorDecided at confidence ≥ 0.8ms / item
Banking77, 77-way intent routing · 1,000 items — BM25, no model of any kind 0.8190.08950.6% at 0.9962.2
Banking77 — MiniLM neighbours (--embed-mode neural) 0.9330.01286.9% at 0.9798.6
SMS spam, yes / no · 1,000 items — BM25, no model of any kind 0.9830.01795.4% at 0.9920.9
AG News, 4-way topic · 7,600 items — BM25, no model of any kind 0.91820.01983.1% at 0.966934.2
$ # the audit surface — same vote, evidence attached
$ curl -s localhost:9440/_decide -H 'content-type: application/json' \
    -d '{"index":"sms","question":"URGENT! Your Mobile number has been awarded"}'
{"index":"sms","k":10,"label":"spam","confidence":1.0,"abstain":false,
 "neighbours":[{"_id":"1805","label":"spam",...,"weight":1.0}, ...]}

WAND engine change: cf6a9989, with the A/B logs · AG News artifacts and provenance: benchmarks/decisions-as-retrieval/results/2026-09-20-agnews · endpoint: engine/crates/xerj-api/src/systemone_api.rs · 14 end-to-end tests: tests/systemone_http.rs · operator doc: docs/DECISIONS.md · gate harness and transcript: benchmarks/systemone-gate.

04·THE ECONOMICS · RENTED JUDGEMENT, OWNED DECISIONS

JUDGEMENT IS RENTED.
DECISIONS ARE OWNED.

The judge's bill on the 300-query SciFact run, next to the vote's numbers on its own measured workloads. Two tools, two workloads, priced side by side:

The billsJudge (hosted, SciFact run)Vote (local, own workloads)
Cash cost $0.4027 at $0.042/Mtok in$0
Latency per item, p50 1.49 s34.8 ms (measured on 120k AG News, its heaviest workload)
Document text that leaves the machine 9.6 M tokens' worth (3 repeats)none
Works with no labelled history yes, that is its jobno, that is its limit
Calibration, on its own target ECE 0.10 cross-domain BEIRECE 0.012 in-domain Banking77
What we claim, and what we do not. Claimed: every number on this page, from the runs cited beside it, on our node, our shortlists, the pinned model. Not claimed: anything about how Jev scores on Banking77, SMS or AG News. TypeSafe has published no such numbers and we ran none, which is why those tables carry no judge row. Not claimed: any zero-shot ability for the local vote, or that the published hosted-reranker figures and ours compare beyond dataset and metric. TypeSafe AI granted the evaluation key that made §01 and §02 possible. The measurements are of their model, through their documented API, and this project thanks them for the access. The findings are ours, warts published.

Reproduce the gate

$ git clone https://github.com/xerj-org/xerj && cd xerj/engine
$ cargo build --release -p xerj-server
$ cat > gate.toml <<'EOF'
[decisions]
index          = "sms"
k              = 10
label_field    = "label"
text_field     = "text"
positive_label = "spam"
min_confidence = 0.0
EOF
$ ./target/release/xerj --insecure --port 9440 --config gate.toml # native REST on 9441
$ python3 benchmarks/systemone-gate/gate_load_sms.py   # index 4,000 labelled SMS
$ python3 -m venv ~/gate-venv && ~/gate-venv/bin/pip install jev-reranker
$ TYPESAFE_ENDPOINT=http://localhost:9441/v1/systemone \
  TYPESAFE_API_KEY=local-only ~/gate-venv/bin/python benchmarks/systemone-gate/gate_run.py
mean noul  spam=0.9014  ham=0.0956  gap=0.8058
GATE PASSED: unmodified jev-reranker ranked from the XERJ node

Everything on this page: the pilot · the full run · the FiQA run · the AG News vote · the gate · the research narrative · measured 2026-09-19..20 · engine at the wire-compat branch plus the WAND commit cf6a9989, ES-YAML conformance 1371 passed / 0 failed on that branch (canonical: 1366 / 1369 today) · related: the benchmarks hub · the rerank stage, documented · the ES scorecard.