Skip to content

fix(flags): compare numeric string properties numerically - #270

Merged
dustinbyrne merged 1 commit into
mainfrom
fix/numeric-string-flag-comparisons
Oct 20, 2026
Merged

dustinbyrne merged 1 commit into
mainfrom
fix/numeric-string-flag-comparisons

Conversation

@dustinbyrne

@dustinbyrne dustinbyrne commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

💡 Motivation and Context

Local gt, gte, lt, and lte comparisons could order numeric strings lexicographically, changing flag membership relative to remote evaluation. For example, "10" gt 2 should match and "100" lt 30 should not.

Use complete ASCII decimal parsing and finite binary64 comparison, following the shared SDK specification. Existing numeric JSON filter operands remain supported alongside the spec's string operands. Invalid actual values are definitive no-matches; invalid conditions remain inconclusive and retain existing remote-fallback eligibility. Native integer and float properties are converted to binary64 before comparison.

Related to PostHog/posthog-python#1077. Part of the coordinated Python, Ruby/Rails, PHP and Java server correction.

Related SDK PRs:

💚 How did you test it?

  • 731 relevant PHPUnit tests, including public local-evaluation and fallback coverage.
  • 1,339 suite tests pass when excluding the unchanged ForkCurl OS argument-length failure.
  • Scoped lint, API snapshot and adapter tests.

The unfiltered local suite hits an existing macOS escapeshellarg length fatal in ForkCurl; the same failure reproduces on base. Coverage was not measured.

Regression coverage checks all four operators, numeric/string operands, strict parsing, non-finite values, rounding, null/missing properties and local-only versus remote fallback. The same regressions were checked across the affected SDKs. Scoped patch release metadata is included because evaluated membership can change.

📝 Checklist

  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I included scoped patch release metadata for the behavior correction.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Implemented with Pi and isolated SDK workers. Ran simplify and the pre-PR review loop with fresh read-only reviewers; shared findings were checked against all four SDKs. Human review is still required.

@dustinbyrne
dustinbyrne requested a review from a team as a code owner October 19, 2026 23:25
@dustinbyrne dustinbyrne self-assigned this Oct 19, 2026

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@github-actions

Copy link
Copy Markdown
Contributor

posthog-php-fork_curl Compliance Report

Date: 2026-10-19T23:27:59.597812+00:00
Duration: 112879ms

⚠️ Some Tests Failed

36/47 tests passed, 11 failed


Capture Tests

⚠️ 19/30 tests passed, 11 failed

View Details
Test Status Duration
Format Validation.Event Has Required Fields ✅ 35ms
Format Validation.Event Has Uuid ✅ 527ms
Format Validation.Event Has Lib Properties ✅ 528ms
Format Validation.Distinct Id Is String ✅ 531ms
Format Validation.Token Is Present ✅ 530ms
Format Validation.Custom Properties Preserved ✅ 531ms
Format Validation.Event Has Timestamp ✅ 530ms
Format Validation.Non Utc Event Timestamp Is Converted To Utc ✅ 530ms
Retry Behavior.Retries On 503 ❌ 5532ms
Retry Behavior.Does Not Retry On 400 ✅ 2535ms
Retry Behavior.Does Not Retry On 401 ✅ 2534ms
Retry Behavior.Respects Retry After Header ❌ 5534ms
Retry Behavior.Implements Backoff ❌ 15547ms
Retry Behavior.Retries On 500 ❌ 5538ms
Retry Behavior.Retries On 502 ❌ 5536ms
Retry Behavior.Retries On 504 ❌ 5536ms
Retry Behavior.Max Retries Respected ❌ 15546ms
Deduplication.Generates Unique Uuids ✅ 538ms
Deduplication.Preserves Uuid On Retry ❌ 5533ms
Deduplication.Preserves Uuid And Timestamp On Retry ❌ 10541ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ❌ 5538ms
Deduplication.No Duplicate Events In Batch ✅ 539ms
Deduplication.Different Events Have Different Uuids ✅ 531ms
Compression.Sends Gzip When Enabled ✅ 533ms
Batch Format.Uses Proper Batch Structure ✅ 529ms
Batch Format.Flush With No Events Sends Nothing ✅ 518ms
Batch Format.Multiple Events Batched Together ✅ 521ms
Error Handling.Does Not Retry On 403 ✅ 2529ms
Error Handling.Does Not Retry On 413 ✅ 2534ms
Error Handling.Retries On 408 ❌ 5536ms

Failures

retry_behavior.retries_on_503

Expected at least 3 requests, got 1

retry_behavior.respects_retry_after_header

Expected at least 2 requests, got 1

retry_behavior.implements_backoff

Expected at least 3 requests, got 1

retry_behavior.retries_on_500

Expected at least 2 requests, got 1

retry_behavior.retries_on_502

Expected at least 2 requests, got 1

retry_behavior.retries_on_504

Expected at least 2 requests, got 1

retry_behavior.max_retries_respected

Expected 4 requests, got 1

deduplication.preserves_uuid_on_retry

Need at least 2 requests to check retry

deduplication.preserves_uuid_and_timestamp_on_retry

Expected at least 3 requests, got 1

deduplication.preserves_uuid_and_timestamp_on_batch_retry

Expected at least 2 requests, got 1

error_handling.retries_on_408

Expected at least 2 requests, got 1

Feature_Flags Tests

✅ 17/17 tests passed

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

@github-actions

Copy link
Copy Markdown
Contributor

posthog-php-lib_curl Compliance Report

Date: 2026-10-19T23:28:05.943755+00:00
Duration: 118429ms

✅ All Tests Passed!

47/47 tests passed


Capture Tests

✅ 30/30 tests passed

View Details
Test Status Duration
Format Validation.Event Has Required Fields ✅ 29ms
Format Validation.Event Has Uuid ✅ 523ms
Format Validation.Event Has Lib Properties ✅ 525ms
Format Validation.Distinct Id Is String ✅ 525ms
Format Validation.Token Is Present ✅ 526ms
Format Validation.Custom Properties Preserved ✅ 525ms
Format Validation.Event Has Timestamp ✅ 525ms
Format Validation.Non Utc Event Timestamp Is Converted To Utc ✅ 525ms
Retry Behavior.Retries On 503 ✅ 5835ms
Retry Behavior.Does Not Retry On 400 ✅ 2528ms
Retry Behavior.Does Not Retry On 401 ✅ 2527ms
Retry Behavior.Respects Retry After Header ✅ 8533ms
Retry Behavior.Implements Backoff ✅ 16250ms
Retry Behavior.Retries On 500 ✅ 5635ms
Retry Behavior.Retries On 502 ✅ 5631ms
Retry Behavior.Retries On 504 ✅ 5635ms
Retry Behavior.Max Retries Respected ✅ 17052ms
Deduplication.Generates Unique Uuids ✅ 233ms
Deduplication.Preserves Uuid On Retry ✅ 5633ms
Deduplication.Preserves Uuid And Timestamp On Retry ✅ 10832ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 5145ms
Deduplication.No Duplicate Events In Batch ✅ 531ms
Deduplication.Different Events Have Different Uuids ✅ 526ms
Compression.Sends Gzip When Enabled ✅ 525ms
Batch Format.Uses Proper Batch Structure ✅ 525ms
Batch Format.Flush With No Events Sends Nothing ✅ 520ms
Batch Format.Multiple Events Batched Together ✅ 514ms
Error Handling.Does Not Retry On 403 ✅ 2527ms
Error Handling.Does Not Retry On 413 ✅ 2528ms
Error Handling.Retries On 408 ✅ 5633ms

Feature_Flags Tests

✅ 17/17 tests passed

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

@github-actions

Copy link
Copy Markdown
Contributor

posthog-php-socket Compliance Report

Date: 2026-10-20T15:03:36.549884+00:00
Duration: 148479ms

⚠️ Some Tests Failed

36/47 tests passed, 11 failed


Capture Tests

⚠️ 19/30 tests passed, 11 failed

View Details
Test Status Duration
Format Validation.Event Has Required Fields ✅ 23ms
Format Validation.Event Has Uuid ✅ 518ms
Format Validation.Event Has Lib Properties ✅ 520ms
Format Validation.Distinct Id Is String ✅ 521ms
Format Validation.Token Is Present ✅ 520ms
Format Validation.Custom Properties Preserved ✅ 521ms
Format Validation.Event Has Timestamp ✅ 520ms
Format Validation.Non Utc Event Timestamp Is Converted To Utc ✅ 520ms
Retry Behavior.Retries On 503 ❌ 9229ms
Retry Behavior.Does Not Retry On 400 ✅ 2525ms
Retry Behavior.Does Not Retry On 401 ✅ 2523ms
Retry Behavior.Respects Retry After Header ❌ 9231ms
Retry Behavior.Implements Backoff ❌ 19228ms
Retry Behavior.Retries On 500 ❌ 8747ms
Retry Behavior.Retries On 502 ❌ 9230ms
Retry Behavior.Retries On 504 ❌ 9236ms
Retry Behavior.Max Retries Respected ❌ 19273ms
Deduplication.Generates Unique Uuids ✅ 525ms
Deduplication.Preserves Uuid On Retry ❌ 9230ms
Deduplication.Preserves Uuid And Timestamp On Retry ❌ 14229ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ❌ 8737ms
Deduplication.No Duplicate Events In Batch ✅ 525ms
Deduplication.Different Events Have Different Uuids ✅ 524ms
Compression.Sends Gzip When Enabled ✅ 520ms
Batch Format.Uses Proper Batch Structure ✅ 519ms
Batch Format.Flush With No Events Sends Nothing ✅ 516ms
Batch Format.Multiple Events Batched Together ✅ 510ms
Error Handling.Does Not Retry On 403 ✅ 2522ms
Error Handling.Does Not Retry On 413 ✅ 2521ms
Error Handling.Retries On 408 ❌ 5524ms

Failures

retry_behavior.retries_on_503

Expected at least 3 requests, got 1

retry_behavior.respects_retry_after_header

Expected at least 2 requests, got 1

retry_behavior.implements_backoff

Expected at least 3 requests, got 1

retry_behavior.retries_on_500

Expected at least 2 requests, got 1

retry_behavior.retries_on_502

Expected at least 2 requests, got 1

retry_behavior.retries_on_504

Expected at least 2 requests, got 1

retry_behavior.max_retries_respected

Expected 4 requests, got 1

deduplication.preserves_uuid_on_retry

Need at least 2 requests to check retry

deduplication.preserves_uuid_and_timestamp_on_retry

Expected at least 3 requests, got 1

deduplication.preserves_uuid_and_timestamp_on_batch_retry

Expected at least 2 requests, got 1

error_handling.retries_on_408

Expected at least 2 requests, got 1

Feature_Flags Tests

✅ 17/17 tests passed

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

@dustinbyrne
dustinbyrne merged commit d2e0dbd into main Oct 20, 2026
34 of 35 checks passed
@dustinbyrne
dustinbyrne deleted the fix/numeric-string-flag-comparisons branch October 20, 2026 21:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants