Skip to content

[AI-FSSDK] [FSSDK-12368] Local Holdouts - Cleanup flag base setup and add includedRules and rule-level lookup#629

Open
Mat001 wants to merge 1 commit intomasterfrom
ai/mat001/FSSDK-12368-mpirnovar-ai-flow-sdk-fssdk-12368
Open

[AI-FSSDK] [FSSDK-12368] Local Holdouts - Cleanup flag base setup and add includedRules and rule-level lookup#629
Mat001 wants to merge 1 commit intomasterfrom
ai/mat001/FSSDK-12368-mpirnovar-ai-flow-sdk-fssdk-12368

Conversation

@Mat001
Copy link
Copy Markdown

@Mat001 Mat001 commented Apr 10, 2026

Summary

Implements Local Holdouts support for the Swift SDK, replacing legacy flag-level holdout targeting (includedFlags/excludedFlags) with rule-level targeting (includedRules).

Related Ticket: FSSDK-12368

Changes

Data Model

  • Added includedRules: [String]? field to Holdout struct
  • Added isGlobal computed property (true when includedRules == nil)
  • Removed legacy fields: includedFlags, excludedFlags
  • Uses decodeIfPresent for optional parsing

Holdout Configuration

  • Updated HoldoutConfig from flag-level to rule-level mapping
  • Implemented getGlobalHoldouts() -> [Holdout]
  • Implemented getHoldoutsForRule(ruleId:) -> [Holdout]
  • Simplified updateHoldoutMapping() logic

Decision Flow

  • Global holdouts evaluated at flag level in getDecisionForFlag()
  • Local holdouts evaluated per-rule in getVariationFromExperimentRule() and getVariationFromDeliveryRule()
  • Added holdout field to VariationDecision and DeliveryRuleDecision
  • Proper precedence: forced decision → local holdout → normal rule evaluation

Edge Cases

  • Missing includedRules field defaults to nil (global holdout)
  • Empty array [] vs nil distinction preserved (empty = local with no rules, nil = global)
  • Invalid rule IDs return empty array (no crashes)
  • Cross-flag targeting supported (one holdout can target rules from multiple flags)

Testing

Comprehensive Test Coverage

  • Global holdout evaluation
  • Local single-rule targeting
  • Local multi-rule targeting (same flag and cross-flag)
  • Precedence (global before local)
  • Edge cases (missing field, empty array, invalid rule IDs)
  • Integration tests for decision flow
  • Event tracking verification

Quality Metrics

  • ✅ Compilation: PASSED (swift build successful)
  • ✅ Critical Issues: 0
  • ✅ Warnings: 0
  • ✅ Test Coverage: Comprehensive (unit + integration)

Files Modified

Core Implementation (4 files)

  • Sources/Data Model/Holdout.swift - Data model updates
  • Sources/Data Model/HoldoutConfig.swift - Configuration mapping
  • Sources/Data Model/ProjectConfig.swift - Project-level API
  • Sources/Implementation/DefaultDecisionService.swift - Decision logic

Tests Updated (9 files)

  • Tests/OptimizelyTests-DataModel/HoldoutTests.swift
  • Tests/OptimizelyTests-DataModel/HoldoutConfigTests.swift
  • Tests/OptimizelyTests-DataModel/ProjectConfigTests.swift
  • Tests/OptimizelyTests-Common/DecisionServiceTests_Holdouts.swift
  • Tests/OptimizelyTests-Common/DecisionServiceTests_LocalHoldouts.swift (NEW)
  • Tests/OptimizelyTests-Common/DecisionListenerTest_Holdouts.swift
  • Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Holdouts.swift
  • Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_With_Holdouts_Reasons.swift
  • Tests/OptimizelyTests-Common/BatchEventBuilderTests_Events.swift

Test Plan

  • Compilation passes
  • Global holdouts work correctly
  • Local holdouts work correctly (single rule, multiple rules)
  • Edge cases handled (missing field, empty array, invalid IDs)
  • Backward compatibility verified
  • Cross-flag targeting works
  • Precedence correct (global → forced → local → rule)
  • Event tracking correct

🤖 Generated with Claude Code

Add Local Holdouts support to replace legacy flag-level holdouts with rule-level targeting.

Changes:
- Add includedRules field to Holdout model (replaces includedFlags/excludedFlags)
- Add isGlobal computed property for global vs local holdout detection
- Update HoldoutConfig mapping from flag-level to rule-level
- Implement getGlobalHoldouts() and getHoldoutsForRule() methods
- Integrate local holdout evaluation in decision flow (per-rule, before audience/traffic)
- Handle edge cases (missing field, empty array, invalid rule IDs, cross-flag targeting)
- Add comprehensive unit and integration tests for local holdouts
- Update existing tests to use new API

Quality Metrics:
- Compilation: PASSED
- Critical Issues: 0
- Warnings: 0
- Test Coverage: Comprehensive

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@coveralls
Copy link
Copy Markdown

Coverage Status

coverage: 93.828% (+0.06%) from 93.766% — ai/mat001/FSSDK-12368-mpirnovar-ai-flow-sdk-fssdk-12368 into master

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants