Skip to content

feat: extend OpenFeature numeric, payload, and reason support - #216

Merged
marandaneto merged 3 commits into
posthog/openfeature-providerfrom
feat/openfeature-provider-parity
Sep 25, 2026
Merged

marandaneto merged 3 commits into
posthog/openfeature-providerfrom
feat/openfeature-provider-parity

Conversation

@marandaneto

@marandaneto marandaneto commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

💡 Motivation and Context

Stacked on #214. This PR targets posthog/openfeature-provider, not main, so #214 can merge independently. After #214 merges, retarget this follow-up to main.

The cross-provider comparison in #214 identified three additions:

  • Accept unsigned hexadecimal numeric variants such as 0x10 and 0Xff. Invalid and signed hexadecimal strings remain type mismatches.
  • Return JSON array payloads, including empty and nested arrays. The upstream OpenFeature SDK still requires a map default (%{}), even when get_map_value/4 returns an array. This restriction is documented in the module docs and a code comment beside resolve_map_value/4, and tested through the real client.
  • Expose flag_metadata["posthog_reason"], using the PostHog description, code, or existing string reason. Explicit server flag_disabled codes map off results to :disabled. Other off results stay :default. Type-mismatch details retain available reason metadata while preserving the error code and caller default. This uses a stable code rather than Python's description substring check.

Error-hook behavior is unchanged and tracked separately in #215. There is no OpenFeature-specific SDK spec.

Related work: PostHog/posthog-python#980 and PostHog/posthog-js#5099.

💚 How did you test it?

  • Five additional regression tests reproduced missing metadata on type mismatches before the fix. All 75 provider tests now pass.
  • Added regression tests first. Nineteen tests failed before implementation for hexadecimal parsing, array payloads, and reason metadata.
  • mix test: 596 passed, 18 integration tests excluded.
  • mix format --check-formatted, MIX_ENV=test mix compile --warnings-as-errors, and mix credo --strict passed.
  • Isolated autoreview of the final branch against origin/posthog/openfeature-provider reported no actionable findings.

Checks ran on Elixir 1.20.4 / OTP 29. No live PostHog project was used.

📝 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 sampo add to generate a changeset file

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Implemented with Pi using local file editing, shell tests, and the isolated autoreview helper. The user requested a separate follow-up so the initial provider PR can merge without waiting for these additions. The upstream map-default restriction is documented rather than changing the OpenFeature dependency. No shared session link is available.

@marandaneto marandaneto self-assigned this Sep 25, 2026
@greptile-apps

greptile-apps Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Retrigger

[Medium risk] Extends OpenFeature flag resolution to handle more data types.

The new change appears safe to merge, though the previously reported missing reason metadata on type mismatches remains.

Reviews (2) · Last reviewed commit: "docs: explain upstream OpenFeature array..."

@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

posthog-elixir Compliance Report

Date: 2026-09-25T06:33:47.152904+00:00
Duration: 108660ms

⚠️ Some Tests Failed

40/47 tests passed, 7 failed


Capture Tests

⚠️ 29/30 tests passed, 1 failed

View Details
Test Status Duration
Format Validation.Event Has Required Fields ✅ 609ms
Format Validation.Event Has Uuid ✅ 610ms
Format Validation.Event Has Lib Properties ✅ 610ms
Format Validation.Distinct Id Is String ✅ 610ms
Format Validation.Token Is Present ✅ 610ms
Format Validation.Custom Properties Preserved ✅ 610ms
Format Validation.Event Has Timestamp ✅ 610ms
Format Validation.Non Utc Event Timestamp Is Converted To Utc ❌ 610ms
Retry Behavior.Retries On 503 ✅ 5616ms
Retry Behavior.Does Not Retry On 400 ✅ 2611ms
Retry Behavior.Does Not Retry On 401 ✅ 2611ms
Retry Behavior.Respects Retry After Header ✅ 5617ms
Retry Behavior.Implements Backoff ✅ 15624ms
Retry Behavior.Retries On 500 ✅ 5616ms
Retry Behavior.Retries On 502 ✅ 5616ms
Retry Behavior.Retries On 504 ✅ 5616ms
Retry Behavior.Max Retries Respected ✅ 15625ms
Deduplication.Generates Unique Uuids ✅ 623ms
Deduplication.Preserves Uuid On Retry ✅ 5615ms
Deduplication.Preserves Uuid And Timestamp On Retry ✅ 10611ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 5618ms
Deduplication.No Duplicate Events In Batch ✅ 615ms
Deduplication.Different Events Have Different Uuids ✅ 612ms
Compression.Sends Gzip When Enabled ✅ 610ms
Batch Format.Uses Proper Batch Structure ✅ 610ms
Batch Format.Flush With No Events Sends Nothing ✅ 607ms
Batch Format.Multiple Events Batched Together ✅ 615ms
Error Handling.Does Not Retry On 403 ✅ 2612ms
Error Handling.Does Not Retry On 413 ✅ 2613ms
Error Handling.Retries On 408 ✅ 5616ms

