---
title: Anchor the offseason gate to the constitutional Offseason
type: task
status: completed
description: >-
  is_offseason was week === 0, which asks whether the week counter wrapped rather than which
  constitutional period it is. It is now !is_regular_season, matching Article II at both ends, with
  all five raw period comparisons swept onto the getter.
base_uri: user:task/league/anchor-offseason-gate-to-the-constitutional-offseason.md
created_at: '2026-09-10T16:17:31.208Z'
entity_id: 7ec2fd97-98a7-4759-a1a3-391e0f5d2722
finished_at: '2026-09-10T20:17:16.938Z'
observations:
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
owner_identity_uri: █████████████████████████
priority: Medium
public_read: true
tags:
  - user:tag/league-xo-football.md
updated_at: '2026-09-10T20:17:16.938Z'
visibility_analyzed_at: '2026-11-05T23:48:22.422Z'
---

Article II partitions the year into exactly two periods. Regular Season runs from 12:00 AM EST on the first Tuesday of NFL Week 1 to the resolution of the LEAGUE CHAMPIONSHIP, and includes the post-season. Offseason is everything else.

`is_offseason` was `week === 0`, which asks a different question: whether the week counter has WRAPPED. It has not wrapped after the championship. `week` is a `diff(regular_season_start, 'weeks')` against an anchor set by hand in `season-dates.mjs`, so past the championship it keeps climbing and returns to 0 only when a human bumps that file for the next season.

`is_regular_season` was already constitutionally exact at both ends, which is what made the fix a one-liner. `week > 0` is the first Tuesday of NFL Week 1 (`regular_season_start + 1 week`, the invariant documented at `season.mjs` and pinned by `test/season.spec.mjs` against `opening_day`). `week <= final_week` is the resolution of the LEAGUE CHAMPIONSHIP, with the fantasy post-season inside the range as Article II requires. So `is_offseason` is now `!this.is_regular_season` and no second getter was added.

## What the divergence actually was

Measured against injected clocks on the 2026 constants: `is_offseason` read FALSE from the championship's resolution on 2027-01-05 until the anchor bump in late August — roughly eight months, not the narrow window originally assumed. `is_regular_season` is false there too, so that span belonged to neither period.

The original body claimed the postseason was constitutionally Regular Season and read false in both getters. That is inverted: past `final_week` the championship has resolved, so the span is constitutionally Offseason. The fantasy post-season (weeks `wildcard_week` through `final_week`) already read `is_regular_season` true.

Three consumers were live-broken across the dead zone: `import-nfl-coaches` could not take the offseason short-circuit its zero-rows guard depends on (signal #113494's own failure mode), the Sleeper importer's in-season-only monitors stayed armed all spring, and `get-players` never included the rookies its offseason branch names.

Amendment XLVIII made it a written-rule divergence: Article XIII Section 17 grants a reserve route "during the Offseason", gated in `is-reserve-eligible.mjs`.

## Plan

- [x] Determine whether the two boundaries diverge, using the `_now` injection rather than a wall-clock run. They diverge at the END only, by about eight months.
- [x] Move `is_offseason` itself rather than adding a constitutionally-anchored near-synonym beside it.
- [x] Sweep every raw `week === 0` comparison onto the getter: `is-reserve-eligible.mjs`, `submit-reserve.mjs`, `get-players.mjs`, and both `api/routes/teams/tag.mjs` guards.
- [x] Pin both boundaries with specs sampling more than one instant — `is_offseason is the complement of is_regular_season at every instant` walks 57 injected clocks across a full year. Negative control run: restoring `week === 0` fails it at week offset 18.

## Two sites that did NOT move, and why

- `app/views/pages/stats/stats.js` keeps its raw `week === 0`. It asks about DATA AVAILABILITY, not the period. Substituting `is_offseason` would blank a fully-played season for the five weeks between the championship and the Super Bowl, since `year` does not flip until `end`.
- `libs-server/get-top-practice-squad-waiver.mjs` had to move the other way. It gated its `nfl_games` join on `!is_offseason` and so covered the NFL postseason only by accident of the old definition — coverage `test/waiver-top-practice-squad.spec.mjs` had since pinned deliberately. Its question is the NFL calendar's, not the constitution's, so both gates were removed rather than re-spelled. Behaviour-preserving: the join is a LEFT join and the kickoff filter already treats a null date as "no game, keep the row".
