survey-sdk-audit
Audit PostHog survey SDK features and version requirements
npx skills add https://github.com/posthog/posthog-foss --skill survey-sdk-auditSurveys SDK Feature Audit Skill
Use this skill when auditing survey feature support across PostHog SDKs for surveyVersionRequirements.ts.
Feature to audit: $ARGUMENTS
Setup Check (Run First)
Before starting, verify the SDK paths are accessible. Run ls on each path:
$POSTHOG_JS_PATH$POSTHOG_IOS_PATH$POSTHOG_ANDROID_PATH$POSTHOG_FLUTTER_PATH
If any path is empty or doesn't exist, ask the user: "I need the path to [SDK repo] on your machine. Where is it located?"
Once you have all paths, ask the user if they'd like to save them for future sessions by adding to .claude/settings.local.json:
{
"env": {
"POSTHOG_JS_PATH": "/path/to/posthog-js",
"POSTHOG_IOS_PATH": "/path/to/posthog-ios",
"POSTHOG_ANDROID_PATH": "/path/to/posthog-android",
"POSTHOG_FLUTTER_PATH": "/path/to/posthog-flutter"
},
"permissions": {
"allow": [
"Read(/path/to/posthog-js/**)",
"Read(/path/to/posthog-ios/**)",
"Read(/path/to/posthog-android/**)",
"Read(/path/to/posthog-flutter/**)",
"Grep(/path/to/posthog-js/**)",
"Grep(/path/to/posthog-ios/**)",
"Grep(/path/to/posthog-android/**)",
"Grep(/path/to/posthog-flutter/**)"
]
}
}
Note: The Read and Grep permissions grant Claude access to these external SDK repositories without prompting each time.
Using SDK Paths in Commands
IMPORTANT: Environment variables like $POSTHOG_JS_PATH do NOT expand reliably in Bash tool commands.
Instead of bash commands, prefer:
- Use the Read tool to read files (works with permissions)
- Use the Grep tool to search files (works with permissions)
If you must use bash, first expand the variable:
echo $POSTHOG_JS_PATH
Then use the echoed path directly in subsequent commands.
Issue Visibility
Survey SDK feature parity has no central tracking issue. Visibility lives at repo level: surveyVersionRequirements.ts links unsupported SDKs to their issues, and each new issue cross-links its siblings via a ## Related section (see the issue template below).
SDK Paths and Changelogs
| SDK | Code Path | Changelog |
|---|---|---|
| posthog-js (browser) | $POSTHOG_JS_PATH/packages/browser | $POSTHOG_JS_PATH/packages/browser/CHANGELOG.md |
| posthog-react-native | $POSTHOG_JS_PATH/packages/react-native | $POSTHOG_JS_PATH/packages/react-native/CHANGELOG.md |
| posthog-ios | $POSTHOG_IOS_PATH | $POSTHOG_IOS_PATH/CHANGELOG.md |
| posthog-android | $POSTHOG_ANDROID_PATH | $POSTHOG_ANDROID_PATH/CHANGELOG.md |
| posthog-flutter | $POSTHOG_FLUTTER_PATH | $POSTHOG_FLUTTER_PATH/CHANGELOG.md |
Flutter Native Dependencies
Flutter wraps native SDKs. Check dependency versions in:
- iOS:
$POSTHOG_FLUTTER_PATH/ios/posthog_flutter.podspec(look fors.dependency 'PostHog') - Android:
$POSTHOG_FLUTTER_PATH/android/build.gradle(look forposthog-androiddependency)
Audit Process
Step 1: Understand the Feature
Look at the check function in surveyVersionRequirements.ts to understand what field/condition triggers this feature:
s.conditions?.deviceTypes→ search for "deviceTypes"s.appearance?.fontFamily→ search for "fontFamily"s.conditions?.urlMatchType→ search for "urlMatchType"
Step 2: Search Changelogs First
# Search changelog for the feature keyword
grep -n -i "KEYWORD" /path/to/CHANGELOG.md
If found, read the surrounding lines to get the version number.
Step 3: Search Code If Not in Changelog
# Find commits that added the keyword (use -S for exact string match)
cd /path/to/sdk && git log --oneline --all -S "KEYWORD" -- "*.swift" "*.kt" "*.ts" "*.tsx"
# Then find the first version tag containing that commit
git tag --contains COMMIT_HASH | sort -V | head -3
Step 4: For Flutter, Find When Native Dependency Was Bumped
# Find when Flutter started requiring iOS version X.Y.Z
cd $POSTHOG_FLUTTER_PATH && git log --oneline -p -- "ios/posthog_flutter.podspec" | grep -B10 "X.Y.Z"
# Get the Flutter version for that commit
git tag --contains COMMIT_HASH | sort -V | head -1
Step 5: Verify Feature Actually Works (Not Just Types)
CRITICAL: Having a field in a data model does NOT mean the feature is implemented. You must check the actual filtering/matching logic.
SDK rendering capabilities:
- posthog-js (browser): Built-in survey rendering (HTML/CSS popup)
- posthog-react-native: Built-in survey rendering (React Native components in
packages/react-native/src/surveys/) - posthog-ios: Built-in survey rendering (SwiftUI views in
PostHog/Surveys/—SurveySheet.swift,QuestionTypes.swift,MultipleChoiceOptions.swift) - posthog-android: No built-in UI — pure delegate pattern. Exposes display models (
PostHogDisplaySurvey,PostHogDisplayChoiceQuestion, etc.) for developers to render themselves. Useissue: falsefor rendering-only features. - posthog-flutter: Built-in survey rendering (Flutter widgets in
lib/src/surveys/widgets/—survey_bottom_sheet.dart,choice_question.dart)
For SDKs with built-in rendering, a feature must be actually implemented in the rendering code, not just present as a field on the data model. For Android (delegate-only), exposing the field on the display model is sufficient — mark as issue: false with a comment.
Key files to check for survey filtering logic:
- posthog-js (browser):
$POSTHOG_JS_PATH/packages/browser/src/extensions/surveys/surveys-extension-utils.tsx- utility functions likecanActivateRepeatedly,getSurveySeen,hasEvents - posthog-js (browser):
$POSTHOG_JS_PATH/packages/browser/src/extensions/surveys.tsx- main survey logic - posthog-react-native:
$POSTHOG_JS_PATH/packages/react-native/src/surveys/getActiveMatchingSurveys.ts- main filtering logic - posthog-react-native:
$POSTHOG_JS_PATH/packages/react-native/src/surveys/surveys-utils.ts- utility functions likecanActivateRepeatedly,hasEvents - posthog-ios:
$POSTHOG_IOS_PATH/PostHog/Surveys/PostHogSurveyIntegration.swift→getActiveMatchingSurveys()method,canActivateRepeatedlycomputed property - posthog-android:
$POSTHOG_ANDROID_PATH/posthog-android/src/main/java/com/posthog/android/surveys/PostHogSurveysIntegration.kt→getActiveMatchingSurveys()method,canActivateRepeatedly()function
Key utility functions to compare across SDKs:
canActivateRepeatedly- determines if a survey can be shown again after being seenhasEvents- checks if survey has event-based triggersgetSurveySeen- checks if user has already seen the survey
Example pitfall 1: Both iOS and Android have linkedFlagKey in their Survey model, but neither implements linkedFlagVariant checking. They only call isFeatureEnabled(key) (boolean) instead of comparing flags[key] === variant.
Example pitfall 2: The browser canActivateRepeatedly checks THREE conditions: (1) event repeatedActivation, (2) schedule === 'always', (3) survey in progress. Mobile SDKs may only check condition (1), missing the schedule check entirely.
Key files to check for survey rendering logic:
- posthog-js (browser):
$POSTHOG_JS_PATH/packages/browser/src/extensions/surveys/surveys-extension-utils.tsx-getDisplayOrderQuestions(),getDisplayOrderChoices() - posthog-react-native:
$POSTHOG_JS_PATH/packages/react-native/src/surveys/surveys-utils.ts-getDisplayOrderQuestions(),getDisplayOrderChoices() - posthog-ios:
$POSTHOG_IOS_PATH/PostHog/Surveys/QuestionTypes.swift-SingleChoiceQuestionView,MultipleChoiceQuestionView;$POSTHOG_IOS_PATH/PostHog/Surveys/SurveySheet.swift- question ordering - posthog-android: No built-in UI — only check display model exposure in
$POSTHOG_ANDROID_PATH/posthog/src/main/java/com/posthog/surveys/PostHogDisplaySurveyQuestion.ktandPostHogDisplaySurveyAppearance.kt - posthog-flutter:
$POSTHOG_FLUTTER_PATH/lib/src/surveys/widgets/survey_bottom_sheet.dart- question ordering;$POSTHOG_FLUTTER_PATH/lib/src/surveys/widgets/choice_question.dart- choice rendering
What to look for:
- Is the field parsed from JSON into the model? (necessary but not sufficient)
- Is the field used in filtering logic like
getActiveMatchingSurveys()? - For rendering features: Is the field actually used by the built-in UI? (check rendering code, not just data models)
- Does the logic match the reference implementation behavior?
- Test files don't count as implementation
Reference Implementation
posthog-js browser is the canonical implementation - it has every feature and is the source of truth for how things are supposed to work.
When auditing a feature:
- First check
$POSTHOG_JS_PATH/packages/browser/src/extensions/surveys.tsto understand the complete, correct behavior - Then compare mobile SDKs against posthog-react-native (
$POSTHOG_JS_PATH/packages/react-native/src/surveys/getActiveMatchingSurveys.ts) which is the reference for mobile-specific implementations
Web-Only vs Cross-Platform Features
Some features only make sense on web:
- URL targeting: No concept of "current URL" in native apps →
issue: falsefor all mobile - CSS selector targeting: No DOM in native apps →
issue: falsefor all mobile - Custom fonts via CSS: May need native implementation or may not be applicable
Output Format
For each feature, produce:
{
feature: 'Feature Name',
sdkVersions: {
'posthog-js': 'X.Y.Z',
'posthog-react-native': 'X.Y.Z', // or omit if unsupported
'posthog-ios': 'X.Y.Z',
'posthog-android': 'X.Y.Z',
'posthog_flutter': 'X.Y.Z', // add comment: first version to require native SDK >= X.Y
},
unsupportedSdks: [
{ sdk: 'sdk-name', issue: 'https://github.com/PostHog/repo/issues/123' }, // needs implementation
{ sdk: 'sdk-name', issue: false }, // not applicable (e.g., web-only feature)
],
check: (s) => ...,
}
Creating GitHub Issues
IMPORTANT: Always search for existing issues BEFORE creating new ones.
# Search in the SDK-specific repo
gh issue list --repo PostHog/posthog-ios --search "FEATURE_KEYWORD" --state all --limit 20
gh issue list --repo PostHog/posthog-android --search "FEATURE_KEYWORD" --state all --limit 20
gh issue list --repo PostHog/posthog-flutter --search "FEATURE_KEYWORD" --state all --limit 20
# Also search with broader terms
gh issue list --repo PostHog/posthog-ios --search "survey feature flag" --state all --limit 20
Always search issues in the main repo PostHog/posthog AND the SDK-specific repo(s) to ensure an issue does not already exist anywhere.
Labels by Repository
| Repository | Labels for Survey Features |
|---|---|
| PostHog/posthog-js | feature/surveys |
| PostHog/posthog-ios | Survey, enhancement |
| PostHog/posthog-android | Survey, enhancement |
| PostHog/posthog-flutter | Survey, enhancement |
Issue Creation Command
# posthog-js (covers browser and react-native)
gh issue create --repo PostHog/posthog-js --label "feature/surveys" --title "..." --body "..."
# posthog-ios
gh issue create --repo PostHog/posthog-ios --label "Survey" --label "enhancement" --title "..." --body "..."
# posthog-android
gh issue create --repo PostHog/posthog-android --label "Survey" --label "enhancement" --title "..." --body "..."
# posthog-flutter
gh issue create --repo PostHog/posthog-flutter --label "Survey" --label "enhancement" --title "..." --body "..."
Issue Template
## 🚨 IMPORTANT
This issue is likely user-facing in the main PostHog app, see [`surveyVersionRequirements.ts`](https://github.com/PostHog/posthog/blob/master/frontend/src/scenes/surveys/surveyVersionRequirements.ts). If you delete or close this issue, be sure to update the version requirements list here.
## Summary
The [SDK] SDK does not support [feature] for surveys.
## Current State
- [What exists, if anything - types, partial implementation, etc.]
## Expected Behavior
[What should happen when this feature is configured]
## Reference Implementation
See posthog-js browser: `packages/browser/src/extensions/surveys.ts`
For mobile-specific patterns, see posthog-react-native: `packages/react-native/src/surveys/getActiveMatchingSurveys.ts`
## Related
- [links to the sibling SDK issues created for the same feature — list the ones that exist at creation time; the rest are backfilled below]
_This issue was generated by Claude using the `/survey-sdk-audit` skill._
Backfill Sibling Links
Issues are created one at a time, so earlier issues cannot link siblings that do not exist yet. After creating all issues for the feature, backfill each issue's ## Related section so every issue links every sibling:
Step 1: Fetch the current body to a temp file:
gh issue view 123 --repo PostHog/posthog-ios --json body --jq '.body' > /tmp/issue_body.md
Step 2: Use the Edit tool on the temp file to fill in the sibling links (and remove the placeholder). This lets the user review the diff before anything is pushed.
Step 3: Push the update:
gh issue edit 123 --repo PostHog/posthog-ios --body-file /tmp/issue_body.md
Completion Checklist
Before finishing the audit, verify all steps are complete:
- Understand the feature - Read the check function in
surveyVersionRequirements.ts - Check browser SDK - Find version in changelog (this is the reference implementation)
- Check react-native SDK - Find version in changelog
- Check iOS SDK - Verify if feature is actually implemented (not just types)
- Check Android SDK - Verify if feature is actually implemented (not just types)
- Check Flutter SDK - Check native dependency versions
- Search for existing issues - Before creating new ones
- Create GitHub issues - For any unsupported SDKs (with proper labels)
- Update surveyVersionRequirements.ts - Fix versions and add issue links to unsupportedSdks
- Regenerate SDK parity docs - Run
pnpm --filter=@posthog/frontend build:survey-sdk-docsto updatedocs/published/docs/surveys/sdk-feature-support.mdx - Backfill sibling links - After all issues are created, update each issue's Related section so every issue links every sibling (see Backfill Sibling Links)
Common Pitfalls
- Don't guess versions - always verify with changelog or git history
- Types ≠ Implementation - having a field in a data class doesn't mean filtering logic exists
- Model field ≠ Feature support - the field may be parsed but never used in decision-making (e.g.,
linkedFlagKeyexists butlinkedFlagVariantcheck is missing) - Test code ≠ Production code - functions only in test files aren't shipped
- Flutter inherits from native - its "support" depends on iOS/Android SDK versions it requires
- Always search for existing issues first - before creating new GitHub issues
- Compare utility function implementations - functions like
canActivateRepeatedlymay have different logic across SDKs; browser is the source of truth - Built-in UI ≠ delegate - iOS, React Native, and Flutter have built-in survey rendering; Android is delegate-only (no built-in UI). A field on the display model is sufficient for Android (
issue: false) but for SDKs with built-in rendering, the rendering code must actually use the field - Check rendering code, not just models - e.g., iOS exposes
shuffleOptionsonPostHogDisplayChoiceQuestionbut the SwiftUIQuestionTypes.swiftcompletely ignores it when rendering choices
Post-Audit: Skill Improvement
After completing an audit, consider whether any learnings should be added to this skill file:
- New pitfalls discovered - Add to Common Pitfalls section
- New key files identified - Add to the file paths in Step 5
- New utility functions to compare - Add to the "Key utility functions" list
- Pattern changes - If SDK implementations have changed structure, update paths
If you find improvements, propose them to the user:
I found some learnings during this audit that could improve the skill:
- [describe the improvement]
Would you like me to update the skill file?