# NEIL.VISION by neilball — durable ingestion

Release 1.4 · researched and verified October 20, 2026

## The product contract

Accept a wide range of original files for private retention, decode supported
containers, and reconstruct only what the data and its declared conventions
justify. A file can be a valid spreadsheet without containing a usable timeline,
stable identities, or calibrated spatial coordinates. Opening it successfully is
not sufficient evidence that it describes a basketball game correctly.

This release adds broad browser readers, a mapping review, reusable import
recipes, source-aware 2D / body-local handling, format diagnostics, and resumable
checksum-verified uploads. It includes a local conversion bridge for additional
scientific containers. It does **not** infer unknown proprietary binary schemas,
recover missing depth from 2D observations, or deploy a server conversion fleet.

## What works in this release

“Browser” means the file is decoded in the import worker after the original is
saved to private R2 storage. Unfamiliar schemas require the mapping review.
“Bridge” means `public/downloads/convert_tracking_data.py`, run locally with the
appropriate optional reader, produces JSONL for the browser's mapping review.
The file extension alone never establishes tracking semantics.

| Family | Current path | Details and limits |
| --- | --- | --- |
| Hawk-Eye pose clean / complete; centroid feeds; wrapped combined events | Browser, native adapter | Existing source metadata, UTC, units, identities, revisions, ball orientation / spin and discrete event geometry remain supported. |
| Second Spectrum basketball / 3D marking tables | Browser, native adapter | Documented marking collections and the existing alignment rules remain; an event-only feed does not become continuous pose. |
| Legacy NBA SportVU JSON moments | Browser, native adapter | Converts corner-origin feet to center-court; retains roster IDs, clocks and ball height. Player height stays unavailable. This is the documented legacy moments layout, not a promise about every SportVU product. |
| JSON documents | Browser | Entity records, nested frame / entity objects, column arrays, named-column matrices, named / indexed joints. Unsafe integer literals are preserved as text where needed. |
| JSONL / NDJSON / `.output` | Browser, streamed text | Records are parsed independently; malformed records are counted. Generic tables are mapped while being read. |
| RFC 7464 JSON text sequences | Browser | Record-separator framing; currently a bounded document decode rather than unlimited streaming. |
| CSV / TSV / semicolon / pipe tables | Browser, streamed text | Quoted newlines, doubled quotes, CRLF / LF, heading / no-heading overrides, explicit delimiter and decimal-comma controls. Duplicate headings require correction. |
| Whitespace and fixed-width text | Bridge | Explicit widths / column names or whitespace mode; no guess about arbitrary fixed-width boundaries. |
| XML | Browser | Elements and attributes become records. DTDs and entity declarations are disabled. An arbitrary XML vocabulary still needs field mapping or a dedicated adapter. |
| YAML / TOML | Browser | Data-only decoding; YAML aliases are disabled. TOML tables / arrays of tables supported. No application-specific object construction. |
| Excel XLSX / XLSB / XLS; ODS | Browser | Verified with actual workbook fixtures. Each sheet is reviewed separately; rosters / summaries can be skipped. Formulas and macros are not executed. Cached values may be read. |
| Other SheetJS formats | Reader available, best effort | SpreadsheetML / FODS, HTML tables, DBF, SYLK / DIF and some Numbers workbooks are decoder capabilities, not individually verified tracking adapters. Convert unusual variants if they fail. |
| Parquet | Browser; bridge for larger files | File signatures, metadata and ranged column / row-group reads; Snappy, gzip, Brotli, Zstandard and LZ4 fixtures verified. Nested frame entities expand before mapping. Encrypted or externally partitioned datasets need preparation. |
| Arrow IPC file / stream; Feather v2 | Browser; bridge | Typed records and nested fields. Feather v1 and unsupported IPC compression / extension types need conversion. This is IPC ingestion, not Arrow Flight connectivity. |
| SQLite | Browser, read-only | Each table is reviewed separately. Text IDs remain text; 64-bit integers are preserved. No source-supplied SQL is executed, and no extensions are loaded. Use a consistent database snapshot, not an incomplete database/WAL copy. |
| MessagePack | Browser | A single serialized value containing records / arrays. Extension payloads and concatenated producer-specific framing need an adapter. No reliable universal magic number; extension or decoder override is required. |
| CBOR / CBOR sequences | Browser | Multiple values, arrays and maps. Unsupported semantic tags require a conversion recipe; cyclic decoded structures are rejected. |
| Numeric NPY | Browser for simple shapes; bridge | Numeric / text dtypes, byte order and Fortran order are handled. Browser path supports 2D tables and `[time,entity,2|3]` positions. Other axes / pose tensors need explicit bridge axes. Object/pickle arrays are rejected. |
| Numeric NPZ | Browser for common arrays; bridge | Equal-length 1D columns, or `positions[T,E,2|3]` with optional time and ID arrays. Array lengths and expansion are checked. Arbitrary tensor layouts require selection and axes. |
| HDF5; MATLAB v7.3 | Bridge | List datasets, select one numeric dataset and declare tensor axes. External / virtual datasets require a self-contained export. |
| Older MATLAB MAT numeric variables | Bridge | Uses SciPy variable selection. MATLAB objects, nested application structs and custom classes are not automatically retargeted. |
| Avro object container | Bridge | Uses the embedded writer schema. Raw schemaless Avro records still require the writer schema and framing. |
| ZIP / ZIP64 pose bundles | Browser; range reads and disk-backed pages | Find nested `output.live.pose*` / `*pose.clean` folders. Up to 5 million selected pose files and 10 million directory entries; stored/Deflate members. Small folders decode in RAM; larger ones use compressed IndexedDB pages. |
| Other ZIP / 7z / TAR | Browser with budgets | General format detection, nesting, path checks and partial recovery. Large 7z/TAR games should be repacked as ZIP/ZIP64 or converted locally. |
| gzip stream; TAR.gz / TAR.bz2 / TAR.xz | Browser | Gzip has a bounded stream decode. bzip2 / xz compressed TAR members verified with the archive reader. |
| Single bzip2 / xz / zstd / LZ4 stream | Bridge | gzip/bz2/xz use Python's readers; zstd / LZ4 require optional readers. Raw streams are distinct from TAR archives. |
| RAR and other archive variants | Best effort / conversion | The bundled archive engine may decode some variants. Encrypted, multipart, unusual filters or unsupported builds need extraction first. ZIP64 workbooks/NPZ need conversion. |
| CVPOSE1 / CVPOSE2; gzip-packed CVPOSE | Browser | Existing compact frame index and normalized playback format remain supported. |

