---
title: Pin the season projected period to the season key instead of the week param
type: task
status: completed
description: >-
  Retired the last week-zero param sentinel from the data-view read path: a week-keyed projected
  column asked for without a week now refuses at the request boundary via source.requires_week
  instead of falling back to params.week || 0. Shipped and deployed; the points-added valuation gate
  was run and moves nothing.
base_uri: user:task/league/pin-season-projected-period-to-season-key.md
created_at: '2026-08-04T23:38:46.312Z'
entity_id: 76f39517-6061-4c04-8b18-8ccf7dc250f5
finished_at: '2026-08-29T20:54:10.412Z'
observations:
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
owner_identity_uri: █████████████████████████
priority: Medium
public_read: true
relations:
  - follows [[███████████████████████-████-████-██████-████-████-██████████████]]
  - relates [[user:task/league/surface-projected-points-added-net.md]]
  - relates [[████████████████████████████████-█████-████████████]]
  - relates [[███████████████████████████-████-██-██████-███████████]]
  - relates [[████████████████████████-████-█████████████████]]
tags:
  - user:tag/league-xo-football.md
updated_at: '2026-08-29T21:02:18.899Z'
visibility_analyzed_at: '2026-11-05T23:49:48.418Z'
---

# Pin the Season Projected Period to the Season Key

## Problem

`league_format_player_projection_values` stores every period in one `week` column: `'0'` (season), a numeric week, `'ros'`, and `'ros_net'`. The `week` and `season` prefixes produced by `create_projected_stat` both follow the request's week param, so `player_season_projected_points_added` and `player_season_projected_market_salary` pin `week = String(params.week)` rather than `week = '0'`.

It reads correctly today only because the default week param is `0`, which happens to be the season key. Set a week param and the season column silently returns that week's value under a season header — no error, no failing test.

This is the same defect the file's own comment records as already fixed for the salary-adjusted family:

> Registered explicitly per period below rather than fanned out by `create_projected_stat`. The fan-out shares one table_alias and one source across all three prefixes, which is what produced the latent defect here: every prefix resolved to whatever `params.week` was (default 0), so the season and rest-of-season columns silently returned a per-week value instead of their own period.

The rest-of-season half of that defect was fixed for `points_added` / `market_salary` in `67278d518` (`mistakia/league`), which added a `period_week` to the source and put the period into the table alias. The season half was deliberately left alone there, because it changes a shipped column's values.

## Where

- `libs-server/data-views-column-definitions/player-projected-column-definitions.mjs` — `create_projected_stat` sets `period_week = is_rest_of_season ? 'ros' : null`. The `season` prefix needs `'0'`.
- The alias factory `league_format_player_projection_values_table_alias_for` already carries the period, so separating the season alias from the week alias needs no further plumbing.

## Constraint

`███████████████████████████████████████████████████` governs this. It changes what a shipped column returns, so the pre-deploy gate applies in full: backtest across every available season on the point-in-time board, show the resulting board to the operator, and state which of the three goal questions it improves and which it leaves alone or degrades. Interactive gate — surface it and wait.

## Verify

- A view requesting `player_season_projected_points_added` with an explicit non-zero week param emits `week = '0'`, not that week.
- The season and week columns in one view resolve to two distinct joins with two distinct predicates.
- Add a golden pinning that shape and confirm it fails at the pre-fix revision before trusting it green.
- Run `db/gates/check-data-view-sql-validity.mjs` from a clean worktree at your HEAD, never the shared checkout.

## Plan

- [x] **Pin the season prefix to `week = '0'`** — no work needed, superseded by the period-table cutover
  - The cutover had already done it by a different mechanism: season columns bind `league_format_player_season_projection_values` with `join_week` false, so no week-0 pin remained to add. What survived was the `params.week || 0` fallback on the WEEK-scoped columns, which became the actual work.

- [x] **Retire the `params.week || 0` fallback and refuse a weekless week-keyed column**
  - Shipped as `source.requires_week`, satisfied by a week param, an `nfl_week_id`, or a week row axis, refusing at the request boundary in `libs-server/data-views/validate-week-requirement.mjs`. NOT the inherited grain design — `grain: 'player_year_week'` refuses a shipped flat player-row table with an explicit week, which the golden corpus proved.
  - Verify: `eslint-rules/no-week-zero-sentinel.mjs` runs at zero with the by-name exemption deleted.

- [x] **Add goldens covering the refusal and the separated joins**
  - `test/data-views-subject-compatibility.spec.mjs` (125 lines added) plus the row-axis-sort-reachability and all-periods fixtures.
  - Verify: full suite 5,108 passing, exit 0.

- [x] **Run the data-view SQL validity gate**
  - Found three real defects the 5,105-test suite was green over, because no golden pairs `year_offset` with a week axis: two sources emitting `week = 'null'` (22P02 on smallint) and the in-query scorer raising `invalid data view param: week`. Fixed in `b6f6089f2`.
  - Verify: 4,538 statements clean from a clean worktree at HEAD.

- [x] **Backtest across every available season on the point-in-time board**
  - Run 2026-08-29 at both `fc71f9a24` and `b6c962aa2` from two clean worktrees. Source is `projections_history` plus `season_projections_history` at the kickoff cutoff — NOT `projections_index` week 0, which the plan named before the guideline's 2026-08-05 correction and which no longer exists.
  - Verify: `scratch/league/week-zero-backtest/findings.md`; six seasons, mean gap negative 6/6 at every aggregate, outputs byte-identical across revisions.

- [x] **Show the resulting board to the operator**
  - Top of the board, positional shares and concentration for all six seasons, before and after, in the findings artifact.

- [x] **State which goal questions this improves and which it does not**
  - It improves none and degrades none: positional value, the value curve, and individual player value are all identical at both revisions. It is a read-path correctness fix with no board arithmetic on its path.
