- 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>
284 lines
12 KiB
Markdown
284 lines
12 KiB
Markdown
# 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** |
|