Validation Gauntlet¶
The Validation Gauntlet is a gold-labelled disposition benchmark for
FreshData's validation surfaces: fd.clean, fd.validate_fields,
fd.clean_text, the semantic layer, the domain packs, text-encoding linting
and PII detection. It lives in benchmarks/gauntlet/ and runs on every pull
request (.github/workflows/gauntlet.yml).
Where CleanBench scores whole-frame repair fidelity against a clean oracle, the gauntlet scores decisions. Every injected problem cell carries the disposition FreshData should choose:
| disposition | meaning |
|---|---|
preserve |
valid (often unusual) data — must survive byte-identical, no error-severity issue |
repair |
a safe deterministic repair exists — the gold value is known |
flag |
must be detected, never auto-changed |
review |
ambiguous — must be routed to quarantine / manual review, never guessed |
Automatic removal is never the default correct answer: a flag or review
cell that the pipeline mutates counts as a corruption, the most severe
verdict in the report.
Fixtures¶
Five deterministic fixtures (seeded, 300 rows each by default) in
benchmarks/gauntlet/fixtures.py: finance, healthcare, crm,
ecommerce and text (adversarial free text). They cover missing values,
impossible ranges, malformed and impossible dates, invalid identifiers,
duplicates, numbers stored as text, currency/percent formats, unit confusion,
casing and whitespace noise, misspellings, Unicode/encoding noise, emojis,
HTML fragments, mixed-language values, PII, and adversarial traps designed to
trigger false corrections (X Æ A-12 as a name, 007 as an id, NA as a
country, None as a brand token, AB- as a blood type, BRK.B as a ticker).
The flagship case: the string apple in a price column must be quarantined
with its original value preserved in report.coerced_cells — never silently
imputed — while Apple (company), AAPL (ticker) survive untouched and the
lowercase ticker apple is routed to review with the suggestion AAPL.
Metrics and gates¶
Per fixture: detection precision / recall / F1, repair accuracy (with the surface that produced each repair), review-routing rate, preservation rate, corruption count, escape rate, false-positive rate, audit completeness, determinism, trust-score monotonicity, wall-clock and peak memory.
CI fails a pull request when any fixture breaks the absolute gates
(benchmarks/gauntlet/report.py::GATES — zero corruption, 100% preservation
and audit completeness, F1 ≥ 0.85, repair accuracy ≥ 0.95, escapes ≤ 10%) or
when detection recall/F1 drops below the stored baseline
(benchmarks/gauntlet/baseline.json).
Running locally¶
python -m benchmarks.gauntlet run # JSON + Markdown into benchmarks/gauntlet/results/
python -m benchmarks.gauntlet run --check # CI mode: exit 1 on gate failure
python -m benchmarks.gauntlet run --rows 2000 # heavier run, manual only
python -m benchmarks.gauntlet run --update-baseline
Opt-in behaviour is graded separately from defaults: the safe clean_text
config is never scored on lossy operations; HTML stripping and NFKC folding
earn repair credit only from the explicit opt-in pass, and imputation credit
for sentinel cells requires the fill to be audited.
Known accepted gap¶
A hostile SQL-injection payload inside a person_name field escapes
detection. Any plausibility heuristic tight enough to catch it false-positives
on legally real names (X Æ A-12), so the gauntlet documents the escape
rather than forcing a lossy check.