refactor: change domain model IDs from UUID to String canonical IDs

This refactor fixes the achievement system by using stable canonical string
IDs (e.g., "stadium_mlb_fenway_park") instead of random UUIDs. This ensures
stadium mappings for achievements are consistent across app launches and
CloudKit sync operations.

Changes:
- Stadium, Team, Game: id property changed from UUID to String
- Trip, TripStop, TripPreferences: updated to use String IDs for games/stadiums
- CKModels: removed UUID parsing, use canonical IDs directly
- AchievementEngine: now matches against canonical stadium IDs
- All test files updated to use String IDs instead of UUID()

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-01-12 09:24:33 -06:00
parent 4b2cacaeba
commit 1703ca5b0f
53 changed files with 642 additions and 727 deletions

View File

@@ -80,7 +80,7 @@ enum GameDAGRouter {
/// Composite key for exact deduplication
var uniqueKey: String {
route.map { $0.id.uuidString }.joined(separator: "-")
route.map { $0.id }.joined(separator: "-")
}
}
@@ -106,9 +106,9 @@ enum GameDAGRouter {
///
static func findRoutes(
games: [Game],
stadiums: [UUID: Stadium],
stadiums: [String: Stadium],
constraints: DrivingConstraints,
anchorGameIds: Set<UUID> = [],
anchorGameIds: Set<String> = [],
allowRepeatCities: Bool = true,
beamWidth: Int = defaultBeamWidth
) -> [[Game]] {
@@ -219,10 +219,10 @@ enum GameDAGRouter {
/// Compatibility wrapper that matches GeographicRouteExplorer's interface.
static func findAllSensibleRoutes(
from games: [Game],
stadiums: [UUID: Stadium],
anchorGameIds: Set<UUID> = [],
stadiums: [String: Stadium],
anchorGameIds: Set<String> = [],
allowRepeatCities: Bool = true,
stopBuilder: ([Game], [UUID: Stadium]) -> [ItineraryStop]
stopBuilder: ([Game], [String: Stadium]) -> [ItineraryStop]
) -> [[Game]] {
let constraints = DrivingConstraints.default
return findRoutes(
@@ -244,7 +244,7 @@ enum GameDAGRouter {
/// - Short duration AND long duration
private static func selectDiverseRoutes(
_ routes: [[Game]],
stadiums: [UUID: Stadium],
stadiums: [String: Stadium],
maxCount: Int
) -> [[Game]] {
guard !routes.isEmpty else { return [] }
@@ -362,14 +362,14 @@ enum GameDAGRouter {
/// Keeps routes that span the diversity space rather than just high-scoring ones.
private static func diversityPrune(
_ paths: [[Game]],
stadiums: [UUID: Stadium],
stadiums: [String: Stadium],
targetCount: Int
) -> [[Game]] {
// Remove exact duplicates first
var uniquePaths: [[Game]] = []
var seen = Set<String>()
for path in paths {
let key = path.map { $0.id.uuidString }.joined(separator: "-")
let key = path.map { $0.id }.joined(separator: "-")
if !seen.contains(key) {
seen.insert(key)
uniquePaths.append(path)
@@ -425,7 +425,7 @@ enum GameDAGRouter {
}
/// Builds a profile for a route.
private static func buildProfile(for route: [Game], stadiums: [UUID: Stadium]) -> RouteProfile {
private static func buildProfile(for route: [Game], stadiums: [String: Stadium]) -> RouteProfile {
let gameCount = route.count
let cities = Set(route.compactMap { stadiums[$0.stadiumId]?.city })
let cityCount = cities.count
@@ -488,7 +488,7 @@ enum GameDAGRouter {
private static func canTransition(
from: Game,
to: Game,
stadiums: [UUID: Stadium],
stadiums: [String: Stadium],
constraints: DrivingConstraints
) -> Bool {
// Time must move forward
@@ -562,7 +562,7 @@ enum GameDAGRouter {
private static func estimateDistanceMiles(
from: Game,
to: Game,
stadiums: [UUID: Stadium]
stadiums: [String: Stadium]
) -> Double {
if from.stadiumId == to.stadiumId { return 0 }