Built and run by one person.

Validations

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.

The chain, in the order the data travels

1. Cricsheet to masterThe 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 servedA 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 APIThis battery runs against what the API actually returns, on both engines, and publishes every result below.
all 22 checks passed

1. Cricsheet to master

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).

ResultCheckExpectedActual
PASSZIP vs DB match count (T20)57005700
PASSZIP vs DB match count (ODI)31823182
PASSZIP vs DB match count (TEST)918918
PASSZIP vs DB match count (IPL)12431243
INFOCricsheet duplicate player-of-match rowsreported as filed, not corrected1 match(es) list the same player twice: 1394759
PASSCricsheet published counts tallyour counts within 5 of cricsheet's published figuresexact tally on every format and gender
PASSSource CSV matches DB values (sampled)0 mismatches over 150 matches / 598,024 field comparisonsverbatim match
PASSNo unknown cricsheet info keys37 consumed + 0 declinedno unknown keys
INFONon-match files in zips are skipped, not ingestedreported, not judged1 skipped: ['IPL:all_matches.csv']
The other 42 checks the pipeline ran on that build, by family

Integrity of the master

ResultCheckExpectedActual
PASSNo orphan ball_by_ball match_ids00
PASSNo NULL match_type / start_date0type=0 date=0
PASSNo NULL team1/team200
PASSNo teams lost131 old teams all present131 teams, missing: none
PASSWinner is team1/team2/NULL00
PASSToss winner is team1/team2/NULL00
PASSNo duplicate match_ids00
PASSNo negative runs_off_bat00
PASSAll wicket types recognizedknown types onlyall known
PASSSchema identical (column names, order, types)staging schema == live schemaidentical
PASSAll expected tables present in staging4 tables: ['match_info', 'ball_by_ball', 'match_players', 'people']all present

Cricket rules in the master

ResultCheckExpectedActual
PASSInnings domain valid per formatTEST 1-4; ODI/T20 innings>2 only as a short super over0 violations; max innings {'T20': 8, 'ODI': 4, 'TEST': 4, 'IPL': 6}
PASSNo innings exceeds 10 dismissalsmax 10 per innings0 innings over 10; observed max 10
PASSNo-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}
PASSDuplicate ball slots explained by long overs00 duplicate slots not inside a 10+ delivery over
PASSMulti-value fields ordered and consistent0 on every invariantall consistent

Nothing lost since the previous run

ResultCheckExpectedActual
PASSMatch counts by type+gender never regressall segments new >= oldnone
PASSDelivery counts by type never regressall types new >= oldnone
PASSMax date per type extends or staysnew 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'}
PASSAll old match_ids preserved11012 old ids all presentlost: 0
PASSSpot-check: 50 old matches identical delivery counts0 mismatches0 mismatches
PASSRetained matches: per-delivery values unchangedidentical sums over 11012 retained matchesno drift
PASSRetained matches: metadata values unchangedidentical team/winner/toss/venue/date/gender profileno drift

Player registry

ResultCheckExpectedActual
PASSEvery match has a full player registry0 matches with fewer than 12 named people0 thin/absent
PASSPlayer identifiers are 8-char hex00
PASSOne row per person per match0 duplicate names, 0 duplicate idsnames=0 ids=0
PASSPlayer role values recognizedparts drawn from ['match_referee', 'player', 'reserve_umpire', 'tv_umpire', 'umpire']all recognized
INFORegistry names cricsheet does not placereported, not judged1516 rows with no role
INFONamed officials absent from the match registryreported, not judged5 rows with no identifier
PASSBall-by-ball names resolve in the match registry>= 99.95% of name references resolve99.9938% (947 of 15,383,576 unresolved)

Player master

ResultCheckExpectedActual
PASSPeople identifiers unique00
PASSPeople row count in band14,000 - 30,00018,539
PASSEvery match identifier exists in people00

Plausibility bands