Unknown or unsupported originals are still retained after the visitor accepts
the notice. An unsupported file gets a diagnostic; a bundle can still load its
valid members. The retained original is the authoritative source, not a claim
that every field survived into the 29-joint playback representation.

## Data structures are a separate compatibility dimension

| Structure | How to handle it |
| --- | --- |
| One entity per time/frame row | Map time, ID, X/Y, optional Z, team, game and period. |
| One joint per entity/time row | Map joint name or declared numeric index; samples are grouped by entity/time. |
| Wide entity columns | Review the explicit prefix → entity-ID list. Remove velocity, diagnostic or other non-entity groups. Named joint groups are supported. |
| Nested frame objects with people/entities | Frame context is carried into entity records. Stable IDs must be supplied or explicitly declared. An array index alone is not proof of a persistent identity. |
| Parallel column arrays | Equal-length fields become records; incompatible lengths require conversion. |
| Named-column matrices | Supply a columns array or a heading row. Unlabelled matrix columns are exposed for manual mapping. |
| Dense position / pose tensors | Declare time, entity, joint and component axes. The bridge supports permutations of `tec` and `tejc`; do not assume the first dimension is time in every scientific file. |
| Named / indexed pose arrays | Choose named joints, COCO 17, BODY_25, MediaPipe 33, or an explicit custom order / alias map. Numeric tuples must declare XY, XYZ, XY+confidence, or XYZ+confidence. |
| Bounding boxes / MOT-style detections | Choose box center or bottom center, actual frame rate and persistent track IDs. Image coordinates need calibration. A box alone does not provide a skeleton or height. |
| Events, rosters and aggregate statistics | Keep them distinct from measured geometry. Skip unrelated sheets; join known marking schemas using their documented timeline rules. Generic roster joins and arbitrary event vocabularies need an adapter. |
| One JSON pose file per image | Needs explicit filename/frame timing and identity association if the JSON omits them. This release does not infer persistent person tracks or timestamps from arbitrary filenames. Prepare a timed table first. |

