From 882c1d5d18bdee8810a2b5d6e92ae5ff65bc2074 Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Thu, 30 Apr 2026 17:35:33 +0600 Subject: [PATCH 1/8] feat: add FeatureGates struct with localHoldouts flag Default false to suppress local holdout logic until backend is ready. Co-Authored-By: Claude Opus 4.6 --- Sources/Utils/FeatureGates.swift | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Sources/Utils/FeatureGates.swift diff --git a/Sources/Utils/FeatureGates.swift b/Sources/Utils/FeatureGates.swift new file mode 100644 index 00000000..1f9da939 --- /dev/null +++ b/Sources/Utils/FeatureGates.swift @@ -0,0 +1,21 @@ +// +// Copyright 2026, Optimizely, Inc. and contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +struct FeatureGates { + static var localHoldouts = false +} From a725bd6b20892f5ecc422cf34bbc6663027de933 Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Thu, 30 Apr 2026 17:40:56 +0600 Subject: [PATCH 2/8] feat: guard local holdout evaluation behind FeatureGates.localHoldouts Both experiment rule and delivery rule local holdout checks are now gated. When false, these blocks are skipped entirely. Global holdout evaluation in getDecisionForFlag() is unaffected. Co-Authored-By: Claude Opus 4.6 --- .claude/settings.local.json | 14 + FeatureGates.d | 1 + FeatureGates.o | Bin 0 -> 15376 bytes FeatureGates.swiftdeps | Bin 0 -> 18288 bytes .../DefaultDecisionService.swift | 52 +-- .../2026-04-30-local-holdout-feature-gate.md | 366 ++++++++++++++++++ 6 files changed, 409 insertions(+), 24 deletions(-) create mode 100644 .claude/settings.local.json create mode 100644 FeatureGates.d create mode 100644 FeatureGates.o create mode 100644 FeatureGates.swiftdeps create mode 100644 docs/superpowers/plans/2026-04-30-local-holdout-feature-gate.md diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 00000000..67ad1a5d --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,14 @@ +{ + "permissions": { + "allow": [ + "mcp__jira__getConfluencePage", + "mcp__jira__createJiraIssue", + "mcp__jira__searchConfluenceUsingCql", + "Bash(open:*)" + ] + }, + "enabledMcpjsonServers": [ + "jira", + "github" + ] +} diff --git a/FeatureGates.d b/FeatureGates.d new file mode 100644 index 00000000..5eaa20ae --- /dev/null +++ b/FeatureGates.d @@ -0,0 +1 @@ +FeatureGates.o : /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/MurmurHash3.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/OptimizelyJSON+ObjC.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/OptimizelyConfig+ObjC.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/OptimizelyClient+ObjC.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely+Decide/OptimizelyUserContext+ObjC.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/OptimizelyJSON.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Cmab.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/CMAB/CmabService.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Customization/Protocols/OPTUserProfileService.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Customization/DefaultUserProfileService.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Protocols/OPTDecisionService.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/DefaultDecisionService.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/HandlerRegistryService.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Audience/Audience.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely+Decide/OptimizelyClient+Decide.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/LogMessage.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/LruCache.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Variable.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/FeatureVariable.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/Datastore/DataStoreFile.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/ExperimentCore.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Protocols/OPTDataStore.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/DecisionResponse.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Attribute.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Audience/UserAttribute.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Audience/AttributeValue.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Audience/ConditionLeaf.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/FeatureFlag.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/CMAB/CmabConfig.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/OdpConfig.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/ProjectConfig.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/HoldoutConfig.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/OptimizelyConfig.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/DispatchEvents/EventForDispatch.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Protocols/DataStoreQueueStack.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/OptimizelyLogLevel.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/Datastore/DataStoreQueueStackImpl.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely+Decide/OptimizelyDecision.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Extensions/ArrayEventForDispatch+Extension.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Extensions/DataStoreQueueStackImpl+Extension.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Extensions/OptimizelyClient+Extension.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Extensions/Array+Extension.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/SDKVersion.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Audience/SemanticVersion.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/TrafficAllocation.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Variation.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Integration.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely+Decide/OptimizelyDecideOption.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/OptimizelySegmentOption.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/DecisionInfo.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Group.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/Events/BatchEventBuilder.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Audience/ConditionHolder.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/VuidManager.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/OdpSegmentApiManager.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/OdpEventApiManager.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/OdpManager.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/OdpSegmentManager.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/OdpEventManager.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Customization/Protocols/OPTLogger.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/ThreadSafeLogger.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Customization/DefaultLogger.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Customization/Protocols/OPTEventDispatcher.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Customization/DefaultEventDispatcher.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/watchOS/WatchBackgroundNotifier.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/UserProfileTracker.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Customization/Protocols/OPTDatafileHandler.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Customization/DefaultDatafileHandler.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Protocols/OPTBucketer.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/DefaultBucketer.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Protocols/OPTNotificationCenter.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/DefaultNotificationCenter.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/OptimizelyError.swift /Users/muzahidul.islam/workspace/swift-sdk/.build/arm64-apple-macosx/debug/Optimizely.build/DerivedSources/resource_bundle_accessor.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/FeatureGates.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/OptimizelySdkSettings.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Protocols/BackgroundingCallbacks.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/Utils.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/Notifications.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/DecisionReasons.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/Datastore/DataStoreUserDefaults.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/Constants.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Project.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/OptimizelyResult.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/CMAB/CmabClient.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/OptimizelyClient.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Experiment.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Event.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/DispatchEvents/BatchEvent.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/OdpEvent.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Holdout.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Rollout.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely+Decide/OptimizelyUserContext.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/AtomicArray.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/RetryStrategy.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/AtomicDictionary.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/Datastore/DataStoreMemory.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/NetworkReachability.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/AtomicProperty.swift /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/XPC.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/ObjectiveC.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/CoreData.framework/Modules/CoreData.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/Distributed.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/unistd.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/CoreImage.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/CoreTransferable.framework/Modules/CoreTransferable.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/_time.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/sys_time.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/Combine.framework/Modules/Combine.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/QuartzCore.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/_StringProcessing.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/OSLog.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/Dispatch.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/_math.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/Network.framework/Modules/Network.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/_signal.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/Metal.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/System.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/Darwin.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/Foundation.framework/Modules/Foundation.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/CoreFoundation.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/Observation.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/_stdio.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/_errno.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/CoreGraphics.framework/Modules/CoreGraphics.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/Symbols.framework/Modules/Symbols.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/os.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/UniformTypeIdentifiers.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/_Builtin_float.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/Swift.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/IOKit.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/AppKit.framework/Modules/AppKit.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/SwiftOnoneSupport.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/DeveloperToolsSupport.framework/Modules/DeveloperToolsSupport.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/CoreText.framework/Modules/CoreText.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/_Concurrency.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/Accessibility.framework/Modules/Accessibility.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/XPC.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/ObjectiveC.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/CoreData.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Distributed.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/unistd.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/CoreImage.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/CoreTransferable.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/_time.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/sys_time.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Combine.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/QuartzCore.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/_StringProcessing.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/OSLog.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Dispatch.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/_math.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Network.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/_signal.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Metal.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/System.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Darwin.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Foundation.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/CoreFoundation.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Observation.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/_stdio.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/_errno.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/CoreGraphics.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Symbols.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/os.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/UniformTypeIdentifiers.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/_Builtin_float.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Swift.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/IOKit.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/AppKit.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/SwiftOnoneSupport.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/DeveloperToolsSupport.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/CoreText.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/_Concurrency.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Accessibility.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/include/ObjectiveC.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/CoreData.framework/Headers/CoreData.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/CoreImage.framework/Headers/CoreImage.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/include/_time.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/QuartzCore.framework/Headers/QuartzCore.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/include/dispatch/Dispatch.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/Network.framework/Headers/Network.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/Metal.framework/Headers/Metal.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/Foundation.framework/Headers/Foundation.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/CoreGraphics.framework/Headers/CoreGraphics.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/ApplicationServices.framework/Headers/ApplicationServices.apinotes /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/apinotes/os.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/UniformTypeIdentifiers.framework/Headers/UniformTypeIdentifiers.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/AppKit.framework/Headers/AppKit.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/CoreText.framework/Headers/CoreText.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/Security.framework/Headers/Security.apinotes diff --git a/FeatureGates.o b/FeatureGates.o new file mode 100644 index 0000000000000000000000000000000000000000..d10497738ab35688ba938a0b4c59457191ba668b GIT binary patch literal 15376 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 CEfo;} literal 0 HcmV?d00001 diff --git a/Sources/Implementation/DefaultDecisionService.swift b/Sources/Implementation/DefaultDecisionService.swift index f2210635..7036d5cd 100644 --- a/Sources/Implementation/DefaultDecisionService.swift +++ b/Sources/Implementation/DefaultDecisionService.swift @@ -658,18 +658,20 @@ class DefaultDecisionService: OPTDecisionService { } // check local holdouts targeting this rule - let localHoldouts = config.getHoldoutsForRule(ruleId: rule.id) - for holdout in localHoldouts { - let holdoutDecision = getVariationForHoldout(config: config, - flagKey: flagKey, - holdout: holdout, - user: user, - options: options) - reasons.merge(holdoutDecision.reasons) - if let variation = holdoutDecision.result { - // User is in holdout — return holdout variation immediately, skip this rule - let variationDecision = VariationDecision(variation: variation, holdout: holdout) - return DecisionResponse(result: variationDecision, reasons: reasons) + if FeatureGates.localHoldouts { + let localHoldouts = config.getHoldoutsForRule(ruleId: rule.id) + for holdout in localHoldouts { + let holdoutDecision = getVariationForHoldout(config: config, + flagKey: flagKey, + holdout: holdout, + user: user, + options: options) + reasons.merge(holdoutDecision.reasons) + if let variation = holdoutDecision.result { + // User is in holdout — return holdout variation immediately, skip this rule + let variationDecision = VariationDecision(variation: variation, holdout: holdout) + return DecisionResponse(result: variationDecision, reasons: reasons) + } } } @@ -716,18 +718,20 @@ class DefaultDecisionService: OPTDecisionService { } // check local holdouts targeting this delivery rule - let localHoldouts = config.getHoldoutsForRule(ruleId: rule.id) - for holdout in localHoldouts { - let holdoutDecision = getVariationForHoldout(config: config, - flagKey: flagKey, - holdout: holdout, - user: user, - options: options) - reasons.merge(holdoutDecision.reasons) - if let variation = holdoutDecision.result { - // User is in holdout — return holdout variation with holdout info - let decision = DeliveryRuleDecision(variation: variation, skipToEveryoneElse: skipToEveryoneElse, holdout: holdout) - return DecisionResponse(result: decision, reasons: reasons) + if FeatureGates.localHoldouts { + let localHoldouts = config.getHoldoutsForRule(ruleId: rule.id) + for holdout in localHoldouts { + let holdoutDecision = getVariationForHoldout(config: config, + flagKey: flagKey, + holdout: holdout, + user: user, + options: options) + reasons.merge(holdoutDecision.reasons) + if let variation = holdoutDecision.result { + // User is in holdout — return holdout variation with holdout info + let decision = DeliveryRuleDecision(variation: variation, skipToEveryoneElse: skipToEveryoneElse, holdout: holdout) + return DecisionResponse(result: decision, reasons: reasons) + } } } diff --git a/docs/superpowers/plans/2026-04-30-local-holdout-feature-gate.md b/docs/superpowers/plans/2026-04-30-local-holdout-feature-gate.md new file mode 100644 index 00000000..57b36626 --- /dev/null +++ b/docs/superpowers/plans/2026-04-30-local-holdout-feature-gate.md @@ -0,0 +1,366 @@ +# Local Holdout Feature Gate Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Suppress local holdout logic behind a feature gate so the feature-rollout release ships without local holdout support. When the backend is ready, flip the gate to `true`. + +**Architecture:** Add a standalone `FeatureGates` struct with a `static var localHoldouts` flag (default `false`). Guard the two local holdout evaluation blocks in `DefaultDecisionService.swift` behind this flag. Test setUp/tearDown overrides the flag to `true` so all existing tests pass without modification. + +**Tech Stack:** Swift, XCTest + +--- + +## File Map + +| File | Action | Responsibility | +|------|--------|----------------| +| `Sources/Utils/FeatureGates.swift` | Create | Standalone `FeatureGates` struct with `localHoldouts` flag | +| `Sources/Implementation/DefaultDecisionService.swift` | Modify | Guard local holdout checks in experiment and delivery rule methods behind gate | +| `Tests/OptimizelyTests-Common/DecisionServiceTests_LocalHoldouts.swift` | Modify | Override gate to `true` in setUp/tearDown | +| `Tests/OptimizelyTests-DataModel/HoldoutConfigTests.swift` | Modify | Override gate to `true` in setUp/tearDown | +| `Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Holdouts.swift` | Modify | Override gate to `true` in setUp/tearDown | +| `Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift` | Modify | Override gate to `true` in setUp/tearDown | +| `Tests/OptimizelyTests-Common/DecisionListenerTest_Holdouts.swift` | Modify | Override gate to `true` in setUp/tearDown | + +--- + +### Task 1: Create `FeatureGates` struct + +**Files:** +- Create: `Sources/Utils/FeatureGates.swift` + +- [ ] **Step 1: Create the FeatureGates file** + +Create `Sources/Utils/FeatureGates.swift`: + +```swift +// +// Copyright 2026, Optimizely, Inc. and contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +struct FeatureGates { + static var localHoldouts = false +} +``` + +- [ ] **Step 2: Build to verify no compilation errors** + +Run: `swift build 2>&1 | tail -5` +Expected: Build succeeds. + +- [ ] **Step 3: Commit** + +```bash +git add Sources/Utils/FeatureGates.swift +git commit -m "feat: add FeatureGates struct with localHoldouts flag + +Default false to suppress local holdout logic until backend is ready. + +Co-Authored-By: Claude Opus 4.6 " +``` + +--- + +### Task 2: Guard local holdout checks in DefaultDecisionService + +**Files:** +- Modify: `Sources/Implementation/DefaultDecisionService.swift:660-674` (experiment rule local holdout block) +- Modify: `Sources/Implementation/DefaultDecisionService.swift:718-732` (delivery rule local holdout block) + +- [ ] **Step 1: Guard experiment rule local holdout block** + +In `getVariationFromExperimentRule()`, wrap the local holdout block (lines 660-674) with the feature gate. + +Replace: + +```swift + // check local holdouts targeting this rule + let localHoldouts = config.getHoldoutsForRule(ruleId: rule.id) + for holdout in localHoldouts { + let holdoutDecision = getVariationForHoldout(config: config, + flagKey: flagKey, + holdout: holdout, + user: user, + options: options) + reasons.merge(holdoutDecision.reasons) + if let variation = holdoutDecision.result { + // User is in holdout — return holdout variation immediately, skip this rule + let variationDecision = VariationDecision(variation: variation, holdout: holdout) + return DecisionResponse(result: variationDecision, reasons: reasons) + } + } +``` + +With: + +```swift + // check local holdouts targeting this rule + if FeatureGates.localHoldouts { + let localHoldouts = config.getHoldoutsForRule(ruleId: rule.id) + for holdout in localHoldouts { + let holdoutDecision = getVariationForHoldout(config: config, + flagKey: flagKey, + holdout: holdout, + user: user, + options: options) + reasons.merge(holdoutDecision.reasons) + if let variation = holdoutDecision.result { + // User is in holdout — return holdout variation immediately, skip this rule + let variationDecision = VariationDecision(variation: variation, holdout: holdout) + return DecisionResponse(result: variationDecision, reasons: reasons) + } + } + } +``` + +- [ ] **Step 2: Guard delivery rule local holdout block** + +In `getVariationFromDeliveryRule()`, wrap the local holdout block (lines 718-732) with the same gate. + +Replace: + +```swift + // check local holdouts targeting this delivery rule + let localHoldouts = config.getHoldoutsForRule(ruleId: rule.id) + for holdout in localHoldouts { + let holdoutDecision = getVariationForHoldout(config: config, + flagKey: flagKey, + holdout: holdout, + user: user, + options: options) + reasons.merge(holdoutDecision.reasons) + if let variation = holdoutDecision.result { + // User is in holdout — return holdout variation with holdout info + let decision = DeliveryRuleDecision(variation: variation, skipToEveryoneElse: skipToEveryoneElse, holdout: holdout) + return DecisionResponse(result: decision, reasons: reasons) + } + } +``` + +With: + +```swift + // check local holdouts targeting this delivery rule + if FeatureGates.localHoldouts { + let localHoldouts = config.getHoldoutsForRule(ruleId: rule.id) + for holdout in localHoldouts { + let holdoutDecision = getVariationForHoldout(config: config, + flagKey: flagKey, + holdout: holdout, + user: user, + options: options) + reasons.merge(holdoutDecision.reasons) + if let variation = holdoutDecision.result { + // User is in holdout — return holdout variation with holdout info + let decision = DeliveryRuleDecision(variation: variation, skipToEveryoneElse: skipToEveryoneElse, holdout: holdout) + return DecisionResponse(result: decision, reasons: reasons) + } + } + } +``` + +- [ ] **Step 3: Build to verify no compilation errors** + +Run: `swift build 2>&1 | tail -5` +Expected: Build succeeds. + +- [ ] **Step 4: Commit** + +```bash +git add Sources/Implementation/DefaultDecisionService.swift +git commit -m "feat: guard local holdout evaluation behind FeatureGates.localHoldouts + +Both experiment rule and delivery rule local holdout checks are now +gated. When false, these blocks are skipped entirely. Global holdout +evaluation in getDecisionForFlag() is unaffected. + +Co-Authored-By: Claude Opus 4.6 " +``` + +--- + +### Task 3: Override feature gate in test setUp/tearDown + +**Files:** +- Modify: `Tests/OptimizelyTests-Common/DecisionServiceTests_LocalHoldouts.swift:53-61` +- Modify: `Tests/OptimizelyTests-DataModel/HoldoutConfigTests.swift:19` +- Modify: `Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Holdouts.swift:46` +- Modify: `Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift:45` +- Modify: `Tests/OptimizelyTests-Common/DecisionListenerTest_Holdouts.swift:62` + +- [ ] **Step 1: Add gate override to DecisionServiceTests_LocalHoldouts** + +Add `FeatureGates.localHoldouts = true` at the start of `setUp()`, and add a `tearDown()` method to reset it. This class has no existing `tearDown`. + +The existing `setUp()` becomes: + +```swift + override func setUp() { + super.setUp() + FeatureGates.localHoldouts = true + + // Load a real datafile for testing + optimizely = OTUtils.createOptimizely(datafileName: "decide_datafile", + clearUserProfileService: true) + config = optimizely.config! + decisionService = optimizely.decisionService as? DefaultDecisionService + } + + override func tearDown() { + FeatureGates.localHoldouts = false + super.tearDown() + } +``` + +- [ ] **Step 2: Add gate override to HoldoutConfigTests** + +This class has no existing `setUp`/`tearDown`. Add them right after the class declaration (line 19): + +```swift +class HoldoutConfigTests: XCTestCase { + override func setUp() { + super.setUp() + FeatureGates.localHoldouts = true + } + + override func tearDown() { + FeatureGates.localHoldouts = false + super.tearDown() + } + + func testEmptyHoldouts_shouldHaveEmptyMaps() { + // ... existing code unchanged +``` + +- [ ] **Step 3: Add gate override to OptimizelyUserContextTests_Decide_Holdouts** + +Add the gate override at the start of the existing `setUp()` (after `super.setUp()`) and add a `tearDown()`. The class has no existing `tearDown`. + +```swift + override func setUp() { + super.setUp() + FeatureGates.localHoldouts = true + // ... rest of existing setUp code unchanged + } + + override func tearDown() { + FeatureGates.localHoldouts = false + super.tearDown() + } +``` + +- [ ] **Step 4: Add gate override to OptimizelyUserContextTests_Decide_With_Holdouts_Reasons** + +Add the gate override at the start of the existing `setUp()` (after `super.setUp()`) and add a `tearDown()`. The class has no existing `tearDown`. + +```swift + override func setUp() { + super.setUp() + FeatureGates.localHoldouts = true + // ... rest of existing setUp code unchanged + } + + override func tearDown() { + FeatureGates.localHoldouts = false + super.tearDown() + } +``` + +- [ ] **Step 5: Add gate override to DecisionListenerTests_Holdouts** + +Add the gate override at the start of the existing `setUp()` (after `super.setUp()`) and add a `tearDown()`. The class has no existing `tearDown`. + +```swift + override func setUp() { + super.setUp() + FeatureGates.localHoldouts = true + // ... rest of existing setUp code unchanged + } + + override func tearDown() { + FeatureGates.localHoldouts = false + super.tearDown() + } +``` + +- [ ] **Step 6: Build and run tests** + +Run: `swift test 2>&1 | tail -20` +Expected: All tests pass. Local holdout tests pass because the gate is overridden to `true` in setUp. Non-holdout tests are unaffected because the gate defaults to `false`. + +- [ ] **Step 7: Commit** + +```bash +git add Tests/OptimizelyTests-Common/DecisionServiceTests_LocalHoldouts.swift \ + Tests/OptimizelyTests-DataModel/HoldoutConfigTests.swift \ + Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Holdouts.swift \ + Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift \ + Tests/OptimizelyTests-Common/DecisionListenerTest_Holdouts.swift +git commit -m "test: override FeatureGates.localHoldouts in holdout test setUp/tearDown + +All holdout test classes set the gate to true in setUp and reset to false +in tearDown. Existing test methods are unchanged — they pass because the +gate enables the local holdout code paths during test execution. + +Co-Authored-By: Claude Opus 4.6 " +``` + +--- + +### Task 4: Verify end-to-end behavior + +**Files:** None (verification only) + +- [ ] **Step 1: Verify global holdouts still work with gate off** + +Run the global holdout tests specifically: + +```bash +xcodebuild test \ + -workspace OptimizelySwiftSDK.xcworkspace \ + -scheme OptimizelySwiftSDK-iOS \ + -destination 'platform=iOS Simulator,name=iPhone 16' \ + -only-testing:OptimizelyTests-Common-iOS/DecisionServiceTests_Holdouts 2>&1 | tail -20 +``` + +Expected: All global holdout tests pass (they don't depend on the local holdout gate). + +- [ ] **Step 2: Run full test suite** + +```bash +xcodebuild test \ + -workspace OptimizelySwiftSDK.xcworkspace \ + -scheme OptimizelySwiftSDK-iOS \ + -destination 'platform=iOS Simulator,name=iPhone 16' 2>&1 | grep -E "Test Suite|Executed|failed" +``` + +Expected: All test suites pass with 0 failures. + +--- + +## Future: Enabling Local Holdouts + +When the backend is ready, the only change needed is: + +```swift +// In FeatureGates.swift +struct FeatureGates { + static var localHoldouts = true // ← flip from false to true +} +``` + +Then remove the `setUp`/`tearDown` overrides from the 5 test files (they become no-ops but are unnecessary cleanup). From 99f66d79c71f81bd77a02a7e3604efda68479f98 Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Thu, 30 Apr 2026 17:44:27 +0600 Subject: [PATCH 3/8] test: override FeatureGates.localHoldouts in holdout test setUp/tearDown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All holdout test classes set the gate to true in setUp and reset to false in tearDown. Existing test methods are unchanged — they pass because the gate enables the local holdout code paths during test execution. Co-Authored-By: Claude Opus 4.6 --- .../DecisionListenerTest_Holdouts.swift | 12 +++++++++--- .../DecisionServiceTests_LocalHoldouts.swift | 6 ++++++ .../OptimizelyUserContextTests_Decide_Holdouts.swift | 10 ++++++++-- ...erContextTests_Decide_With_Holdouts_Reasons.swift | 10 ++++++++-- .../HoldoutConfigTests.swift | 10 ++++++++++ 5 files changed, 41 insertions(+), 7 deletions(-) diff --git a/Tests/OptimizelyTests-Common/DecisionListenerTest_Holdouts.swift b/Tests/OptimizelyTests-Common/DecisionListenerTest_Holdouts.swift index 0470af28..c5bf8e56 100644 --- a/Tests/OptimizelyTests-Common/DecisionListenerTest_Holdouts.swift +++ b/Tests/OptimizelyTests-Common/DecisionListenerTest_Holdouts.swift @@ -61,13 +61,14 @@ class DecisionListenerTests_Holdouts: XCTestCase { override func setUp() { super.setUp() - + FeatureGates.localHoldouts = true + optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, eventDispatcher: eventDispatcher, userProfileService: OTUtils.createClearUserProfileService()) - + try! optimizely.start(datafile: OTUtils.loadJSONDatafile("decide_datafile")!) - + var holdout = try! OTUtils.model(from: sampleHoldout) as Holdout // Audience "13389130056" requires "country" = "US" holdout.audienceIds = ["13389130056"] @@ -79,6 +80,11 @@ class DecisionListenerTests_Holdouts: XCTestCase { self.notificationCenter = self.optimizely.notificationCenter! } + + override func tearDown() { + FeatureGates.localHoldouts = false + super.tearDown() + } func testDecisionListenerDecideWithUserInHoldout() { let exp = expectation(description: "x") diff --git a/Tests/OptimizelyTests-Common/DecisionServiceTests_LocalHoldouts.swift b/Tests/OptimizelyTests-Common/DecisionServiceTests_LocalHoldouts.swift index 0d39787a..e5d64fd1 100644 --- a/Tests/OptimizelyTests-Common/DecisionServiceTests_LocalHoldouts.swift +++ b/Tests/OptimizelyTests-Common/DecisionServiceTests_LocalHoldouts.swift @@ -52,6 +52,7 @@ class DecisionServiceTests_LocalHoldouts: XCTestCase { override func setUp() { super.setUp() + FeatureGates.localHoldouts = true // Load a real datafile for testing optimizely = OTUtils.createOptimizely(datafileName: "decide_datafile", @@ -60,6 +61,11 @@ class DecisionServiceTests_LocalHoldouts: XCTestCase { decisionService = optimizely.decisionService as? DefaultDecisionService } + override func tearDown() { + FeatureGates.localHoldouts = false + super.tearDown() + } + // MARK: - Global Holdouts Tests func testGlobalHoldout_EvaluatedBeforeAllRules() { diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Holdouts.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Holdouts.swift index a21ca3c0..2a33db0b 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Holdouts.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Holdouts.swift @@ -45,13 +45,19 @@ class OptimizelyUserContextTests_Decide_Holdouts: XCTestCase { override func setUp() { super.setUp() - + FeatureGates.localHoldouts = true + optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, eventDispatcher: eventDispatcher, userProfileService: OTUtils.createClearUserProfileService()) - + try! optimizely.start(datafile: OTUtils.loadJSONDatafile("decide_datafile")!) } + + override func tearDown() { + FeatureGates.localHoldouts = false + super.tearDown() + } func test_decide_with_global_holdout_audience_matched() { let featureKey = "feature_1" diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift index f05d7ba0..e459b4a1 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift @@ -44,12 +44,18 @@ class OptimizelyUserContextTests_Decide_With_Holdouts_Reasons: XCTestCase { override func setUp() { super.setUp() - + FeatureGates.localHoldouts = true + optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, userProfileService: OTUtils.createClearUserProfileService()) - + try! optimizely.start(datafile: OTUtils.loadJSONDatafile("decide_datafile")!) } + + override func tearDown() { + FeatureGates.localHoldouts = false + super.tearDown() + } /// Test when user is bucketed into the global holdout func testDecideReasons_userBucketedIntoGlobalHoldout() { diff --git a/Tests/OptimizelyTests-DataModel/HoldoutConfigTests.swift b/Tests/OptimizelyTests-DataModel/HoldoutConfigTests.swift index acc9f804..96d70d24 100644 --- a/Tests/OptimizelyTests-DataModel/HoldoutConfigTests.swift +++ b/Tests/OptimizelyTests-DataModel/HoldoutConfigTests.swift @@ -17,6 +17,16 @@ import XCTest class HoldoutConfigTests: XCTestCase { + override func setUp() { + super.setUp() + FeatureGates.localHoldouts = true + } + + override func tearDown() { + FeatureGates.localHoldouts = false + super.tearDown() + } + func testEmptyHoldouts_shouldHaveEmptyMaps() { let config = HoldoutConfig(allholdouts: []) From 15644501e1a96bbd1b103a9a2d7b0a28ad3a3814 Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Thu, 30 Apr 2026 17:48:51 +0600 Subject: [PATCH 4/8] fix: move FeatureGates into Constants.swift for Xcode project compatibility FeatureGates.swift was not registered in the Xcode project pbxproj file, causing build failures. Moving into Constants.swift which is already included in all build targets. Co-Authored-By: Claude Opus 4.6 --- Sources/Utils/Constants.swift | 4 ++++ Sources/Utils/FeatureGates.swift | 21 --------------------- 2 files changed, 4 insertions(+), 21 deletions(-) delete mode 100644 Sources/Utils/FeatureGates.swift diff --git a/Sources/Utils/Constants.swift b/Sources/Utils/Constants.swift index 0c106b2a..db04f22c 100644 --- a/Sources/Utils/Constants.swift +++ b/Sources/Utils/Constants.swift @@ -16,6 +16,10 @@ import Foundation +struct FeatureGates { + static var localHoldouts = false +} + struct Constants { struct Attributes { static let reservedBucketIdAttribute = "$opt_bucketing_id" diff --git a/Sources/Utils/FeatureGates.swift b/Sources/Utils/FeatureGates.swift deleted file mode 100644 index 1f9da939..00000000 --- a/Sources/Utils/FeatureGates.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright 2026, Optimizely, Inc. and contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -struct FeatureGates { - static var localHoldouts = false -} From 7eb16a9f0abbedb9d68b823953fe2667a92b9563 Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Thu, 30 Apr 2026 17:58:19 +0600 Subject: [PATCH 5/8] test: add FeatureGates override to DecisionServiceTests_Holdouts This test class also uses local holdout data (includedRules), so it needs the gate enabled during test execution. Co-Authored-By: Claude Opus 4.6 --- .../DecisionServiceTests_Holdouts.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Tests/OptimizelyTests-Common/DecisionServiceTests_Holdouts.swift b/Tests/OptimizelyTests-Common/DecisionServiceTests_Holdouts.swift index 31f2e857..5c24fa62 100644 --- a/Tests/OptimizelyTests-Common/DecisionServiceTests_Holdouts.swift +++ b/Tests/OptimizelyTests-Common/DecisionServiceTests_Holdouts.swift @@ -189,7 +189,7 @@ class DecisionServiceTests_Holdouts: XCTestCase { override func setUp() { super.setUp() - + FeatureGates.localHoldouts = true self.optimizely = OTUtils.createOptimizely(datafileName: "empty_datafile", clearUserProfileService: true) self.config = self.optimizely.config! @@ -212,6 +212,10 @@ class DecisionServiceTests_Holdouts: XCTestCase { self.config.holdoutConfig.allHoldouts = [holdout] } + override func tearDown() { + FeatureGates.localHoldouts = false + } + } // MARK: - Test doesMeetAudienceConditions() From c56ddbf12f63037a5ff1fc08e00b34d4b41c6e5c Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Thu, 30 Apr 2026 17:59:30 +0600 Subject: [PATCH 6/8] chore: remove accidentally committed build artifacts Co-Authored-By: Claude Opus 4.6 --- FeatureGates.d | 1 - FeatureGates.o | Bin 15376 -> 0 bytes FeatureGates.swiftdeps | Bin 18288 -> 0 bytes 3 files changed, 1 deletion(-) delete mode 100644 FeatureGates.d delete mode 100644 FeatureGates.o delete mode 100644 FeatureGates.swiftdeps diff --git a/FeatureGates.d b/FeatureGates.d deleted file mode 100644 index 5eaa20ae..00000000 --- a/FeatureGates.d +++ /dev/null @@ -1 +0,0 @@ -FeatureGates.o : /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/MurmurHash3.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/OptimizelyJSON+ObjC.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/OptimizelyConfig+ObjC.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/OptimizelyClient+ObjC.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely+Decide/OptimizelyUserContext+ObjC.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/OptimizelyJSON.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Cmab.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/CMAB/CmabService.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Customization/Protocols/OPTUserProfileService.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Customization/DefaultUserProfileService.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Protocols/OPTDecisionService.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/DefaultDecisionService.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/HandlerRegistryService.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Audience/Audience.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely+Decide/OptimizelyClient+Decide.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/LogMessage.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/LruCache.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Variable.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/FeatureVariable.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/Datastore/DataStoreFile.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/ExperimentCore.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Protocols/OPTDataStore.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/DecisionResponse.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Attribute.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Audience/UserAttribute.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Audience/AttributeValue.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Audience/ConditionLeaf.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/FeatureFlag.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/CMAB/CmabConfig.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/OdpConfig.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/ProjectConfig.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/HoldoutConfig.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/OptimizelyConfig.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/DispatchEvents/EventForDispatch.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Protocols/DataStoreQueueStack.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/OptimizelyLogLevel.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/Datastore/DataStoreQueueStackImpl.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely+Decide/OptimizelyDecision.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Extensions/ArrayEventForDispatch+Extension.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Extensions/DataStoreQueueStackImpl+Extension.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Extensions/OptimizelyClient+Extension.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Extensions/Array+Extension.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/SDKVersion.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Audience/SemanticVersion.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/TrafficAllocation.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Variation.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Integration.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely+Decide/OptimizelyDecideOption.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/OptimizelySegmentOption.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/DecisionInfo.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Group.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/Events/BatchEventBuilder.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Audience/ConditionHolder.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/VuidManager.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/OdpSegmentApiManager.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/OdpEventApiManager.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/OdpManager.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/OdpSegmentManager.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/OdpEventManager.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Customization/Protocols/OPTLogger.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/ThreadSafeLogger.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Customization/DefaultLogger.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Customization/Protocols/OPTEventDispatcher.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Customization/DefaultEventDispatcher.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/watchOS/WatchBackgroundNotifier.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/UserProfileTracker.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Customization/Protocols/OPTDatafileHandler.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Customization/DefaultDatafileHandler.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Protocols/OPTBucketer.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/DefaultBucketer.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Protocols/OPTNotificationCenter.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/DefaultNotificationCenter.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/OptimizelyError.swift /Users/muzahidul.islam/workspace/swift-sdk/.build/arm64-apple-macosx/debug/Optimizely.build/DerivedSources/resource_bundle_accessor.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/FeatureGates.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/OptimizelySdkSettings.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Protocols/BackgroundingCallbacks.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/Utils.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/Notifications.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/DecisionReasons.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/Datastore/DataStoreUserDefaults.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/Constants.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Project.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/OptimizelyResult.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/CMAB/CmabClient.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely/OptimizelyClient.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Experiment.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Event.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/DispatchEvents/BatchEvent.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/ODP/OdpEvent.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Holdout.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Data\ Model/Rollout.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Optimizely+Decide/OptimizelyUserContext.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/AtomicArray.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/RetryStrategy.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/AtomicDictionary.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Implementation/Datastore/DataStoreMemory.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/NetworkReachability.swift /Users/muzahidul.islam/workspace/swift-sdk/Sources/Utils/AtomicProperty.swift /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/XPC.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/ObjectiveC.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/CoreData.framework/Modules/CoreData.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/Distributed.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/unistd.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/CoreImage.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/CoreTransferable.framework/Modules/CoreTransferable.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/_time.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/sys_time.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/Combine.framework/Modules/Combine.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/QuartzCore.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/_StringProcessing.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/OSLog.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/Dispatch.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/_math.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/Network.framework/Modules/Network.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/_signal.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/Metal.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/System.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/Darwin.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/Foundation.framework/Modules/Foundation.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/CoreFoundation.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/Observation.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/_stdio.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/_errno.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/CoreGraphics.framework/Modules/CoreGraphics.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/Symbols.framework/Modules/Symbols.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/os.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/UniformTypeIdentifiers.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/_Builtin_float.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/Swift.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/IOKit.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/AppKit.framework/Modules/AppKit.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/SwiftOnoneSupport.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/DeveloperToolsSupport.framework/Modules/DeveloperToolsSupport.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/CoreText.framework/Modules/CoreText.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift/_Concurrency.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/Accessibility.framework/Modules/Accessibility.swiftmodule/arm64e-apple-macos.swiftinterface /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/XPC.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/ObjectiveC.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/CoreData.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Distributed.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/unistd.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/CoreImage.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/CoreTransferable.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/_time.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/sys_time.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Combine.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/QuartzCore.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/_StringProcessing.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/OSLog.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Dispatch.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/_math.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Network.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/_signal.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Metal.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/System.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Darwin.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Foundation.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/CoreFoundation.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Observation.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/_stdio.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/_errno.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/CoreGraphics.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Symbols.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/os.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/UniformTypeIdentifiers.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/_Builtin_float.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Swift.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/IOKit.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/AppKit.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/SwiftOnoneSupport.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/DeveloperToolsSupport.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/CoreText.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/_Concurrency.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/prebuilt-modules/15.0/Accessibility.swiftmodule/arm64e-apple-macos.swiftmodule /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/include/ObjectiveC.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/CoreData.framework/Headers/CoreData.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/CoreImage.framework/Headers/CoreImage.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/include/_time.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/QuartzCore.framework/Headers/QuartzCore.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/include/dispatch/Dispatch.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/Network.framework/Headers/Network.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/Metal.framework/Headers/Metal.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/Foundation.framework/Headers/Foundation.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/CoreGraphics.framework/Headers/CoreGraphics.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/ApplicationServices.framework/Headers/ApplicationServices.apinotes /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/apinotes/os.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/UniformTypeIdentifiers.framework/Headers/UniformTypeIdentifiers.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/AppKit.framework/Headers/AppKit.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/CoreText.framework/Headers/CoreText.apinotes /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/System/Library/Frameworks/Security.framework/Headers/Security.apinotes diff --git a/FeatureGates.o b/FeatureGates.o deleted file mode 100644 index d10497738ab35688ba938a0b4c59457191ba668b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15376 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 CEfo;} From b925fd88617010ac1aba2a87e67334c60984146b Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Thu, 30 Apr 2026 18:00:07 +0600 Subject: [PATCH 7/8] chore: remove local settings and plan doc from tracking Co-Authored-By: Claude Opus 4.6 --- .claude/settings.local.json | 14 - .../2026-04-30-local-holdout-feature-gate.md | 366 ------------------ 2 files changed, 380 deletions(-) delete mode 100644 .claude/settings.local.json delete mode 100644 docs/superpowers/plans/2026-04-30-local-holdout-feature-gate.md diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index 67ad1a5d..00000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "permissions": { - "allow": [ - "mcp__jira__getConfluencePage", - "mcp__jira__createJiraIssue", - "mcp__jira__searchConfluenceUsingCql", - "Bash(open:*)" - ] - }, - "enabledMcpjsonServers": [ - "jira", - "github" - ] -} diff --git a/docs/superpowers/plans/2026-04-30-local-holdout-feature-gate.md b/docs/superpowers/plans/2026-04-30-local-holdout-feature-gate.md deleted file mode 100644 index 57b36626..00000000 --- a/docs/superpowers/plans/2026-04-30-local-holdout-feature-gate.md +++ /dev/null @@ -1,366 +0,0 @@ -# Local Holdout Feature Gate Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Suppress local holdout logic behind a feature gate so the feature-rollout release ships without local holdout support. When the backend is ready, flip the gate to `true`. - -**Architecture:** Add a standalone `FeatureGates` struct with a `static var localHoldouts` flag (default `false`). Guard the two local holdout evaluation blocks in `DefaultDecisionService.swift` behind this flag. Test setUp/tearDown overrides the flag to `true` so all existing tests pass without modification. - -**Tech Stack:** Swift, XCTest - ---- - -## File Map - -| File | Action | Responsibility | -|------|--------|----------------| -| `Sources/Utils/FeatureGates.swift` | Create | Standalone `FeatureGates` struct with `localHoldouts` flag | -| `Sources/Implementation/DefaultDecisionService.swift` | Modify | Guard local holdout checks in experiment and delivery rule methods behind gate | -| `Tests/OptimizelyTests-Common/DecisionServiceTests_LocalHoldouts.swift` | Modify | Override gate to `true` in setUp/tearDown | -| `Tests/OptimizelyTests-DataModel/HoldoutConfigTests.swift` | Modify | Override gate to `true` in setUp/tearDown | -| `Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Holdouts.swift` | Modify | Override gate to `true` in setUp/tearDown | -| `Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift` | Modify | Override gate to `true` in setUp/tearDown | -| `Tests/OptimizelyTests-Common/DecisionListenerTest_Holdouts.swift` | Modify | Override gate to `true` in setUp/tearDown | - ---- - -### Task 1: Create `FeatureGates` struct - -**Files:** -- Create: `Sources/Utils/FeatureGates.swift` - -- [ ] **Step 1: Create the FeatureGates file** - -Create `Sources/Utils/FeatureGates.swift`: - -```swift -// -// Copyright 2026, Optimizely, Inc. and contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -struct FeatureGates { - static var localHoldouts = false -} -``` - -- [ ] **Step 2: Build to verify no compilation errors** - -Run: `swift build 2>&1 | tail -5` -Expected: Build succeeds. - -- [ ] **Step 3: Commit** - -```bash -git add Sources/Utils/FeatureGates.swift -git commit -m "feat: add FeatureGates struct with localHoldouts flag - -Default false to suppress local holdout logic until backend is ready. - -Co-Authored-By: Claude Opus 4.6 " -``` - ---- - -### Task 2: Guard local holdout checks in DefaultDecisionService - -**Files:** -- Modify: `Sources/Implementation/DefaultDecisionService.swift:660-674` (experiment rule local holdout block) -- Modify: `Sources/Implementation/DefaultDecisionService.swift:718-732` (delivery rule local holdout block) - -- [ ] **Step 1: Guard experiment rule local holdout block** - -In `getVariationFromExperimentRule()`, wrap the local holdout block (lines 660-674) with the feature gate. - -Replace: - -```swift - // check local holdouts targeting this rule - let localHoldouts = config.getHoldoutsForRule(ruleId: rule.id) - for holdout in localHoldouts { - let holdoutDecision = getVariationForHoldout(config: config, - flagKey: flagKey, - holdout: holdout, - user: user, - options: options) - reasons.merge(holdoutDecision.reasons) - if let variation = holdoutDecision.result { - // User is in holdout — return holdout variation immediately, skip this rule - let variationDecision = VariationDecision(variation: variation, holdout: holdout) - return DecisionResponse(result: variationDecision, reasons: reasons) - } - } -``` - -With: - -```swift - // check local holdouts targeting this rule - if FeatureGates.localHoldouts { - let localHoldouts = config.getHoldoutsForRule(ruleId: rule.id) - for holdout in localHoldouts { - let holdoutDecision = getVariationForHoldout(config: config, - flagKey: flagKey, - holdout: holdout, - user: user, - options: options) - reasons.merge(holdoutDecision.reasons) - if let variation = holdoutDecision.result { - // User is in holdout — return holdout variation immediately, skip this rule - let variationDecision = VariationDecision(variation: variation, holdout: holdout) - return DecisionResponse(result: variationDecision, reasons: reasons) - } - } - } -``` - -- [ ] **Step 2: Guard delivery rule local holdout block** - -In `getVariationFromDeliveryRule()`, wrap the local holdout block (lines 718-732) with the same gate. - -Replace: - -```swift - // check local holdouts targeting this delivery rule - let localHoldouts = config.getHoldoutsForRule(ruleId: rule.id) - for holdout in localHoldouts { - let holdoutDecision = getVariationForHoldout(config: config, - flagKey: flagKey, - holdout: holdout, - user: user, - options: options) - reasons.merge(holdoutDecision.reasons) - if let variation = holdoutDecision.result { - // User is in holdout — return holdout variation with holdout info - let decision = DeliveryRuleDecision(variation: variation, skipToEveryoneElse: skipToEveryoneElse, holdout: holdout) - return DecisionResponse(result: decision, reasons: reasons) - } - } -``` - -With: - -```swift - // check local holdouts targeting this delivery rule - if FeatureGates.localHoldouts { - let localHoldouts = config.getHoldoutsForRule(ruleId: rule.id) - for holdout in localHoldouts { - let holdoutDecision = getVariationForHoldout(config: config, - flagKey: flagKey, - holdout: holdout, - user: user, - options: options) - reasons.merge(holdoutDecision.reasons) - if let variation = holdoutDecision.result { - // User is in holdout — return holdout variation with holdout info - let decision = DeliveryRuleDecision(variation: variation, skipToEveryoneElse: skipToEveryoneElse, holdout: holdout) - return DecisionResponse(result: decision, reasons: reasons) - } - } - } -``` - -- [ ] **Step 3: Build to verify no compilation errors** - -Run: `swift build 2>&1 | tail -5` -Expected: Build succeeds. - -- [ ] **Step 4: Commit** - -```bash -git add Sources/Implementation/DefaultDecisionService.swift -git commit -m "feat: guard local holdout evaluation behind FeatureGates.localHoldouts - -Both experiment rule and delivery rule local holdout checks are now -gated. When false, these blocks are skipped entirely. Global holdout -evaluation in getDecisionForFlag() is unaffected. - -Co-Authored-By: Claude Opus 4.6 " -``` - ---- - -### Task 3: Override feature gate in test setUp/tearDown - -**Files:** -- Modify: `Tests/OptimizelyTests-Common/DecisionServiceTests_LocalHoldouts.swift:53-61` -- Modify: `Tests/OptimizelyTests-DataModel/HoldoutConfigTests.swift:19` -- Modify: `Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Holdouts.swift:46` -- Modify: `Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift:45` -- Modify: `Tests/OptimizelyTests-Common/DecisionListenerTest_Holdouts.swift:62` - -- [ ] **Step 1: Add gate override to DecisionServiceTests_LocalHoldouts** - -Add `FeatureGates.localHoldouts = true` at the start of `setUp()`, and add a `tearDown()` method to reset it. This class has no existing `tearDown`. - -The existing `setUp()` becomes: - -```swift - override func setUp() { - super.setUp() - FeatureGates.localHoldouts = true - - // Load a real datafile for testing - optimizely = OTUtils.createOptimizely(datafileName: "decide_datafile", - clearUserProfileService: true) - config = optimizely.config! - decisionService = optimizely.decisionService as? DefaultDecisionService - } - - override func tearDown() { - FeatureGates.localHoldouts = false - super.tearDown() - } -``` - -- [ ] **Step 2: Add gate override to HoldoutConfigTests** - -This class has no existing `setUp`/`tearDown`. Add them right after the class declaration (line 19): - -```swift -class HoldoutConfigTests: XCTestCase { - override func setUp() { - super.setUp() - FeatureGates.localHoldouts = true - } - - override func tearDown() { - FeatureGates.localHoldouts = false - super.tearDown() - } - - func testEmptyHoldouts_shouldHaveEmptyMaps() { - // ... existing code unchanged -``` - -- [ ] **Step 3: Add gate override to OptimizelyUserContextTests_Decide_Holdouts** - -Add the gate override at the start of the existing `setUp()` (after `super.setUp()`) and add a `tearDown()`. The class has no existing `tearDown`. - -```swift - override func setUp() { - super.setUp() - FeatureGates.localHoldouts = true - // ... rest of existing setUp code unchanged - } - - override func tearDown() { - FeatureGates.localHoldouts = false - super.tearDown() - } -``` - -- [ ] **Step 4: Add gate override to OptimizelyUserContextTests_Decide_With_Holdouts_Reasons** - -Add the gate override at the start of the existing `setUp()` (after `super.setUp()`) and add a `tearDown()`. The class has no existing `tearDown`. - -```swift - override func setUp() { - super.setUp() - FeatureGates.localHoldouts = true - // ... rest of existing setUp code unchanged - } - - override func tearDown() { - FeatureGates.localHoldouts = false - super.tearDown() - } -``` - -- [ ] **Step 5: Add gate override to DecisionListenerTests_Holdouts** - -Add the gate override at the start of the existing `setUp()` (after `super.setUp()`) and add a `tearDown()`. The class has no existing `tearDown`. - -```swift - override func setUp() { - super.setUp() - FeatureGates.localHoldouts = true - // ... rest of existing setUp code unchanged - } - - override func tearDown() { - FeatureGates.localHoldouts = false - super.tearDown() - } -``` - -- [ ] **Step 6: Build and run tests** - -Run: `swift test 2>&1 | tail -20` -Expected: All tests pass. Local holdout tests pass because the gate is overridden to `true` in setUp. Non-holdout tests are unaffected because the gate defaults to `false`. - -- [ ] **Step 7: Commit** - -```bash -git add Tests/OptimizelyTests-Common/DecisionServiceTests_LocalHoldouts.swift \ - Tests/OptimizelyTests-DataModel/HoldoutConfigTests.swift \ - Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Holdouts.swift \ - Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift \ - Tests/OptimizelyTests-Common/DecisionListenerTest_Holdouts.swift -git commit -m "test: override FeatureGates.localHoldouts in holdout test setUp/tearDown - -All holdout test classes set the gate to true in setUp and reset to false -in tearDown. Existing test methods are unchanged — they pass because the -gate enables the local holdout code paths during test execution. - -Co-Authored-By: Claude Opus 4.6 " -``` - ---- - -### Task 4: Verify end-to-end behavior - -**Files:** None (verification only) - -- [ ] **Step 1: Verify global holdouts still work with gate off** - -Run the global holdout tests specifically: - -```bash -xcodebuild test \ - -workspace OptimizelySwiftSDK.xcworkspace \ - -scheme OptimizelySwiftSDK-iOS \ - -destination 'platform=iOS Simulator,name=iPhone 16' \ - -only-testing:OptimizelyTests-Common-iOS/DecisionServiceTests_Holdouts 2>&1 | tail -20 -``` - -Expected: All global holdout tests pass (they don't depend on the local holdout gate). - -- [ ] **Step 2: Run full test suite** - -```bash -xcodebuild test \ - -workspace OptimizelySwiftSDK.xcworkspace \ - -scheme OptimizelySwiftSDK-iOS \ - -destination 'platform=iOS Simulator,name=iPhone 16' 2>&1 | grep -E "Test Suite|Executed|failed" -``` - -Expected: All test suites pass with 0 failures. - ---- - -## Future: Enabling Local Holdouts - -When the backend is ready, the only change needed is: - -```swift -// In FeatureGates.swift -struct FeatureGates { - static var localHoldouts = true // ← flip from false to true -} -``` - -Then remove the `setUp`/`tearDown` overrides from the 5 test files (they become no-ops but are unnecessary cleanup). From dd8442da27da9774916c30b64f98bf307d8f4777 Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Thu, 30 Apr 2026 18:12:33 +0600 Subject: [PATCH 8/8] fix: add missing super.tearDown() and remove unnecessary gate override - Added super.tearDown() to DecisionServiceTests_Holdouts tearDown - Removed FeatureGates override from HoldoutConfigTests since it tests the data model directly, not the gated decision service path Co-Authored-By: Claude Opus 4.6 --- .../DecisionServiceTests_Holdouts.swift | 1 + .../HoldoutConfigTests.swift | 11 +---------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/Tests/OptimizelyTests-Common/DecisionServiceTests_Holdouts.swift b/Tests/OptimizelyTests-Common/DecisionServiceTests_Holdouts.swift index 5c24fa62..c6028120 100644 --- a/Tests/OptimizelyTests-Common/DecisionServiceTests_Holdouts.swift +++ b/Tests/OptimizelyTests-Common/DecisionServiceTests_Holdouts.swift @@ -214,6 +214,7 @@ class DecisionServiceTests_Holdouts: XCTestCase { override func tearDown() { FeatureGates.localHoldouts = false + super.tearDown() } } diff --git a/Tests/OptimizelyTests-DataModel/HoldoutConfigTests.swift b/Tests/OptimizelyTests-DataModel/HoldoutConfigTests.swift index 96d70d24..b9d02535 100644 --- a/Tests/OptimizelyTests-DataModel/HoldoutConfigTests.swift +++ b/Tests/OptimizelyTests-DataModel/HoldoutConfigTests.swift @@ -17,16 +17,7 @@ import XCTest class HoldoutConfigTests: XCTestCase { - override func setUp() { - super.setUp() - FeatureGates.localHoldouts = true - } - - override func tearDown() { - FeatureGates.localHoldouts = false - super.tearDown() - } - + func testEmptyHoldouts_shouldHaveEmptyMaps() { let config = HoldoutConfig(allholdouts: [])