Skip to main content

The Benchmark Grows Up

Trevor McCormick
Data Product @ Disney+

That clip up top: the benchmark at scale. Seven matches land on the wall one tile at a time — the four cv-15 shipped on, then the three new finals in green — while the counter rolls 135 → 491 human-charted points. Then the calibration numbers correct themselves on camera: 93% @ 32.6% becomes 88% @ 21.2% becomes 94% @ 19.6%. The closer is a Sabalenka–Pegula point from the US Open final playing with its draft string, s5b2f1f2f2x@ at 0.902 — flagged HIGH by a model that had never seen a single frame of that feed when it was scored.

cv-15 ended by naming the binding constraint: not any component of the pipeline, but 135 calibration points at an 11% base rate. This session spent itself on exactly that — three matches staged in one pass, the confidence layer recalibrated at 3.6x the data, and then an autopsy of the fold the bigger benchmark exposed. The headline number went down, then up, and both moves were the honest direction.

Plain English: what actually happened here

The test set more than tripled: three more finals — Melbourne at night, New York, indoor Turin — joined the four matches the machine already grades itself against. 491 points where a human charter's record is the answer key. The pipeline didn't change; what changed is how honest its self-grade could be. On the small set, the machine claimed its HIGH-confidence drafts held up 93% of the time; on the big set that corrected to 88% — like a player whose ranking finally reflects a full season instead of one hot tournament. Then the weakest match was audited and the inflation had a mechanism: one of the trust checks — "is this serve really a serve?" — could never fire on Wimbledon's footage, so it waved every point through, a let-cord sensor that had been unplugged all fortnight. Two new checks that judge the whole point (did the ball actually cross the net as often as the chart claims?) replaced the free pass, and the honest score rose to 94%. More on precision, coverage, and calibration: Court Vision in Plain English.

Three matches, zero new scripts

The candidate pool was MCP-charted finals cross-referenced against YouTube, and the winning format was the "condensed match" upload — 20–40 minute re-cuts that keep nearly every point and none of the studio filler; they beat extended highlights for alignment yield everywhere they exist. Picked: t5 Sinner–Zverev, AO F 2025 (hard, night, AO feed), t6 Sabalenka–Pegula, US Open F 2024 (hard, WTA, USO feed), t7 Djokovic–Sinner, ATP Finals 2023 (indoor hard, Tennis TV feed). 575 MCP points on the table, 372 clips extracted, 356 scored.

The consolidation earned its keep immediately: everything ran through the package stages — fitcourt / probe / extract / boundaries / align — plus one YAML per match. No script twins, no forks. Each feed broke exactly one thing, and each fix landed in court_detect as config: t5's bright coating starved the tophat default (a threshold knob), t6's green apron can't share an HSV band with its blue court (a second hull band, OR'd in), t7's light-blue apron rides V≈252 (open the value ceiling). One staging lesson re-learned the hard way: the homography fit window must be motion-scanned first — the first t5 window had 26.5 px of camera pan smearing the plate; a static 5-second serve setup landed the fit at ≤0.2 px residuals.

The genuinely new failure was unanimous: all three broadcasters hide zero-valued columns. At 0-0 the points column simply isn't rendered, the score-bug era window sees live background, and the plateau machinery splits game-start points into duplicate-score fragment groups. The adjudication that emerged is better than the one I planned: MCP's own serve columns arbitrate most groups — when the human's row reads 4w plus a second-serve rally, the fragment pair is the fault and the point, filmed either side of a cutaway, and the serve lives in the later piece. The rest fell to changeover parity and eyeballed frame strips; losers get blanked so the eval skips them instead of double-charging one human-charted point.

And one piece of tennis arithmetic that flipped an entire fold: a tiebreak set is not 13 games. The staged parity prior counted Turin's set 2 as 12 games plus a tiebreak = 13 swap-units, odd. Wrong: the 7-4 breaker is 11 points — one internal end-change at 6 points — and the set-end change cancels it, so the set contributes even parity. The set-3 server-end vote had come back 17 agree / 37 disagree, systematically flipped; moving the prior by one flipped it to 37/17, verified on video (Sinner opens set 3 serving from the far end). Filed for the next tiebreak match: swap parity depends on the breaker's point total, not on calling it a 13th game.

Process failure, on the record: the run stalled overnight when the background-job watcher died silently between t6 ball tracking and t7 players — both jobs finished fine; nothing was listening. The rest of the run polled long jobs from the driving loop instead of trusting completion callbacks.

The scorecards, same eval, same constants, nothing re-tuned on the new matches:

metric              t5 AO night    t6 USO WTA      t7 Turin indoor
server end 53/71 (75%) 121/128 (95%) 133/157 (85%)
rally length ±1 47/71 (66%) 99/128 (77%) 131/157 (83%)
letters (aligned) 93/117 (79%) 120/148 (81%) 112/157 (71%)
acceptance ≤1 edit 2/71 10/128 9/157

