Installs into .claude/skills of the current project.
Are you the author of Survey Sdk Audit?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/posthog-survey-sdk-audit-posthog)
---
name: survey-sdk-audit
description: Audit PostHog survey SDK features and version requirements, and implement survey changes across the backend, UI, and SDK repositories.
---
# Surveys SDK Feature Audit Skill
Use this skill when auditing survey feature support across PostHog SDKs for `surveyVersionRequirements.ts`.
**Feature to audit:** $ARGUMENTS
For survey feature work, read [Contributing survey changes](references/contributing-surveys.md).
For customer diagnosis without a repository checkout, use the published `debugging-surveys` skill.
## 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`:
```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:
```bash
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 for `s.dependency 'PostHog'`)
- Android: `$POSTHOG_FLUTTER_PATH/android/build.gradle` (look for `posthog-android` dependency)
## 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
```bash
# 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
```bash
# 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
```bash
# 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. Use `issue: false` for 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 like `canActivateRepeatedly`, `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 like `canActivateRepeatedly`, `hasEvents`
- **posthog-ios**: `$POSTHOG_IOS_PATH/PostHog/Surveys/PostHogSurveyIntegration.swift` → `getActiveMatchingSurveys()` method, `canActivateRepeatedly` computed 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 seen
- `hasEvents` - checks if survey has event-based triggers
- `getSurveySeen` - 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.kt` and `PostHogDisplaySurveyAppearance.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:
1. First check `$POSTHOG_JS_PATH/packages/browser/src/extensions/surveys.ts` to understand the complete, correct behavior
2. 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: false` for all mobile
- **CSS selector targeting**: No DOM in native apps → `issue: false` for all mobile
- **Custom fonts via CSS**: May need native implementation or may not be applicable
## Output Format
For each feature, produce:
```typescript
{
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.**
```bash
# 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
```bash
# 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
```markdown
## 🚨 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:
```bash
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:
```bash
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-docs` to update `docs/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
1. **Don't guess versions** - always verify with changelog or git history
2. **Types ≠ Implementation** - having a field in a data class doesn't mean filtering logic exists
3. **Model field ≠ Feature support** - the field may be parsed but never used in decision-making (e.g., `linkedFlagKey` exists but `linkedFlagVariant` check is missing)
4. **Test code ≠ Production code** - functions only in test files aren't shipped
5. **Flutter inherits from native** - its "support" depends on iOS/Android SDK versions it requires
6. **Always search for existing issues first** - before creating new GitHub issues
7. **Compare utility function implementations** - functions like `canActivateRepeatedly` may have different logic across SDKs; browser is the source of truth
8. **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
9. **Check rendering code, not just models** - e.g., iOS exposes `shuffleOptions` on `PostHogDisplayChoiceQuestion` but the SwiftUI `QuestionTypes.swift` completely ignores it when rendering choices
## Post-Audit: Skill Improvement
After completing an audit, consider whether any learnings should be added to this skill file:
1. **New pitfalls discovered** - Add to Common Pitfalls section
2. **New key files identified** - Add to the file paths in Step 5
3. **New utility functions to compare** - Add to the "Key utility functions" list
4. **Pattern changes** - If SDK implementations have changed structure, update paths
If you find improvements, propose them to the user:
```plaintext
I found some learnings during this audit that could improve the skill:
- [describe the improvement]
Would you like me to update the skill file?
```