Cricket data · the data behind db-mcp.tigzig.com
Everything tallies, end to end
Served-data checks last run 11 Sep 2026, 23:20 IST (17:50 UTC). Refreshed twice a day, and every run is published, pass or fail.
| 1. Cricsheet to master | The pipeline downloads the source, builds a master file, and runs its own checks before it lets the new master replace the old one. all 51 checks passed · 10 Sep 2026, 10:08 IST (04:38 UTC) |
| 2. Master to served | A separate guard compares what db-mcp serves against what the pipeline says it wrote, slice by slice, so a stalled or half-applied refresh cannot look green. all 21 comparisons tally · 11 Sep 2026, 23:05 IST (17:35 UTC) |
| 3. Served data, over the public API | This battery runs against what the API actually returns, on both engines, and publishes every result below. all 22 checks passed |
Last build that loaded new data: 10 Sep 2026, 10:08 IST (04:38 UTC), 31 matches added, 51 of 51 checks passed. Since then the pipeline has run 3 more times and found nothing new at the source; the latest was 11 Sep 2026, 22:08 IST (16:38 UTC).
| Result | Check | Expected | Actual |
|---|---|---|---|
| PASS | ZIP vs DB match count (T20) | 5700 | 5700 |
| PASS | ZIP vs DB match count (ODI) | 3182 | 3182 |
| PASS | ZIP vs DB match count (TEST) | 918 | 918 |
| PASS | ZIP vs DB match count (IPL) | 1243 | 1243 |
| INFO | Cricsheet duplicate player-of-match rows | reported as filed, not corrected | 1 match(es) list the same player twice: 1394759 |
| PASS | Cricsheet published counts tally | our counts within 5 of cricsheet's published figures | exact tally on every format and gender |
| PASS | Source CSV matches DB values (sampled) | 0 mismatches over 150 matches / 598,024 field comparisons | verbatim match |
| PASS | No unknown cricsheet info keys | 37 consumed + 0 declined | no unknown keys |
| INFO | Non-match files in zips are skipped, not ingested | reported, not judged | 1 skipped: ['IPL:all_matches.csv'] |
| Result | Check | Expected | Actual |
|---|---|---|---|
| PASS | No orphan ball_by_ball match_ids | 0 | 0 |
| PASS | No NULL match_type / start_date | 0 | type=0 date=0 |
| PASS | No NULL team1/team2 | 0 | 0 |
| PASS | No teams lost | 131 old teams all present | 131 teams, missing: none |
| PASS | Winner is team1/team2/NULL | 0 | 0 |
| PASS | Toss winner is team1/team2/NULL | 0 | 0 |
| PASS | No duplicate match_ids | 0 | 0 |
| PASS | No negative runs_off_bat | 0 | 0 |
| PASS | All wicket types recognized | known types only | all known |
| PASS | Schema identical (column names, order, types) | staging schema == live schema | identical |
| PASS | All expected tables present in staging | 4 tables: ['match_info', 'ball_by_ball', 'match_players', 'people'] | all present |
| Result | Check | Expected | Actual |
|---|---|---|---|
| PASS | Innings domain valid per format | TEST 1-4; ODI/T20 innings>2 only as a short super over | 0 violations; max innings {'T20': 8, 'ODI': 4, 'TEST': 4, 'IPL': 6} |
| PASS | No innings exceeds 10 dismissals | max 10 per innings | 0 innings over 10; observed max 10 |
| PASS | No-result share per format in band | {'TEST': (12.0, 28.0), 'ODI': (2.0, 10.0), 'T20': (1.0, 8.0), 'IPL': (0.5, 8.0)} | in band: {'T20': 2.9, 'ODI': 4.9, 'TEST': 19.5, 'IPL': 2.0} |
| PASS | Duplicate ball slots explained by long overs | 0 | 0 duplicate slots not inside a 10+ delivery over |
| PASS | Multi-value fields ordered and consistent | 0 on every invariant | all consistent |
| Result | Check | Expected | Actual |
|---|---|---|---|
| PASS | Match counts by type+gender never regress | all segments new >= old | none |
| PASS | Delivery counts by type never regress | all types new >= old | none |
| PASS | Max date per type extends or stays | new max >= old max ({'T20': '2026-09-01', 'ODI': '2026-09-01', 'TEST': '2026-08-27', 'IPL': '2026-05-31'}) | {'T20': '2026-09-09', 'ODI': '2026-09-09', 'TEST': '2026-08-27', 'IPL': '2026-05-31'} |
| PASS | All old match_ids preserved | 11012 old ids all present | lost: 0 |
| PASS | Spot-check: 50 old matches identical delivery counts | 0 mismatches | 0 mismatches |
| PASS | Retained matches: per-delivery values unchanged | identical sums over 11012 retained matches | no drift |
| PASS | Retained matches: metadata values unchanged | identical team/winner/toss/venue/date/gender profile | no drift |
| Result | Check | Expected | Actual |
|---|---|---|---|
| PASS | Every match has a full player registry | 0 matches with fewer than 12 named people | 0 thin/absent |
| PASS | Player identifiers are 8-char hex | 0 | 0 |
| PASS | One row per person per match | 0 duplicate names, 0 duplicate ids | names=0 ids=0 |
| PASS | Player role values recognized | parts drawn from ['match_referee', 'player', 'reserve_umpire', 'tv_umpire', 'umpire'] | all recognized |
| INFO | Registry names cricsheet does not place | reported, not judged | 1516 rows with no role |
| INFO | Named officials absent from the match registry | reported, not judged | 5 rows with no identifier |
| PASS | Ball-by-ball names resolve in the match registry | >= 99.95% of name references resolve | 99.9938% (947 of 15,383,576 unresolved) |
| Result | Check | Expected | Actual |
|---|---|---|---|
| PASS | People identifiers unique | 0 | 0 |
| PASS | People row count in band | 14,000 - 30,000 | 18,539 |
| PASS | Every match identifier exists in people | 0 | 0 |
| Result | Check | Expected | Actual |
|---|---|---|---|
| PASS | Avg deliveries/match in expected band | {'TEST': (1000, 2500), 'ODI': (300, 700), 'T20': (150, 400)} | all in band |
| PASS | ICC classification distribution stable | per-class change <= 72 appearances | {"Full Member": 11361, "Associate": 6248, "null": 2486, "Associate-ODI": 1991} |
| PASS | Added match volume plausible | <= 1500 added | 31 |
| Result | Check | Expected | Actual |
|---|---|---|---|
| PASS | Post-deploy: live match counts == staging | {"IPL|male": 1243, "ODI|female": 611, "ODI|male": 2571, "T20|female": 2161, "T20|male": 3539, "TEST|female": 24, "TEST|male": 894} | {"IPL|male": 1243, "ODI|female": 611, "ODI|male": 2571, "T20|female": 2161, "T20|male": 3539, "TEST|female": 24, "TEST|male": 894} |
| PASS | Post-deploy: live delivery count == staging | 5037109 | 5037109 |
| PASS | Post-deploy: live match_players count == staging | 292532 | 292532 |
| PASS | Post-deploy: live people count == staging | 18539 | 18539 |
| Result | Check | Expected | Actual |
|---|---|---|---|
| INFO | Low-delivery matches (<20, abandoned) | 16 | |
| INFO | Venue/city quality | venues 602->602, null city 634 |
Checked 11 Sep 2026, 23:05 IST (17:35 UTC) against pipeline build 20260910_043812. Every published slice of the served copy is compared with the count the pipeline recorded when it wrote the master, in both directions.
| Result | Slice | Master | Served |
|---|---|---|---|
| PASS | matches, IPL men | 1,243 | 1,243 |
| PASS | matches, ODI women | 611 | 611 |
| PASS | matches, ODI men | 2,571 | 2,571 |
| PASS | matches, T20 women | 2,161 | 2,161 |
| PASS | matches, T20 men | 3,539 | 3,539 |
| PASS | matches, TEST women | 24 | 24 |
| PASS | matches, TEST men | 894 | 894 |
| PASS | matches total | 11,043 | 11,043 |
| PASS | deliveries, IPL | 295,732 | 295,732 |
| PASS | deliveries, ODI | 1,683,966 | 1,683,966 |
| PASS | deliveries, T20 | 1,289,472 | 1,289,472 |
| PASS | deliveries, TEST | 1,767,939 | 1,767,939 |
| PASS | deliveries total | 5,037,109 | 5,037,109 |
| PASS | max date, IPL | 2026-05-31 | 2026-05-31 |
| PASS | max date, ODI | 2026-09-09 | 2026-09-09 |
| PASS | max date, T20 | 2026-09-09 | 2026-09-09 |
| PASS | max date, TEST | 2026-08-27 | 2026-08-27 |
| PASS | match players total | 292,532 | 292,532 |
| PASS | people total | 18,539 | 18,539 |
| PASS | null gender rows | 0 | 0 |
| PASS | null team type rows | 0 | 0 |
The API answers from two database engines, and most of these checks run against both and compare them row for row. A few run against one engine only: they read every delivery ever recorded, which one of the two engines cannot finish inside its own time limit. The Engines column says which applies to each check, and the first row compares the two engines directly on everything added recently, which is where a half-finished refresh would show.
| Result | What it proves | Engines | Check |
|---|---|---|---|
| PASS | The two engines behind the API hold identical ball data for everything added recently. 1 | both agree · 5 rows | engine_parity_recent |
| INFO | The column list and its order have not changed, on any of the four published tables. | both agree · 104 rows | schema_published_tables |
| INFO | Table sizes, match counts and the date span they cover. | DuckDB only · 2 rows | row_counts |
| INFO | Format and gender mix, and the date span of each. | both agree · 7 rows | matches_by_type |
| PASS | No ball and no squad entry belongs to a match that does not exist. | both agree · 2 rows | orphan_balls |
| PASS | Every match has ball-by-ball data behind it. 2 | both agree · 4 rows | matches_without_balls |
| PASS | Every player named in a squad exists in the player master. 3 | both agree · 1 row | squad_integrity |
| PASS | Every player in the player master has one unique identifier. 4 | both agree · 1 row | reference_table_counts |
| PASS | No missing venue, bowler, striker, runs or season. | DuckDB only · 4 rows | null_density |
| PASS | Innings numbering and the ten-wicket ceiling hold in every format. 5 | DuckDB only · 4 rows | format_shape_validity |
| PASS | No T20 or IPL innings past over 19, no ODI innings past over 49. | DuckDB only · 1 row | over_index_bounds |
| PASS | No delivery scores below 0 or above 8 runs. | DuckDB only · 1 row | negative_or_absurd_runs |
| PASS | Each delivery can be pinned to one over and one ball within it, with no gaps and no duplicates. | DuckDB only · 1 row | delivery_numbering |
| PASS | Every match records the toss, and the toss winner was playing in it. 6 | both agree · 4 rows | toss_recorded_and_valid |
| INFO | Every result is won by runs or by wickets, never both. 7 | both agree · 4 rows | result_margin_shape |
| PASS | Every match is exactly one of won, drawn, tied or no result, and a super-over decision sits inside tied rather than beside it. | both agree · 4 rows | outcome_partition |
| PASS | A super-over or bowl-out winner is always one of the two teams playing, and only ever appears on a tied match. | both agree · 1 row | tiebreak_fields_sane |
| INFO | Recent matches are present, so the twice-daily refresh is running. | both agree · 40 rows | recent_matches |
| INFO | Test matches are current in the match table. | both agree · 8 rows | recent_test |
| INFO | Deliveries per over stay in a plausible range. 8 | DuckDB only · 8 rows | balls_per_over_extremes |
| INFO | Innings counts per match are 1, 2, or a super over. 9 | DuckDB only · 15 rows | innings_per_match |
| INFO | How often one delivery slot carries more than one row. 10 | DuckDB only · 4 rows | repeated_delivery_address |
PASS is an assertion that held. INFO is a distribution published for inspection rather than asserted - deliveries per over and innings per match are legitimately variable, so calling them failures would make this page noise. The rows behind every anomaly the checks count are on the Anomalies tab.
| Run | Checks | Failed |
|---|---|---|
| 11 Sep 2026, 23:20 IST (17:50 UTC) | 22 | 0 |
| 11 Sep 2026, 20:19 IST (14:49 UTC) | 22 | 0 |
| 11 Sep 2026, 20:16 IST (14:46 UTC) | 22 | 0 |
| 11 Sep 2026, 20:13 IST (14:43 UTC) | 22 | 0 |
| 11 Sep 2026, 12:38 IST (07:08 UTC) | 22 | 0 |
| 11 Sep 2026, 12:37 IST (07:07 UTC) | 22 | 0 |
| 11 Sep 2026, 12:36 IST (07:06 UTC) | 22 | 0 |
| 11 Sep 2026, 12:33 IST (07:03 UTC) | 22 | 0 |
| 11 Sep 2026, 12:31 IST (07:01 UTC) | 22 | 1 |
| 24 Aug 2026, 14:39 IST (09:09 UTC) | 19 | 0 |
| 24 Aug 2026, 14:36 IST (09:06 UTC) | 17 | 0 |
| 23 Aug 2026, 18:55 IST (13:25 UTC) | 17 | 0 |
| 23 Aug 2026, 18:54 IST (13:24 UTC) | 17 | 0 |
| 23 Aug 2026, 18:41 IST (13:11 UTC) | 16 | 0 |
| 23 Aug 2026, 16:52 IST (11:22 UTC) | 16 | 0 |
| 23 Aug 2026, 15:45 IST (10:15 UTC) | 16 | 0 |
| 23 Aug 2026, 15:26 IST (09:56 UTC) | 16 | 0 |
| 23 Aug 2026, 15:24 IST (09:54 UTC) | 15 | 0 |
| 22 Aug 2026, 12:50 IST (07:20 UTC) | 15 | 0 |
| 22 Aug 2026, 12:49 IST (07:19 UTC) | 15 | 1 |
| 22 Aug 2026, 12:02 IST (06:32 UTC) | 12 | 0 |
| 21 Aug 2026, 17:00 IST (11:30 UTC) | 12 | 0 |
| 21 Aug 2026, 16:50 IST (11:20 UTC) | 8 | 0 |
| 21 Aug 2026, 16:49 IST (11:19 UTC) | 8 | 1 |
| 21 Aug 2026, 16:40 IST (11:10 UTC) | 8 | 6 |
Cricsheet publishes the source data. A pipeline parses it into a master file and checks its own work before deploying it. A refresh job loads that master into the two engines the API serves from, and a guard confirms the served copies still match the master. The battery at the end runs against what the API actually returns - not against an internal copy of it. Each layer is blind to what the others see, which is why all three are on one page.
Anything found is published as found. On the first run of this battery the two engines disagreed by one row - a Test match present in one and missing from the other - because the refresh job's skip test looked only at ball-by-ball data and a Test match adds none. That was fixed, and published rather than quietly corrected.
Machine-readable: /index.md for this tab, /anomalies.md for the anomalies, /llms.txt for what this site is.