The renderer uses its existing 29 canonical joints. Aliases and custom ordering
can retarget compatible observed joints; they do not reconstruct unobserved
anatomy. SMPL/SMPL-X, arbitrary marker sets, COCO WholeBody and meshes should
retain their full original schema and use a deliberate retargeting adapter.

## Conventions that must remain explicit

**Space.** Declare source length units, center/corner/normalized-court/body-local
origin, source axis order, sign reversals, and actual court dimensions in feet.
Meters are not guessed from small numbers. Normalized court coordinates are not
normalized image coordinates. A calibrated image-to-court homography produces
court-plane positions, not airborne ball or joint depth. Lens distortion and
moving-camera calibration require preparation beyond a fixed homography.

**Depth.** Missing Z remains `NaN` internally and `null` in JSON exports. 2D
centroids use floor markers; 2D joints use a planar skeleton. Those render
positions do not become measured heights. Body-local data opens a selected
entity pose inspection view with display-only floor alignment. Court geometry,
travel speed, absolute height, court rulers and court heatmaps are unavailable
there. The source coordinates remain unchanged in exported data.

**Time.** Choose explicit-offset ISO timestamps, Unix seconds/milliseconds/
microseconds/nanoseconds, elapsed time, actual-FPS frame numbers, or explicitly
interpreted Excel 1900/1904 UTC serials. High-resolution integer timestamps are
differenced before numeric conversion. Playback uses float32 relative seconds
and millisecond UTC; retained originals preserve finer source precision.
Naive local timestamps require a timezone-aware export. A stopped/countdown
basketball clock cannot by itself establish real elapsed time or speed. Repeated
times across periods require continuous timing or an explicitly declared offset.

**Identity.** Preserve leading zeros and large integer IDs as strings. Numeric
Excel cells that already lost leading zeros cannot recover them. Unknown games
are isolated per import/table instead of merged merely because filenames match.
Declare a stable game ID in the mapping when related files should be combined.
Generic mapped games and native provider games have separate namespaces. Missing
players, missing teams and track identity switches are not repaired by invention.

**Quality.** Source confidence/visibility is separate from XYZ. A reviewed
threshold can exclude supplied low-confidence observations. Full per-joint
confidence vectors are currently retained in originals, not in CVPOSE's fixed
joint payload. A pelvis / hip-midpoint anchor may be used when a centroid is
absent; it is labelled as an anchor, not a measured center of mass. Conflicting
entity/time positions are reported; the first is retained. Large court-bound
excursions produce a warning and retain the values for inspection.

## Reusable mapping recipes

Review a representative file, choose **Download reusable mapping**, then select
that UTF-8 `.cvmap.json` alongside future data files. Recipes use schema
`neil-vision-import-v1`. They include column names, units, timing, coordinate
transforms, court dimensions, skeleton ordering/aliases and confidence policy.
A `match` suffix can scope a recipe to a file or `file#sheet` path. Use a declared
`gameId` to combine known pieces. Recipes apply only when their mapped column
names exist; unfamiliar tables still require review. Put sidecars alongside the
uploaded archive, so they are available before archive-member traversal.

For example, a long centroid table can use:

```json
{
  "schema": "neil-vision-import-v1",
  "match": "tracking.csv",
  "layout": "long",
  "units": "meters",
  "origin": "center",
  "axisOrder": "xyz",
  "timeBasis": "seconds",
  "courtLength": 94,
  "courtWidth": 50,
  "courtRules": "NBA",
  "gameId": "YOUR-ACTUAL-GAME-ID",
  "ballId": "ball",
  "columns": {"time":"elapsed_seconds","id":"entity_id","x":"x","y":"y","z":"z","team":"team_id"}
}
```

This example is a declaration, not evidence that those conventions match a
particular file. Text encoding, delimiter and heading overrides are also
available in the upload dialog for ambiguous files.

## Local scientific / flat-file conversion

