17 Chapter 17 — Event Data & the Common Representation
PBN formalization, rugby's missing schema, and the one-spine standards that make it all queryable
17.1 Coordinates Are Not Analysis
A coach does not read 10,000 coordinate rows. The bridge from "where everyone was" to "what happened" is the event row — a typed, timestamped, evidence-linked statement about the game. The discipline of this chapter: the event row is a contract, and the contract is conservative. It says what it knows, marks what it does not, and refuses to invent. Everything here is source-backed (kloppy/SPADL/EPTS standards) or measured (the PBN validator ran on our own 11 rows).
Start with the object itself. What is an event? In pickleball, the atomic event is a shot — one player, one contact (or candidate contact), one ball state — and the tactical unit above it is the rally, the ordered span of shots from serve to fault or dead ball. In rugby league, the atomic event is the tackle — ball-carrier stopped, play-the-ball begun — and the tactical unit above it is the set: up to six tackles, conditioned by the count, terminated by a try, an error, a kick, or a handover. The two sports look different on video, yet every event in both answers the same five questions: who (athlete), what (action), when (time), where (location), and what came of it (outcome). A third-shot drop at frame 214 by the near-left player landing in the kitchen, and a hit-up on tackle 2 at 31 metres from the attacking line, are the same shape of fact. That shape is the common representation.
The book's own Pickleball Notation (PBN) schema is the reference implementation. Its 31-field data dictionary (written 2026-06-29, validated 2026-09-01) is a real, enforceable contract — and it carries the field's most important convention: "unknown" is a legal value. The schema does not force a shot label; it forces honesty. A PBN row is a structured evidence object: what is known, what is candidate, what is missing, and what a human must correct before it becomes stronger evidence.
17.2 The One-Spine Data Model
Chapter 5 established the one spine: tracks + events in one schema, one join key. The reason a practitioner needs a spine at all is fragmentation — every vendor ships its own coordinate system, its own event vocabulary, and its own file format. The proven answer, from the soccer ecosystem, is a provider-agnostic object model with one canonical coordinate space and one join key. kloppy is the working proof: load StatsBomb, Opta, Wyscout, Metrica, Sportec, Tracab, SkillCorner, or Second Spectrum through separate serializers into the same EventDataset/TrackingDataset objects, then .transform() to whatever coordinate system and orientation the analysis needs. The book extends that pattern in two directions the soccer tools never went: the join key reaches back to video frames, not just match time, and every row carries an evidence layer.
The standards that make it real:
| Standard | What It Gives | License | Integration |
|---|---|---|---|
| kloppy (PySport, 3.19.0) | Data model + vendor serializers; periods, frames, events, players, pitch dimensions | BSD-3-Clause (verified 2026-09-01 — corrects an earlier Apache-2.0 note) | The ingestion spine — vendor feeds in, kloppy objects out |
| SPADL | Atomic play-by-play actions (start/end/result) — the explicit PBN ancestor | Apache-2.0 | Event semantics: what an action row must contain |
| FIFA EPTS | Standard tracking data format — the export contract clubs ingest | FIFA docs | Export lane: "book outputs are EPTS-readable" |
| MOTChallenge CSV | Benchmark track format (1-based, id=-1 dets) | challenge | Evaluation lane: track exports comparable to benchmarks |
| SoccerNet GSR JSON | Broadcast-derived game-state format | challenge | Reference for the rugby broadcast lane |
Three of these shape the book's schema directly. kloppy splits data into EventDataset and TrackingDataset, both wrapped in metadata that declares coordinate system, pitch dimensions, teams, and players — the PBN rule at annotation scale: slot first, stable identity only when it survives review. SPADL contributes the row semantics: one row per action, with start and end coordinates, a separate action type vocabulary, and a separate result vocabulary. The design decisions the book inherits: actions are the atom; the action is a vector; result stays separate from type; provider converters map vendor feeds into the language. PBN is, in this book's framing, SPADL for racket sports with evidence fields. FIFA EPTS is the professional export contract: a metadata header plus one line per frame sample, per-person channels in centimeters in a center-origin metric frame, ball as its own channel — the vendor-neutral interchange IFAB mandated in 2015.
The join key is (clip_id, frame_idx, pts_s) — everything joins on it; nothing is matched on fuzzy timestamps. The triplet, not a pair, because the E06 review windows are time intervals but the review UI and dataset schema key frames. pts_s bridges the analyst's clock and the pipeline's frame index, and both must come from one authoritative frame_map.csv — never recomputed downstream. Spine rules: meters canonical, pixels evidence, feet presentation; every event row's evidence_refs resolves to existing (clip_id, frame_idx) pairs; MOTChallenge export is a projection (drop spine columns, fill x,y,z=-1), so TrackEval/HOTA is one SQL query away.
17.3 Coordinate Conventions
A coordinate is meaningless without its tag. kloppy formalizes three degrees of freedom — Origin, VerticalOrientation, and PitchDimensions — plus the separate orientation problem. The practitioner meets four systems in the wild:
| Convention | Origin / units | Used by | Failure mode |
|---|---|---|---|
| Normalized [0..1] | Corner origin, unit square | kloppy default; event vendors projecting to 100×100 equivalents | Distances meaningless until mapped back — all metric information is distorted in standardization |
| Own-center metric | Pitch/court center, m or cm | FIFA EPTS raw data (cm, signed — verified against FIFA's own example file) | Needs declared dimensions to interpret in/out of bounds |
| Corner metric | A corner, all-positive | Most tracking vendors | Origin/orientation varies per vendor — silent flips when merging feeds |
| Image pixels | Top-left, y down, px | Every detector; MOTChallenge bbox columns | Useless for tactics until a homography; the book's rule: pixels are evidence, never canonical |
The book's choice is metric everywhere, declared on the row: pickleball on the 13.41 × 6.10 m court, NVZ 2.13 m (C03-verified); rugby league on the 100 × 68 m field, 6–11 m in-goal (C03-verified). Three sport-specific consequences. First, all bounding lines are part of the NVZ, so zone-membership predicates use <=/>= on the line, never < — an off-by-one-pixel bug becomes a foot-fault mislabel. Second, the NVZ governs contact, not airspace, so a volley label needs foot position at contact. Third, rugby's 10 m stripes are a calibration gift: broken lines every ten metres make broadcast homography easier than soccer's sparse markings, which puts pitch-meter coordinates within reach for nrl-001. Every other space — pixels, feet, normalized — is a deterministic projection declared in the manifest. Never mix: the coordinate column declares its space and every query filters on it.
17.4 The PBN Contract (Validated W4.1)
The 31 PBN fields group into five blocks, and the grouping is the teaching object: identity & spine (event_id, rally_id, sequence_id, source_clip, frame_id, timestamp, event_index), actor (player_slot, player_id, team_side, court_zone, bbox), action (shot_phase, shot_type, contact_state, ball_visible, paddle_visible, body_state, movement_before/after_contact), context (partner_spacing, opponent_spacing, cue, court_state, outcome), and evidence & governance (evidence_level, evidence_refs, confidence, human_correction, review_required, coaching_note). The data dictionary's rules, in the form that matters:
| Rule | Contract |
|---|---|
| Player identity | Player slot (near_left/near_right/far_left/far_right/unknown/not_visible) is the unit; player_id is optional and blank until identity survives multi-frame review — never a name |
| Shot type | unknown is expected unless contact/ball evidence supports the label |
| Contact | contact_state ∈ {confirmed, candidate, not_visible, not_contact, unknown}; candidate means "might be contact, needs review" — not "contact"; do not infer ball path from body posture alone |
| Evidence | evidence_level ∈ {real-experiment-output, real-experiment-output+manual-proxy, manual-proxy, concept-transfer}; evidence_refs point at disk artifacts (e.g. pb-003-frame-01; det_id=1; yolo_conf=0.8713), never claims |
| Review | review_required=yes until a human pass completes; confidence ∈ {high, medium, medium-low, low} |
The dictionary's safe-versus-unsafe note discipline is the chapter's ethics in one example. Safe: "This row can be used as a contact-review target. Shot type is not confirmed." Unsafe: "The player executed a poor speed-up and left the middle open" — a tactical claim from a row with no confirmed ball path, contact, outcome, or identity. The schema cannot enforce taste, but it makes the overreach visible: the unsafe sentence asserts fields the row marks unknown.
The action vocabulary is the pinned 12-event taxonomy from the dataset schema (E06, ch07 v1): serve, return, third_shot_drop, third_shot_drive, dink, speedup, lob, volley, fault, let, kitchen_foot_fault, contact, with actor ∈ {A1, A2, B1, B2, null}. Notice what the taxonomy encodes about the sport: the third shot gets two types (drop and drive) because it is pickleball's decisive tactical fork; the kitchen foot fault is an event type of its own because the NVZ rule (17.3) makes it detectable from foot-at-contact geometry; and contact exists as a bare type so the pipeline can log a confirmed contact before anyone commits to a shot label. That last entry is the honesty clause operating at the vocabulary level.
17.4b The Honest Experiment: Validating the Contract
A schema nobody has run is a draft. Our lab loaded the actual canonical event rows — 11 rows × 31 fields, from the pb-003 clip with its manual contact-review correction layer — and validated every machine-checkable dictionary rule: enum membership for player_slot, contact_state, ball_visible, paddle_visible, evidence_level, confidence, human_correction, review_required, and team_side; non-empty evidence_refs; and the spine rule that every referenced frame resolves in the manifest. The measured result (transcript and metrics on disk at experiments/c17-event-data/outputs/metrics.json): 0 violations, verdict PASS. Mandatory-field coverage was 100%; player_id was correctly 0% filled — identity unresolved, which is chapter 13's problem, and the schema behaving exactly as designed by not guessing.
The validator is about 30 lines of Python and runs on every commit. Its lesson, verbatim from the lab output: "The PBN contract is conservative BY DESIGN: unknown is a legal value, candidate is not confirmed, human_correction=needed is expected on most rows. Validation is about catching typos, not forcing spec — the honesty gate is the 'unknown' bucket." This is the failure mode the chapter exists to prevent, stated positively: most event pipelines fail by being too confident — every detector guess promoted to a fact, every gap silently interpolated. A schema whose allowed values include unknown, candidate, and not_visible makes under-confidence representable, and the CI gate makes overconfidence a build failure. The validator catches typos; the taxonomy catches hubris.
17.5 The Rugby Schema: NRLSheet
The verified gap: rugby has no kloppy-equivalent open event pipeline and no public tracking corpus. The official NRL pipe is closed — Stats Perform/Opta is the official data provider (a decade-long partnership feeding NRL.com and broadcast), Hudl owns the club workflow, Catapult owns wearables. What exists openly is aggregate-level: nrlR (CRAN) scrapes NRL.com, Rugby League Project, and Zero Tackle into tidy tibbles of fixtures, ladders, and player stats; Rugby League Project provides the stable entity IDs that make entity resolution possible; UselessNRLStats ships cleaned CSVs of in-game stats. Whether any open source exposes true tackle-by-tackle event rows, rather than match aggregates, is [verify] — the sources on record are aggregate-level, and that gap is precisely the book's contribution.
The model for filling it is Cricsheet, which solved the same problem in cricket: a closed official pipe and a community that needed a shareable, versioned, ball-by-ball open format. Its lessons transfer directly: one delivery equals one row with innings/over/ball addressing — rugby league's equivalent addressing is set:tackle; a registry of people IDs separate from names; a structured source of truth (YAML/JSON) with CSV as a projection; and a per-file metadata header (match, teams, venue, date) so rows are interpretable standalone. The book's proposal — NRLSheet — is tackle-by-tackle: set count, tackle number, PTB timestamp, field position, possession, kick type, try/conversion/penalty fields.
The field semantics differ from football in the way the set grammar demands. A soccer SPADL row is a pass or a shot; a rugby league row is a state row: the tackle count is the conditioning variable (a run on tackle 1 is a field-position play; on tackle 6 with no kick it is a turnover). Contact, which soccer records only as fouls and cards, is the centre of the rugby game — a tackle event carries tackler_ids[], tackle_type, and, in the welfare lane, head-contact flags per the Bath 2025 CV framework. NRLSheet therefore carries: set_number, tackle_number (1-6, plus six-again resets), ptb_timestamp, field_position_m (metres from the attacking team's own try line on the 100 × 68 m field), possession, kick_type ∈ {grubber, bomb, chip, touch_finder, goal_line_dropout, last_tackle}, outcome ∈ {try, error, handover, penalty}, and the identity fields (player_id from the roster join, chapter 13). The schema is the book's contribution, and it is stated as such.
Why not just reuse kloppy? Because kloppy is soccer-first by construction: its event taxonomy (PassEvent, ShotEvent, CardEvent, …) and serializers cover association-football providers only — no rugby serializers, no rugby event types, no set/tackle counting semantics, no play-the-ball, no kick-on-last structure; even scoring breaks the model (try = 4, conversion, penalty goal, field goal). What transfers is the chassis — datasets, metadata, coordinate systems, .transform() — which is why NRLSheet is specified as kloppy-shaped plus the PBN evidence layer rather than as a kloppy fork. And the schema is validated by a published model's appetite: Kempton, Kennedy & Coutts (2016) built the canonical NRL expected-possession-value model on 768 matches (2010–13), showing possession value rises monotonically toward the try line. EPV needs exactly the fields NRLSheet proposes — set, tackle, field position, outcome — so the value model for the schema already exists in the literature.
The honest boundary: NRLSheet v1 codifies what event data can say. Tracking data (per-frame positions) stays a separate table under MOTChallenge/EPTS conventions, joined by (clip_id, pts_s). Events reference the track-level evidence that produced them, and the evidence-tier column carries the same four labels as PBN.
17.5b The Common Representation Across Sports
The five questions of §17.1 map onto both row contracts:
| Question | SPADL (soccer) | PBN (pickleball) | NRLSheet (rugby league) |
|---|---|---|---|
| Athlete | player_id, team_id |
player_slot + blank player_id |
actor_id (roster join) / tackler_ids[] |
| Action | actiontype, bodypart |
12-event enum + shot_phase |
tackle / PTB / run / pass / kick / try |
| Time | period_id, time_seconds |
frame_id, timestamp (spine key) |
half, clock_s + set_no, tackle_no |
| Location | start_x/y, end_x/y (105×68) |
court meters, NVZ-aware zones | field_position_m from own try line |
| Outcome | result (separate from type) |
outcome, unknown-first |
outcome ∈ {try, error, handover, penalty} |
| Evidence | — (provider-trusted) | evidence_level, evidence_refs, confidence |
same four-tier layer, CV or scrape sourced |
The evidence row is where the schemas diverge from their ancestors on purpose. SPADL rows trust the provider; PBN and NRLSheet rows carry provenance because they are produced by a CV pipeline that can be wrong, corrected by a human layer, and versioned by a schema that evolves. The taxonomies stay sport-specific — nobody needs dink in rugby or six_again in pickleball — but the skeleton and the evidence layer are shared verbatim. "Common representation" means one shape for the fact, with the vocabulary as a pluggable enum. The payoff: C18, C19, and C20 each read one row shape; the sport-specific code is the enum and the geometry, nothing else.
17.6 Versioning & Validation
The PBN v2 proposal (from the dossier) is experiment-gated — each delta ships only when a lab proves it: the spine key on every row, court-meter start/end coordinates, taxonomy promotion, structured evidence_refs, schema_version, review_state machine, rally-span fields. Non-goals stated: no per-player identity as a hard field (identity is chapter 13), no spin fields (the C12 frontier), no rugby fields in PBN (NRLSheet is a sibling schema, not a superset — the spine and evidence layer are shared, the taxonomy is not).
A schema without tests is a suggestion. The discipline:
- Schema as JSON Schema: the dataset/pb003 schema is v1 (12 court keypoints, 4 players, ball states, 12 event types, draft 2020-12 with a versioned
$id,constpins on identity fields, patterns on frame IDs, enums on every categorical). It is machine-validatable and it is what the G0.5 dataset contribution ships. - Data dictionary in markdown: the PBN dictionary's 31 fields with allowed values and the "current rule" column — the human-readable half of the contract. The two halves must never drift: every enum in the schema appears in the dictionary; every dictionary rule that can be an enum is an enum.
- Validation tests: every row against the dictionary in CI (the C17 validator is 30 lines of Python; it ran on our 11 rows: 0 violations, 100% mandatory-field coverage).
- Migration rules: v1 → v2 additive (new fields default to unknown; no renames without a migration script plus a fixture assert). Old files stay valid under their declared version; readers dispatch on
schema_version. - Manifest hash on every change: the schema version is part of the evidence chain; a row produced by v1 is not the same as one from v2, and a diff of the schema is a diff of the dataset's meaning.
- The unknown is first-class: the schema's allowed values include unknown, candidate, not_visible. The validation is about catching typos, not forcing spec.
Two further rules deserve emphasis. Annotation quality is schema-level: the pb003 schema carries an annotators block with role, agreement metric (cohen_kappa, iou, distance_px), and value — inter-rater reliability is part of the dataset, not a paper afterthought, and C18 and C20 read it. License travels with the data: the license object is required, not metadata-by-convention — pb-003 labels can ship while PPA frames stay non-redistributable, and NRLSheet rows scraped via nrlR inherit their sources' terms. The lesson generalizes from C16's licensing landmines.
17.6b The Event-Model Use Cases: Applied Framework
The use cases below are the applied bridge from the contracts above to daily practice. They follow three categories: Cat-A The Row as a Contract (01-03), Cat-B Time, Space & Provenance (04-07), and Cat-C Storage, Granularity & Audit (08-10). Each case pairs a pickleball and a rugby league application so the spine transfers, and each carries its evidence label: measured (book experiment), source-backed (paper/standard), or [verify] (practitioner model, not yet established).
Category A: The Row as a Contract (01-03)
UC 01 — Reading a PBN Row: Anatomy of One Shot
A coach asks "what happened on the third shot?" and the honest answer is a row, not a paragraph. The mechanism is the five-block read: identity & spine fields resolve the row against frame_map.csv; actor fields give the slot, never a guessed name; action fields say what is confirmed versus candidate; context fields state visibility honestly; evidence fields point at disk artifacts. Formally the row is a tuple over the 31-field dictionary with enum domains — contact_state ∈ {confirmed, candidate, not_visible, not_contact, unknown} — and the compliance test is: every claim the row makes is licensed by a field whose value supports it. Measured: the canonical 11-row pb-003 set passes this read with 0 violations (W4.1).
shot_type=unknown, contact_state=candidate — a review target, not a verdict. Rugby league: the same read discipline applied to a tackle row whose ptb_speed_s is still provisional.Payoff: the reviewer and the coach — one row answers "what do we actually know?" in under a minute, and the unknowns route straight into the C18 review queue.
UC 02 — Reading an NRL-Set Row: Set:Tackle Addressing
A rugby league possession is not a timestamp range — it is a numbered set of up to six tackles, and the tackle count is the conditioning variable. The mechanism: address every event by (half, clock_s, set_no, tackle_no), store field_position_m as metres from the attacking team's own try line with an orientation flag, and compute ruck speed as ptb_speed_s = t(first_touch) − t(ptb_frame). The schema is validated by a published model's appetite: Kempton, Kennedy & Coutts (2016) need exactly set, tackle, field position, and outcome for expected possession value — 768 matches, source-backed.
Payoff: the analyst — EPV-style value models (C22) become computable on day one because the schema was designed around the covariates the literature already proved matter.
UC 03 — The Common Representation in One Query
The test of a common representation is not a diagram — it is whether one SQL query runs unchanged against both sports' rows. The mechanism: one row shape (the five questions plus the evidence layer), the sport isolated in the taxonomy enum and the geometry. A query like SELECT actor, event, x_m, outcome FROM events WHERE evidence_level = 'real-experiment-output' reads PBN rows and NRLSheet rows identically; swapping sports means swapping the enum, not the schema. Source-backed design (SPADL/kloppy); measured on the PBN side (11 rows); NRLSheet side is the proposed sibling [verify] until its v1 validator runs (17.8).
Payoff: the data engineer — C18, C19, and C20 each read one row shape; the sport-specific code is the enum and the geometry, nothing else.
Category B: Time, Space & Provenance (04-07)
UC 04 — Event Timestamp Alignment: One Clock, One Map
The analyst thinks in seconds; the pipeline thinks in frames; the review UI scrubs frames while the review windows are time intervals. The mechanism is the spine triplet (clip_id, frame_idx, pts_s) with the rule that pts_s = frame_idx / fps is computed once, in the ingest frame_map.csv, and never recomputed downstream — every recomputation is a drift opportunity. Measured context: E06's review windows (8.33 s / 12.83 s) are time intervals, but the dataset schema keys frames (^pb-003-frame-[0-9]{3}$) — the triplet is what makes both addressable from one row.
Payoff: the reviewer — a click in the review UI lands on the exact frame the detector saw, not a frame 40 ms away because someone recomputed time downstream.
UC 05 — Event Metadata: Model, Version, Confidence
An event row without provenance is an assertion; with provenance it is evidence. The mechanism: evidence_refs is structured, not free text — {clip_id, frame_idx, det_id, model, model_version, conf} — and evidence_level carries the four provenance tiers (real-experiment-output → concept-transfer). Confidence is a pinned enum (high, medium, medium-low, low), not a float that invites false precision. The canonical rows already smuggle this in text (det_id=1; yolo_conf=0.8713); v2 makes it queryable and makes the spine rule "every reference resolves in the manifest" checkable in CI.
Payoff: the auditor and the downstream model — C19's automatic eventing can filter "CV-sourced, unreviewed" rows out of any published stat with one WHERE clause.
UC 06 — Coordinate Conventions in Practice: The Tagged-Space Rule
The most expensive silent bug in sport analytics is merging two feeds with different corner origins and watching every player teleport 6 metres. The mechanism is kloppy's formalization — every coordinate carries (Origin, VerticalOrientation, PitchDimensions) plus an orientation flag, and transform is a pure function between tagged spaces — with the book's storage rule on top: meters canonical, pixels evidence, feet presentation. Sport geometry rides the same rule: NVZ membership is <= on the line because all bounding lines are part of the zone (C03-verified); rugby's field_position_m is meaningless without the orientation flag saying which end they attack.
Payoff: the data engineer — merging a CV feed with a vendor feed becomes a declared transform instead of a debugging week, and foot-fault predicates stop having off-by-one-pixel bugs.
UC 07 — The JSON Schema in Production: CI as the Contract's Teeth
A schema nobody runs is a suggestion. The mechanism: JSON Schema draft 2020-12 with a versioned $id, const pins on identity fields (dataset_id, clip path, fps: 25), regex patterns on frame IDs, and enums on every categorical — validated by the jsonschema library in CI on every commit, plus the 30-line dictionary validator that checks the rules the schema cannot express. Measured: the pb-003 schema validates the canonical 11 rows × 31 fields with 0 violations and 100% mandatory-field coverage (experiments/c17-event-data/outputs/metrics.json, 2026-09-01).
Payoff: the whole pipeline — overconfidence becomes a build failure, and "the dataset changed meaning" shows up as a schema diff, not a silent drift.
Category C: Storage, Granularity & Audit (08-10)
UC 08 — Event Storage & the C19 Feed
Event rows are write-once, read-many: the CV pipeline and the correction layer write them; annotation (C18), automatic eventing (C19), and statistics (C20) read them. The mechanism: event rows land in Parquet beside the track rows, both keyed by the spine triplet, with DuckDB as the SQL layer — and the standards projections are views, not copies: MOTChallenge export is one query (drop spine columns, fill x,y,z=-1), so TrackEval/HOTA evaluation of any tracking change is one SQL statement away. The correction layer is first-class storage: first-pass generated rows are never overwritten in place; human corrections live alongside until a regeneration pass supersedes them.
Payoff: the pipeline owner — no export scripts to maintain, no stale copies; the benchmark projection and the coaching query read the same bytes.
UC 09 — Fine Events vs Coarse Events: The Granularity Tradeoff
Every annotation program picks a granularity and pays for it twice — once in labeling time, once in error surface. Fine events (12 shot types per rally) maximize tactical resolution but multiply the rows a human must review; coarse events (rally spans with outcomes) are cheap and robust but cannot answer "was the third shot a drop or a drive?" The mechanism is the honest rule: record at the finest granularity you can validate, publish at the coarsest granularity that answers the question — the fine rows carry review_required=yes, the coarse aggregates carry only what survived review. E06's rally spans (8.33 s / 12.83 s) are the measured coarse layer; the 12-event taxonomy is the pinned fine layer.
Payoff: the analyst and the budget — review effort concentrates on the rows that carry tactical value, and published numbers never rest on unreviewed fine grain.
UC 10 — The Event-Quality Audit: Agreement as Data
An event dataset without a measured disagreement rate is an unaudited claim. The mechanism is schema-level: the annotators block carries role and an agreement metric (cohen_kappa, iou, or distance_px) with its value, so inter-rater reliability ships inside the dataset — C18 and C20 read it directly, with Landis-Koch interpretation on record. The audit loop closes against C16's lesson: any generated sentence about a match is falsifiable against the rows (E07 measured a 66.7% ungrounded-claim error baseline), and every correction lands in the correction layer, never as a silent overwrite. [verify] for the rugby side: no public tackle-by-tackle corpus exists to audit against — the audit discipline is what NRLSheet v1 must be born with.
Payoff: the reviewer and the reader — every published stat carries its measured agreement, and every correction is auditable back to the row it superseded.
These ten use cases are one system read ten ways: the row as contract (01-03), the spine in operation (04-07), and the store under audit (08-10). They extend existing chapters — C05's spine, C18's review workflow, C19's automatic eventing, C20's statistics, C22's value models — and the honest labels mark exactly which claims are measured in the book's lab (the 11-row validation, the E06 windows, the E07 baseline) versus proposed for the rugby sibling (NRLSheet v1).
17.7 The Event-Row Recipe (Copy This)
- Schema first: define the row contract (event_id, rally_id, clip, frame, time, slot, type, confidence, evidence_refs) — the PBN dictionary is the pickleball reference; NRLSheet is the rugby one. Answer the five questions (athlete, action, time, location, outcome) plus evidence; plug in the sport's taxonomy as an enum.
- Validate in CI: every row against the dictionary's allowed values (the C17 validator is 30 lines of Python and runs on every commit).
- Coordinate declare: metric court/pitch coordinates; never mix spaces without declaring; respect sport geometry (NVZ lines inclusive with
<=; rugby metres from the attacking team's own line with an orientation flag). - Conservative defaults: shot_type=unknown, player_id blank, contact_state=candidate — the schema's honesty IS the value.
- Evidence links: every row's evidence_refs point at the artifact that produced it, and CI checks that the referenced frames exist in the manifest.
- Version the schema: additive migrations, manifest hash, fixture assert against format drift; corrections live in a correction layer, never as silent in-place overwrites.
17.8 What I Would Measure Next
- Convert the PBN rows to kloppy-normalized events (the vendor-format bridge): wrap the pb-003 track rows and E06 rally spans (8.33 s / 12.83 s) in a kloppy-shaped object — one
RallyPeriodper rally, A1–B2 player slots, 13.41 × 6.10 mPitchDimensionswith NVZ markings at 2.13 m — and prove the.to_df()output joins the Parquet track rows on(clip_id, frame_idx, pts_s)with zero orphans. - Draft the NRLSheet schema v1 and validate 50 nrlR event rows against it — then the harder, more valuable half: produce one NRLSheet-conformant tackle sequence from the nrl-001 broadcast by hand-coding one set of six (tackle candidates from the C19 detector lane per the Bath 2025 framework, field position via homography on the 10 m stripes, PTB speed from PTB frame to first-touch frame, evidence layer exactly as PBN). Acceptance: one JSON file passing the v1 NRLSheet JSON Schema — the schema itself is the deliverable — with rows joining nrl-001's
frame_map.csv. - The pklmart prior bridge: 300K+ shot records across ~1,000 matches give pickleball its first scale-grade statistical prior (license [verify] before print); a pklmart→PBN serializer would let priors and video-evidence rows share one shape.
- The EPTS writer: no open-source Python EPTS writer was found ([verify] once more before building); shipping one would make every book output club-ingestable.
17.9 Sources
- kloppy (PySport, BSD-3-Clause, 3.19.0):
https://kloppy.pysport.org/,https://github.com/PySport/kloppy, coordinates guidehttps://kloppy.pysport.org/user-guide/concepts/coordinates/; SPADL/socceraction (Apache-2.0):https://socceraction.readthedocs.io/en/latest/documentation/spadl/spadl.html; Atomic VAEP arXiv:2011.00126 [verify arXiv id]. - FIFA EPTS Standard Data Format (page updated 2026-04-01):
https://inside.fifa.com/innovation/standards/epts/research-development-epts-standard-data-format; MOTChallenge:https://motchallenge.net/instructions/+ TrackEval format spec; SoccerNet GSR:https://github.com/SoccerNet/sn-gamestate. - Kempton, Kennedy & Coutts (2016), NRL expected possession value, 768 matches:
https://pubmed.ncbi.nlm.nih.gov/26190116/; Bath 2025 head-contact CV framework:https://pubmed.ncbi.nlm.nih.gov/39832883/. - NRL open data: nrlR
https://cran.r-project.org/package=nrlR; Rugby League Projecthttps://www.rugbyleagueproject.org; UselessNRLStatshttps://github.com/uselessnrlstats/uselessnrlstats; Stats Perform/Opta NRL partnershiphttps://www.statsperform.com/insights/ten-years-as-official-nrl-data-provider/; Cricsheethttps://cricsheet.org(exact license [verify]); pklmarthttps://www.kaggle.com/datasets/cakesofspan/pklmarts-competitive-pickleball-extracts(license [verify]). - PBN Data Dictionary (book-research/PBN, 2026-06-29) — 31 fields, allowed values; canonical rows
2026-06-29-pb-003-pbn-event-rows.csv+ correction layer2026-06-29-pb-003-manual-contact-review.csv; dataset schemadataset/pb003/pb003.schema.json. - Lab:
lab/w4_lab_event_data.py→experiments/c17-event-data/outputs/metrics.json(11 rows, 0 violations, measured 2026-09-01).