Skip to content

docs(flags): defer local regex evaluation that could diverge from the service - #144

Draft
posthog[bot] wants to merge 3 commits into
mainfrom
posthog/defer-engine-divergent-local-regex
Draft

posthog[bot] wants to merge 3 commits into
mainfrom
posthog/defer-engine-divergent-local-regex

Conversation

@posthog

@posthog posthog Bot commented Oct 24, 2026

Copy link
Copy Markdown
Contributor

Why

Seven SDKs fixed the same bug within two days, all referencing one agreed cross-SDK approach (posthog-js#5354 and its cross-SDK discussion): local flag evaluation could return a definitive regex result that disagrees with the flags service's Rust engine, and because the result was definitive it also suppressed the remote fallback that would have been correct.

The Regular-expression criteria perform safe case-sensitive search requirement only made an SDK defer when its own engine could not handle a pattern. A pattern that compiles and executes locally but means something different to the service was — and still is, in spec terms — compliant.

Source SDK changes:

SDK PR Divergence it stopped guessing about
posthog-js #5354 ^\d+$ vs Arabic digits, \p{L} without Unicode mode, \b/\B vs the service's backtracking budget
posthog-python #1108 ^foo$ matching "foo\n", [a&&b], Unicode property escapes
posthog-go #387 RE2 ^\d+$ vs ١, lookarounds/backreferences only the service compiles
posthog-android #876 same classification, server-side local evaluator
posthog-dotnet #351 same
posthog-php #277 same
posthog-ruby #316 same

What changed

One requirement in local-feature-flag-evaluator, proposed, applied and archived on this branch:

  • Unescaped ^ / $ anchor to the whole input, with no trailing-line-feed exemption (^foo$ does not match "foo\n"). An SDK whose engine differs translates the anchor or defers — this is the concrete result-level rule behind posthog-python's strict end anchors.
  • Local regex matching is limited to a portable subset on which the SDK's engine agrees with the service. Outside that subset the criterion is inconclusive even when the local engine would return a definitive result. The named divergence classes are Unicode inputs under shorthand/property classes, \b/\B outside a character class, quantified/nested/oversized repetition, and constructs only one engine compiles.
  • Deferral keeps the existing guarantees: never a match via not_regex or cohort negation, and local-only evaluation stays unresolved rather than guessing.
  • An SDK may defer instead of returning the definitive invalid-pattern no-match when its engine's notion of invalidity differs from the service's — posthog-go documents exactly this limitation for ambiguous brace forms like a{2 }.
  • New scenarios in the spec and in acceptance/private/local-feature-flag-evaluator.feature, plus strict end-anchor rows on the existing outline.

Conservative deferral means more /flags requests and more unresolved local-only results. That is the intended trade against a wrong flag value, and the spec now says so.

Uncertain / for reviewers

  • New acceptance steps. The deferral scenarios need harness bindings for a divergence-class pattern/input pair and for "no remote flag evaluation request should be sent". No adapter implements them yet.
  • The divergence list is descriptive, not a closed set. It is drawn from what the seven SDKs classify today. Reviewers closer to the flags service should check whether anything is missing or over-stated.
  • Not specified here: posthog-go and posthog-python make an unsupported-regex deferral sticky across cohort and dependency boundaries, so a later catch-all variant cannot resolve a flag whose earlier regex was unresolved. The SDKs do not obviously agree on that yet, so it is left out — flagging it as a separate question.
  • openspec validate --specs --strict reports the same pre-existing long-requirement findings before and after this change; the change itself validates.

Created with PostHog Desktop

🤖 Generated with Claude Code

dustinbyrne and others added 3 commits October 8, 2026 13:27
… service

Seven SDKs converged on limiting local regex matching to a portable subset and
deferring to the flags service everywhere their engine could disagree. The spec
only required deferral when the SDK's own engine could not handle the pattern, so
a compiling-but-divergent definitive boolean was compliant and suppressed remote
fallback.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 8e7f7655-037c-4a54-9dc4-321bf02219fe
@turnipdabeets
turnipdabeets changed the base branch from docs/acceptance-step-catalogue to main October 27, 2026 22:00
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