The bridge reads sources without modifying them and refuses to overwrite an
existing output. Optional packages are needed only for relevant formats:

```sh
python -m pip install numpy pyarrow h5py scipy fastavro
python convert_tracking_data.py game.h5 --list
python convert_tracking_data.py game.h5 --dataset positions --axes tec --out tracking.jsonl
python convert_tracking_data.py poses.npz --dataset joints --axes tejc --joints joint_names.json --ids player_ids.json --times times.json --out poses.jsonl
python convert_tracking_data.py data.mat --dataset positions --axes etc --out matlab.jsonl
python convert_tracking_data.py records.avro --out tracking.jsonl
python convert_tracking_data.py fixed.txt --widths 12,18,12,12 --columns '["time","entity_id","x","y"]' --out fixed.jsonl
```

`--ids`, `--times` and `--joints` are JSON arrays matching the declared axes.
Without a time array, the output contains frame indices: the viewer still needs
the real FPS. Without supplied IDs, tensor indices are labelled as indices, not
roster identities. Review units, axes and time in the viewer after conversion.
PowerShell quoting differs from POSIX shells; JSON argument examples above use
POSIX quoting. Use the script's `--help` for all flags.

The bridge is not a universal binary reverse-engineering tool. Protobuf, raw
Avro, proprietary packet streams and encrypted sources require their schema,
framing and appropriate decoding/export workflow.

## Additional formats worth accepting through dedicated adapters

These are a researched roadmap, **not implemented browser support**.

| Priority | Families | Required adapter information |
| --- | --- | --- |
| Next | C3D, TRC; Vicon / Qualisys / OptiTrack exports | Marker labels, scale, frame rate, coordinate conventions and a marker-to-joint model. Marker locations are not automatically joint centers. C3D can include synchronized analog channels. |
| Next | Protobuf / length-delimited protobuf; Avro raw; BSON; JSON5 | Descriptors / writer schema, message framing, numeric fidelity and versioned mappings. Protobuf field numbers alone do not identify semantic fields. |
| Next | Vendor-specific TRACAB, Metrica EPTS, SkillCorner and other provider feeds | Documented schema/version, roster links, clock model, period orientation and coordinate transforms. Kloppy is a useful reference for provider adapters; its public Hawk-Eye reader is specifically 2D, not a substitute for the NBA pose adapter here. |
| Next | Server conversion jobs for large Parquet / HDF5 / MAT / Arrow sources | Resource-isolated jobs, dataset selection, resumable job state, a normalized index and authenticated status/retrieval. Keep originals and reproducible recipes. |
| Later | Zarr, NetCDF, HDF4, MATLAB application structs | Dataset/chunk metadata, dimension meanings, calendars, units and transforms; support directory/object-store manifests rather than just one blob. |
| Later | DuckDB files, database snapshots, SQL/CSV exports, R data formats | Read-only table selection, consistent snapshots, type/ID fidelity and a safe export path; never execute uploaded SQL dumps or serialized application code. |
| Later | BVH, ASF/AMC, FBX, glTF/GLB, USD, SMPL/SMPL-X parameter archives | Skeleton hierarchy, rest pose, rotation convention, forward kinematics, scale, global translation and deliberate rig retargeting. Rotations are not court-space XYZ. |
| Later | ROS bag, MCAP, PCAP/PCAPNG, live WebSocket/Kafka captures | Transport framing, schema/topic registry, synchronized clocks and sensor extrinsics; packet parsing is separate from tracking interpretation. |
| Later | FIT/TCX/GPX, IMU/UWB/LPS telemetry, wearable sensor files | Device identity, clock synchronization, sensor-to-court calibration and fusion. Geographical positions or orientation alone do not supply full basketball pose. |
| Later | GeoJSON, WKT/WKB, point clouds, PLY/PCD/LAS, CAD/court geometry | Coordinate reference systems and explicit time/entity semantics. Static geometry is context, not a game trajectory. |
| Separate pipeline | MP4/MOV, image sequences, PDFs and scanned tables | Video pose estimation / tracking, camera calibration or verified table extraction. These are measurement/extraction tasks, not file readers. Local reference videos stay local in the existing video tool. |
| Export only | Pickle, object NPY, joblib, many PyTorch/R application snapshots | Export non-executable numeric/tabular data with the producing trusted environment. The website must not execute arbitrary serialization. |

