---
title: Simplify the data-view param compat layer
type: task
status: planned
description: >-
  The rename registry is applied in six places and 175 of its 246 rules match nothing in production.
  Collapse to one canonicalization point per process, carry shape changes only in the storage
  version chain, and retire the dead rules after validating the zero against a control.
base_uri: user:task/league/simplify-data-view-param-compat-layer.md
created_at: '2026-09-20T00:26:18.284Z'
entity_id: 9b089f50-500c-41d7-ae48-9ae34e7d0b4e
owner_identity_uri: █████████████████████████
priority: Medium
public_read: true
relations:
  - succeeds [[user:task/league/collapse-single-variant-column-param-keys.md]]
tags:
  - user:tag/league-xo-football.md
  - user:tag/software-task.md
updated_at: '2026-09-20T00:26:35.826Z'
visibility_analyzed_at: '2026-11-05T23:50:27.184Z'
---

# Simplify the data-view param compat layer

## Problem

The same rename registry is applied in SIX places, each added when someone
noticed a surface was uncovered — two of them on 2026-09-19 during the
twin-key collapse, which is the tell that the pattern is wrong rather than
merely incomplete. The next new surface will also be uncovered, and will be
found by a bug rather than by design.

1. `libs-server/get-data-view-results.mjs` — server request boundary
2. `app/core/data-views/parse-table-state-from-url.mjs` — short URLs
3. `app/core/data-views/reducer.js:43` — saved-view read
4. `libs-shared/data-view-storage/migrations.mjs` — browser storage chain
5. `libs-server/data-views/generation/resolve-generated-table-state.mjs`
6. `scripts/migrate-data-views-saved.mjs` — durable rewrite

## Measured 2026-09-19, VALIDATED 2026-09-20

The original measurement asked whether a legacy name appears literally in the
URL text, via SQL `LIKE`. It has since been redone by PARSING every short URL
into a `table_state` and reading the actual param keys and column ids, which
removes the text assumption the first pass rested on.

Reachability of every rename rule, parsed:

| | total | in views | in URLs | dead |
| --- | --- | --- | --- | --- |
| param-key renames | 195 | 5 | 48 | **145** |
| column-id renames | 51 | 0 | 6 | **45** |

**190 of 246 rules (77%) match nothing in production** — more dead than the 175
of 246 (71%) first recorded, not fewer. Every canonicalization point walks all
of them on every request, and every gate asserts over them. Nothing writes
legacy keys any more, so the population can only shrink.

**The zero now carries a known-positive control and it passed.**
`single_nfl_week_id` is a param-key rule known live on 49 production short
URLs; the extractor sees it. Had it not, every zero would have been worthless.
The script refuses to report at all if that control fails.

**The two measurements disagree on exactly 15 param-key rules, all explained.**
Every one is a `LIKE` substring artifact rather than parse blindness: nine are
fragments of COLUMN ids (`pass_yds` inside `player_season_projected_pass_yds`,
`qb_hurry` inside `play_qb_hurry`, and similar), and six are short tokens
inside unrelated words (`ot` in `football`, `db` in a view_id hex string, `ep`
in `keeptradecut`, `temp` in `attempts`, `cov` in `cov_type_ngs`, `wind` in the
phrase "wind up" in a view name). Not one is a real param key the parser missed,
so the parsed figure is the accurate one and the larger dead list is safe.

Reproduce with `NODE_ENV=production node scripts/check-rename-rule-reachability.mjs`
in the league repo — promoted out of scratch so this task's verify step is
runnable on any machine. Read-only, and it refuses to report if its control
fails. The one-off script explaining the 15-rule gap stayed in the collapse
task's scratch directory; it answered a question that is now closed.

Re-run before deleting: the population drifts, and the URL count moved from 938
to 1,006 between the two measurements.

## Plan

- [x] **Validate the reachability zero with a known-positive control**
  - Done 2026-09-20. Parsed measurement replaces the `LIKE` one; control
    passed; the 15-rule disagreement is fully explained as text artifacts.
  - Scripts live in the collapse task's scratch directory and are read-only.

- [ ] **Delete the unreachable rules**
  - Files: `libs-shared/data-views-saved-view-migration.mjs`
  - Purpose: 190 of 246 rules are walked on every request and asserted over by
    every gate while matching nothing.
  - Verify: re-run `rename-reachability.mjs` immediately before deleting —
    the population drifts. The control must pass in that same run, or the
    deletion has no evidence behind it.

- [ ] **Collapse six canonicalization points to two**
  - Purpose: the floor is one per PROCESS — client paths (2, 3, 4) converge on
    a table_state entering the store, server paths (1, 5) on params attached
    to a column entry.
  - Verify: the short-URL parse digest over all production URLs is unchanged
    before and after. That check caught a real defect during the collapse and
    is the right oracle here too.

- [ ] **Drop the storage-version steps that only re-run the saved-view migration**
  - Files: `libs-shared/data-view-storage/migrations.mjs`
  - Changes: `v1_to_v2` and `v3_to_v4` exist solely to re-run the saved-view
    migration; under unconditional canonicalization on load neither is needed.
    Only a genuine shape step like `v2_to_v3` (`splits` to `row_axes`) survives.
  - Verify: a stored snapshot at each version still loads to current shape.

- [ ] **Re-examine `retirable: false`**
  - Purpose: it was applied categorically on the reasoning that short URLs are
    immutable. That is sound per rule, but only for a key that actually appears
    in one — which is now a measured question rather than an assumed one.
- [ ] Rule whether the seas_type bindings in the data-view column definitions conform to season_type or stay as the frozen view-param name, and rename the camelCase seasType binding in the player seasonlogs column definitions either way

## Target end state

- **One canonicalization point per process.** The client paths (2, 3, 4)
  converge on a table_state entering the store; the server paths (1, 5)
  converge on params attached to a column entry. Client and server are
  separate processes, so the floor is TWO, not six. Everything else deletes
  its copy.
- **The storage version chain carries SHAPE changes only.** `v1_to_v2` and
  `v3_to_v4` both exist solely to re-run the saved-view migration. Under
  unconditional canonicalization on load neither is needed; only a genuine
  shape step like `v2_to_v3` (`splits` to `row_axes`) survives.
- **The registry holds only reachable rules.** `retirable: false` was applied
  categorically on the reasoning that short URLs are immutable. That is sound
  per rule but only for a key that actually appears in one.

## Ordering

Do the reachability validation first — it is independent, and a validated dead
list makes the canonicalization collapse smaller. The six-to-two collapse is
the part that stops the system accumulating this.

## Not in scope

The rewrite of durable rows, which belongs to the collapse task.
