Skip to content

The benchmark campaign wizard

latence-campaign drives the framework's end-to-end SOTA benchmark campaign — acquire real public corpora, OCR them, push them through the full enterprise pipeline, then measure retrieval — as gated, resumable stages with filesystem done-probes and an atomic STATE.json. It is how the numbers on the front page were produced, and it is deliberately drivable from a clone with the framework installed and nothing else (stdlib argparse, no extra deps).

latence-campaign init      # the wizard: answers -> a validated campaign.yaml
latence-campaign status    # the stage table + probe results
latence-campaign run <stage>   # runs ONE stage (refused unless the previous one is approved)
latence-campaign approve <stage>  # operator sign-off on a done stage (opens the next gate)
latence-campaign resume    # continue an interrupted campaign (filesystem probes, not exit codes)

The wizard contract

init follows the latence setup contract: flag > prompt > default, a "why it matters" line per question, one frozen answers struct from either path, generate → dry-validate → write, and exactly one next command printed. Identical answers produce a byte-identical campaign.yaml — the config is an artifact, not a session. --non-interactive takes every default/flag for CI use. Exit codes: 2 usage/contradiction, 1 invalid artifact or a blocked/failed stage, 0 success.

The prompts cover: the campaign root, the dataset selection (from the known-dataset registry), the pod SSH target for GPU stages, the S3 induction mode, and the two headline retrieval choices (policy-gate mode, selector) — the full retrieval: block with measured defaults is documented in Graph-rescue retrieval. The s5: matrix block is flag-only (--s5-datasets, --s5-legs, --s5-limit, --s5-fusion): its default is the full matrix over the corpus selection, so every deviation is a deliberate narrowing rather than an answer worth interrogating up front.

The stage chain

Order is the gate chain — each stage refuses to run until the previous one is approved, and approve is an explicit operator sign-off, never automatic:

Stage What it does Gate artifact
s1-corpus Acquires the selected public datasets under their machine-enforced license lanes (a research-only corpus requires --accept-non-commercial; an unverifiable one is refused unconditionally) acquisition manifest + per-dataset counts
s2-ocr OCRs the scanned corpora into markdown (served LightOn-OCR-2), page maps carried in sidecars OCR metrics + spot-check dir
s3-pipeline Runs the full enterprise pipeline per dataset on the pod (extraction, redaction, KG, hyperedges, embeddings, exports) per-dataset metrics.json — the campaign scale numbers
s5-retrieval Runs the retrieval feature matrix — every (dataset × leg) cell from dense through +bm25 / +KG / +hyperedges / +CoE / +packer, the content-only ablation and the three graph-rescue legs one JSON cell per (dataset, leg) under stages/s5-retrieval/results/, plus REPORT.md + metrics.json

The matrix is configured by the s5: block (datasets, legs, limit, fusion) and, when s5.datasets is left empty, follows the campaign's own corpus.datasets — so trimming the corpus trims the matrix with it. The stage derives its plan from that block and calls the harness's API (benchmark.s5.run_matrix.run) directly; the same harness is still drivable standalone as python -m benchmark.s5.run_matrix, and a cell produced either way is the same file. Resumability is per cell: a matrix that takes hours can be interrupted and re-entered, and run never re-runs a cell it already has.

Two refusals are load-bearing. A leg whose export artifacts are absent is recorded as missing-inputs with the file list, never as a zero — the reused MuSiQue/HotpotQA exports genuinely carry no embedding column, and the matrix says so. But a dataset whose every leg refused blocks the stage: that is an absent or unfinished export being advanced as a measurement. Gold resolution is audited per dataset before any recall number is trusted, and single-hop control datasets are reported as controls, not headlines.

The run has to be pinned, twice over

The harness refuses to start unless PYTHONHASHSEED=0 and OMP_NUM_THREADS, OPENBLAS_NUM_THREADS, MKL_NUM_THREADS are all 1:

PYTHONHASHSEED=0 OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 \
  python -m benchmark.s5.run_matrix --datasets all --legs all

The hash seed is the obvious one — the hypergraph entry is hash-order sensitive. The thread pin is the one that costs people a day. The dense leg is a brute-force cosine (a BLAS gemv) and the query embedder is a torch forward pass; both reassociate their floating-point sums differently at different thread counts, and that is enough to change which documents land in the pool. Measured on this corpus: running musique/bm25 at 4 threads instead of 1, with identical code, gives 33 of 1,209 queries a different dense pool and one query a different gold-hit count — moving recall@10 from 0.3404 to 0.3406. An unpinned harness reports thread scheduling as a quality change. Because the campaign stage calls the harness in-process, the pins have to be in the environment you launch latence-campaign from.

Every dataset's corpus/toolkit licenses and lanes are recorded in one place — THIRD-PARTY-LICENSES.md — and corpus-derived artifacts (raw acquisitions, OCR output, exports, reports) stay local by .gitignore discipline; only aggregate metrics and stage reports are committed.

Interruption is a state, not a failure

Stages checkpoint through the same WAL/checkpoint discipline as the pipeline Runner; resume re-probes the filesystem (never trusts a recorded exit code) and relaunches only what is not provably done. A blocked stage records its reason in STATE.json and status renders it — fix the cause, resume under the same run id, finalized checkpoints replay from disk.