Reconcile starter seeds on connect and add restore + duplicate cleanup
Seeding now covers existing installs, not just empty containers: after the same settle delay as auto-seed, connect() branches to reconcileSeeds(), driven by a pure tested planner — upgrade an older seed revision in place (safe: clone-on-edit guarantees no user content at seed ULIDs), skip up-to-date/quarantined files, respect delete-veto stubs, and write missing seeds unless a same-name legacy split exists. Settings gains Restore Starter Splits (the one deliberate veto lift, writing current bundle bytes) and a dev duplicate-cleanup tool backed by a fail-closed scanner. The HealthKit estimate now follows the clone redirect when resolving a workout's split. Claude-Session: https://claude.ai/code/session_01LEoff8bXGBS83tK1c55Mf7
This commit is contained in:
@@ -1,32 +0,0 @@
|
||||
import Foundation
|
||||
import SwiftData
|
||||
|
||||
/// The bundled immutable starter-split library, brought back on demand. The seeds
|
||||
/// themselves — fixed ULIDs, byte-canonical bundle JSON — live in `SeedLibrary`;
|
||||
/// editing one clones it and permanently tombstones the seed (see
|
||||
/// `SyncEngine.save(split:)`). The true first-run case is handled automatically by
|
||||
/// `SyncEngine.autoSeedIfEmpty`; this on-demand path (the "Add Starter Splits"
|
||||
/// button) is for a user who wants the starters back after removing some.
|
||||
enum SplitSeeder {
|
||||
/// Add every starter split the user doesn't already have. For each seed:
|
||||
/// • skip if a live split with the same NAME exists — a user's edited clone of
|
||||
/// "Upper Body" must not be joined by a resurrected seed of the same name;
|
||||
/// • else if the seed was deleted (tombstoned), restore it (lifting the veto);
|
||||
/// • else write it fresh.
|
||||
/// Seed `order` values are fixed (0–3); a collision with a user split's order is
|
||||
/// an accepted cosmetic tie. Idempotent against double-taps and partial prior seeds.
|
||||
@MainActor
|
||||
static func seedDefaults(into context: ModelContext, using sync: SyncEngine) async {
|
||||
let existing = (try? context.fetch(FetchDescriptor<Split>())) ?? []
|
||||
let existingNames = Set(existing.map(\.name))
|
||||
|
||||
for seed in SeedLibrary.seeds {
|
||||
if existingNames.contains(seed.doc.name) { continue }
|
||||
if await sync.isTombstoned(id: seed.id) {
|
||||
await sync.restoreSeed(seed)
|
||||
} else {
|
||||
await sync.writeSeed(seed)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user