A format registry should version **container decoder + provider schema +
coordinate/time policy + skeleton mapping + conformance fixtures**. File
extensions are hints, not the registry's semantic key. Avoid claiming a new
provider is supported merely because its transport happens to be JSON or CSV.

## Upload durability and current budgets

Original uploads use private R2 multipart objects and D1 receipts. New clients
hash each 16 MiB part with SHA-256; the Worker independently streams and checks
that digest before recording the accepted part. Completion checks every part and
the final object size. Release 1.2 then streams the exact stored object through
server-side SHA-256. Legacy sessions remain compatible; an R2 multipart ETag
stays distinct from the whole-file SHA-256.

Byte-identical files share one retained R2 object, regardless of filename or
uploader. Each submission keeps its own D1 receipt, filename, timestamps,
consent, network hash and browser-reported parse result. A unique SHA-256/size
entry elects the retained object during concurrent completions. Receipt linking
and a durable cleanup record commit before deleting a redundant staged copy;
failed deletions retry through a 15-minute Cron Trigger. The task also recovers
interrupted finalization and backfills two earlier completed receipts per run.
Maintenance errors are recorded with bounded recovery/backfill attempts. See
SETUP.md for inspection and migration `0003_content_dedup.sql`.

Release 1.3 adds migration `0004_upload_guardrails.sql`: exact transactional
intake/storage reservations, signed uploader identifiers, shared active-batch
limits, native edge throttles, operation leases and abandoned-multipart cleanup.
Defaults are 50 GiB total managed capacity, 100 GiB/2,000 admitted files per UTC
month, 20 GiB/200 files site-wide per UTC day, and 5 GiB/40 files per browser
and network per day. At most eight batches are active globally, two per browser
and network. The shared budgets hold across IP rotation. IPv6 uses /64 groups.
All selected files reserve their full size before R2 begins; failed cleanup
does not credit capacity until bytes have been removed. Four part attempts and
five full-file verification attempts bound repeated storage work. Scheduled
cleanup preserves complete/finalizing originals and aborts expired incomplete
transfers. Admission counters are not refunded for cancellation or duplicates.

The 50 GiB application cap covers known existing and new retained/staged files;
it is not a provider-wide dollar ceiling. Keep the bucket dedicated to this app.
Native edge limits are approximate/per location, so D1 enforces exact shared
admission limits. SETUP.md includes the production WAF rule, billing alert,
reservation inspection and deployment steps. Identity checks deter abuse but
do not establish unique people; distributed actors can exhaust a public quota
and block further admission without obtaining unlimited storage. Requests,
database operations, logs and other Cloudflare services remain billable.

All bytes still travel through the upload API and count against admission quotas.
Temporary duplicate objects may exist during verification or failed cleanup.
This saves retained file space, not upload bandwidth; equivalent data in a
different serialization or compressed archive remains distinct. Small D1
receipt/part records remain for every submission. Operators retrieve originals
through `upload_storage.stored_r2_key`, rather than assuming each receipt's old
staging key still exists. Shared originals require reference-aware deletion.

The browser stores only a small resume manifest, file fingerprints and temporary
session credentials in IndexedDB, with an in-memory fallback if site storage is
unavailable. Reselect unchanged files to resume within 24 hours **of session
creation**. The client queries authenticated server progress, hashes saved chunks
again, skips matching ones and rejects mismatches. Clearing site storage,
private-mode restrictions, session expiry, or losing the initial session response
can require a fresh upload. Initialization itself is not yet an idempotent,
recoverable job protocol; completed originals remain retained under the notice.