ResultCheckExpectedActual
PASSAvg deliveries/match in expected band{'TEST': (1000, 2500), 'ODI': (300, 700), 'T20': (150, 400)}all in band
PASSICC classification distribution stableper-class change <= 72 appearances{"Full Member": 11361, "Associate": 6248, "null": 2486, "Associate-ODI": 1991}
PASSAdded match volume plausible<= 1500 added31

Post-deploy: live counts equal what was built

ResultCheckExpectedActual
PASSPost-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}
PASSPost-deploy: live delivery count == staging50371095037109
PASSPost-deploy: live match_players count == staging292532292532
PASSPost-deploy: live people count == staging1853918539

Reported, not judged

ResultCheckExpectedActual
INFOLow-delivery matches (<20, abandoned)16
INFOVenue/city qualityvenues 602->602, null city 634

2. Master to served

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.

ResultSliceMasterServed
PASSmatches, IPL men1,2431,243
PASSmatches, ODI women611611
PASSmatches, ODI men2,5712,571
PASSmatches, T20 women2,1612,161
PASSmatches, T20 men3,5393,539
PASSmatches, TEST women2424
PASSmatches, TEST men894894
PASSmatches total11,04311,043
PASSdeliveries, IPL295,732295,732
PASSdeliveries, ODI1,683,9661,683,966
PASSdeliveries, T201,289,4721,289,472
PASSdeliveries, TEST1,767,9391,767,939
PASSdeliveries total5,037,1095,037,109
PASSmax date, IPL2026-05-312026-05-31
PASSmax date, ODI2026-09-092026-09-09
PASSmax date, T202026-09-092026-09-09
PASSmax date, TEST2026-08-272026-08-27
PASSmatch players total292,532292,532
PASSpeople total18,53918,539
PASSnull gender rows00
PASSnull team type rows00

3. Served data, over the public API

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.

