Repository navigation
fix(flags): fall back for incompatible local regex evaluation - #316
Merged
Merged
Conversation
posthog-ruby-sync Compliance ReportDate: 2026-10-23T10:23:43.339047+00:00
|
| Test | Status | Duration |
|---|---|---|
| Format Validation.Event Has Required Fields | ✅ | 10ms |
| Format Validation.Event Has Uuid | ✅ | 6ms |
| Format Validation.Event Has Lib Properties | ✅ | 6ms |
| Format Validation.Distinct Id Is String | ✅ | 9ms |
| Format Validation.Token Is Present | ✅ | 7ms |
| Format Validation.Custom Properties Preserved | ✅ | 6ms |
| Format Validation.Event Has Timestamp | ✅ | 8ms |
| Format Validation.Non Utc Event Timestamp Is Converted To Utc | ✅ | 7ms |
| Retry Behavior.Retries On 503 | ✅ | 5245ms |
| Retry Behavior.Does Not Retry On 400 | ✅ | 2010ms |
| Retry Behavior.Does Not Retry On 401 | ✅ | 2012ms |
| Retry Behavior.Respects Retry After Header | ✅ | 8016ms |
| Retry Behavior.Implements Backoff | ✅ | 15318ms |
| Retry Behavior.Retries On 500 | ✅ | 5145ms |
| Retry Behavior.Retries On 502 | ✅ | 5160ms |
| Retry Behavior.Retries On 504 | ✅ | 5165ms |
| Retry Behavior.Max Retries Respected | ✅ | 15660ms |
| Deduplication.Generates Unique Uuids | ✅ | 23ms |
| Deduplication.Preserves Uuid On Retry | ✅ | 5157ms |
| Deduplication.Preserves Uuid And Timestamp On Retry | ✅ | 10319ms |
| Deduplication.Preserves Uuid And Timestamp On Batch Retry | ✅ | 5165ms |
| Deduplication.No Duplicate Events In Batch | ✅ | 20ms |
| Deduplication.Different Events Have Different Uuids | ✅ | 8ms |
| Compression.Sends Gzip When Enabled | ✅ | 8ms |
| Batch Format.Uses Proper Batch Structure | ✅ | 5ms |
| Batch Format.Flush With No Events Sends Nothing | ✅ | 3ms |
| Batch Format.Multiple Events Batched Together | ❌ | 17ms |
| Error Handling.Does Not Retry On 403 | ✅ | 2008ms |
| Error Handling.Does Not Retry On 413 | ✅ | 2010ms |
| Error Handling.Retries On 408 | ✅ | 5117ms |
Failures
batch_format.multiple_events_batched_together
Expected 1 requests, got 5
Feature_Flags Tests
View Details
| Test | Status | Duration |
|---|---|---|
| Request Payload.Request With Person Properties Device Id | ✅ | 11ms |
| Request Payload.Flags Request Uses V2 Query Param | ✅ | 8ms |
| Request Payload.Flags Request Hits Flags Path Not Decide | ✅ | 8ms |
| Request Payload.Flags Request Omits Authorization Header | ✅ | 7ms |
| Request Payload.Token In Flags Body Matches Init | ✅ | 6ms |
| Request Payload.Groups Round Trip | ✅ | 7ms |
| Request Payload.Groups Default To Empty Object | ✅ | 6ms |
| Request Payload.Disable Geoip False Propagates As Geoip Disable False | ✅ | 7ms |
| Request Payload.Disable Geoip Omitted Defaults To False | ❌ | 6ms |
| Request Payload.Flag Keys To Evaluate Contains Only Requested Key | ✅ | 7ms |
| Request Lifecycle.No Flags Request On Init Alone | ✅ | 2ms |
| Request Lifecycle.No Flags Request On Normal Capture | ✅ | 6ms |
| Request Lifecycle.Two Flag Calls Produce Two Remote Requests | ✅ | 9ms |
| Request Lifecycle.Mock Response Value Is Returned To Caller | ✅ | 8ms |
| Retry Behavior.Retries Flags On 502 | ✅ | 109ms |
| Retry Behavior.Retries Flags On 504 | ✅ | 142ms |
| Side Effect Events.Get Feature Flag Captures Feature Flag Called Event | ✅ | 9ms |
Failures
request_payload.disable_geoip_omitted_defaults_to_false
Field 'geoip_disable' not found in /flags request body at path 'geoip_disable'. Available keys: ['distinct_id', 'groups', 'person_properties', 'group_properties', 'flag_keys_to_evaluate', 'token']
posthog-ruby-async Compliance ReportDate: 2026-10-23T10:23:56.637543+00:00
|
| Test | Status | Duration |
|---|---|---|
| Format Validation.Event Has Required Fields | ✅ | 107ms |
| Format Validation.Event Has Uuid | ✅ | 104ms |
| Format Validation.Event Has Lib Properties | ✅ | 107ms |
| Format Validation.Distinct Id Is String | ✅ | 105ms |
| Format Validation.Token Is Present | ✅ | 106ms |
| Format Validation.Custom Properties Preserved | ✅ | 106ms |
| Format Validation.Event Has Timestamp | ✅ | 105ms |
| Format Validation.Non Utc Event Timestamp Is Converted To Utc | ✅ | 8ms |
| Retry Behavior.Retries On 503 | ✅ | 5309ms |
| Retry Behavior.Does Not Retry On 400 | ✅ | 2107ms |
| Retry Behavior.Does Not Retry On 401 | ✅ | 2107ms |
| Retry Behavior.Respects Retry After Header | ✅ | 8014ms |
| Retry Behavior.Implements Backoff | ✅ | 15713ms |
| Retry Behavior.Retries On 500 | ✅ | 5211ms |
| Retry Behavior.Retries On 502 | ✅ | 5211ms |
| Retry Behavior.Retries On 504 | ✅ | 5210ms |
| Retry Behavior.Max Retries Respected | ✅ | 15512ms |
| Deduplication.Generates Unique Uuids | ✅ | 109ms |
| Deduplication.Preserves Uuid On Retry | ✅ | 5210ms |
| Deduplication.Preserves Uuid And Timestamp On Retry | ✅ | 10417ms |
| Deduplication.Preserves Uuid And Timestamp On Batch Retry | ✅ | 5211ms |
| Deduplication.No Duplicate Events In Batch | ✅ | 108ms |
| Deduplication.Different Events Have Different Uuids | ✅ | 105ms |
| Compression.Sends Gzip When Enabled | ✅ | 104ms |
| Batch Format.Uses Proper Batch Structure | ✅ | 105ms |
| Batch Format.Flush With No Events Sends Nothing | ✅ | 2ms |
| Batch Format.Multiple Events Batched Together | ✅ | 107ms |
| Error Handling.Does Not Retry On 403 | ✅ | 2107ms |
| Error Handling.Does Not Retry On 413 | ✅ | 2106ms |
| Error Handling.Retries On 408 | ✅ | 5210ms |
Feature_Flags Tests
View Details
| Test | Status | Duration |
|---|---|---|
| Request Payload.Request With Person Properties Device Id | ✅ | 107ms |
| Request Payload.Flags Request Uses V2 Query Param | ✅ | 106ms |
| Request Payload.Flags Request Hits Flags Path Not Decide | ✅ | 107ms |
| Request Payload.Flags Request Omits Authorization Header | ✅ | 107ms |
| Request Payload.Token In Flags Body Matches Init | ✅ | 105ms |
| Request Payload.Groups Round Trip | ✅ | 105ms |
| Request Payload.Groups Default To Empty Object | ✅ | 104ms |
| Request Payload.Disable Geoip False Propagates As Geoip Disable False | ✅ | 105ms |
| Request Payload.Disable Geoip Omitted Defaults To False | ❌ | 106ms |
| Request Payload.Flag Keys To Evaluate Contains Only Requested Key | ✅ | 105ms |
| Request Lifecycle.No Flags Request On Init Alone | ✅ | 3ms |
| Request Lifecycle.No Flags Request On Normal Capture | ✅ | 104ms |
| Request Lifecycle.Two Flag Calls Produce Two Remote Requests | ✅ | 109ms |
| Request Lifecycle.Mock Response Value Is Returned To Caller | ✅ | 105ms |
| Retry Behavior.Retries Flags On 502 | ✅ | 239ms |
| Retry Behavior.Retries Flags On 504 | ✅ | 208ms |
| Side Effect Events.Get Feature Flag Captures Feature Flag Called Event | ✅ | 107ms |
Failures
request_payload.disable_geoip_omitted_defaults_to_false
Field 'geoip_disable' not found in /flags request body at path 'geoip_disable'. Available keys: ['distinct_id', 'groups', 'person_properties', 'group_properties', 'flag_keys_to_evaluate', 'token']
Contributor
There was a problem hiding this comment.
PostHog Review · Standard · reviewed d75ef7f: 1 Should fix.
This is the only automatic review while this pull request is a draft. Comment @posthog review to review new changes sooner. Otherwise, a review of the new changes runs when you mark it ready for review.
marandaneto
marked this pull request as ready for review
October 23, 2026 08:42
ioannisj
approved these changes
Oct 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
💡 Motivation and Context
Local feature flag regex evaluation can return a different boolean from the service even when Ruby compiles the pattern. For example, Ruby's
\d+does not match١, while the service matches it. Ruby also treats^foo$as a line match within a multiline value, unlike the service's default absolute anchors. These local booleans prevent remote fallback.This adapts the agreed shared-syntax/local and unsupported-syntax/fallback behavior from posthog-js #5354 and its cross-SDK discussion to Ruby's Regexp semantics. It does not assume that JS PR is merged or released.
The private classifier now raises the existing inconclusive error for incompatible or uncertain cases. Remote-capable callers fall back, and local-only callers remain unresolved.
not_regexonly negates successful searches. Invalid patterns in the supported subset still return false for both operators, and unsupported failures cannot become matches through cohort negation.Common patterns stay local, including ASCII shorthand, scalar Unicode literals and dot, empty patterns, escaped punctuation and class-local backspace. The compatibility scan now tokenizes multibyte patterns once and parses repetition bounds with a forward-only cursor instead of repeatedly indexing Unicode strings or copying suffixes. This preserves Unicode literals locally with O(pattern length) token storage. Coverage is deliberately reduced for uncertain syntax, POSIX/set classes, Unicode shorthand on non-ASCII values, newline-sensitive anchors, boundaries and some repetitions. This does not claim universal Unicode, compiler-limit or backtracking-budget equivalence. Existing operand stringification and property-matching-version behavior are unchanged. No public API, dependency or regex engine was added.
💚 How did you test it?
After safely fast-forwarding this branch to current main, on Ruby 4.0.7 with the committed bundle:
The traversal concern was reproduced before source changes on Ruby 4.0.7. Median classifier timings for 25k/50k/100k multibyte literals were 138/551/2191 ms before and 3.2/6.1/12.1 ms after. The real matcher at 100k went from 2272 ms to 15.2 ms. Repeated-bound patterns also scaled approximately linearly afterward. These are bounded three-sample measurements, not unit-test timing thresholds or universal latency guarantees.
Other Ruby versions and the optional OpenTelemetry bundle were not run locally. No live PostHog service was used. CI results are separate from these local checks.
At the post-push CI checkpoint, RSpec on Ruby 3.2/3.3/3.4, both locked OpenTelemetry integration jobs, lint, API and gem build passed. The latest Rails / stable Ruby job failed 48 MCP integration examples after its fresh dependency resolution, including MCP 1.8.0. Those failures were recorded, not investigated or fixed as part of this regex traversal comment. Overall CI is not green.
📝 Checklist
If releasing new changes
posthog-rubypatch changeset directly, followingRELEASING.md. No version bump was made.🤖 Agent context
Autonomy: Human-driven (agent-assisted)
@marandaneto directed this Ruby port. A Pi worker used local Git, Ruby/RSpec, the frozen Rust service oracle, deslop, test-audit and isolated branch autoreview. The session transcript is local, not publicly linked. The implementation uses conservative existing fallback instead of replacing the regex engine.
Final branch autoreview on
dadad6eb8992dd46cfcff72a98e98b3476bc450cagainstorigin/maincompleted with no findings. Earlier class-backspace concerns were checked with actual service and Ruby probes rather than plain Rust regex assumptions. Human review is still required.