---
title: ████████████████ sync design-consistency cleanup
type: task
status: completed
description: >-
  Bundled design choices: dry-run output should populate validation counts (currently sparse);
  get_players default scope (currently global NFL catalog) is counterintuitive for league-scoped
  fetches. Each finding requires a design choice before implementation.
base_uri: user:task/league/external-leagues-sync-design-cleanup.md
created_at: '2026-05-08T01:20:27.872Z'
entity_id: 513b5fb3-1ee7-4527-8d6f-f4954c7297cc
finished_at: '2026-05-09T03:55:26.492Z'
owner_identity_uri: █████████████████████████
priority: Low
public_read: true
relations:
  - follows [[user:task/github/mistakia/league/162-import-and-sync-external-leagues.md]]
tags:
  - user:tag/league-xo-football.md
updated_at: '2026-05-09T03:55:26.492Z'
visibility_analyzed_at: '2026-11-05T23:49:15.744Z'
---

## Context

Two design-consistency findings surfaced during post-merge validation of mistakia/league#162. Bundled here because each requires a small design choice rather than mechanical implementation; the mechanic of changing the code is trivial once the choice is made.

## Findings

### A. Dry-run output is sparse

`SyncOrchestrator._perform_validation_sync` (`libs-server/external-fantasy-leagues/sync/sync-orchestrator.mjs:538-619`) only fetches `league_config` and returns success/duration. The CLI's `format_summary` (`scripts/sync-external-league.mjs:71-95`) shows blank `sync_stats` (no players_mapped, transactions_imported, rosters_updated). For a `--dry-run` flag whose stated purpose in the original implementation plan is "manual testing", the output should at least include: count of teams in the config, presence of expected sections, mapper-readiness validation.

**Two design choices:**

1. **Expand `_perform_validation_sync`**: Have it call `fetch_league_data` (which already does the full read-only fetch) plus a mapper dry-run, and report counts. Single CLI flag (`--dry-run`).
2. **Split into two flags**: `--dry-run` (cheap connectivity check, current behavior, renamed to make it explicit) + `--validate` (full pipeline against fetched data, no DB writes). Two CLI surfaces with clear contracts.

Recommend (1). The user-facing `--dry-run` semantics are "do everything except write" — the current implementation is closer to a `--ping` and surprises users.

### B. `get_players` returns the entire NFL catalog

By design, `SleeperAdapter.get_players` returns all 12067 NFL players from `/players/nfl`. This is correct for the Sleeper endpoint — Sleeper exposes a global catalog, not a league-scoped roster. But a user calling `fetch_league_data({ external_league_id })` reasonably expects league-scoped data; receiving the global catalog is counterintuitive and inflates dry-run output by ~18 MB.

**Three design choices:**

1. **Filter to rostered players**: `fetch_league_data` intersects `players` with the player_ids appearing in fetched rosters. League-scoped output by default.
2. **Tri-state include flag**: Replace boolean `include_players` with `'rostered' | 'all' | false`. Default `'rostered'`. Explicit for callers who actually want the global catalog.
3. **Document the global behavior**: Keep current shape, add a prominent note in `docs/fixture-maintenance.md` and CLI `--help` that `players` is the platform-global catalog, not league-scoped.

Recommend (2). Tri-state default-`rostered` matches the principle of least surprise without removing the global-catalog capability (some callers genuinely need the full set, e.g., for player-mapping seed runs).

## Out of Scope (Tracked Elsewhere)

- TransactionMapper add/drop bug: [[████████████████████-███████-███████████-██████-███-████-████████████]]
- Multi-week transaction iteration: [[user:task/league/sleeper-multi-week-transaction-iteration.md]]
- create_standardized_output input/output naming: addressed inline in the same session that filed this task.

## Acceptance Criteria

For (A):

- `--dry-run` invocation surfaces non-empty validation summary including team count, roster count, transaction count (post-multi-week-iter), player count.
- `_perform_validation_sync` (or replacement) does NOT write to the DB. Verify by inspection of sync_stats and absence of write-path code in the dry-run path.
- Test in `test/external-fantasy-leagues-sync-orchestrator.spec.mjs` asserts dry-run output shape includes the expected validation counts.

For (B):

- Decision recorded in this task body before implementation.
- If (B2): `include_players` accepts `'rostered'`, `'all'`, `false` (and `true` as backward-compatible alias for `'all'` if needed). Default `'rostered'`.
- `fetch_league_data` output `players` array reflects the chosen filter.
- CLI `--help` documents the option.
- Test asserts default behavior returns intersection-with-rosters and `'all'` returns the full catalog.

## Decision (recorded 2026-05-08)

- **A — Expand `_perform_validation_sync`** (option A1): route the dry-run path through `fetch_league_data` so it produces the same canonical shape as a real sync, just without DB writes. Report team count, roster count, transaction count, player count in the output `validation` block. Single CLI flag (`--dry-run`) with full read-only semantics.
- **B — Tri-state `include_players`** (option B2): accept `'rostered'` | `'all'` | `false`, defaulting to `'rostered'`. Boolean `true` is preserved as a backward-compatible alias for `'all'`. `fetch_league_data` intersects the global player catalog with player IDs appearing in the fetched rosters when `'rostered'` is in effect.

Both rationales follow the recommendations above (least surprise, preserves the global-catalog escape hatch).

## Notes

- These two findings touch the same files (orchestrator + CLI) but are independent in semantics; landing as a single change.
