---
title: Declare playoff formats for re-seeded and bracket-less mirrors
type: task
status: completed
description: >-
  Seven 2026 mirrors hold a NULL playoff_format_kind and fail process_playoffs each finalize-week:
  five Sleeper re-seeded brackets, one Sleeper league serving no bracket, one ESPN field of 6.
  Declare a format where the vendor supports it and refuse cleanly otherwise.
base_uri: user:task/league/declare-playoff-formats-for-reseeded-and-bracketless-mirrors.md
created_at: '2026-10-27T14:19:50.224Z'
entity_id: 96e38a6a-02bd-4072-88dd-194e340d267d
finished_at: '2026-11-03T08:04:50.262Z'
observations:
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
owner_identity_uri: █████████████████████████
priority: High
public_read: true
relations:
  - follows [[██████████████████████-████████-██████-██████-█████████-███████-█████████]]
  - follows [[█████████████████████-███████-██████████-██████████-███-████████-██████████]]
started_at: '2026-10-27T15:14:47.582Z'
tags:
  - user:tag/league-xo-football.md
  - user:tag/software-task.md
updated_at: '2026-11-03T08:04:50.262Z'
visibility_analyzed_at: '2026-11-05T23:49:06.993Z'
---

## Overview

- Give every 2026 external-league mirror a declared playoff format wherever the vendor states one, and turn the rest into a designed refusal instead of a weekly failure.
- Impact: the seven `process_playoffs` failures (lids 130, 132, 133, 134, 136, 137, 138) leave finalize-week. Five re-seeded Sleeper mirrors and one Sleeper league with playoffs off get playoff forecasts and postseason finishes. The ESPN field-of-6 mirror is reported as refused, with its reason, rather than failing.
- Ordering: this follows [[██████████████████████-████████-██████-██████-█████████-███████-█████████]] and corrects a finding on [[█████████████████████-███████-██████████-██████████-███-████████-██████████]]. It must land before NFL week 15 (mid-December 2026), where these mirrors' Sleeper playoffs start, for finishes and forecasts to be right this season. A finalize-week fully green also needs it.

## Background

Measured 2026-10-27 from the latest mirror syncs (`external_league_import_jobs.results` errors), `season_playoff_matchups`, and the Sleeper public API.

