Skip to main content
verify_bronze.py re-checks everything ingestion already wrote as a standalone pass over data/bronze/: which races should be there, whether each has its datasets, the laps quality checks, the same completeness rules ingest.py applies before writing, per-race telemetry coverage, and row counts. It’s the script to reach for after a backfill finishes, or any time you want to confirm Bronze is healthy without re-reading ingestion logs. Nothing about the seasons is hand-typed. The races a season should have are the rounds on its schedule snapshot (schedule/season=YYYY/, written by ingest.py from FastF1’s event schedule) whose race has finished, so a new season is checked as soon as it’s ingested. Documented source gaps (KNOWN_TELEMETRY_GAPS / KNOWN_THIN in ingestion/src/bronze_checks.py, e.g. 2018 Rd1/Rd2 telemetry) are printed as KNOWN and don’t fail the run; anything else is printed as NEW and makes it exit 1.
1

Run it

2

Read the five sections

The output walks through presence, laps data quality, completeness, telemetry coverage and row counts, each printed as its own block, then a summary of NEW vs KNOWN findings. --season YYYY limits it to one season; --bronze-dir PATH points it at another bronze root.
Only seasons present on disk are checked, so this works identically after a single-race pull, make ingest-recent, or a full make ingest-all there’s no separate “partial” mode to remember.

What each section checks

For every season with a laps/season=<year>/ directory, every finished round on the schedule snapshot must have race laps, qualifying laps, weather, race_control and telemetry. A finished round with no laps, or a race missing a dataset, is flagged by name; a known gap is reported as KNOWN. With no schedule snapshot the check falls back to the races on disk.
Runs DataQualityEngine’s schema check (required columns) and duplicate lap-key check on every race and qualifying laps file. Fewer than 50 laps is reported as info, not a failure, matching ingestion (red-flagged sessions are short).
The thin-load rules ingest.py applies before writing, run on the files instead: tyre columns (Compound, TyreLife, Stint) at least 90% non-null, LapTime at least 25%, as many results rows as drivers who set laps, race Points/GridPosition at least 90% and qualifying Q1 at least 50% non-null, and non-empty weather and race control. This is what catches a load that was written thin and would otherwise be skipped as done forever.
The share of each race’s laps with merged per-lap telemetry. Every healthy race 2018-2025 is at or above 99.7%; below 95% is flagged.
For each season and dataset, reports the number of races present and the median/min/max row count per race (from Parquet metadata). Informational.
The all-column null-rate warning ingest.py logs while writing (see Data Quality) is not re-checked here: most FastF1 lap columns (pit in/out times, for instance) are mostly null by design. Completeness is judged on the key columns in section 3 instead.

Regenerating the coverage table

emits the Bronze coverage table season-by-season counts of how many races have each dataset as Markdown, read straight off disk. It covers the seasons in MARKDOWN_SEASONS (2018–2025); widening it adds rows to the published docs, so that is a deliberate change. make docs-coverage wraps this into docs/snippets/bronze-coverage.mdx, the snippet embedded on Known Issues; a CI gate fails if the committed snippet ever drifts from a fresh regeneration, so the table in the docs can’t go stale.

Next

Manifest report

Per-run status and schema-drift detection, instead of a point-in-time disk check.

Architecture

How the data-quality gate runs inline during ingestion itself.