| Boundary | Default |
| --- | --- |
| Upload admission | 2 GiB/file, 20 selected files/batch; existing network/site daily quotas remain |
| Chunk size | 16 MiB, with bounded retry/backoff |
| Upload token lifetime | 24 hours from creation |
| Managed retained + staged capacity | 50 GiB; credits follow confirmed cleanup |
| Monthly intake / active batches | 100 GiB and 2,000 files per UTC month; 8 active batches globally, 2 per browser/network |
| Structured document / binary decode | 256 MiB/file |
| Native pose ZIP / ZIP64 | Range-read; existing 2 GiB upload cap; selected member ≤64 MiB; depth 3 |
| Other compressed archives | 256 MiB for non-ZIP containers; general ZIP members ≤256 MiB; nested depth 3 |
| Workbook / NPZ expanded members | 256 MiB; ZIP directory preflight |
| Gzip stream expansion | 512 MiB |
| Selected pose ZIP data | 256 GiB expanded / 5 million pose members; 10 million directory entries; local disk quota also applies |
| General archive expansion / member count | 8 GiB / 500,000 members |
| Generic table observations | 2 million/table; numeric NPY decode also caps 16 million elements; actual browser memory can be more restrictive |
| Manifest attached to playback metadata | First 1,000 files plus omitted-file count and aggregate format counts |
| Error examples | Bounded examples plus totals; originals retained for full investigation |

These are admission/decode ceilings, not a promise that a phone can hold the
maximum. CSV/JSONL/Parquet read incrementally, but the normalized game and generic
frame groups still consume memory. Use indexed native conversion or time
segments for full games. A worker timeout/error leaves the private original
available to the operator. Unsupported or malformed input does not become a
successful reconstruction merely to make an upload look green.

For production-scale automatic conversion, my recommendation is R2 originals →
queued job → isolated container/batch converter → normalized Parquet/Arrow plus
indexed playback segments. The web Worker should handle admission and streaming,
not load multi-gigabyte scientific arrays into an edge isolate. This queue and
container service is an architectural recommendation; it is not deployed by this
package. Add authenticated libraries, conversion-job recovery,
retention/deletion controls and administrative inspection as explicit product
features when moving beyond anonymous upload logging.

## Verification

Automated fixtures cover CSV quoting/encodings, JSON structures and integer
precision, XML/YAML/TOML, MessagePack/CBOR, Arrow, XLSX/XLSB/XLS/ODS, SQLite,
five Parquet codecs, endian/Fortran NPY and NPZ, joint layouts, custom aliases,
confidence-vs-height, transforms, conflicting observations, body-local metrics,
TAR compression, archive safety, and partial imports. Import-worker messaging
and buffer transfer are tested. Real workerd/R2/D1 checks cover original bytes,
checksums, consent, authorization, quotas, status, retry, expiry and completion.
They also verify renamed and simultaneous duplicates, whole-file hashes,
independent receipts, failed deletes and linking transactions, interrupted reads,
scheduled recovery, legacy backfill retries and missing-object repair.
Abuse fixtures exercise twelve IPs, simultaneous capacity admission, browser
allowances across IP changes, IPv6 /64s, monthly intake, partial-batch rollback,
operation leases/retry caps, expired multipart cleanup failures, protection
configuration failures and existing-data budget migration.
IndexedDB tests exercise a fresh uploader, finalizing-session resume and
changed-middle-byte rejection.
DOM checks cover required mapping declarations and table skipping.

The supplied Hawk-Eye event sample yields **1,201 events / 1,102 indexed
snapshots**. The supplied pose archive yields **118,032 samples, 15 identities,
40,168 empty samples and three gaps**, with no malformed-record errors. The
original private data is excluded from the public package. Scientific bridge
checks exercised numeric NPY/NPZ, Parquet, HDF5, MATLAB numeric variables, named pose tensors and Avro; optional combinations
and unsupported variants still need their own fixtures. Browser WebGL pixels,
production CAPTCHA and your Cloudflare account/domain require the launch check
in SETUP.md; they were not tested through a live deployment here.

## Primary sources consulted

