Skip to content

Report reference

Every cairn eval writes one of these. It is the unit of evidence: the leaderboard ranks them, and the gate compares two.

Where they go

Path Purpose
reports/<config>-<UTC timestamp>.json the run, kept
reports/latest/<config>.json the current baseline, overwritten each run
reports/leaderboard.md written by cairn bench
reports/attack-<config>.json and .md written by cairn attack

Reports are committed on purpose, so the history of quality lives in the repository rather than in someone's terminal.

Structure

{
  "cairn_version": "0.1.0",
  "config_name": "default",
  "config_hash": "e26592bbd90d0924",
  "corpus_hash": "3f9c...",
  "golden_version": "1",
  "split": "eval",
  "created_at": "2026-01-01T00:00:00Z",
  "n_cases": 72,
  "retrieval": {"recall_at_k": {"1": 0.0, "3": 0.0, "5": 0.0, "10": 0.0}, "ndcg_at_10": 0.0, "mrr": 0.0, "n_with_support": 54},
  "answer": {"exact_match": 0.0, "token_f1": 0.0, "judge_score": null, "citation_validity": 0.0, "faithfulness": 0.0},
  "abstention": {"alpha": 0.1, "threshold": 0.0, "answer_rate": 0.0, "error_rate_answered": 0.0, "guarantee_met": true, "unanswerable_abstained_rate": 0.0, "answerable_answered_rate": 0.0},
  "operational": {"cost_usd_total": 0.0, "cost_per_question_usd": 0.0, "latency_p50_ms": 0, "latency_p95_ms": 0, "tokens_total": 0, "cache_hit_rate": 1.0},
  "per_case": [],
  "notes": {}
}

Identity

Field Why it is there
cairn_version the code that produced the numbers
config_name, config_hash the experiment; the hash excludes paths, so two machines agree
corpus_hash sha256 over the sorted document path and content hashes
golden_version from golden.version, or a content hash of the set when that file is absent
split eval, calibration or all
n_cases how many cases produced these numbers

The gate fails outright when corpus_hash or golden_version differ, because the two reports did not measure the same thing.

retrieval

Over cases whose support quote was located in the chunk store, counted in n_with_support. The runner searches the whole store, not just what came back, so recall has an honest denominator even when retrieval missed entirely.

Field Definition
recall_at_k share of cases whose supporting chunk is in the top k, for 1, 3, 5, 10
ndcg_at_10 one relevant chunk, so 1 / log2(rank + 1) inside the cutoff, else 0
mrr mean of 1 / rank, 0 when the chunk never surfaced
n_with_support the denominator above

answer

Over answered, answerable cases.

Field Definition
exact_match equal after lowercase, punctuation removed, articles dropped, whitespace collapsed
token_f1 the same normalisation, then multiset token overlap: the SQuAD definition
judge_score mean of the judge's 0 to 1 grade, or null when no judge ran
citation_validity verified claims over all claims, across answered cases
faithfulness share of answers where every claim verified

abstention

Field Definition
alpha the target from the configuration
threshold the threshold in force, null when uncalibrated
answer_rate answered over all cases
error_rate_answered judged wrong over answered; an answered unanswerable case is wrong by definition
guarantee_met error_rate_answered <= alpha, or null when nothing was answered
unanswerable_abstained_rate unanswerable cases that abstained
answerable_answered_rate answerable cases that answered

Read the first three together. A high answer rate with a low error rate is the result worth having; either alone can be bought at the other's expense.

operational

Field Definition
cost_usd_total, cost_per_question_usd from each provider's own price table; zero with the stub
latency_p50_ms, latency_p95_ms interpolated percentiles over all cases
tokens_total input, output and cache tokens
cache_hit_rate share of calls whose stable prefix matched the previous call's; null with fewer than two calls

per_case

One row per case, which is where a summary number gets explained.

Field Meaning
case_id, answerable from the golden set
status answered or abstained
confidence the scalar the threshold was applied to
correct the judge's verdict; null when abstained
token_f1, judge_score, citation_validity per case
retrieved_support_rank where the supporting chunk landed; null if it never did
latency_ms, cost_usd per case
answer_text what was returned

Sort by confidence to see what sits near the threshold; filter to retrieved_support_rank: null to see what retrieval missed entirely.

notes

Context that is not a metric: the judge and its rubric version, the provider and the models seen, the retrieval mode and depth, whether a reranker or rewriting ran, whether the threshold was calibrated, and the budget check.

threshold_source is worth reading first. It says calibrated or uncalibrated (model abstain flag), and an uncalibrated report carries no guarantee whatever guarantee_met says.

environment records the machine the report was measured on: operating system, architecture, Python version and CPU count. The gate compares latency only between reports from the same kind of machine; across machines it reports the delta in a note and does not block on it.

The leaderboard

cairn bench writes a markdown table sorted by the primary metric, which is the judge when every report has one and token F1 otherwise:

config recall@5 nDCG@10 token F1 judge citation validity answer rate error among answered cost/q p95 ms

The footer records the golden set version and the corpus hash, because a leaderboard whose rows came from different corpora is a chart, not a comparison.