Skip to content

fix(detection): stop generic Node claiming Vite apps and Android claiming Flutter#817

Merged
gewenyu99 merged 5 commits into
mainfrom
posthog-code/framework-detection-fixes
Jul 9, 2026
Merged

fix(detection): stop generic Node claiming Vite apps and Android claiming Flutter#817
gewenyu99 merged 5 commits into
mainfrom
posthog-code/framework-detection-fixes

Conversation

@gewenyu99

@gewenyu99 gewenyu99 commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Item E1–E2 from the 2026-07-07 triage — the "$8.29 bug": wrong-framework detection burning agent spend. Simplified per review: no new detection machinery, no framework knowledge in the wrong config — the whole ordering fix is a two-line enum reorder.

The fix

detectFramework's first-match loop over Object.values(Integration) was already the right mechanism (the enum's doc comment even says "Detection order matters") — but the fallback section declared javascriptNode (matches any package.json) before javascript_web (requires a lockfile + a frontend signal). So generic Node claimed every frontend project that matched no specific framework — Vite apps were just the reported case.

The enum's fallback section is now python, ruby, javascript_web, javascriptNode, with a comment explaining why node must stay last. framework.ts is untouched. A Vite/browser app falls to javascript_web via ordering alone.

Flutter-as-Android

android's detect() bails on a root pubspec.yaml, so Flutter projects are never claimed via their android/ subtree (manifests + MainActivity.kt satisfy its glob strategy — the code carried a "we'll figure that out later" comment about exactly this). Unlike the JS case there's no more-specific config to order ahead — Flutter has no framework config yet — so the exclusion is the only lever. A Flutter project now gets "could not auto-detect" instead of a wrong integration; a proper flutter config (integration-flutter exists in the skill menu) is the follow-up.

Tests (9)

  • Enum-order invariants: every language fallback after every framework; javascriptNode last overall (anything after it would be unreachable for a JS project).
  • End-to-end over real temp dirs: a Next.js app beats the fallbacks, a Vite React app → javascript_web, a plain browser app (lockfile + index.html — previously misclaimed by node) → javascript_web, an express project still → javascript_node, a Flutter project claimed by nothing.
  • android detect: claims native Android, refuses Flutter.

pnpm build && pnpm test green (1220 tests).

🤖 Generated with Claude Code

…ming Flutter

Two misdetections from the 2026-07-07 triage (the "$8.29 bug" — wrong-framework integrations burning agent spend):

- javascript-node's detect() matched on package.json alone, and it sits before javascript_web in the detection loop, so every Vite app was claimed as a server-side Node project and got posthog-node integrated into a browser codebase (2 users hit this). It now returns false when `vite` is a declared dependency or a vite.config.* exists, letting detection fall through to javascript_web.

- android's detect() claimed Flutter projects via their android/ subtree (manifests + MainActivity.kt satisfy strategy 2 — the code even had a "we'll figure that out later" comment). It now bails when pubspec.yaml sits at the project root. A proper flutter framework config (integration-flutter already exists in the skill menu) is the follow-up; this stops the wrong integration today.

Adds the first framework-detection test file, including a registry-order guard that language fallbacks stay after every framework in the Integration enum, and an end-to-end assertion that a Vite React app resolves to javascript_web.

Generated-By: PostHog Code
Task-Id: bcb96bcb-e37f-421d-841d-738647c73b8e
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

🧙 Wizard CI

Run the Wizard CI and test your changes against wizard-workbench example apps by replying with a GitHub comment using one of the following commands:

Test all apps:

  • /wizard-ci all

Test all apps in a directory:

  • /wizard-ci basic-integration
  • /wizard-ci error-tracking-upload-source-maps
  • /wizard-ci mcp-analytics
  • /wizard-ci misc
  • /wizard-ci revenue

Test an individual app:

  • /wizard-ci basic-integration/android
  • /wizard-ci basic-integration/angular
  • /wizard-ci basic-integration/astro