- **Sleeper re-seeded brackets — lids 130, 132, 133, 136, 138.** Each serves `playoff_seed_type` 1 with a field of 6. The unplayed 2026 round-2 rows name the bye team and leave the opponent slot empty with no `t1_from`/`t2_from`. The 5th-place row has no feeders either. `resolve_bracket_seed_slots` (`libs-server/simulation/resolve-single-elimination.mjs`) refuses "seeds its away slot directly but the slot covers seed(s) 5 as well", and the bracket sync withholds the claim.
- **Sleeper with playoffs off — lid 137.** `playoff_week_start` 0, `playoff_teams` 6, and `/winners_bracket` returns null. The sync logs "served no bracket" and leaves the kind NULL, with no error recorded.
- **ESPN field of 6 — lid 134.** `playoff-format-derivation-sync.mjs` refuses "only a field of 4 has been observed played on ESPN".
- **Root cause:** the earlier 400-bracket probe accepted a later-round slot holding "a from-provenance or a bye team". In a PLAYED re-seeded bracket, Sleeper fills round 2 with both teams and no feeders, which passes that test, so re-seeding read as a fixed tree. (Inferred from lid 130's played 2025 bracket and unplayed 2026 bracket; the earlier probe was not re-run.)
- `process_playoffs` (`scripts/process-playoffs.mjs:88`) throws a plain Error on a NULL kind, while the forecast's `resolve_playoff_format` (`libs-server/simulation/playoff-format.mjs:363`) already raises `DerivedDataRefusal` for the same NULL.
- `calculate_careerlogs_138` also failed in run 1791314. It is a separate finalize-week step from `process_playoffs`, and a write-stubbed run on 2026-10-27 14:xx completed without error, so it is not in scope. Re-check it on the 2026-11-03 run.
- Prior work, all Completed: [[██████████████████████-████████-██████-██████-█████████-███████-█████████]], [[█████████████████████-███████-██████████-██████████-███-████████-██████████]], [[user:task/league/import-sleeper-playoff-seeding-for-bracket-mirrors.md]], [[███████████████████████-█-███████-██████-███-████-██████████]].

### Assumptions

- `playoff_seed_type` 1 means re-seeding: checked, 40 of 40 sampled complete 2025 field-of-6 leagues had feederless round 2, and seed 1 met the lowest remaining winner in 39 of 39 seedable ones (seeds by wins then points for). `playoff_seed_type` 0 had linked feeders in 40 of 40.
- `playoff_week_start` 0 means no postseason: checked, 43 of 43 complete 2025 Sleeper leagues with it serve a null winners bracket.
- Re-seeding was measured only for a field of 6 with two byes; other fields with `playoff_seed_type` 1 are unchecked and refuse until measured.
- `calculate_bracket_postseason_finishes` needs no feeder links, only decided rows: unchecked (source read of its inputs only). The finishes item verifies it with a fixture.
- The bracket sync's format stamp can replace a config-written `no_postseason` once a league turns playoffs on: unchecked. The playoffs-off item verifies it.
- `has_playoff_reseeding` passes the boolean-prefix rule (`is_`/`has_`) in `db/tools/audit-schema-conformance.mjs`, matching `has_median_match`: to verify with the audit at the schema item.

## Design

### Re-seeding is a season setting, not a new kind

Add `seasons.has_playoff_reseeding boolean` (NULL = not established). `bracket` and `derived_bracket` keep their meaning as a single-elimination tree, held or derived. The flag states how later rounds are paired. A new `reseeded_bracket` kind would duplicate every consumer of `bracket`, and ESPN carries the same switch (`playoffReseed`).

- **Writer:** the config path, like `has_median_match`. The canonical settings already define `playoff_reseeding_enabled` (`schemas/canonical-league-format.json`), which the adapters deliberately stopped emitting because nothing had measured it. The Sleeper adapter now emits it from `playoff_seed_type` (1 true, 0 false, anything else null). The ESPN adapter emits it from `playoffReseed`. `map_season_format` writes it to `seasons.has_playoff_reseeding`. A hosted league is `false`.
- **Import:** the bracket mapper keeps a re-seeded bracket's later-round rows as served, with empty feeders. Sleeper fills their teams once round 1 is decided, and each sync re-imports them.
- **Seeding:** the bye count for a re-seeded tree is the field minus the round-1 participants, since a feederless slot no longer marks a bye.
- **Forecast:** when `has_playoff_reseeding` is true, `resolve_playoff_format` resolves the bracket from `playoff_team_count` and `bye_count` with re-seeding. Each round pairs the best remaining seed against the worst, with byes entering by seed. It does not read the imported tree. The fixed-tree path is unchanged. A NULL flag on a `bracket` or `derived_bracket` season is a `DerivedDataRefusal`, the same as a NULL kind.
- **Finishes:** `process_bracket_playoffs` places teams from decided rows, which carry teams once played, so re-seeding changes nothing there. The plan verifies this rather than assuming it.

### Sleeper with playoffs off

`map_season_format` declares `playoff_format_kind: 'no_postseason'` when the canonical `playoff_week_start` is 0. This is the same writer that declares an elimination league's `no_postseason`. Lid 137 resolves on its next sync.

### Undeclared mirror formats

`process_playoffs` raises `DerivedDataRefusal` on a NULL kind for a mirror, matching `resolve_playoff_format`. The premise transfers: a mirror's format comes from the vendor and the syncs, so a NULL there is a recorded fact about the input that recurs until the vendor or a measurement changes, not a fault in this run. A hosted league stamps its own format, so a NULL on a hosted league still throws.

### Out of scope

- Admitting ESPN fields other than 4. That waits for a played ESPN bracket to measure, on [[███████████████████████-█-███████-██████-███-████-██████████]]'s terms.
- Re-seeded fields other than 6.

### Knowledge Base Impact

- `COMMENT ON COLUMN seasons.has_playoff_reseeding`, and the `playoff_format_kind` comment names it.
- Supersede the "no reseeding to model" finding on [[█████████████████████-███████-██████████-██████████-███-████████-██████████]] in place.
- No new text entity, no scratch slug, and no orchestrator: the work is bounded to this task.

## Notes

- The lid 130 "results mid-season" question is settled: that bracket was its complete 2025 league. Every 2026 bracket probed is unplayed and projected from current standings.
- Probe used for the measurements: 40 random complete 2025 Sleeper leagues per `playoff_seed_type` with `playoff_teams` 6 and `playoff_week_start` not 0, plus every complete 2025 league with `playoff_week_start` 0, all from `external_leagues`. For each, fetch `/winners_bracket` and `/rosters`, then check round-2 feeders and whether seed 1 met the lowest remaining round-1 winner.

## Plan

### Schema

- [x] **Add `seasons.has_playoff_reseeding`**
  - Files: `private/db/adhoc/<date>-add-seasons-has-playoff-reseeding.sql`, `db/schema.postgres.sql`, `db/schema-types.d.ts`
  - Purpose: hold the re-seeding setting beside the format kind.
  - Changes: nullable boolean with a column comment; amend the `playoff_format_kind` comment. Follow `docs/guides/schema.md` for applying it and for `yarn export:schema`.
  - Verify: `base db query league "select column_name, is_nullable from information_schema.columns where table_name='seasons' and column_name='has_playoff_reseeding'"` returns one nullable row, and `node db/tools/audit-schema-conformance.mjs` reports no new finding.

### Import

- [x] **Sleeper adapter emits `playoff_reseeding_enabled`**
  - File: `libs-server/external-fantasy-leagues/adapters/sleeper.mjs`
  - Changes: map `playoff_seed_type` 1 to true, 0 to false, anything else to null. Replace the comment that says it is deliberately not emitted with the measurement it now rests on.
  - Verify: a spec in `test/external-fantasy-leagues.season-format.spec.mjs` covers 0, 1 and an unknown value.
- [x] **ESPN adapter emits `playoff_reseeding_enabled`**
  - File: `libs-server/external-fantasy-leagues/adapters/espn.mjs`
  - Changes: emit it from `playoffReseed`, null when absent.
  - Verify: spec case for true, false and absent.
- [x] **`map_season_format` writes the flag and declares playoffs off**
  - File: `libs-server/external-fantasy-leagues/mappers/league-config-mapper.mjs`
  - Changes: emit `has_playoff_reseeding` from the canonical setting. Emit `playoff_format_kind: 'no_postseason'` when `playoff_week_start` is 0. Otherwise leave the kind to the bracket and derivation syncs, as today.
  - Verify: `test/external-fantasy-leagues.season-format.spec.mjs` covers `playoff_week_start` 0, 15 and null. A sync spec shows the bracket sync's stamp replaces `no_postseason` once a bracket is served (`test/external-fantasy-leagues.playoff-bracket-sync-writes.spec.mjs`).
- [x] **Bracket mapper and seeding accept a re-seeded tree**
  - Files: `libs-server/external-fantasy-leagues/mappers/playoff-bracket-mapper.mjs`, `libs-server/external-fantasy-leagues/adapters/sleeper-playoff-seeding.mjs`, `libs-server/external-fantasy-leagues/sync/playoff-bracket-sync.mjs`
  - Changes:
    - Map a re-seeded bracket's feederless later-round rows as served.
    - Count byes as the field minus the round-1 participants when the season re-seeds.
    - Claim `bracket` for a re-seeded field of 6, and refuse other re-seeded fields by name.
  - Verify: `test/external-fantasy-leagues-playoff-bracket-mapper.spec.mjs` and `test/external-fantasy-leagues.sleeper-playoff-seeding.spec.mjs` gain a fixture built from lid 130's 2026 bracket shape (unplayed) and its 2025 shape (played). Both claim `bracket` with a bye count of 2.
  - Landed differently: the mapper and the seeding adapter needed no change, since the mapper already keeps feederless rows and the bye count is structural. The re-seeded reading is `resolve_reseeded_advancement_rounds` in `playoff-format.mjs`, called by the sync, which also refuses a NULL flag. The lid 130 fixtures live in `test/external-fantasy-leagues.playoff-bracket-sync-writes.spec.mjs` and `test/simulation.playoff-format.spec.mjs`. A served bracket also retracts a config-written `no_postseason` when its claim is withheld.

### Forecast and finishes

- [x] **Re-seeded resolution in the forecast**
  - Files: `libs-server/simulation/playoff-format.mjs`, `libs-server/simulation/resolve-single-elimination.mjs`
  - Changes: when `has_playoff_reseeding` is true, resolve rounds from `playoff_team_count` and `bye_count`, pairing best remaining seed against worst each round. Refuse a NULL flag on `bracket` and `derived_bracket` with `DerivedDataRefusal`. The fixed-tree path is unchanged.
  - Verify: `test/simulation.playoff-format.spec.mjs` pins a field of 6 with two byes where the seed-6 upset sends seed 1 against seed 5 rather than the fixed tree's pairing. `test/simulation.season-forecast.spec.mjs` passes unchanged for a fixed-tree league.
  - The pinned case is seed 6 over seed 3 with seed 4 over seed 5: re-seeded, seed 1 meets seed 6, while the fixed tree sends it against seed 4. The two formats crown different champions on the same scores.
- [x] **Finishes from a re-seeded bracket**
  - File: `libs-server/calculate-bracket-postseason-finishes.mjs` (change only if the fixture fails)
  - Verify: a spec places finishes 1–6 from lid 130's played 2025 bracket rows.
- [x] **`process_playoffs` refuses a NULL mirror format**
  - File: `scripts/process-playoffs.mjs`
  - Changes: on a NULL kind, raise `DerivedDataRefusal` naming the mirror and the season when the league is not hosted; keep the throw for a hosted league.
  - Verify: `test/scripts.process-playoffs-format-dispatch.spec.mjs` covers a NULL mirror (refusal) and a NULL hosted league (error).

### Rollout

- [x] **Deploy and re-sync the seven mirrors**
  - Changes: deploy per [[██████████████████████████████-█████████]], then re-sync lids 130, 132, 133, 134, 136, 137 and 138 through the mirror sync scheduler.
  - Verify (population check, catching a fix that lands for only one bracket shape): `base db query league "select lid, playoff_format_kind, has_playoff_reseeding from seasons where season_year=2026 and lid between 119 and 139 order by lid"` shows every mirror except 134 with a non-NULL kind; 130, 132, 133, 136 and 138 read `bracket` with `has_playoff_reseeding` true; 137 reads `no_postseason`; every other Sleeper bracket mirror reads `has_playoff_reseeding` false.
  - Passed 2026-10-27 ~17:05Z after a deploy to 3f32bce82 and two re-sync passes over all 19 mirrors. The first pass withheld every Sleeper bracket claim, because the config sync's season write is deferred past the bracket sync. 89bd46769 hands the flag over in-run; it is pushed, and the next league deploy carries it.
- [x] **Finalize-week confirms**
  - Verify: the next finalize-week run's reason names no `process_playoffs_*` failure, and lid 134 appears as refused. `calculate_careerlogs_138` also passes, or its error is recorded here.
  - Passed 2026-11-03: finalize_week job 1822562 ran green; no `process_playoffs_*` failure; lid 134 (NULL kind mirror) reads as refused via `DerivedDataRefusal`; `calculate_careerlogs_138` passed.

### Knowledge Base Updates

- [x] **Supersede the "no reseeding to model" finding**
  - Entity: [[█████████████████████-███████-██████████-██████████-███-████████-██████████]]
  - Changes: replace that observation in place with the measured re-seeding finding and its root cause.
  - Verify: `base entity get` on that task shows the replacement and no longer shows the original.
