From df8e2924e2e08decf4b6615aae6e8ed574b1659c Mon Sep 17 00:00:00 2001 From: OpenCow Date: Thu, 23 Jul 2026 12:37:24 +0200 Subject: [PATCH] fix: stop working-set metadata retry storm --- PotassiumProviderCore/KDriveModels.swift | 7 ++++++- doc/FILE_PROVIDER_LIFECYCLE.md | 9 +++++++-- .../PotassiumFileProviderExtension.swift | 11 ++++++++++- potassiumProviderTests/potassiumProviderTests.swift | 4 ++++ 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/PotassiumProviderCore/KDriveModels.swift b/PotassiumProviderCore/KDriveModels.swift index 270488c..312862d 100644 --- a/PotassiumProviderCore/KDriveModels.swift +++ b/PotassiumProviderCore/KDriveModels.swift @@ -514,6 +514,7 @@ public enum KDriveConflictFilename { public enum KDriveItemIdentifier: Equatable, Hashable, Sendable { case root + case workingSet case trash case item(Int) @@ -521,6 +522,8 @@ public enum KDriveItemIdentifier: Equatable, Hashable, Sendable { switch rawValue { case "NSFileProviderRootContainerItemIdentifier": self = .root + case "NSFileProviderWorkingSetContainerItemIdentifier": + self = .workingSet case "NSFileProviderTrashContainerItemIdentifier": self = .trash default: @@ -543,7 +546,7 @@ public enum KDriveItemIdentifier: Equatable, Hashable, Sendable { switch self { case .root: return rootFileID - case .trash: + case .workingSet, .trash: return nil case .item(let id): return id @@ -554,6 +557,8 @@ public enum KDriveItemIdentifier: Equatable, Hashable, Sendable { switch self { case .root: return "NSFileProviderRootContainerItemIdentifier" + case .workingSet: + return "NSFileProviderWorkingSetContainerItemIdentifier" case .trash: return "NSFileProviderTrashContainerItemIdentifier" case .item(let id): diff --git a/doc/FILE_PROVIDER_LIFECYCLE.md b/doc/FILE_PROVIDER_LIFECYCLE.md index bdc704e..ae23c0f 100644 --- a/doc/FILE_PROVIDER_LIFECYCLE.md +++ b/doc/FILE_PROVIDER_LIFECYCLE.md @@ -48,12 +48,17 @@ Behavior: - `.rootContainer` returns a synthetic `FileProviderItem` from `ProviderDomainConfiguration`. -- Other identifiers are parsed as `KDriveItemIdentifier.item(fileID)`. +- `.workingSet` is a virtual enumeration container, not a kDrive item. Metadata + lookup returns `.noSuchItem` immediately without loading a runtime, making a + remote request, or recording an expected failure activity. +- Other identifiers are parsed as `KDriveItemIdentifier`; only identifiers with + a concrete kDrive file ID proceed to remote metadata lookup. - The extension calls `PotassiumKDriveService.item(...)`, which uses `KDriveService.getFile(...)`. - The result is wrapped as `FileProviderItem`. -SQLite: not touched. +SQLite: successful lookups do not update snapshots. The expected `.workingSet` +rejection is not written to the activity audit. ## `fetchContents` diff --git a/potassiumProviderFileProvider/PotassiumFileProviderExtension.swift b/potassiumProviderFileProvider/PotassiumFileProviderExtension.swift index d324797..cff7fa4 100644 --- a/potassiumProviderFileProvider/PotassiumFileProviderExtension.swift +++ b/potassiumProviderFileProvider/PotassiumFileProviderExtension.swift @@ -85,6 +85,14 @@ public final class PotassiumFileProviderExtension: NSObject, NSFileProviderRepli lifecycle.start { lifecycle in var runtime: FileProviderRuntime? do { + if identifier == .workingSet { + FileProviderLog.replicatedExtension.debug("working set is a virtual enumeration container; return noSuchItem for metadata lookup") + await lifecycle.finish(markProgressComplete: false) { + completionHandler(nil, NSFileProviderError(.noSuchItem)) + } + return + } + let loadedRuntime = try await FileProviderRuntime.load(domain: self.domain) runtime = loadedRuntime if identifier == .rootContainer { @@ -640,7 +648,8 @@ public final class PotassiumFileProviderExtension: NSObject, NSFileProviderRepli if parentIdentifier == .rootContainer { return runtime.configuration.rootFileID } - guard parentIdentifier != .trashContainer else { + guard parentIdentifier != .trashContainer, + parentIdentifier != .workingSet else { throw NSFileProviderError(.cannotSynchronize) } return try KDriveItemIdentifier(rawValue: parentIdentifier.rawValue).fileID(rootFileID: runtime.configuration.rootFileID) diff --git a/potassiumProviderTests/potassiumProviderTests.swift b/potassiumProviderTests/potassiumProviderTests.swift index 00dfd0a..60869d6 100644 --- a/potassiumProviderTests/potassiumProviderTests.swift +++ b/potassiumProviderTests/potassiumProviderTests.swift @@ -1138,11 +1138,15 @@ struct PotassiumProviderCoreTests { @Test func itemIdentifierParsesFileProviderAndKDriveValues() throws { #expect(try KDriveItemIdentifier(rawValue: "NSFileProviderRootContainerItemIdentifier") == .root) + #expect(try KDriveItemIdentifier(rawValue: "NSFileProviderWorkingSetContainerItemIdentifier") == .workingSet) #expect(try KDriveItemIdentifier(rawValue: "NSFileProviderTrashContainerItemIdentifier") == .trash) #expect(try KDriveItemIdentifier(rawValue: "123") == .item(123)) + #expect(KDriveItemIdentifier.workingSet.rawValue == "NSFileProviderWorkingSetContainerItemIdentifier") #expect(KDriveItemIdentifier.item(456).rawValue == "456") #expect(KDriveItemIdentifier.root.fileID == ProviderConstants.defaultRootFileID) #expect(KDriveItemIdentifier.root.fileID(rootFileID: 999) == 999) + #expect(KDriveItemIdentifier.workingSet.fileID == nil) + #expect(KDriveItemIdentifier.trash.fileID == nil) #expect(throws: KDriveItemIdentifierError.invalid("not-a-number")) { try KDriveItemIdentifier(rawValue: "not-a-number") }