Repository navigation
docs(flags): read an empty variant as disabled in get-feature-flag-result - #193
Conversation
…ctive_feature_flags Add on-feature-flags and capture requirements, list posthog-kmp in the impact, and record the posthog-js core and react-native gap. Also refresh the Android 4.0 and Flutter 6.0 reload signatures and Flutter 6.0's per-call sendFeatureFlagEvent option.
|
worth checking with flags if this is ok, it was intentionally set as empty |
gustavohstrassburger
left a comment
There was a problem hiding this comment.
Approving. I checked how many flags this actually affects: in US there are only 6 flags with a "" variant key (5 active, across 4 projects). Since PostHog/posthog#118803 the API no longer accepts new "" variant keys, so that number can only go down. I haven't checked EU yet.
Given that, I'm fine keeping the behavior this PR specifies. I'd still reach out to those customers so they rename these invalid variants, or at least confirm the change doesn't affect them before the iOS 4.0 / Android 4.0 / Flutter 6.0 majors ship. Low risk, but not zero: /flags still returns enabled: true for these flags, so anyone relying on that today will see it flip to false after upgrading.
…results Where getAllFeatureFlags() returns flag result objects (iOS 4.0+, Android 4.0+, Flutter 6.0+, KMP), each entry matches getFeatureFlagResult, so a "" flag stays in the list with enabled: false. Key-to-value maps keep "". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…st main 752f899 and #146 d387ab7 Score Capture against the client $lib pair fill #146 now requires (posthog-android#956), the #193 empty-variant rule and kept null array elements (#955). Drop the settled compression and $lib conflict notes: #146 exempts a lane with its own compression setting from the opt-out. Update line numbers and the MIGRATION.md links, list the out-of-scope rows later 4.0 and spec changes leave stale, and add note anchors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…52f899 and #146 d387ab7 Capture, Capture Exception and Capture AI move to Pass now that null-valued properties are omitted (posthog-ios#1047) and the SDK fills its own $lib pair after the caller's properties (posthog-ios#1050). On Feature Flags moves to Pass: loads in flight across reset() are dropped (posthog-ios#1048) and empty variants are left out of listener flags. HTTP Client is scored Pass on its merits, since #146 now leaves a lane with its own compression setting out of the whole-client opt-out. Surveys stays Partial only for the popup-delay eligibility re-check (SHOULD); posthog-ios#1049 fixed the rest. Opt In meets the new deferred-crash consent requirement (#199); the empty variant (#193), variant key rename (#203) and job-retry (#205) changes need no other status change. Line references move to e27cbe4cb. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(compliance): re-audit posthog-android capture rows for 4.0 Re-score Capture, Capture AI, Event Batcher, HTTP Client and Retry Queue against posthog-android 4.0 (be3ed4b4) and the capture v1 text in #146 and #147. Capture AI now applies to android. Capture, Capture AI and HTTP Client move to partial on post-beforeSend uuid/options restore and a caller httpClient's redirect policy; Retry Queue stays a pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): re-check posthog-android 4.0 rows against #146 at 5c26b9b Re-checks the five capture v1 rows at posthog-android 8b63a135 and #146 at 5c26b9b, which includes #147. #146 now allows the post-hook uuid and options restore and the redirect rule on a caller's httpClient, so Capture and HTTP Client stay ✅ and Capture AI scores ✅. Android 4.0 now keeps $lib in properties (#942). Two #146 conflicts (compression opt-out scope and client $lib precedence) are recorded as open questions, not scored. Tally 38/16/5/5. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): score posthog-android 4.0 rows against #146 as merged Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): treat the #146 $lib precedence conflict as resolved for clients Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): re-check posthog-android 4.0 rows at aaa23d39 against main 752f899 and #146 d387ab7 Score Capture against the client $lib pair fill #146 now requires (posthog-android#956), the #193 empty-variant rule and kept null array elements (#955). Drop the settled compression and $lib conflict notes: #146 exempts a lane with its own compression setting from the opt-out. Update line numbers and the MIGRATION.md links, list the out-of-scope rows later 4.0 and spec changes leave stale, and add note anchors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): re-audit posthog-android consent, identify, setup, shutdown and lifecycle rows at 4.0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): re-audit posthog-android remote config, flag cache, reload, flag-called tracker, before-send and logs rows at 4.0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): re-audit posthog-android flag-read rows at 4.0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): re-audit posthog-android session replay rows at 4.0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): re-audit posthog-android surveys and exception rows at 4.0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): update posthog-android 4.0 totals, scope and README roll-ups Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): re-score posthog-android Alias at 4.0 alias() sets properties.distinct_id since PostHog/posthog-android#819, which is in aaa23d39, so the row moves from ❌ to ✅ and joins the 4.0 re-audit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): score posthog-android Capture partial for the identity-call guide gap #146 at 15501ca requires migration guides to show a process_person_profile: true call option on identity calls. The 4.0 MIGRATION.md doesn't yet; PostHog/posthog-android#966 adds it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): add the five newer contracts to posthog-android as N/A Experiment Exposure, AI Datasets, AI Scorers, Offline Evaluations and Emails join the matrix, matching main's 69 contracts and the iOS 4.0 page. All are N/A: android requests /flags?v=2 and the other four are server-only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): keep posthog-android Capture passing while tracking 4.0 The identity-call migration-guide example #146 requires lands in 4.0 with PostHog/posthog-android#966, which targets the release branch. This audit tracks 4.0, so a pending release-branch PR is noted rather than scored as a gap. Undoes the Capture downgrade from cfc72e1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): make the 69-contract README note SDK-neutral The same paragraph lands from both the iOS and Android 4.0 audits, so it merges cleanly in either order and stays true after both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): point posthog-android Identify remediation at #967 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* chore(compliance): audit posthog-ios 4.0 Re-audit all 64 posthog-ios rows at 3460e534a (4.0 release branch) against sdk-specs dd11a6b, add the five rows added to the README table since the last audit, and update the iOS roll-up and gap entries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(compliance): score posthog-ios HTTP Client partial compression = .none leaves captureAi requests gzipped through the separate captureAiCompression setting, so iOS 4.0 has no whole-client compression opt-out. Roll-up becomes 40/17/1/11 (58%), 18 gaps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): list iOS capture v1 retries as matching #146 Draft #146 already permits the client-SDK durable queue, unlimited attempts, 1-30 s backoff and multi-event 413 halving for iOS 4.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(compliance): score posthog-ios Session Replay Privacy partial SwiftUI's postHogMask() wins over an enclosing postHogNoMask(), the reverse of the spec's unmask-over-mask precedence. It over-masks rather than leaks. Roll-up becomes 39/18/1/11 (57%), 19 gaps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): re-check posthog-ios 4.0 at 1f91c5b2 against main and #146 Re-checks the iOS audit at 1f91c5b2 and sdk-specs 8306698. #1036 keeps $lib/$lib_version, which now matches #146. Opt In meets the #138 replay restart requirements. Surveys gains a #102 gap: setSurveySeen moves the last-seen date on submit and dismiss. HTTP Client goes back to ✅: its compression gap comes from conflicting http-client and capture-ai rules in #146, raised on that PR. Tally 40/17/1/11. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): score posthog-ios 4.0 capture v1 rows against #146 as merged Scores Capture, Capture AI, Before Send Hook, Event Batcher, HTTP Client and Retry Queue against #146 at 5c26b9b, treated as merged. The capture v1 wire, transport and retry policy conform. Capture AI applies to iOS 4.0 and is 🟡 for the same NSNull serialization gap as Capture; starting the AI queue at setup is what #146 asks of client SDKs. Tally 40/18/1/10. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): treat the #146 $lib precedence conflict as resolved for clients Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): re-check posthog-ios 4.0 at e27cbe4cb against main 752f899 and #146 d387ab7 Capture, Capture Exception and Capture AI move to Pass now that null-valued properties are omitted (posthog-ios#1047) and the SDK fills its own $lib pair after the caller's properties (posthog-ios#1050). On Feature Flags moves to Pass: loads in flight across reset() are dropped (posthog-ios#1048) and empty variants are left out of listener flags. HTTP Client is scored Pass on its merits, since #146 now leaves a lane with its own compression setting out of the whole-client opt-out. Surveys stays Partial only for the popup-delay eligibility re-check (SHOULD); posthog-ios#1049 fixed the rest. Opt In meets the new deferred-crash consent requirement (#199); the empty variant (#193), variant key rename (#203) and job-retry (#205) changes need no other status change. Line references move to e27cbe4cb. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): score posthog-ios Surveys as pass The popup-delay and wait-period SHALLs are met since posthog-ios#1049. The one unmet requirement is a SHOULD (re-check eligibility when the delay elapses), which is recorded but not scored. Totals become 45/13/1/10. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): score iOS Exception Steps partial on NSNull normalization Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): score posthog-ios Capture partial until the migration guide shows the identity call option #146 at 15501ca requires migration guides to show a per-call process_person_profile: true on identity calls; MIGRATION.md at e27cbe4cb lacks it until PostHog/posthog-ios#1053 merges. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): note the README's 69 contracts and the iOS row's 69-row basis Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): keep posthog-ios Capture passing while tracking 4.0 The identity-call migration-guide example #146 requires lands in 4.0 with PostHog/posthog-ios#1053, which targets the release branch. This audit tracks 4.0, so a pending release-branch PR is noted rather than scored as a gap. Reverts 85d4acd. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(compliance): make the 69-contract README note SDK-neutral The same paragraph lands from both the iOS and Android 4.0 audits, so it merges cleanly in either order and stays true after both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The pin move brings in PostHog/sdk-specs#193, which adds "Listener does not receive an empty variant flag" to acceptance/public/on-feature-flags.feature. Like the other four, it is not applicable to the server profile. The SDK_V2_SPECS-gated boundary test counted four.
Summary
Say what a flag whose variant is the empty string
""reads as: disabled.get-feature-flag-result: new Empty variant reads as disabled requirement (enabled: false,variant: "", andis-feature-enabledreturnsfalse), with a@bothscenario in the spec and inacceptance/public/get-feature-flag-result.feature. The client and server flow descriptions say the same.is-feature-enabled: the server-side flow says "non-empty variant string →true", matching the client-side flow.openspec/changes/archive/2026-11-06-empty-variant-disabled/.Why
posthog-ios #1001 made
isFeatureEnabledreturnfalsefor"", butgetFeatureFlagResult().enabledstayedtruethere and on Android (review thread). posthog-js (browser) already reads""as disabled everywhere, so it's named the winner. Since PostHog/posthog#118803 (2026-10-16) PostHog rejects new""variant keys, so only older flags created through the API, MCP or Terraform can hit this.SDK PRs:
Notes
@bothbecause the server-side flow now says the same thing.""cell incached feature flags are:is meant as the empty string; a feature comment says so.openspec validate --all --strict: 69 passed.🤖 Generated with Claude Code