fix: 12 planning engine bugs + App Store preview export at 886x1920

Planning engine fixes (from adversarial code review):
- Bug #1: sortByLeisure tie-breaking uses totalDrivingHours
- Bug #2: allDates/calculateRestDays guard-let-break prevents infinite loop
- Bug #3: same-day trip no longer rejected (>= in dateRange guard)
- Bug #4: ScenarioD rationale shows game count not stop count
- Bug #5: ScenarioD departureDate advanced to next day after last game
- Bug #6: ScenarioC date range boundary uses <= instead of <
- Bug #7: DrivingConstraints clamps maxHoursPerDriverPerDay via max(1.0,...)
- Bug #8: effectiveTripDuration uses inclusive day counting (+1)
- Bug #9: TripWizardViewModel validates endDate >= startDate
- Bug #10: allDates() uses min/max instead of first/last for robustness
- Bug #12: arrivalBeforeGameStart accounts for game end time at departure
- Bug #15: ScenarioBPlanner replaces force unwraps with safe unwrapping

Tests: 16 regression test suites + updated existing test expectations
Marketing: Remotion canvas set to 886x1920 for App Store preview spec

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-02-15 17:08:50 -06:00
parent b320a773aa
commit 787a0f795e
14 changed files with 820 additions and 27 deletions

View File

@@ -316,16 +316,16 @@ final class ScenarioBPlanner: ScenarioPlanner {
// First window: last selected game is on last day of window
// Window end = lastGameDate + 1 day (to include the game)
// Window start = end - duration days
let firstWindowEnd = Calendar.current.date(
guard let firstWindowEnd = Calendar.current.date(
byAdding: .day,
value: 1,
to: lastGameDate
)!
let firstWindowStart = Calendar.current.date(
) else { return [] }
guard let firstWindowStart = Calendar.current.date(
byAdding: .day,
value: -duration,
to: firstWindowEnd
)!
) else { return [] }
// Last window: first selected game is on first day of window
// Window start = firstGameDate
@@ -334,21 +334,22 @@ final class ScenarioBPlanner: ScenarioPlanner {
// Slide from first window to last window
var currentStart = firstWindowStart
while currentStart <= lastWindowStart {
let windowEnd = Calendar.current.date(
guard let windowEnd = Calendar.current.date(
byAdding: .day,
value: duration,
to: currentStart
)!
) else { break }
let window = DateInterval(start: currentStart, end: windowEnd)
dateRanges.append(window)
// Slide forward one day
currentStart = Calendar.current.date(
guard let nextStart = Calendar.current.date(
byAdding: .day,
value: 1,
to: currentStart
)!
) else { break }
currentStart = nextStart
}
return dateRanges