- CSV quoting / headings: https://www.rfc-editor.org/rfc/rfc4180
- JSON text sequence framing: https://www.rfc-editor.org/rfc/rfc7464
- SheetJS formats and standalone distribution: https://docs.sheetjs.com/docs/getting-started/installation/standalone/
- Parquet layout / row groups: https://parquet.apache.org/docs/file-format/
- Arrow typed/nested data and IPC: https://arrow.apache.org/docs/format/Columnar.html
- Browser Parquet implementation: https://github.com/hyparam/hyparquet
- SQLite file layout: https://www.sqlite.org/fileformat.html
- sql.js owner implementation: https://github.com/sql-js/sql.js
- NumPy format, shape/dtype and object-array caveat: https://numpy.org/doc/stable/reference/generated/numpy.lib.format.html
- OpenPose output ordering, 2D/3D confidence: https://github.com/CMU-Perceptual-Computing-Lab/openpose/blob/master/doc/02_output.md
- MediaPipe image vs hip-relative world landmarks: https://ai.google.dev/edge/mediapipe/solutions/vision/pose_landmarker/web_js
- Protobuf wire schema: https://protobuf.dev/programming-guides/encoding/
- Avro container/schema specification: https://avro.apache.org/docs/1.12.0/specification/
- YAML data model: https://yaml.org/spec/1.2.2/
- HDF5 specification: https://docs.hdfgroup.org/documentation/hdf5/latest/_f_m_t3.html
- C3D point / analog data: https://www.c3d.org/HTML/Documents/the3dpointdatasection.htm
- OpenSim marker preparation / TRC: https://opensimconfluence.atlassian.net/wiki/spaces/OpenSim/pages/53090644/Preparing+Marker+Coordinate+Data
- MOTChallenge framing in the evaluation owner's implementation: https://github.com/JonathonLuiten/TrackEval/blob/master/docs/MOTChallenge-format.txt
- Legacy SportVU owner renderer: https://github.com/linouk23/NBA-Player-Movements
- Provider adapter coverage and conventions: https://kloppy.pysport.org/user-guide/loading-data/
- Metrica owner samples: https://github.com/metrica-sports/sample-data
- Zarr chunked arrays: https://zarr-specs.readthedocs.io/en/latest/v3/core/index.html
- NetCDF family: https://docs.unidata.ucar.edu/netcdf-c/current/faq.html
- MCAP framing/schema records: https://mcap.dev/spec
- Worker streaming digest: https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#digeststream
- R2 multipart API: https://developers.cloudflare.com/r2/api/workers/workers-multipart-usage/
- R2 consistency: https://developers.cloudflare.com/r2/reference/consistency/
- D1 transactional batches: https://developers.cloudflare.com/d1/worker-api/d1-database/#batch
- Worker Cron Triggers: https://developers.cloudflare.com/workers/configuration/cron-triggers/
- Worker resource boundaries: https://developers.cloudflare.com/workers/platform/limits/
- Worker rate limits: https://developers.cloudflare.com/workers/runtime-apis/bindings/rate-limit/
- WAF rate rules: https://developers.cloudflare.com/waf/rate-limiting-rules/
- Billing alerts: https://developers.cloudflare.com/billing/manage/budget-alerts/

Recommendations and prioritization above are engineering judgments informed by
these sources and the actual fixtures, not claims made by those vendors.

### Disk-backed playback in release 1.4

Pose ZIPs with over 20,000 selected files or over 128 MiB declared selected data use compressed local pages. ZIP directory traversal holds at most two 2 MiB range caches. Pending pose rows are bounded; pages are indexed by game and two-second time buckets, including out-of-order input. Seeking reads a bounded neighborhood, with a 96 MiB decoded-window ceiling. Game time and displayed coordinates come from payloads, never filenames. Revisions and processed/delayed/live priority resolve within playback windows.

Local site storage must be allowed and have sufficient capacity. A quota failure stops the import explicitly; it does not publish a silently truncated game. The privately retained original is unaffected. Unpinned local indexes older than 24 hours are removed on a later large import. Save the session on the device to pin its index; browser storage can still be cleared or evicted.

Quality summaries, density layers and CSV metrics for disk-backed games describe the loaded window and are labeled accordingly. Frame exports and bounded A–B packed clip exports remain available. Use the local converter for whole-game packed exports. Large pose ZIPs skip non-pose members; their paths and ignored member count are reported. Companion marking files can be imported separately with attachment enabled.

Verification: 70,000 real synthetic pose members through ZIP64, disk paging and seeking. Separately, five million virtual ZIP64 directory entries were scanned with reads capped at 2 MiB. That is not a full five-million-frame decode benchmark. No VANDY@ARK.zip bytes or live authenticated deployment were available for direct verification.