Show more apps
  • /wizard-ci basic-integration/django
  • /wizard-ci basic-integration/fastapi
  • /wizard-ci basic-integration/flask
  • /wizard-ci basic-integration/javascript-node
  • /wizard-ci basic-integration/javascript-web
  • /wizard-ci basic-integration/laravel
  • /wizard-ci basic-integration/next-js
  • /wizard-ci basic-integration/nuxt
  • /wizard-ci basic-integration/python
  • /wizard-ci basic-integration/rails
  • /wizard-ci basic-integration/react-native
  • /wizard-ci basic-integration/react-router
  • /wizard-ci basic-integration/sveltekit
  • /wizard-ci basic-integration/swift
  • /wizard-ci basic-integration/tanstack-router
  • /wizard-ci basic-integration/tanstack-start
  • /wizard-ci basic-integration/vue
  • /wizard-ci error-tracking-upload-source-maps/android
  • /wizard-ci error-tracking-upload-source-maps/cicd-docker-node-raw
  • /wizard-ci error-tracking-upload-source-maps/cicd-github-actions-docker-node-raw
  • /wizard-ci error-tracking-upload-source-maps/cicd-github-actions-nested-docker-node-raw
  • /wizard-ci error-tracking-upload-source-maps/cicd-github-actions-node-raw
  • /wizard-ci error-tracking-upload-source-maps/cicd-github-actions-single-stage-docker-node-raw
  • /wizard-ci error-tracking-upload-source-maps/cicd-gitlab-node-raw
  • /wizard-ci error-tracking-upload-source-maps/cicd-monorepo-pnpm-node-react
  • /wizard-ci error-tracking-upload-source-maps/cicd-monorepo-raw-node-react
  • /wizard-ci error-tracking-upload-source-maps/cicd-ssh-vps-node-raw
  • /wizard-ci error-tracking-upload-source-maps/flutter
  • /wizard-ci error-tracking-upload-source-maps/ios
  • /wizard-ci error-tracking-upload-source-maps/next
  • /wizard-ci error-tracking-upload-source-maps/next-no-posthog
  • /wizard-ci error-tracking-upload-source-maps/node-raw
  • /wizard-ci error-tracking-upload-source-maps/node-rollup
  • /wizard-ci error-tracking-upload-source-maps/node-rollup-typescript-plugin
  • /wizard-ci error-tracking-upload-source-maps/node-webpack
  • /wizard-ci error-tracking-upload-source-maps/nuxt-3-6
  • /wizard-ci error-tracking-upload-source-maps/nuxt-4-3
  • /wizard-ci error-tracking-upload-source-maps/react-native
  • /wizard-ci error-tracking-upload-source-maps/react-vite
  • /wizard-ci error-tracking-upload-source-maps/rust
  • /wizard-ci mcp-analytics/custom-dispatcher
  • /wizard-ci mcp-analytics/typescript-sdk
  • /wizard-ci misc/quack-quack
  • /wizard-ci revenue/stripe

Results will be posted here when complete.

gewenyu99 added 3 commits July 7, 2026 11:37
…eneric Node last

Per review: the structural problem was ordering, not just the Vite special case. detectFramework relied on the Integration enum's declaration order, which put javascriptNode (matches any package.json) BEFORE javascript_web (needs lockfile + a frontend signal) — so generic Node shadowed the web fallback for every frontend project that matched no specific framework, not only Vite apps.

detectFramework now walks an explicit DETECTION_ORDER: every real framework first, then the language fallbacks (python, ruby, javascript_web, javascriptNode) as a second phase, with generic Node dead last. Built explicitly rather than trusting enum order so adding an Integration in the wrong enum position can't silently put a broad fallback ahead of a specific framework.

Better tests: order invariants against the exported DETECTION_ORDER (full coverage/no dupes, fallbacks after every framework, web before node, node last overall) plus end-to-end fixtures — a Next.js app beats the fallbacks, a Vite React app and a plain browser app (lockfile + index.html, previously misclaimed by node) resolve to javascript_web, an express project still falls through to javascript_node, and a Flutter project is claimed by nothing.

The vite/pubspec detect() exclusions from the previous commit stay as belt-and-suspenders for lockfile-less scaffolds.

Generated-By: PostHog Code
Task-Id: bcb96bcb-e37f-421d-841d-738647c73b8e
…e, no new machinery

Per review: framework.ts's plain first-match loop over Object.values(Integration) was already the right mechanism — its doc comment even says "Detection order matters" — so the fix is a two-line enum reorder, not a DETECTION_ORDER layer. Reverted framework.ts and the javascript-node config to main (Vite knowledge doesn't belong in Node detection logic) and swapped javascript_web ahead of javascriptNode in the enum's fallback section, with a comment explaining why node must stay last.

A Vite/browser app now falls to javascript_web via ordering alone (lockfile + frontend signal wins before node's any-package.json). The android pubspec.yaml exclusion stays — Flutter has no config of its own to reorder ahead, so the exclusion is the only lever there.

Tests: enum-order invariants (fallbacks after every framework; javascriptNode last) + the same end-to-end fixtures (Next.js beats fallbacks, Vite React → web, plain browser app → web, express → node, Flutter → nothing) + the android pair.

Generated-By: PostHog Code
Task-Id: bcb96bcb-e37f-421d-841d-738647c73b8e
Cut the multi-line ordering explanations down to one line each. The enum docblock and the order tests already carry the invariant.

Generated-By: PostHog Code
Task-Id: bcb96bcb-e37f-421d-841d-738647c73b8e
@gewenyu99 gewenyu99 requested a review from a team July 8, 2026 20:28
@gewenyu99 gewenyu99 enabled auto-merge (squash) July 9, 2026 13:25
@gewenyu99 gewenyu99 merged commit f76a176 into main Jul 9, 2026
16 checks passed
@gewenyu99 gewenyu99 deleted the posthog-code/framework-detection-fixes branch July 9, 2026 13:35
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.

2 participants