---
title: Add projection source column param to data-view projection columns
type: task
status: completed
description: >-
  Expose a per-column sourceid param on the projections_index-backed raw-stat projection columns so
  view authors can pick a projection provider (default AVERAGE), and fix the latent ros_projections
  sourceid-filter gap.
base_uri: user:task/league/data-views/add-projection-source-column-param.md
created_at: '2026-06-22T02:31:17.705Z'
entity_id: 4d0bfbe7-72d7-4844-9977-168f06bd4f97
finished_at: '2026-06-22T02:39:30.054Z'
observations:
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
owner_identity_uri: █████████████████████████
public_read: true
tags:
  - user:tag/league-xo-football.md
updated_at: '2026-06-22T02:40:17.429Z'
visibility_analyzed_at: '2026-11-05T23:48:43.078Z'
---

# Add projection source column param to data-view projection columns

## Overview

The raw-stat projection data-view columns (`player_{week,season,rest_of_season}_projected_{pass_atts,pass_yds,…,rec_tds}`) are hard-wired to read the consensus **AVERAGE** projection (`external_data_sources.AVERAGE`, sourceid 18) from `projections_index`. The underlying table actually stores per-source rows for every projection provider (CBS, ESPN, NumberFire, 4for4, NFL, FFToday, FantasySharks, FFN, FBG + analysts, FantasyLife, Sleeper, …). This plan exposes a per-column `sourceid` param so a view author can pick which provider's projection a column renders, defaulting to AVERAGE so all existing saved views are byte-identical.

Scope is the `projections_index`-backed raw-stat columns. The four computed columns (`points`, `points_added`, `market_salary`, `salary_adjusted_points_added`) are out of scope — they are derived into `scoring_format_*`/`league_format_*`/`league_*` tables that carry no `sourceid` dimension.

## Background

Findings from code/schema research:

- **Column defs**: `libs-server/data-views-column-definitions/player-projected-column-definitions.mjs`. Raw stats use `make_projections_index_source` (table `projections_index`, or `ros_projections` for rest-of-season) with `attach_owns_join: true` and a custom `attach`. The sourceid is hard-coded to `external_data_sources.AVERAGE` in two places: the `attach` `additional_conditions` (the JOIN) and `extra_predicates` (consumed only by select-string's year-offset correlated subquery).
- **Data availability**: every projection import script (`scripts/import-*-projections.mjs`) writes per-source rows to `projections_index` keyed `(sourceid, pid, userid, week, year, seas_type)`; `process-projections.mjs` adds the AVERAGE rows. So `projections_index` genuinely holds multiple sources → the feature is viable. `ros_projections` is only ever written with AVERAGE (process-projections), so source selection there is currently inert but should still be wired correctly.
- **Alias hashing**: `projections_index_table_alias` → `get_table_hash('projections_index_' + get_alias_key(p))`. Columns sharing a table alias are grouped into one JOIN (`get-data-view-results.mjs`). Two columns at the same year/week/seas_type but different sourceid would collide on one alias and one sourceid predicate — so `sourceid` MUST enter the alias key.
- **Params are lenient**: no per-param allow-list in `validators.mjs` / `data-view-request-schema.json`. New params are accepted; the column def reads them. UI advertises a column's selectable params via `app/core/data-views-fields/*.js` `column_params`, with shared param configs like `libs-shared/rate-type-column-param.mjs`.
- **Latent issues found**:
  1. The inline comment in `make_projections_index_source` claims "ros_projections carries no sourceid" — the schema shows `ros_projections.sourceid` is `NOT NULL`. The comment is wrong and the ros JOIN applies no sourceid filter, so if a second source were ever loaded into `ros_projections` the LEFT JOIN would fan out / pick arbitrarily. Wire `sourceid` into the ros path too (safe: today all rows are AVERAGE, so `= 18` is a no-op selection).
  2. `pass_comps` has a backend projection column but no frontend field — pre-existing gap, noted, not in scope.

## Design

### Approach

One canonical projection-source param threaded backend + frontend, defaulting to AVERAGE.

1. **Canonical source set** — add `projection_data_source_ids` to `libs-shared/constants/source-constants.mjs`: the ordered list of sourceids that populate `projections_index` (AVERAGE first, then the importer set). Single source of truth for "which sources are projection sources," consumed by the UI param and any future validation.

2. **Shared UI param** — new `libs-shared/projection-source-column-param.mjs` exporting `projection_source_param` (SELECT, `single: true`, `default_value: AVERAGE`, values built from `projection_data_source_ids` × `external_data_source_display_names`). Mirrors the `rate-type-column-param.mjs` pattern.

3. **Backend** (`player-projected-column-definitions.mjs`):
   - `get_default_params` resolves `sourceid` from `params.sourceid` (scalar or one-element array), coerced to a number, defaulting to `AVERAGE`.
   - `projections_index_table_alias` includes `sourceid` in the hash key (suffix `_source_<sourceid>`) so distinct sources get distinct aliases. Other (computed-column) aliases untouched.
   - `make_projections_index_source.extra_predicates` and `.attach` use the resolved `sourceid` instead of the literal `AVERAGE`, for **both** the index and ros paths. Replace the wrong ros comment.

4. **Frontend** (`app/core/data-views-fields/projected-table-fields.js`): attach `sourceid: projection_source_param` to the `column_params` of the index-backed raw-stat fields (all base names except the four computed ones), for week / season / rest-of-season periods.

### Default-preservation contract

Absent `sourceid` ⇒ AVERAGE ⇒ identical sourceid predicate text (`= 18`). Only the projections_index table-alias hash changes (now keyed by source). SQL-snapshot fixtures that reference `projections_index`/`ros_projections` get their alias tokens regenerated; result-equivalence behavior for AVERAGE is unchanged.

### Knowledge Base Impact

- **Domain doc**: `repository/active/league/docs/data-views-system.md` already documents the projection param flavors; add a short note that raw-stat projection columns accept a `sourceid` param (default AVERAGE) and that the four computed columns do not. Update the in-repo doc, not a user-base entity.
- **ABOUT.md**: none affected.
- **Tags/relations**: task tagged `user:tag/league-xo-football.md`. No new tags.
- **Observations/guidelines**: no new user-base guideline; the ros sourceid latent-bug fix and the alias-keying rule are captured in this task's completion observation and the code comments.

### Working-tier needs

None — no multi-file scratch artifacts. Snapshot regeneration is a throwaway one-off run inline.

### Orchestrator shape

Bounded single-task change across ~5 files plus tests/fixtures. No orchestrator needed.

## Tasks

1. `libs-shared/constants/source-constants.mjs`: add ordered `projection_data_source_ids` (AVERAGE first, then importer sourceids).
2. `libs-shared/projection-source-column-param.mjs` (new): export `projection_source_param`; re-export from `libs-shared/index.mjs` if that is the import surface used by the fields file.
3. `libs-server/data-views-column-definitions/player-projected-column-definitions.mjs`: resolve `sourceid` in `get_default_params`; key it into `projections_index_table_alias`; use it in `make_projections_index_source` `extra_predicates` + `attach` for index and ros; fix the ros comment.
4. `app/core/data-views-fields/projected-table-fields.js`: add `sourceid: projection_source_param` to index-backed raw-stat fields across periods.
5. Regenerate the 6 `projections_index`/`ros_projections` SQL-snapshot fixtures under `test/data-view-queries/` (alias tokens only; no templates present).
6. Add a result-equivalence fixture seeding `projections_index` with two sourceids (AVERAGE + one provider) asserting default→AVERAGE and `sourceid: <provider>`→provider value.
7. `docs/data-views-system.md`: document the `sourceid` projection param and its scope.
8. Run `yarn test:local` projection specs; `yarn lint`/`yarn prettier`. Commit on main (small, additive). Frontend field change requires `yarn build && yarn deploy:dist`; backend requires `yarn deploy` — note for the operator, do not auto-deploy.

## Notes

- Default AVERAGE keeps every saved view unchanged; the migration is purely additive.
- `ros_projections` source selection is wired correctly but inert until per-source ROS data is loaded; documented as such.
- `pass_comps` UI gap is pre-existing and left untouched.
