survey-sdk-audit

作者: posthog

审计PostHog调查SDK的功能和版本要求

npx skills add https://github.com/posthog/posthog-foss --skill survey-sdk-audit

Surveys 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

SDKCode PathChangelog
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

# 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. 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.swiftgetActiveMatchingSurveys() method, canActivateRepeatedly computed property
  • posthog-android: $POSTHOG_ANDROID_PATH/posthog-android/src/main/java/com/posthog/android/surveys/PostHogSurveysIntegration.ktgetActiveMatchingSurveys() 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:

{
    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

RepositoryLabels for Survey Features
PostHog/posthog-jsfeature/surveys
PostHog/posthog-iosSurvey, enhancement
PostHog/posthog-androidSurvey, enhancement
PostHog/posthog-flutterSurvey, 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-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:

I found some learnings during this audit that could improve the skill:
- [describe the improvement]

Would you like me to update the skill file?