---
title: Restore preseason gamebook starter capture
type: task
status: planned
description: >-
  Preseason starter capture collapsed from ~100 percent in 2021-2022 to ~25 percent from 2023 onward
  and has stayed there for four seasons, while REG and POST remain at 100 percent every year. Bisect
  the 2022-to-2023 change and decide whether the 95 percent per-game resolution floor is the
  mechanism.
base_uri: user:task/league/restore-preseason-gamebook-starter-capture.md
created_at: '2026-09-08T18:02:22.438Z'
entity_id: 9b770cd7-8b23-4ab7-839e-0610c923ab95
owner_identity_uri: █████████████████████████
public_read: true
tags:
  - user:tag/league-xo-football.md
updated_at: '2026-10-03T03:08:29.947Z'
visibility_analyzed_at: '2026-11-05T23:50:13.230Z'
---

## The measurement

Games with at least one `player_gamelogs.is_starter` row, against games scheduled, from `league_production` on 2026-09-08:

| Season | PRE | REG | POST |
| ------ | --- | --- | ---- |
| 2021 | 98.0% (48/49) | 100% | 100% |
| 2022 | 100% (49/49) | 100% | 100% |
| 2023 | 26.5% (13/49) | 100% | 100% |
| 2024 | 26.5% (13/49) | 100% | 100% |
| 2025 | 22.4% (11/49) | 100% | 100% |
| 2026 | 30.6% (15/49) | in progress | not played |

Regular season and postseason are complete every year. Preseason alone fell off a cliff between 2022 and 2023 and has stayed at roughly a quarter for four seasons.

## Why this was not obvious

The 2026 instance surfaced as signal #129111, whose text is `avg 38.9 starters/game below 40 (rows=311 games=8)`. That oracle measures starters per game among games that WERE written, so it is blind to the games that were refused outright — the failure mode here. A run that captured a quarter of the slate at full fidelity would satisfy it.

The framing this inherited was also wrong in two ways worth recording, because both invite the wrong investigation:

- It read as a 2026 problem. It is not; 2023, 2024 and 2025 are all equally affected and already in the database.
- The count was quoted as 8 of 49. That was a mid-run reading. The completed figure is 15 of 49.

## The suspected mechanism, not yet confirmed

`RESOLUTION_FLOOR_PER_GAME = 0.95` at `scripts/import-nfl-gamebook-starters.mjs:49` drops a game's writes WHOLESALE unless 95% of its declared starter slots resolve to a known player (`scripts/import-nfl-gamebook-starters.mjs:468`). Preseason rosters carry 90 players including undrafted free agents who may never appear in the player table, so a 95% floor is a far harder gate in August than in September.

That is a hypothesis with a suggestive shape, not a finding. It does not by itself explain the discontinuity: the floor has to have either been introduced, raised, or newly reached between the 2022 and 2023 imports. Bisect that before changing the constant.

## Plan

- [ ] Establish when the floor was introduced or last changed (`git log -S RESOLUTION_FLOOR_PER_GAME`), and whether its arrival coincides with the 2022-to-2023 discontinuity. If it does not, the cause is elsewhere — a gamebook format change or a roster-importer coverage change are the next candidates.
- [ ] Re-run one refused 2023 preseason game with the floor logging enabled and read the actual resolved/attempted rate. Confirm the refusals are floor breaches rather than missing PDFs or parse failures — `games_no_pdf`, `games_parse_fail` and `games_team_mismatch` are counted separately and any of them would mean a different defect.
- [ ] Decide the disposition and get it ruled on: keep the floor and accept preseason loss, lower it for PRE only, or write partial rows with the resolution rate recorded per game. Partial capture with a recorded rate is the shape that keeps the data honest without discarding it, but this is a data-quality judgment, not a mechanical one.
- [ ] Whatever the ruling, replace the starters-per-game oracle with one that can see a refused game. The current one is structurally blind to this whole class.
- [ ] Backfill 2023 through 2026 preseason once the resolution path is fixed. Nothing re-runs preseason gamebooks today, so the historical gap does not close on its own.