ResultWhat it provesEnginesCheck
PASSThe two engines behind the API hold identical ball data for everything added recently. 1both agree · 5 rowsengine_parity_recent
INFOThe column list and its order have not changed, on any of the four published tables.both agree · 104 rowsschema_published_tables
INFOTable sizes, match counts and the date span they cover.DuckDB only · 2 rowsrow_counts
INFOFormat and gender mix, and the date span of each.both agree · 7 rowsmatches_by_type
PASSNo ball and no squad entry belongs to a match that does not exist.both agree · 2 rowsorphan_balls
PASSEvery match has ball-by-ball data behind it. 2both agree · 4 rowsmatches_without_balls
PASSEvery player named in a squad exists in the player master. 3both agree · 1 rowsquad_integrity
PASSEvery player in the player master has one unique identifier. 4both agree · 1 rowreference_table_counts
PASSNo missing venue, bowler, striker, runs or season.DuckDB only · 4 rowsnull_density
PASSInnings numbering and the ten-wicket ceiling hold in every format. 5DuckDB only · 4 rowsformat_shape_validity
PASSNo T20 or IPL innings past over 19, no ODI innings past over 49.DuckDB only · 1 rowover_index_bounds
PASSNo delivery scores below 0 or above 8 runs.DuckDB only · 1 rownegative_or_absurd_runs
PASSEach delivery can be pinned to one over and one ball within it, with no gaps and no duplicates.DuckDB only · 1 rowdelivery_numbering
PASSEvery match records the toss, and the toss winner was playing in it. 6both agree · 4 rowstoss_recorded_and_valid
INFOEvery result is won by runs or by wickets, never both. 7both agree · 4 rowsresult_margin_shape
PASSEvery 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 rowsoutcome_partition
PASSA 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 rowtiebreak_fields_sane
INFORecent matches are present, so the twice-daily refresh is running.both agree · 40 rowsrecent_matches
INFOTest matches are current in the match table.both agree · 8 rowsrecent_test
INFODeliveries per over stay in a plausible range. 8DuckDB only · 8 rowsballs_per_over_extremes
INFOInnings counts per match are 1, 2, or a super over. 9DuckDB only · 15 rowsinnings_per_match
INFOHow often one delivery slot carries more than one row. 10DuckDB only · 4 rowsrepeated_delivery_address
  1. The API answers from two independent engines and this compares them on everything added recently, format by format. History is not re-compared on every run because it does not change - a difference between the engines can only be introduced by a refresh, which is what this window covers.
  2. Ball-by-ball data is now served for every published format, so this should stay at or near zero. It last meant something different: until the formats were consolidated, Test matches legitimately had no ball data and a non-zero count here was expected.
  3. Squad role is deliberately not asserted here. It is legitimately empty for substitute fielders, who appear in a match without a batting or bowling role.
  4. The player master carries cross-reference keys to the major cricket sites, so a player can be matched to another source. The keys are sparse by nature and are counted rather than asserted.
  5. Wickets are counted excluding retired hurt and retired not out, which do not end an innings. Retired out IS a dismissal under Law 25.4.3 and is counted. Test cricket has no over limit, so the deepest over reached is reported rather than asserted.
  6. Every match records the toss and the decision, and in every one the toss winner is one of the two teams playing.
  7. Matches with no winner are ties, abandonments and drawn Tests, so a non-zero count there is correct. A small number name a winner with no margin at all, the best known being the 2006 Oval Test, which England won by forfeit and which therefore has no margin by definition. Those rows are listed in full below.
  8. Overs longer than six deliveries are wides and no-balls, both of which are re-bowled. A long tail is ordinary cricket rather than a defect; a sudden jump at the top of the range would not be.
  9. Three or more innings in a limited-overs match is a super over, which is legitimate and rare. The deepest case on record went to three consecutive super overs in one T20.
  10. Explained, and it is not cricket. The source numbers deliveries as a running count within the over, so a ten-ball over reaches .10, which is indistinguishable from .1 once stored as a decimal. One collision per long over, exactly. This is why the data also carries the over and the delivery within it as separate whole numbers, and those are asserted separately.

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.

Previous runs of the served-data checks

RunChecksFailed
11 Sep 2026, 23:20 IST (17:50 UTC)220
11 Sep 2026, 20:19 IST (14:49 UTC)220
11 Sep 2026, 20:16 IST (14:46 UTC)220
11 Sep 2026, 20:13 IST (14:43 UTC)220
11 Sep 2026, 12:38 IST (07:08 UTC)220
11 Sep 2026, 12:37 IST (07:07 UTC)220
11 Sep 2026, 12:36 IST (07:06 UTC)220
11 Sep 2026, 12:33 IST (07:03 UTC)220
11 Sep 2026, 12:31 IST (07:01 UTC)221
24 Aug 2026, 14:39 IST (09:09 UTC)190
24 Aug 2026, 14:36 IST (09:06 UTC)170
23 Aug 2026, 18:55 IST (13:25 UTC)170
23 Aug 2026, 18:54 IST (13:24 UTC)170
23 Aug 2026, 18:41 IST (13:11 UTC)160
23 Aug 2026, 16:52 IST (11:22 UTC)160
23 Aug 2026, 15:45 IST (10:15 UTC)160
23 Aug 2026, 15:26 IST (09:56 UTC)160
23 Aug 2026, 15:24 IST (09:54 UTC)150
22 Aug 2026, 12:50 IST (07:20 UTC)150
22 Aug 2026, 12:49 IST (07:19 UTC)151
22 Aug 2026, 12:02 IST (06:32 UTC)120
21 Aug 2026, 17:00 IST (11:30 UTC)120
21 Aug 2026, 16:50 IST (11:20 UTC)80
21 Aug 2026, 16:49 IST (11:19 UTC)81
21 Aug 2026, 16:40 IST (11:10 UTC)86

What is being checked, and against what

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.