---
title: Cover importer column mapping in specs, not just response shape
type: task
status: completed
description: >-
  The nflverse weekly-rosters spec exercises only the CSV response-shape validator and never the
  mapping into player_gamelogs, which is why a 204-column rename swept past that importer and broke
  it in production. Extract the row builder and spec the mapping.
base_uri: user:task/league/cover-importer-column-mapping-in-specs.md
created_at: '2026-09-01T22:31:52.848Z'
entity_id: 2677b945-a472-46e6-84d8-3fc3dfed8cd6
finished_at: '2026-09-01T23:26:02.167Z'
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-01T23:26:02.167Z'
visibility_analyzed_at: '2026-11-05T23:48:41.322Z'
---

## Problem

`test/scripts.import.nflverse-weekly-rosters.spec.mjs` covers only `validate_response_shape` — the CSV columns arriving from the vendor. It never exercises the object literal that maps a parsed row into a `player_gamelogs` insert.

That is the gap that let commit `72346e579` (2026-08-05, "Conform 204 shorthand columns to full-word names") rename `player_gamelogs.pos` to `player_position` without touching this importer. Every run then died on `column "pos" of relation "player_gamelogs" does not exist`, surfaced as signal 127764, and was fixed in `21d9a3247`.

The spec passed throughout. A green suite was not evidence, because the assertion stopped one layer above the defect.

## Why it recurs

The mapping is an inline object literal inside `import_for_year`, and the module exports only its default function. There is nothing a spec can reach without a database, so the cheapest correct assertion — "the columns this importer writes are the columns the table has" — is currently unwriteable.

Verified during triage that this was the ONLY writer the rename missed: `generate-player-gamelogs.mjs` remaps `pos` to `player_position` in `format_player_gamelog`, and its snaps branch writes `player_position` directly with a comment naming the trap. So this is a coverage defect, not a second outstanding bug.

## Approach

1. Extract the row builder from `import_for_year` into an exported pure function taking a parsed row plus its resolved player match, returning the insert object. No behavior change.
2. Spec it: assert the exact key set, and assert `player_position` specifically carries `player.primary_position` rather than the vendor's spelling, which is the reason that column exists.
3. Consider the general form: a gate asserting that every column literal written to a known table exists in `db/schema.postgres.sql` would catch this class across all importers, not just this one. Weigh against `db/gates/check-schema-conformance-ratchet.mjs`, which already parses the schema and could host it.

Step 3 is the one worth deciding before doing 1 and 2 — a per-importer spec fixes one file, and the rename touched 204 columns across many.

## Oracle

Prove the new spec non-vacuous before trusting it: revert the `player_position` key back to `pos` and require the spec to fail. A check that cannot report is indistinguishable from a clean result.
