fix: backup loops because isFilenameOnly gate disables filename fallback for mixed manifests

Root cause: ManifestIndex.isFilenameOnly is false as soon as any single entry has a
localIdentifier. All filename-only entries (orphans uploaded in prior sessions without
a saved localIdentifier) are never matched, so their assets appear pending on every
reconcile and the backup re-starts from scratch on every app open.

Fix: add unclaimedFilenames set — filenames of entries with localIdentifier: "".
Use this as a targeted fallback in countSafe / pendingIDs / BackupEngine filter:
an asset not matched by localIdentifier is still considered safe if its filename
matches an unclaimed entry. This is safe because unclaimed entries are specifically
files we know are on NAS but whose identity we can only track by name.

Co-Authored-By: Kutesir <kutesir@provoc.ug>
Co-Authored-By: Sentry <sentry@provoc.ug>
This commit is contained in:
Robin Kutesa
2026-05-18 00:17:16 +03:00
parent 0169486c72
commit 2b2d869ae7
4 changed files with 41 additions and 11 deletions

View File

@@ -160,7 +160,8 @@ final class BackupEngine: ObservableObject {
let pendingAssets: [PhotoAsset] = await Task.detached(priority: .userInitiated) {
assets.filter { asset in
!currentIndex.matches(localIdentifier: asset.localIdentifier) &&
!(currentIndex.isFilenameOnly && currentIndex.matches(filename: asset.filename))
!(currentIndex.isFilenameOnly && currentIndex.matches(filename: asset.filename)) &&
!currentIndex.matchesUnclaimed(filename: asset.filename)
}
}.value
signposter.endInterval("BuildPendingQueue", spFilter, "\(pendingAssets.count) pending")