---
title: Classify NFL games on four axes
type: task
status: completed
description: >-
  Extend the team-game classifier to a game grain with margin band, trajectory, character and
  favorite-or-underdog winner, so a profile branch can be graded against measured classes.
base_uri: user:task/league/classify-nfl-games-on-four-axes.md
created_at: '2026-10-20T01:11:47.301Z'
entity_id: 61a73d77-6b96-4172-92df-ec044a421ff6
finished_at: '2026-10-20T04:56:16.856Z'
observations:
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
owner_identity_uri: █████████████████████████
priority: High
public_read: true
relations:
  - subtask_of [[█████████████-██████████████-████-████-███-█████-███-██████████████-███████████]]
  - blocked_by [[user:task/league/build-nfl-classification-registry.md]]
started_at: '2026-10-20T02:52:24.371Z'
tags:
  - user:tag/league-xo-football.md
updated_at: '2026-10-20T15:56:57.395Z'
visibility_analyzed_at: '2026-11-05T23:48:30.451Z'
---

# Implementation Plan: Classify NFL games on four axes

## Overview

- The team-game classifier grows a game grain: margin band, trajectory, character, and whether the favorite or the underdog won. A profile's script branch then declares these classes before kickoff and is graded against them after.
- Impact: "which branch happened" becomes a measured answer instead of a review judgment, and past games become findable by the conditions that produced them.
- Ordering: after [[user:task/league/build-nfl-classification-registry.md]]; [[█████████████-███████████████-██████-███████-██-███████-███████████]] depends on it.

## Background

- `libs-shared/classify-nfl-team-game.mjs` (version 1) classifies per team, mostly second half: script path `led_by_two_scores | trailed_by_two_scores | fourth_quarter_lead_change | within_one_score`, window readings, volume shocks; stored in `nfl_team_classification_gamelogs`, swept by `jobs/classify-changed-team-games.mjs`, backfilled 2024 onward.
- Week 6 profiles carry about 60 branch slugs over 14 games that collapse into margin, control, blowout, upset, close, shootout and grind classes, mixing team, margin and character in one token.

### Assumptions

- Closing spread per game is available in the league database for 2024 onward (unchecked; find the table before the winner axis is built). The spread is used only as a label of who was favored, never as a read input.
- Weather per game (wind, precipitation, roof) is stored (unchecked; if absent, the weather class is film- or report-sourced and marked so in the registry).

## Design

- Registry classes at game grain: `margin_band` (`one_score`, `two_scores`, `three_scores`, existing cut points), `trajectory` (`wire_to_wire`, `comeback`, `lead_change`, `close_throughout`), `game_character_list` (multi-valued: `shootout`, `grind`, `weather_affected`, `fast_paced`, `slow_paced`), `winner_market_role` (`favorite`, `underdog`, `pick`).
- Cut points for trajectory and character are definitions, proposed in this plan and confirmed by the operator at plan review; they are never tuned to any hit rate.
- Today's team-game `script_path` maps onto trajectory from each team's side; the team-game table keeps its per-team readings, and the game grain is stored once per game in `nfl_game_classifications` (operator, 2026-10-19), rewritten by the sweep whenever its content changes, since `nfl_games` carries no update stamp.
- Classifier version moves to 2; the sweep re-derives on version change.
- Knowledge Base Impact: [[user:text/nfl/nfl-betting/method/betting-outcome-grading.md]] game-classification section names the game grain.

## Plan

- [x] **Data availability check** for closing spread and weather, 2024 onward. Verify: per-season count of games with each field.
- [x] **Registry entries and cut points**, operator-confirmed. Verify: registry spec covers all four axes.
- [x] **Game-grain classifier and storage**, version 2. Verify: fixture specs for a wire-to-wire rout, a comeback, a late lead change and a weather grind.
- [x] **Sweep and backfill 2024 onward.** Verify: population check, every final game has one game row; each trajectory value and each margin band is non-empty per season, so a degenerate rule cannot pass silently.
- [x] **Week 6 2026 spot check** against the 14 profiles' filed branches. Verify: each game's measured classes listed on this task beside the branch the review says happened.
- [x] **Grading doc update.** Verify: `rg -n "game grain" text/nfl/nfl-betting/method/betting-outcome-grading.md` finds the section.

## Week 6 2026 spot check

Measured from `nfl_game_classifications` after the backfill, beside the branch the week 6 review names. The review rules a branch for three games only, in its selection traces; the rest are unruled until the per-game review fills `## After the game`.

| Game | Score | Margin band | Trajectory | Character | Winner role | Branch the review names |
| --- | --- | --- | --- | --- | --- | --- |
| SEA @ DEN | 14-17 | one_score | wire_to_wire | grind | underdog | not ruled |
| HOU @ JAX | 34-31 | one_score | wire_to_wire | shootout | underdog | not ruled |
| CHI @ ATL | 31-23 | one_score | wire_to_wire | none | favorite | chi-by-two-plus (Chicago led 31-7 into the fourth) |
| BAL @ CLE | 10-13 | one_score | comeback | grind | underdog | not ruled |
| TEN @ IND | 26-20 | one_score | comeback | none | underdog | not ruled |
| NYJ @ NE | 19-26 | one_score | wire_to_wire | none | favorite | not ruled |
| NO @ NYG | 34-27 | one_score | comeback | shootout | favorite | not ruled |
| CAR @ PHI | 25-14 | two_scores | wire_to_wire | none | underdog | not ruled |
| PIT @ TB | 26-19 | one_score | comeback | fast_paced | favorite | not ruled |
| ARI @ LA | 18-16 | one_score | lead_change | none | underdog | not ruled |
| LAC @ KC | 6-22 | two_scores | wire_to_wire | none | favorite | not ruled |
| BUF @ LV | 33-30 | one_score | wire_to_wire | shootout | favorite | buf-wins-late ("one score all game") |
| DAL @ GB | 13-23 | two_scores | wire_to_wire | fast_paced | favorite | gb-short-field (Green Bay led wire to wire) |
| WAS @ SF | 17-23 | one_score | pending | none | favorite | not ruled |

- CHI @ ATL: a branch named for game control reads as trajectory, not margin band; the final margin of 8 is one score.
- BUF @ LV: the review's "one score all game" is loose. Buffalo led 20-10 in the third and 30-20 late, and the game was tied at 20 in the fourth, which is wire_to_wire because the winner never trailed in the second half.
- WAS @ SF: its plays carried no score_difference at backfill, so trajectory is null until enrichment lands and the sweep rewrites the row.

Worktree: /Users/trashman/worktrees/league/feat/classify-nfl-games-on-four-axes
