Skip to content

fix(server): honor versioned local property matching - #763

Draft
marandaneto wants to merge 1 commit into
mainfrom
fix/versioned-boolean-local-evaluation
Draft

fix(server): honor versioned local property matching#763
marandaneto wants to merge 1 commit into
mainfrom
fix/versioned-boolean-local-evaluation

Conversation

@marandaneto

Copy link
Copy Markdown
Member

💡 Motivation and Context

Server-side local feature flag evaluation needs to honor the property_matching_version returned with flag definitions. This follows the backend change and the shared SDK contract.

  • Missing metadata, version 1, and unsupported versions keep legacy matching. Exactly version 2 uses explicit scalar equality and equality against members of nonempty filter arrays. Empty filters keep recursive truthiness, and is_not complements exact for known properties.
  • Each local evaluation pass keeps one snapshot of definitions, group mappings, cohorts, and its matching version, including recursive flag dependencies.
  • Definition-cache providers retain the version. Version-only refreshes replace the result cache, so an older in-flight remote response cannot repopulate the new cache. A 304 or failed load preserves the previous definitions.

This changes posthog-server plus the shared local-evaluation DTO and API snapshot in posthog. Android mobile matching is unaffected. The existing three-argument Java constructor and Kotlin constructor defaults remain available. The @PostHogInternal data class gains a fourth component and changes its generated copy signatures, so consumers calling that internal generated API must recompile. The existing changeset requests patch releases for posthog and posthog-server.

Optional harness coverage is separate. This PR does not opt an SDK adapter into it.

💚 How did you test it?

On the final source tree, ran:

JAVA_TOOL_OPTIONS=-Dnet.bytebuddy.experimental=true ./gradlew :posthog-server:test --tests 'com.posthog.server.internal.FlagEvaluatorTest' --tests 'com.posthog.server.internal.VersionedPropertyMatchingTest' --tests 'com.posthog.server.internal.PostHogFeatureFlagsTest' --console=plain
make checkFormat
./gradlew :posthog:apiCheck :posthog-server:apiCheck --console=plain
git diff --check

All 188 focused tests passed. Coverage includes exact/is_not rows across missing/1/2/3 versions, person/group/cohort/dependency evaluation, version-only refreshes, disk-cache serialization and async hydration, and latch-controlled stale remote responses. HTTP request counts verify local results do not silently fall back remotely. The new stale-response regression was also observed failing before its repair and passing afterward.

Prior validation on the same source passed all 548 server tests. The shared API test task was up-to-date in that repair run, with retained results for 38 passing tests. The local Java 21 runtime needs the documented Byte Buddy experimental option for existing Mockito tests. No dependencies were upgraded. No mobile/emulator build or full core suite was run, and local results are not a claim that CI passed.

The required isolated committed-branch autoreview against origin/main reported no actionable findings at 66965b7d8ad594eb15b4366d705b9fc01c9206f9.

📝 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

  • Ran pnpm changeset to generate a changeset file

The existing .changeset/quiet-booleans-match.md is included without generating a duplicate. The internal generated API compatibility caveat is noted above.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Pi agents implemented and repaired the server-only matching behavior under human direction. This publication session used Git, GitHub CLI, Gradle, and the isolated Pi autoreview helper to validate and publish the existing implementation. Session reference: sdk-pr-publication/0ee28abf-0f0d-4ec1-bb6e-2ec23145ea0b (local session, no public transcript).

The chosen cache-instance boundary prevents stale remote writes without holding locks across HTTP requests. Existing constructor compatibility and the API snapshot were preserved. Human review is required before merging.

@marandaneto marandaneto self-assigned this Sep 5, 2026
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

posthog-android Compliance Report

Date: 2026-09-05 11:55:50 UTC
Duration: 118125ms

✅ All Tests Passed!

46/46 tests passed


Capture Tests

29/29 tests passed

View Details
Test Status Duration
Format Validation.Event Has Required Fields 263ms
Format Validation.Event Has Uuid 34ms
Format Validation.Event Has Lib Properties 27ms
Format Validation.Distinct Id Is String 23ms
Format Validation.Token Is Present 20ms
Format Validation.Custom Properties Preserved 20ms
Format Validation.Event Has Timestamp 20ms
Retry Behavior.Retries On 503 7020ms
Retry Behavior.Does Not Retry On 400 4026ms
Retry Behavior.Does Not Retry On 401 4026ms
Retry Behavior.Respects Retry After Header 7028ms
Retry Behavior.Implements Backoff 17034ms
Retry Behavior.Retries On 500 7022ms
Retry Behavior.Retries On 502 7022ms
Retry Behavior.Retries On 504 7022ms
Retry Behavior.Max Retries Respected 17097ms
Deduplication.Generates Unique Uuids 37ms
Deduplication.Preserves Uuid On Retry 7017ms
Deduplication.Preserves Uuid And Timestamp On Retry 12032ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry 7022ms
Deduplication.No Duplicate Events In Batch 39ms
Deduplication.Different Events Have Different Uuids 22ms
Compression.Sends Gzip When Enabled 18ms
Batch Format.Uses Proper Batch Structure 15ms
Batch Format.Flush With No Events Sends Nothing 11ms
Batch Format.Multiple Events Batched Together 25ms
Error Handling.Does Not Retry On 403 4017ms
Error Handling.Does Not Retry On 413 4026ms
Error Handling.Retries On 408 5029ms

Feature_Flags Tests

17/17 tests passed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id 37ms
Request Payload.Flags Request Uses V2 Query Param 20ms
Request Payload.Flags Request Hits Flags Path Not Decide 19ms
Request Payload.Flags Request Omits Authorization Header 20ms
Request Payload.Token In Flags Body Matches Init 29ms
Request Payload.Groups Round Trip 18ms
Request Payload.Groups Default To Empty Object 20ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False 16ms
Request Payload.Disable Geoip Omitted Defaults To False 14ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key 15ms
Request Lifecycle.No Flags Request On Init Alone 6ms
Request Lifecycle.No Flags Request On Normal Capture 13ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests 28ms
Request Lifecycle.Mock Response Value Is Returned To Caller 15ms
Retry Behavior.Retries Flags On 502 321ms
Retry Behavior.Retries Flags On 504 324ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event 23ms

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.

1 participant