{
  "source_count": 7,
  "sources": [
    {
      "key": "schedule",
      "label": "Game Metadata",
      "primary_provider": "nflverse/nflreadpy schedules",
      "cadence": "weekly slate plus game-window refresh",
      "required_fields": [
        "game_id",
        "season",
        "week",
        "kickoff_time",
        "home_team",
        "away_team",
        "game_window"
      ],
      "optional_fields": [
        "venue",
        "surface",
        "roof",
        "international_or_holiday"
      ],
      "notes": ""
    },
    {
      "key": "odds",
      "label": "Market Lines",
      "primary_provider": "odds API",
      "cadence": "pre-publish and publish-time refresh",
      "required_fields": [
        "game_id",
        "market",
        "side",
        "line",
        "odds",
        "sportsbook",
        "odds_updated_at"
      ],
      "optional_fields": [
        "over_odds",
        "under_odds",
        "book_count",
        "best_book",
        "best_line",
        "best_odds"
      ],
      "notes": "Best-line shopping is an active gate, so publish-time odds must be refreshable."
    },
    {
      "key": "usage",
      "label": "Usage and Role",
      "primary_provider": "nflverse/nflreadpy participation where available",
      "cadence": "weekly after games, with depth-chart overrides before kickoff",
      "required_fields": [
        "player_id",
        "player_name",
        "team",
        "position",
        "role_context_available"
      ],
      "optional_fields": [
        "snap_share",
        "route_share",
        "target_share",
        "rush_share",
        "red_zone_role",
        "goal_line_role"
      ],
      "notes": ""
    },
    {
      "key": "injuries",
      "label": "Injuries and Inactives",
      "primary_provider": "official team reports and 90-minute inactive report",
      "cadence": "daily practice reports, final status, and per-window 90-minute inactives",
      "required_fields": [
        "inactive_state",
        "injury_news_timestamp",
        "projection_includes_injury_news"
      ],
      "optional_fields": [
        "player_injury_status",
        "player_injury_designation",
        "teammate_absence_boost",
        "next_man_up_role"
      ],
      "notes": "Projection timestamps must be compared against official inactives."
    },
    {
      "key": "team_context",
      "label": "Team Context",
      "primary_provider": "derived from market, nflverse/nflreadpy, and baseline model inputs",
      "cadence": "pre-slate and publish-time refresh",
      "required_fields": [
        "team_context_available",
        "team_total",
        "spread",
        "game_total",
        "implied_team_points"
      ],
      "optional_fields": [
        "pace",
        "proe",
        "efficiency",
        "rest_days",
        "travel_spot",
        "off_bye"
      ],
      "notes": "Team totals can anchor context but cannot be the sole source of graded edge."
    },
    {
      "key": "matchup",
      "label": "Defensive Matchup",
      "primary_provider": "nflverse/nflreadpy plus optional PFF/FTN enrichment",
      "cadence": "weekly season-long and recent-window refresh",
      "required_fields": [
        "defensive_rank",
        "matchup_sample_games"
      ],
      "optional_fields": [
        "sos_adjusted",
        "defensive_injury_matchup_delta",
        "opponent_secondary_injury_count"
      ],
      "notes": ""
    },
    {
      "key": "weather",
      "label": "Weather",
      "primary_provider": "weather API",
      "cadence": "daily, then same-day window refresh",
      "required_fields": [
        "game_id",
        "wind_speed",
        "wind_direction",
        "precipitation",
        "temperature"
      ],
      "optional_fields": [
        "roof_status",
        "field_condition"
      ],
      "notes": "Wind is the primary NFL weather input, especially for passing and kicking context."
    }
  ]
}