Methodology v0.2

How MartingaleBench works

The goal is narrow and honest: detect when a model API behaves differently from itself. We don’t rank which model is “smartest” — we tell you when yours changed.

What we test

Every model gets the same starter suite of short canary cases. Prompts are generated from templates with per-run random variables and never mention Martingale, so they look like ordinary traffic.

CapabilityDetectsDeterministic check
Structured JSONSchema-compliance regressionsJSON parses and matches the requested schema
Instruction followingExact-format instruction driftexact_match on a per-run random token
Arithmetic, answer onlyWrong arithmetic, or a model that starts showing its working when told to give only the numbernumeric_equals on a reply that must be the bare integer; numbers regenerated per run
Long contextSilent context truncationcontains_all, code planted at 10/50/90% depth
Tool callingTool-use format regressionstool_called + argument check
Over-refusalNew safety layers refusing benign requestsnot_refusal (versioned keyword list v1)
Max-tokens respectTruncation and limit changesfinish_reason is length and output_tokens <= max_tokens
Code execution (hard)Reasoning depth or reasoning-budget cutsexact stdout of a generated Python program (5 difficulty levels, 2,500-token budget), verified by running it
Constraint logic (hard)Multi-step deduction regressionsunique solution of a generated seating puzzle, verified by brute force
State tracking (hard)Long-horizon bookkeeping errorsexact final balances after 12-65 generated ledger operations

How we score

How we call drift

A change only becomes an incident when it is statistically significant, large enough to matter, and persistent — the main defence against false alarms.

Fisher exact test

Two-sided. The most recent runs (at least 20 samples) vs. everything before them in the last 14 days (at least 40 samples), per model and category.

Effect-size floor

The pass rate must move at least 10 points.

Multiple comparisons

Benjamini–Hochberg correction at q = 0.01 across every test in a cycle.

Persistence

A signal must fire in 2 consecutive evaluations before an incident opens; it resolves after 3 quiet ones.

Change point

Reported as the start of the first significant recent window, so always “approximately”.

Identity changes

If the API starts reporting a different underlying model name, that opens an incident directly, no statistics needed.

Incidents carry a confidence label: High (adjusted p < 0.0001 and a move of at least 20 points, or an identity change), Medium (adjusted p < 0.001), or Low. While a model has under 48 hours of history, confidence is capped at Medium.

Raven Alpha: changes that scores can’t see

Raven Alpha is our behaviour-fingerprint detector. Pass rates only move when a change is big enough to flip an answer. Most silent changes are not: a cheaper sibling model, a new quantisation, less reasoning effort or a hidden system prompt can leave every canary passing. So each run also asks 28 fixed prompts with no right answer (“Name one color”, “Write one sentence about rain”) at the API’s default sampling (direct Anthropic references are the exception: the runner and martingale check both call that API at temperature 0), and tracks the distribution of what comes back: the answers, early word choice, output length, reasoning-token counts and, where the API exposes them, first-token probabilities.

Two look-back windows are compared with the model’s own earlier runs: the latest 3 runs, to catch a wholesale change within hours, and the latest 24 runs, to catch a partial change that is too faint in 3. Each comparison is an exact permutation test over whole runs (no distributional assumptions), corrected for the two windows and across endpoints. Two significant checks in a row raise a flag; a flag becomes an incident only once the baseline covers 24 hours. A fixed prompt whose input-token count changes is flagged immediately.

Output budget. Each fixed prompt may use 400 output tokens, hidden reasoning included. Models that spend most of that on reasoning returned an empty answer on a large share of calls, which fingerprints the budget rather than the model, so since 6 Oct 2026 these endpoints run with a larger budget and their fingerprint series restarted on that day: stepfun/step-3.7-flash (4,000), bytedance-seed/seed-2-1-turbo (4,000), inception/mercury-2.5 (2,000), google/gemini-3.8-flash (2,000), ibm-granite/granite-4.2-8b (2,000), moonshotai/kimi-k2.6 (2,000), tencent/hy3 (2,000), z-ai/glm-5.3 (2,000), thinkingmachines/inkling (2,000), nvidia/nemotron-3.5-lightning (2,000), cohere/command-a-plus (2,000), minimax/minimax-m3 (2,000). Earlier responses from them stay in the raw data but are not compared with the new ones. A few models keep reasoning past any budget on trivial prompts (StepFun 3.7 Flash on about one call in ten); the share of cut responses per model is published with the series.

Measured sensitivity (blind test on real responses, 05 Oct 2026). We collected real responses from pairs of closely related models. In each trial a sealed coin decided whether the recent window came from the same model or had a share of its calls answered by the sibling; the detector saw only anonymised responses. 300 trials per cell, one check, threshold 0.01.

Baseline model → silent substituteRecent calls10% swapped25%50%100%
gpt-5.4 → gpt-5.4-mini8420%83%100%100%
gpt-5.4 → gpt-5.4-mini16840%99%100%100%
gpt-4.1 → gpt-4.1-mini8431%89%100%100%
gpt-4.1 → gpt-4.1-mini16850%99%100%100%
gpt-6.1-sol → gpt-6-sol845%34%94%100%
gpt-6.1-sol → gpt-6-sol1688%55%100%100%
gpt-6-luna → gpt-5.6-luna8431%89%100%100%
gpt-6-luna → gpt-5.6-luna16845%99%100%100%
gpt-6.1-sol → gpt-6.1-sol (reasoning effort set to low)846%40%97%100%
gpt-6.1-sol → gpt-6.1-sol (reasoning effort set to low)16810%69%100%100%
gpt-6.1-sol → gpt-6-astra842%4%15%83%
gpt-6.1-sol → gpt-6-astra1680%6%35%100%

False alarms when nothing changed: 0.8% (25 of 3300) and 1.0% (34 of 3300) per check, before the two-in-a-row rule. Caveats: the test responses were collected in one session, so false alarms over days are not measured yet; a 10% partial swap is mostly invisible at this sample size; and this measures sensitivity to swaps we staged between real models, not a provider change caught in the wild.

With more history. The table above uses a baseline of only 16 batches, because that is all one collection session gives. A live endpoint builds a far longer baseline. To estimate what that buys, we rebuilt longer baselines and windows by drawing at random from the same real responses (72 baseline batches, about three days of hourly runs). This is an estimate from resampled data, not a measurement over three real days.

Baseline model → silent substituteRecent calls10% swapped25%50%100%
gpt-5.4 → gpt-5.4-mini16894%100%100%100%
gpt-5.4 → gpt-5.4-mini672100%100%100%100%
gpt-4.1 → gpt-4.1-mini16895%100%100%100%
gpt-4.1 → gpt-4.1-mini672100%100%100%100%
gpt-6.1-sol → gpt-6-sol16831%96%100%100%
gpt-6.1-sol → gpt-6-sol67285%100%100%100%
gpt-6-luna → gpt-5.6-luna16891%100%100%100%
gpt-6-luna → gpt-5.6-luna672100%100%100%100%
gpt-6.1-sol → gpt-6.1-sol (reasoning effort set to low)16837%99%100%100%
gpt-6.1-sol → gpt-6.1-sol (reasoning effort set to low)67288%100%100%100%
gpt-6.1-sol → gpt-6-astra1683%24%91%100%
gpt-6.1-sol → gpt-6-astra6729%79%100%100%

False alarms in the same resampled setting: 1.2%, 1.1% per check.

Limitations

Open data

The full results behind every chart are published as results.json. Suite hard-v2 + starter-v2; data generated Sat, 07 Nov 2026 02:24:33 GMT.

Want this on your own endpoints?

Get early access → martingale.io