---
title: Make the nfl_plays_column_params Deferral Real
type: task
status: planned
description: >-
  The 56 KiB column-param registry loads with the data-views page because five field modules spread
  it whole into their column_params. The dynamic import in sagas.js defers nothing and cannot until
  those modules stop needing the object.
base_uri: user:task/league/defer-the-nfl-plays-column-params-registry.md
created_at: '2026-09-11T01:52:36.024Z'
entity_id: faf32a22-eb64-4b91-85f3-177207352cb6
observations:
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
owner_identity_uri: █████████████████████████
priority: Medium
public_read: true
tags:
  - user:tag/league-xo-football.md
  - user:tag/software-task.md
updated_at: '2026-09-11T02:10:44.830Z'
visibility_analyzed_at: '2026-11-05T23:49:07.339Z'
---

## Overview

`libs-shared/nfl-plays-column-params.mjs` is 56,446 bytes and rides in the chunk that loads with the data-views page. `app/core/data-views/sagas.js` dynamically imports it under a `webpackChunkName`, and that import defers nothing — measured from production build stats 2026-09-10, no `nfl-plays-column-params` chunk file is emitted at all.

The cause is structural, not a mistake to undo: five field modules spread the WHOLE registry into their `column_params`, so webpack reaches it statically and hoists it into the page chunk, and the `import()` then resolves against what is already there.

```
app/core/data-views-fields/player-stats-from-plays-table-fields.js
app/core/data-views-fields/player-routes-table-fields.js
app/core/data-views-fields/player-snaps-table-fields.js
app/core/data-views-fields/team-stats-from-plays-table-fields.js
app/core/data-views-fields/fantasy-points-from-plays-table-fields.js
```

Each does `...nfl_plays_column_params` inside a field definition. They need the OBJECT, not its keys, so no import-style change can fix it. `3be66562e` moved all five off the `#libs-shared` barrel onto the subpath, which is a real saving — the page chunk fell from 963,158 to 933,199 bytes — but is not a deferral, and `sagas.js` now says so in place of the comment that claimed otherwise.

## Why this is worth doing

It is 56 KiB every visitor to the data-views page downloads before touching a filter. The generated option sets landed exactly this deferral for the large VOCABULARIES (four chunks, 2,231-12,863 bytes, none in the page chunk); the registry itself is the remaining block.

It is NOT in the initial app bundle — the data-views page is lazy-routed — so this is a page-load cost, not a site-wide one. Size the work against that before taking it.

## The shape of the fix

The field modules need a param DEFINITION at render time, which is what makes this more than an import change. Two directions worth measuring before choosing:

- **Declare keys, resolve definitions lazily.** The field carries the param KEYS and the definitions arrive through the same dynamic path the option sets already use. This is the shape the option-set mechanism established, and `app/core/data-views/option-set-loader.mjs` is the working precedent.
- **Split the registry.** Most of the 56 KiB is the play-by-play FILTER surface — 246 of 247 params on `player_receiving_yards_from_plays` — while the handful that CONFIGURE a column are small. Splitting the filter tail from the configuration params would let the tail defer while the configuration half stays static.

Read `user:text/league/data-views/generate-param-option-sets.md` for the mechanism the first direction would reuse, and `docs/guides/spa.md` for the barrel rule and the measured `minSize` behaviour.

## Verification this needs

Whatever the approach, the oracle is BUILD STATS, not the import graph — that is the whole lesson of the deferral this replaces. Record the chunk the registry lands in and whether that chunk is `initial`, and require the reading to DIFFER from the recorded baseline: chunk 471, 933,199 bytes, `initial: false`, registry module 56,446 bytes.

`local/no-static-option-set-import` guards the generated sets and does not cover the registry. Consider whether the repaired shape earns the same guard.

## Plan

- [ ] **Measure what the five modules actually read off the registry**
  - Purpose: decide between the two directions above from evidence rather than preference.
  - Verify: state, per module, whether it needs definitions or only keys, and how many params each field actually offers.

- [ ] **Implement the chosen direction**
  - Verify: `yarn test:isolated`, `yarn lint`, `yarn check:types`.

- [ ] **Prove the deferral from build stats**
  - Verify: a `nfl-plays-column-params` chunk (or equivalent) is emitted, is not `initial`, and is NOT a child of the data-views page entry. Compare against the 2026-09-10 baseline above and require the two readings to differ.