The new matches score better than the old ones — t6's 95% server end and 7.8% acceptance are the best single-match numbers on record, t7's 83% rally length the best structural fold yet. Pooled acceptance: 28/491 (5.7%) from 7/135 (5.2%) — the bar held under a 3.6x bigger, five-feed test.

More data made the number honest, not better

The recalibration was the point of the whole exercise. Leave-one-match- out across seven folds, 491 points, each match scored by a model that never saw it:

LOMO (held out)   high prec (≤5 edits)   coverage
t1 night 10/10 (100%) 45.5%
t2 ctrl 3/3 (100%) 60.0%
t3 clay 11/12 (92%) 20.3%
t4 grass 18/26 (69%) 53.1%
t5 AO 3/4 (75%) 5.6%
t6 USO 34/35 (97%) 27.3%
t7 Turin 13/14 (93%) 8.9%
pooled 92/104 (88%) 21.2%

Against cv-15's 93% @ 32.6%: precision down 5 points, coverage down 11. That is what the data was for. The 4-match table was 44 flags; this one is 104, and 44 flags was a small-n flattery — the honest generalization estimate moved toward the truth, not away from it. The strict ≤2-edit tier was re-attempted at n=491 and still dies in LOMO (0% coverage at a 19% base rate) — still on the record, still not shipped. And the seven folds did what four couldn't: they named the weak match. t4 grass sits at 69% held-out precision while flagging 53% of its points, and its disease — phantom insertions, invented shots — flies under signals that were built to detect missing data.

The t4 autopsy

Eight t4 false-highs, every one pulled apart against its chart CSV, the human's string, and — house rule — frame strips, because the mechanism gets named from pixels or it doesn't get named. What made them look trustworthy first: the t4-fold model leans on serve commitment, and the launch-plausibility gate — cv-15's star find, the check that a "serve" launches from beyond the baseline — only inspects ball-called serves. Every t4 serve is stance-called. The gate was vacuous on the entire feed: it defaulted to pass on every point and contributed +0.59 logit of unearned trust, a signal that structurally cannot fire on Wimbledon's footage silently passing all of it.

Under the free pass, three mechanisms, none of them the story I'd been telling. The half-cadence chart: t4_point_02 charts 8 shots at 1.5 s spacing, but the pixels show the real exchange running ~0.9 s — every other stroke missing, and the striker chain alternates cleanly over the top because the ball track is blind in exactly the same places the hit detector is. The dissolve-cut mid-rally join: clips that open on a crossfade into a live rally, five seconds of tennis before the stroke our stance detector blessed as the "serve" — the grass editor cuts into points like the clay editor, just wearing a dissolve. And the spineless rally: a four-shot chart whose window holds zero net crossings even at the weak gates — a rally story the ball track never told.

Two dead ends, kept: weak-gated crossings do not recover the missing strokes (point_02's 17-stroke rally yields 7 weak crossings — the track never told the story, so no residual can either), and charted cadence is real but soft (AUC 0.62, marginal flags 50/50 — it gets a sidecar column for the charter's eyes, not a model seat). What shipped is two more rules from physics plus one feature: xr_pre_serve — rally-speed net crossings that end before the charted serve mean the clip joined the point mid-flight, the whole-point tell the launch gate couldn't see — and rally_spineless, a 3+-shot chart with no spine at all. Same discipline, all seven folds:

LOMO (held out)      before                after
t4 grass 18/26 (69%) @ 53.1% 17/20 (85%) @ 40.8%
pooled 92/104 (88%) @ 21.2% 90/96 (94%) @ 19.6%

No other fold below 92%. High-tier disasters halved, 12 → 6. The trade is 1.6 points of coverage, and it's the honest direction — t4 was being flagged at 53% of the match against a 61% base rate; the model wasn't discriminating, it was enjoying t4's rosy serve signals. The residue is named too: t4_point_11 stays flagged at 10 edits — its dissolve-cut leaves no pre-serve crossings because the track is blind there as well. Strokes the ball track never saw remain invisible to every signal built on it; that ceiling belongs to t4's chart, not the calibration.

The meta-lesson joins the permanent file next to cv-14's mirrored compass: a confidence model is only as honest as its least-examined gate. The launch gate was the best find of the confidence build, and on one feed it was a +0.59 rubber stamp. The audit isn't "is the signal good" — it's "can the signal fire here."

Where it lands: seven matches, five feeds, 491 human-charted points, and a HIGH flag that means "start from the draft" with 94% held-out reliability at ~20% coverage. The shipped scorer refit on all 491 flags 97 points high at 96% in-sample; exports regenerated for all seven matches — 508 draft points, 99 flagged high. The benchmark finally has the n its own caveats kept asking for.

Session cost: $0.00 — three majors' worth of new ground truth at $0 marginal. Project total: ~$16.