- 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>
12 KiB
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
- Unit: Window generator finds correct multi-team windows
- Unit: Anchor constraints include all teams' home games
- Integration: 3 teams → returns routes visiting all 3
- Edge: Teams with non-overlapping seasons → graceful "no trips found"
- 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 |