Files
Sportstime/docs/stadiumGuide.md
Trey t dbb0099776 chore: remove scraper, add docs, add marketing-videos gitignore
- Remove Scripts/ directory (scraper no longer needed)
- Add themed background documentation to CLAUDE.md
- Add .gitignore for marketing-videos to prevent node_modules tracking

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 18:13:12 -06:00

12 KiB
Raw Permalink Blame History

Team-First Planning Mode Implementation Plan

Overview

Feature: Select teams (not dates), find optimal trip windows across the entire season.

Use Case: User has 3 stadiums left on bucket list. Select those 3 teams, app finds all windows throughout the season where all 3 teams play at home within a reasonable trip duration.

Requirements

Constraint Value
Sport scope Single sport per search
Start city Flexible (algorithm picks optimal)
Max duration teams × 2 days (3 teams = 6 days)
Drive limit Existing settings value
Optimize for Shortest duration + minimal backtracking
Results Top 10 options with reasoning
Season Current season only

Architecture

Current Flow (Date-First)

User picks dates → Fetch games in range → Find routes → Rank → Display

New Flow (Team-First)

User picks teams → Fetch ALL home games for those teams (full season)
→ Generate sliding windows (N-day chunks)
→ Filter to windows where all teams have ≥1 home game
→ For each valid window: find optimal routes
→ Rank by duration + miles → Return top 10

Reuse Ratio: ~90%

Component Status Notes
allGames(for sports:) Exists Full season fetch
Sliding window generator Exists In ScenarioBPlanner
GameDAGRouter Exists Supports anchor constraints
ItineraryBuilder Exists No changes needed
Max driving time Exists UserPreferences.maxDrivingHoursPerDriver
Multi-team selection UI New Currently single-team only
Team-first planner New ~300 lines adapting Scenario B
Window optimization ⚠️ Adapt Season = 180+ days → need sampling

Parallel Track Structure

┌─────────────────────────────────────────────────────────────────────────────┐
│                              PHASE 1 (Parallel)                              │
├──────────────────────┬──────────────────────┬──────────────────────────────┤
│   Track A: Models    │   Track B: Planner   │   Track C: UI Components     │
│   (Agent 1)          │   (Agent 2)          │   (Agent 3)                  │
├──────────────────────┼──────────────────────┼──────────────────────────────┤
│ A1. Add teamFirst    │ B1. Create           │ C1. Create TeamPickerView    │
│     to PlanningMode  │     ScenarioEPlanner │     (multi-select grid)      │
│                      │     skeleton         │                              │
│ A2. Add              │ B2. Implement        │ C2. Create                   │
│     selectedTeamIds  │     window generator │     TeamFirstWizardStep      │
│     to TripPrefs     │     for teams        │                              │
│                      │                      │                              │
│ A3. Update           │ B3. Implement route  │ C3. Update WizardViewModel   │
│     PlanningRequest  │     finding with     │     for teamFirst mode       │
│     computed props   │     team anchors     │                              │
└──────────────────────┴──────────────────────┴──────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                              PHASE 2 (Parallel)                              │
├─────────────────────────────────┬───────────────────────────────────────────┤
│   Track D: Integration          │   Track E: Tests                          │
│   (Agent 4)                     │   (Agent 5)                               │
├─────────────────────────────────┼───────────────────────────────────────────┤
│ D1. Update ScenarioPlannerFactory│ E1. Unit tests for window generator     │
│     to detect teamFirst mode    │                                           │
│                                 │ E2. Unit tests for ScenarioEPlanner       │
│ D2. Wire TeamPickerView into    │                                           │
│     existing wizard flow        │ E3. Integration test: 3 teams → routes    │
│                                 │                                           │
│ D3. Add teamFirst option to     │ E4. Edge case tests (no windows, etc.)    │
│     planning mode selector      │                                           │
└─────────────────────────────────┴───────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                         PHASE 3 (Sequential)                                 │
├─────────────────────────────────────────────────────────────────────────────┤
│ F1. End-to-end testing & bug fixes                                          │
│ F2. Performance optimization (if needed)                                     │
└─────────────────────────────────────────────────────────────────────────────┘

Detailed Task Specifications

Track A: Data Models (Agent 1)

A1. Add teamFirst to PlanningMode

File: SportsTime/Core/Models/Domain/TripPreferences.swift
Action: Add case to PlanningMode enum

A2. Add selectedTeamIds to TripPreferences

File: SportsTime/Core/Models/Domain/TripPreferences.swift
Action: Add `var selectedTeamIds: Set<String> = []`
Action: Add `var teamFirstMaxDays: Int` computed as `selectedTeamIds.count * 2`

A3. Update PlanningRequest computed properties

File: SportsTime/Planning/Models/PlanningModels.swift
Action: Add computed property to extract teams for selected IDs
Action: Add helper to get all home games for selected teams

Track B: Scenario Planner (Agent 2)

B1. Create ScenarioEPlanner skeleton

File: SportsTime/Planning/Scenarios/ScenarioEPlanner.swift (NEW)
Action: Create class conforming to ScenarioPlanner protocol
Action: Implement required methods with TODOs

B2. Implement window generator for teams

File: SportsTime/Planning/Scenarios/ScenarioEPlanner.swift
Action: Generate all N-day windows across season
Action: Filter to windows where ALL selected teams have ≥1 home game
Action: Cap at 50 windows (sample if more)

B3. Implement route finding with team anchors

File: SportsTime/Planning/Scenarios/ScenarioEPlanner.swift
Action: For each valid window, collect all home games for selected teams
Action: Pass to GameDAGRouter with those games as anchors
Action: Build itineraries, rank by duration + miles
Action: Return top 10

Track C: UI Components (Agent 3)

C1. Create TeamPickerView

File: SportsTime/Features/Trip/Views/TeamPickerView.swift (NEW)
Action: Grid of team logos/names for selected sport
Action: Multi-select with checkmarks
Action: Show count badge "3 selected"
Action: Binding to Set<String> for team IDs

C2. Create TeamFirstWizardStep

File: SportsTime/Features/Trip/Views/Wizard/TeamFirstWizardStep.swift (NEW)
Action: Wrapper that uses TeamPickerView
Action: Validation: require ≥2 teams selected
Action: "Find trips for these teams" CTA

C3. Update WizardViewModel for teamFirst mode

File: SportsTime/Features/Trip/ViewModels/TripWizardViewModel.swift
Action: Add teamFirst step configuration
Action: Handle selectedTeamIds state
Action: Skip date selection step when in teamFirst mode

Track D: Integration (Agent 4)

D1. Update ScenarioPlannerFactory

File: SportsTime/Planning/Scenarios/ScenarioPlannerFactory.swift
Action: Add detection for teamFirst mode
Action: Return ScenarioEPlanner when selectedTeamIds.count >= 2

D2. Wire TeamPickerView into wizard

File: SportsTime/Features/Trip/Views/Wizard/TripWizardView.swift
Action: Add case for teamFirst step
Action: Navigation flow: Sport → Teams → (skip dates) → Results

D3. Add teamFirst to mode selector

File: SportsTime/Features/Trip/Views/Wizard/PlanningModeSelector.swift
Action: Add "Teams First" option with icon
Action: Description: "Pick teams, we'll find the best windows"

Track E: Tests (Agent 5)

E1. Unit tests for window generator

File: SportsTimeTests/Planning/ScenarioEPlannerTests.swift (NEW)
Tests:
- 3 teams, 6-day window → finds valid windows
- Window with only 2 of 3 teams → excluded
- Empty season → returns empty
- Sampling works when >50 windows

E2. Unit tests for ScenarioEPlanner

File: SportsTimeTests/Planning/ScenarioEPlannerTests.swift
Tests:
- Returns PlanningResult with routes
- All routes include all selected teams
- Routes sorted by duration ascending
- Respects max driving time constraint

E3. Integration test

File: SportsTimeTests/Planning/TeamFirstIntegrationTests.swift (NEW)
Tests:
- Full flow: 3 MLB teams → top 10 routes
- Each route visits all 3 stadiums
- Total duration ≤ 6 days

E4. Edge case tests

File: SportsTimeTests/Planning/ScenarioEPlannerTests.swift
Tests:
- Teams with no overlapping games → graceful error
- Single team selected → validation error
- Teams in same city → treated as separate stops

Risks & Mitigations

Risk Mitigation
Season = 180+ days = too many windows Sample every 2nd day, or limit to weekends
No valid windows (teams never align) Early validation + user feedback
Slow computation (10+ windows × routing) Cap at 50 windows, parallelize
Teams in same city (e.g., 2 NYC teams) Treat as separate stops (different stadiums)

Test Strategy

  1. Unit: Window generator finds correct multi-team windows
  2. Unit: Anchor constraints include all teams' home games
  3. Integration: 3 teams → returns routes visiting all 3
  4. Edge: Teams with non-overlapping seasons → graceful "no trips found"
  5. Perf: Full MLB season (2,430 games) completes in <5s

Estimated Effort

Track New Code Modified Code Total
A: Models 50 lines 20 lines 70 lines
B: Planner 300 lines 0 lines 300 lines
C: UI 250 lines 50 lines 300 lines
D: Integration 30 lines 80 lines 110 lines
E: Tests 400 lines 0 lines 400 lines
Total 1,030 lines 150 lines 1,180 lines