Failures

format_validation.non_utc_event_timestamp_is_converted_to_utc

Event 0 field 'timestamp' instant '2026-09-25T06:32:03.085966Z' != expected '2025-01-02T03:04:05Z'

Feature_Flags Tests

⚠️ 11/17 tests passed, 6 failed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id ✅ 8ms
Request Payload.Flags Request Uses V2 Query Param ✅ 7ms
Request Payload.Flags Request Hits Flags Path Not Decide ✅ 6ms
Request Payload.Flags Request Omits Authorization Header ✅ 7ms
Request Payload.Token In Flags Body Matches Init ✅ 7ms
Request Payload.Groups Round Trip ✅ 6ms
Request Payload.Groups Default To Empty Object ❌ 7ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False ✅ 6ms
Request Payload.Disable Geoip Omitted Defaults To False ❌ 7ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key ✅ 7ms
Request Lifecycle.No Flags Request On Init Alone ✅ 3ms
Request Lifecycle.No Flags Request On Normal Capture ✅ 609ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests ✅ 12ms
Request Lifecycle.Mock Response Value Is Returned To Caller ❌ 6ms
Retry Behavior.Retries Flags On 502 ❌ 311ms
Retry Behavior.Retries Flags On 504 ❌ 311ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event ❌ 609ms

Failures

request_payload.groups_default_to_empty_object

Field 'groups' not found in /flags request body at path 'groups'. Available keys: ['api_key', 'distinct_id', 'flag_keys_to_evaluate']

request_payload.disable_geoip_omitted_defaults_to_false

Field 'geoip_disable' not found in /flags request body at path 'geoip_disable'. Available keys: ['api_key', 'distinct_id', 'flag_keys_to_evaluate']

request_lifecycle.mock_response_value_is_returned_to_caller

Last action result missing field 'value'. Keys: ['error', 'success']

retry_behavior.retries_flags_on_502

Last action result missing field 'value'. Keys: ['error', 'success']

retry_behavior.retries_flags_on_504

Last action result missing field 'value'. Keys: ['error', 'success']

side_effect_events.get_feature_flag_captures_feature_flag_called_event

Expected 1 events with name '$feature_flag_called', got 0

Comment thread lib/posthog/open_feature.ex
Comment on lines +144 to +146
# open-feature/elixir-sdk 0.1.3 guards get_map_value/get_map_details defaults
# with is_map/1 before calling the provider, but does not restrict returned
# values. Array payloads work with a map default; list defaults need an upstream fix.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@marandaneto
marandaneto marked this pull request as ready for review September 25, 2026 06:27
@marandaneto
marandaneto requested a review from a team as a code owner September 25, 2026 06:27
@marandaneto
marandaneto merged commit 829dbe2 into posthog/openfeature-provider Sep 25, 2026
27 of 28 checks passed
@marandaneto
marandaneto deleted the feat/openfeature-provider-parity branch September 25, 2026 12:48
@marandaneto marandaneto mentioned this pull request Sep 25, 2026
3 of 5 tasks
marandaneto added a commit that referenced this pull request Sep 25, 2026
* feat: add OpenFeature provider

Add PostHog.OpenFeature.Provider for the Elixir OpenFeature SDK, with
open_feature as an optional dependency. The provider mirrors the
posthog-node OpenFeature provider: targeting_key maps to distinct_id,
groups and group_properties map to groups, and other context keys map
to person properties.

Generated-By: PostHog Desktop
Task-Id: 45365cd7-19ef-46e1-8d2a-46d2f484d229

* fix: return the default for disabled flags and update public API snapshot

The string, number, and map resolvers now check `enabled: false` before inspecting the variant or payload, so a disabled flag that still carries a remote value resolves to the caller-supplied default. Adds a test for each type.

Regenerates public_api.snapshot to include PostHog.OpenFeature.Provider, which the "Compile with warnings as errors" CI job checks.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 8167c421-7e51-4669-be55-fc7b39e39d26

* docs: describe disabled-flag resolution after resolver reorder

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 8167c421-7e51-4669-be55-fc7b39e39d26

* fix: match Node and Python providers for disabled flags and numeric variants

A disabled flag that still carries a variant or payload now resolves to that value with reason `:default`, as the Node and Python providers do. Number resolution accepts `.5`, `-.5`, and `1.`, which JavaScript's Number() and Python's float() accept but Float.parse/1 does not.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 8167c421-7e51-4669-be55-fc7b39e39d26

* feat: extend OpenFeature numeric, payload, and reason support (#216)

* feat: extend OpenFeature numeric, payload, and reason support

* docs: explain upstream OpenFeature array-default limitation

* fix: preserve OpenFeature reason metadata on type mismatches

* chore: remove redundant OpenFeature follow-up changeset

---------

Co-authored-by: Patricio <patricio@posthog.com>
Co-authored-by: Manoel Aranda Neto <5731772+marandaneto@users.noreply.github.com>
Co-authored-by: Manoel Aranda Neto <marandaneto@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants