Skip to content

feat(flags): support mixed targeting in local evaluation#188

Merged
patricio-posthog merged 3 commits intomainfrom
feat/mixed-targeting-local-eval
Apr 29, 2026
Merged

feat(flags): support mixed targeting in local evaluation#188
patricio-posthog merged 3 commits intomainfrom
feat/mixed-targeting-local-eval

Conversation

@patricio-posthog
Copy link
Copy Markdown
Contributor

@patricio-posthog patricio-posthog commented Apr 28, 2026

💡 Motivation and Context

Feature flags with "mixed targeting" (the feature-flag-mixed-targeting beta) allow different condition sets within a single flag to target different aggregation levels — some targeting users/persons, others targeting groups.

The SDK only reads aggregation_group_type_index at the flag level. For mixed flags this is null, so the SDK treats all conditions as person-targeted. Group conditions fail property matching and the SDK falls back to a server-side HTTP call.

This breaks customers using flags in environments that cannot make HTTP calls (e.g., background workers).

Ports posthog-python#523 to posthog-dotnet.

💚 How did you test it?

Updated MatchFeatureFlagProperties in LocalEvaluator to resolve aggregation per condition when a condition sets its own AggregationGroupTypeIndex that differs from the flag level. Each condition uses the correct properties and bucketing value for its aggregation type. Backwards compatible with existing pure person and pure group flags.

Added a new AggregationGroupTypeIndex property on FeatureFlagGroup (internal record — not part of the public API surface, so non-breaking).

Added 5 new unit tests in TheMixedTargetingEvaluation: person match, group match, no match, only-group-no-groups, and rollout bucketing. All 767 existing UnitTests continue to pass on net8.0; xUnit CI run reports 805 passed, 0 failed, 2 skipped.

Note on the SDK compliance report

The auto-posted posthog-dotnet Compliance Report comment shows request_payload.request_with_person_properties_device_id failing with a 404 from http://sdk-adapter:8080/get_feature_flag. This failure pre-exists on main and is not related to this PR — it's reproducible on the three most recent merged PRs (#182, #183, #184). The cause is that the .NET compliance adapter (sdk_compliance_adapter/) doesn't implement the /get_feature_flag endpoint yet (mirrors the same gap that posthog-go had). Worth tracking as a follow-up to bring the adapter to parity with the harness contract, but out of scope here.

📝 Checklist

  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change or entry added to the changelog.

If releasing new changes

  • Added the release label to the PR
  • Added exactly one version bump label: bump-patch, bump-minor, or bump-major

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 28, 2026

posthog-dotnet Compliance Report

Date: 2026-04-29 17:21:08 UTC
Duration: 277ms

⚠️ Some Tests Failed

0/1 tests passed, 1 failed


Feature_Flags Tests

⚠️ 0/1 tests passed, 1 failed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id 41ms

Failures

request_payload.request_with_person_properties_device_id

404, message='Not Found', url='http://sdk-adapter:8080/get_feature_flag'

@patricio-posthog patricio-posthog marked this pull request as ready for review April 29, 2026 13:45
@patricio-posthog patricio-posthog requested review from a team and haacked as code owners April 29, 2026 13:45
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 29, 2026

Prompt To Fix All With AI
This is a comment left during a code review.
Path: tests/UnitTests/Features/LocalEvaluatorTests.cs
Line: 880-938

Comment:
**Non-parameterized tests where parameterization applies**

The first three facts (`PersonConditionMatchesWhenNoGroupsPassed`, `GroupConditionMatchesWhenGroupPropsMatch`, `NoMatchWhenBothPersonAndGroupFail`) all call `EvaluateFeatureFlag` on the same `"mixed-flag"` fixture with different person properties, groups, and expected outcomes. The project convention — and the simplicity rules — favour a single `[Theory]` + `[MemberData]` over multiple `[Fact]` methods to avoid repeating the arrange/act/assert skeleton and make it easy to add new cases later.

**Context Used:** Do not attempt to comment on incorrect alphabetica... ([source](https://app.greptile.com/review/custom-context?memory=instruction-0))

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/PostHog/Features/LocalEvaluator.cs
Line: 284-298

Comment:
**Superfluous `HasValue` guard is unreachable**

Inside `if (conditionAggregation != flagAggregation)`, `conditionAggregation` is computed as `condition.AggregationGroupTypeIndex ?? flagAggregation`. The only way `conditionAggregation` can be `null` is when both `condition.AggregationGroupTypeIndex` and `flagAggregation` are `null` — but then `conditionAggregation == flagAggregation == null`, so the outer condition is false and we never enter this block. Therefore `conditionAggregation.HasValue` is always `true` here and the inner `if` is dead code (simplicity rule 4: no superfluous parts).

```csharp
// Mixed-override path: condition-level aggregation differs from flag-level.
if (conditionAggregation != flagAggregation)
{
    if (!_groupTypeMapping.TryGetValue(conditionAggregation!.Value, out var groupType)
        || groups is null
        || !groups.TryGetGroup(groupType, out var group))
    {
        continue;
    }
    effectiveProperties = group.Properties;
    effectiveBucketingId = group.GroupKey;
}
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "feat(flags): support mixed targeting in ..." | Re-trigger Greptile

Comment thread tests/UnitTests/Features/LocalEvaluatorTests.cs Outdated
Comment thread src/PostHog/Features/LocalEvaluator.cs
Copy link
Copy Markdown
Contributor

@haacked haacked left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh! Perhaps another C# developer convert! 😆

Two suggestions inline: the new rollout test does not actually exercise group-key bucketing (rollout 100 short-circuits before hashing), and one comment about the override path is narrower than the code. Greptile already covered the unreachable HasValue guard.

Comment thread tests/UnitTests/Features/LocalEvaluatorTests.cs Outdated
Comment thread src/PostHog/Features/LocalEvaluator.cs Outdated
@patricio-posthog patricio-posthog merged commit e35bf94 into main Apr 29, 2026
15 checks passed
@patricio-posthog patricio-posthog deleted the feat/mixed-targeting-local-eval branch April 29, 2026 17:34
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