---
title: Repair the is_goal_to_go play flag, unpopulated since 2023
type: task
status: completed
description: >-
  nfl_plays.is_goal_to_go was written false across the live-play population and true almost nowhere
  from 2023 on. The NFL v1 feed sends goalToGo false on every play and the Sportradar importer used
  an inverted field-position formula; the column is derived now, 13,569 rows were repaired, and a
  registered check grades it.
base_uri: user:task/league/repair-is-goal-to-go-play-flag.md
created_at: '2026-09-26T00:03:09.591Z'
entity_id: 3f64ae4c-36ed-4594-9fce-6fe9686b8023
finished_at: '2026-09-26T00:21:14.723Z'
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-26T01:26:22.656Z'
visibility_analyzed_at: '2026-11-05T23:50:05.833Z'
---

## Overview

`nfl_plays.is_goal_to_go` is populated with `false` across the whole table and set `true` almost nowhere from 2023 onward. It is a plausible, populated, silently wrong column: a query filtering on it returns an empty or heavily truncated result rather than an error.

Surfaced 2026-09-25 while auditing the play-param vocabulary for the default data-view catalog build-out. The param is declared and fully wired — `is_goal_to_go` is a BOOLEAN param in `libs-shared/nfl-plays-column-params.mjs:813`, and `libs-server/apply-play-by-play-column-params-to-query.mjs:155` emits a predicate for it — so nothing upstream of the data is broken.

## The measurement

Scrimmage plays only, `season_type = 'REG'`, `play_classification = 'live_play'`, `play_type in ('PASS','RUSH')`. The derived control is `yards_to_go >= yard_line_100 and yard_line_100 between 1 and 10`, which is goal-to-go by definition and independent of the flag:

| season | derived goal-to-go | `is_goal_to_go` true |
| --- | --- | --- |
| 2022 | 1861 | 1969 |
| 2023 | 1927 | 0 |
| 2024 | 1993 | 0 |
| 2025 | 1928 | 699 |
| 2026 | 225 | 0 |

2022 agrees with the control, so the column worked and then stopped. 2025 carries a partial 699, which reads like a partial repair or a source that covered some weeks. 2023, 2024 and 2026 are zero against roughly 1900, 2000 and 225 real goal-to-go plays.

The column is NOT sparse-because-unwritten: the `false` count equals the full live-play population in every season, so a writer IS setting it and setting it wrong.

## Reproduce

```sql
select season_year,
 count(*) filter (where yards_to_go >= yard_line_100 and yard_line_100 <= 10 and yard_line_100 > 0) derived_goal_to_go,
 count(*) filter (where is_goal_to_go is true) flagged_goal_to_go
from nfl_plays
where season_year >= 2022 and season_type = 'REG'
  and play_classification = 'live_play' and play_type in ('PASS','RUSH')
group by season_year order by season_year;
```

## Scope

Find the writer, establish why it stopped after 2022 and what wrote the partial 2025 slice, fix it, and backfill 2023 onward. Decide whether the value should be derived at import from `yards_to_go` and `yard_line_100` rather than taken from a source field — a derivation is checkable against the control above, and a source field is what failed.

## Out of scope

The default data-view catalog build-out that surfaced this. That work uses `yard_line_100: [0, 5]` for its goal-line view and does not depend on this column.

## Plan

- [x] Identify every writer of `is_goal_to_go` and which source field each reads — verify: name the statement that persists it, not a grep hit
- [x] Explain the 2022-to-2023 cliff and the partial 2025 slice — verify: the explanation predicts the per-season counts in the table above
- [x] Fix the writer — verify: re-run the importer over a sample 2024 game and show the flag agrees with the derived control
- [x] Backfill 2023 onward — verify: the reproduce query returns flagged within a stated tolerance of derived for every season 2022-2026
- [x] Add a registered data check per [[user:guideline/software/design-data-checks.md]] comparing flagged against derived per season — verify: the check goes red on today's data before the backfill and green after

## Outcome

`is_goal_to_go` is a derived column now, written only by
`libs-server/play-enrichment/goal-to-go-enrichment.mjs` and graded by the
registered `goal-to-go-derivation-agreement` check. The backfill moved 13,569
rows and the check reads zero on all 77 season-and-type grain rows.

The reproduce query above now returns flagged consistently about six percent
above its ten-yard control in every season 2022 through 2026 — 2023 at 2045
against 1927, 2024 at 2122 against 1993, 2025 at 2039 against 1928, 2026 at 248
against 228 — which is the same ratio 2022 has always carried. That residual is
the control's own bound rather than a gap: a first and 15 from the 12 is goal to
go and `yard_line_100 <= 10` drops it.

### What this did NOT fix

`yard_line_100` is wrong on 10,415 scrimmage plays, concentrated at roughly 700
a season across 2001 through 2015, where it is the inverse of what
`yard_line_side` against `possession_nfl_team` implies. The derivation declines
those rows rather than repairing the wrong column, so they keep whatever flag
the feed of the day wrote. Owned by
[[user:task/league/repair-contradicted-yard-line-100.md]].
