---
title: Repair the yard_line_100 values that contradict yard_line_side
type: task
status: completed
description: >-
  nfl_plays.yard_line_100 disagrees with yard_line_side against possession_nfl_team on 10,415
  scrimmage plays, roughly 700 a season across 2001-2015, and the stored value is the inverse of the
  truth. It is the field-position input to every situational cut.
base_uri: user:task/league/repair-contradicted-yard-line-100.md
created_at: '2026-09-26T00:21:52.135Z'
entity_id: 44321f44-e970-40b1-b89e-4df901d3da28
finished_at: '2026-09-26T01:16:23.243Z'
observations:
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
owner_identity_uri: █████████████████████████
priority: Medium
public_read: true
relations:
  - relates [[user:task/league/repair-is-goal-to-go-play-flag.md]]
  - relates [[user:task/league/adjudicate-self-consistent-wrong-yard-lines.md]]
tags:
  - user:tag/league-xo-football.md
  - user:tag/software-task.md
updated_at: '2026-09-26T01:16:23.243Z'
visibility_analyzed_at: '2026-11-05T23:50:04.003Z'
---

## Overview

`nfl_plays.yard_line_100` contradicts `yard_line_side` and
`possession_nfl_team` on 10,415 scrimmage plays, concentrated at roughly 700 a
season across 2001 through 2015. The three columns determine each other, so a
disagreement is unambiguous: if `yard_line_side = possession_nfl_team` then
`yard_line_100` must be `100 - yard_line_number`, else it must be
`yard_line_number`.

Surfaced 2026-09-25 while repairing `is_goal_to_go`
([[user:task/league/repair-is-goal-to-go-play-flag.md]]). That repair derives
its column from `yard_line_100` and DECLINES these rows rather than writing a
faithful projection of a corrupt input, so the defect is currently contained
rather than fixed.

## The measurement

```sql
select season_year, count(*) as contradicted
from nfl_plays
where down_number between 1 and 4 and yards_to_go > 0 and yard_line_100 > 0
  and yard_line_side is not null and yard_line_number is not null
  and possession_nfl_team is not null
  and yard_line_100 <> (case when yard_line_side = possession_nfl_team
                             then 100 - yard_line_number
                             else yard_line_number end)
group by 1 order by 1;
```

2001 through 2015 carry 690 to 775 rows each. 2020 carries 7, 2021 two, 2022
one, 2023 five, 2026 two, and 2025 carries 70 kickoff rows that the down guard
above already excludes. 2016 through 2019 and 2024 are clean.

**`yard_line_100` is the wrong one, not the trio.** `esbid` 2012112503 play 767
reads `yard_line_side` TEN, `yard_line_number` 1, possession JAX and
`yard_line_100` 99, and its description is "R.Jennings up the middle for 1 yard,
TOUCHDOWN" — a third and goal from the opponent 1. The stored value is the
INVERSE of the truth, which is the signature to check the whole population
against.

## A second population with no self-contradiction

Thirteen rows in 2002 through 2004 carry a `yard_line_100` that is merely wrong
rather than inverted, and the trio agrees with itself, so nothing on the row
reports it. Only the description does: "1st and 2 from the WAS 6, A.Green up the
middle for 2 yards, TOUCHDOWN" cannot be a play from the 6. The `is_goal_to_go`
repair flipped these to false, correctly as a projection and wrongly as a fact,
and repairing the yard line repairs the flag with them.

## Why this matters beyond one column

`yard_line_100` is the field-position input to every situational cut — red zone,
goal line, field position splits, and the expected-stats and target-probability
models, all of which take it as a feature. An inverted value places a snap at
the wrong end of the field, which reads as an ordinary play rather than as
missing data.

## Plan

- [x] Establish which column the vendor got wrong and whether the inversion is
      systematic — verify: partition the 10,415 by whether `yard_line_100` equals
      `100 - implied`, and state the residue

      10,401 of the 10,415 are an exact inversion and 14 are off by one or two.
      **The measurement in this task was too narrow**: its `down_number between
      1 and 4` guard hid 8,509 corrupt KICKOFF rows in 2022-2024, which that
      guard excludes and which read as 0 to 5 rows per season with it applied.
      The real table-wide population was 20,306, of which 20,292 are exact
      inversions.

- [x] Find the writer for the 2001-2015 era — verify: name the statement that
      persists it, not a grep hit, and check whether it still exists

      A JAX/JAC alias defect, and the era's population is ONE TEAM.
      Jacksonville is the only pre-2016 team in it, across exactly 10 of their
      20 games in every season — their home games. The feed spelled the home
      yard-line side `JAC` while `possession_nfl_team` was already normalized to
      `JAX`, so the branch compare in the importer picked the wrong side. A
      later pass normalized `yard_line_side` to `JAX` as well, leaving no `JAC`
      anywhere in the table, without recomputing `yard_line_100`. The live path
      is already correct — `scripts/import-plays-nfl-v1.mjs:282` applies
      `fixTeam()` before comparing — so no writer fix was needed.

- [x] Decide whether to repair `yard_line_100` from the trio or to declare the
      trio authoritative and derive it — verify: the choice is argued from a
      population where an independent oracle (the play description) settles it

      Repaired from the trio, and **deliberately NOT derived**. Deriving it
      would make the stored value and the trio agree by construction, which
      deletes the detector while looking like the permanent fix. The importers
      compute it independently and that independence is the property now being
      graded.

- [x] Repair, and re-derive `is_goal_to_go` over the repaired rows — verify: the
      contradiction query returns zero, and
      `goal-to-go-derivation-agreement` stays green

      20,306 rows repaired; the contradiction query returns 0. `is_goal_to_go`
      needed almost no re-derivation — exactly ONE row disagreed table-wide,
      which independently confirms the predecessor's claim that the stored flag
      was already correct on the contradicted rows. That one row's whole trio is
      wrong, so it was repaired individually; the check is back to 0 findings
      with coverage trustworthy.

- [x] Widen the goal-to-go derivation's guard back out once the contradiction is
      gone, or record why it stays — verify: the declined population is zero and
      the guard's cost is stated

      **It stays**, recorded in the module. It is a true statement about the
      derivation rather than a workaround — a projection of a self-contradicting
      input has no meaning at any population size — and the contradiction is now
      owned by `yard-line-100-trio-agreement`, whose subject it actually is.
      Dropping it would move the reporting onto the goal-to-go check, where it
      would name the wrong column and overwrite a correct flag from a corrupt
      yard line. It declines nothing today, so the cost is zero.

## Carried forward

A class this repair's invariant is blind to **by construction**: 282 rows
table-wide carry a `yard_line_number` that disagrees with the number inside
their own `yard_line_start`, 104 in 2002 and 171 in 2003. On those the three
columns agree with each other and with `yard_line_100` while all four are
wrong, so nothing self-contradicts and only the description or the neighbouring
play reveals it. The predecessor's 13 deliberately-flipped rows are in this
class, which is why they did NOT resolve with this repair as expected.

One such row was repaired individually because it turned the goal-to-go check
red. The rest need an adjudication of which column is authoritative —
`yard_line_start` or the side/number pair — which has been established on
exactly one row and not across the population.

The ~5,000-a-season rows where only the SIDE differs are NOT in this class:
`yard_line_start` preserves the raw feed alias (`HST`, `STL`, `SD`) while
`yard_line_side` is normalized, so they differ on spelling rather than on
position.
