[backport cloud/1.38] telemetry: route GTM, reactive begin_checkout user_id, and Impact attribution#8813
Conversation
## Summary Use reactive `userId` reads for `begin_checkout` telemetry so delayed auth state updates are reflected at event time instead of using a stale snapshot. ## Changes - **What**: switched subscription checkout telemetry paths to `storeToRefs(useFirebaseAuthStore())` and read `userId.value` when dispatching `trackBeginCheckout`. - **What**: added regression tests that mutate `userId` after setup / after checkout starts and assert telemetry uses the updated ID. ## Review Focus - Verify `PricingTable` and `performSubscriptionCheckout` still emit exactly one `begin_checkout` event per action, with `checkout_type: change` and `checkout_type: new` in their respective paths. - Verify the new tests would fail with stale store destructuring (manually validated during development). ## Screenshots (if applicable) N/A ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8726-fix-keep-begin_checkout-user_id-reactive-in-subscription-flows-3006d73d365081888c84c0335ab52e09) by [Unito](https://www.unito.io) (cherry picked from commit 815be49)
…iptions (#8688) Implement Impact telemetry and checkout attribution through cloud subscription checkout flows. This PR adds Impact.com tracking support and carries attribution context from landing-page visits into subscription checkout requests so conversion attribution can be validated end-to-end. - Register a new `ImpactTelemetryProvider` during cloud telemetry initialization. - Initialize the Impact queue/runtime (`ire`) and load the Universal Tracking Tag script once. - Invoke `ire('identify', ...)` on page views with dynamic `customerId` and SHA-1 `customerEmail` (or empty strings when unknown). - Expand checkout attribution capture to include `im_ref`, UTM fields, and Google click IDs, with local persistence across navigation. - Attempt `ire('generateClickId')` with a timeout and fall back to URL/local attribution when unavailable. - Include attribution payloads in checkout creation requests for both: - `/customers/cloud-subscription-checkout` - `/customers/cloud-subscription-checkout/{tier}` - Extend begin-checkout telemetry metadata typing to include attribution fields. - Add focused unit coverage for provider behavior, attribution persistence/fallback logic, and checkout request payloads. Tradeoffs / constraints: - Attribution collection is treated as best-effort in tiered checkout flow to avoid blocking purchases. - Backend checkout handlers must accept and process the additional JSON attribution fields. ## Screenshots <img width="908" height="208" alt="image" src="https://github.com/user-attachments/assets/03c16d60-ffda-40c9-9bd6-8914d841be50"/> <img width="1144" height="460" alt="image" src="https://github.com/user-attachments/assets/74b97fde-ce0a-43e6-838e-9a4aba484488"/> <img width="1432" height="320" alt="image" src="https://github.com/user-attachments/assets/30c22a9f-7bd8-409f-b0ef-e4d02343780a"/> <img width="341" height="135" alt="image" src="https://github.com/user-attachments/assets/f6d918ae-5f80-45e0-855a-601abea61dec"/> (cherry picked from commit da56c9e)
|
Important Review skippedAuto reviews are limited based on label configuration. 🚫 Excluded labels (none allowed) (1)
Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🎭 Playwright Tests:
|
🎨 Storybook Build Status✅ Build completed successfully! ⏰ Completed at: 02/12/2026, 12:40:28 AM UTC 🔗 Links🎉 Your Storybook is ready for review! |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ebc57e7aad
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| await refreshRemoteConfig({ useAuth: false }) | ||
|
|
||
| const { initTelemetry } = await import('@/platform/telemetry/initTelemetry') | ||
| await initTelemetry() |
There was a problem hiding this comment.
Initialize telemetry after Pinia is installed
Calling initTelemetry() here runs provider constructors before createPinia()/app.use(pinia), but MixpanelTelemetryProvider's loaded callback invokes useCurrentUser() (which immediately calls Pinia stores). In cloud builds with a Mixpanel token, that callback can throw "no active Pinia", causing the provider's import chain to hit its error path and disable Mixpanel tracking for the session.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
So, I want to fix this in main, then backport that PR instead of fixing it here:
- It's a race/intermittent
- This only affects a fallback branch of Impact telemetry
a. Per Impact's technical guide, we only need the generated ref for subscription attribution. And Pinia only provides the fallback as customerId + customerEmail - I don't want to block the entirety of telemetry based on this, since the impact isn't high
- I don't want to muddy already reviewed code. This PR is basically a clean cherry pick, I don't want to add new code to the staging branch.
There was a problem hiding this comment.
Pull request overview
Backports cloud telemetry enhancements to cloud/1.38, adding a registry-based telemetry entrypoint, GTM + Impact providers, and checkout attribution propagation through subscription checkout flows.
Changes:
- Introduce a cloud-only telemetry initialization path (
initTelemetry) that registers multiple providers (Mixpanel/GTM/Impact) behind a dispatcher. - Add page-view tracking and enrich auth + begin-checkout telemetry with user identity and attribution metadata.
- Persist and attach checkout attribution (UTM/click IDs + GA identity) to checkout telemetry and checkout POST bodies, with accompanying unit tests.
Reviewed changes
Copilot reviewed 26 out of 27 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/stores/subgraphStore.ts | Update workflow type imports after extracting ComfyWorkflow. |
| src/stores/firebaseAuthStore.ts | Include user_id in auth telemetry events for cloud. |
| src/router.ts | Add route-based page view tracking via telemetry. |
| src/platform/workflow/management/stores/workflowStore.ts | Extract ComfyWorkflow to a new module; tighten typing. |
| src/platform/workflow/management/stores/comfyWorkflow.ts | New module containing ComfyWorkflow + lazy imports. |
| src/platform/telemetry/utils/checkoutAttribution.ts | New attribution capture/persistence + GA identity + Impact click-id generation. |
| src/platform/telemetry/utils/tests/checkoutAttribution.test.ts | Unit tests for attribution capture, persistence, and fallbacks. |
| src/platform/telemetry/types.ts | Extend telemetry types (page view, checkout attribution, begin_checkout metadata); add PAGE_VIEW. |
| src/platform/telemetry/providers/cloud/ImpactTelemetryProvider.ts | New Impact provider (script init + identify + attribution capture on page views). |
| src/platform/telemetry/providers/cloud/ImpactTelemetryProvider.test.ts | Unit tests for Impact provider behavior and hashing. |
| src/platform/telemetry/providers/cloud/GtmTelemetryProvider.ts | New GTM provider pushing dataLayer events (page_view/auth/begin_checkout). |
| src/platform/telemetry/initTelemetry.ts | Cloud-only telemetry registry initialization and provider registration. |
| src/platform/telemetry/index.ts | Replace singleton provider with registry-backed dispatcher setter/getter. |
| src/platform/telemetry/TelemetryRegistry.ts | New registry that dispatches events to multiple providers safely. |
| src/platform/remoteConfig/types.ts | Add gtm_container_id to runtime remote config typing. |
| src/platform/cloud/subscription/utils/subscriptionCheckoutUtil.ts | Attach attribution to tiered checkout POST body + begin_checkout telemetry; reactive userId. |
| src/platform/cloud/subscription/utils/subscriptionCheckoutUtil.test.ts | New tests for checkout payload attribution + reactive userId + best-effort attribution. |
| src/platform/cloud/subscription/composables/useSubscriptionCancellationWatcher.ts | Update telemetry typing to dispatcher-based shape. |
| src/platform/cloud/subscription/composables/useSubscriptionCancellationWatcher.test.ts | Update tests for dispatcher-based telemetry typing. |
| src/platform/cloud/subscription/composables/useSubscription.ts | Attach attribution to non-tiered checkout POST body. |
| src/platform/cloud/subscription/composables/useSubscription.test.ts | Update tests for new checkout request body and setup scoping. |
| src/platform/cloud/subscription/components/PricingTable.vue | Track begin_checkout (change flow) with reactive userId + attribution; use tiered checkout util for new subs. |
| src/platform/cloud/subscription/components/PricingTable.test.ts | Add regression coverage for reactive userId + begin_checkout tracking. |
| src/platform/cloud/onboarding/CloudSubscriptionRedirectView.test.ts | Update subscription composable mock shape (subscriptionStatus). |
| src/main.ts | Initialize telemetry in cloud builds after remote config load. |
| global.d.ts | Add typings for GTM/Impact/GA globals and __CONFIG__.gtm_container_id. |
| .github/workflows/ci-dist-telemetry-scan.yaml | New CI workflow scanning OSS build artifacts for GTM references. |
| await import('@/platform/workflow/persistence/stores/workflowDraftStore') | ||
| const draftStore = useWorkflowDraftStore() | ||
| this.content = JSON.stringify(this.activeState) | ||
| // Force save to ensure the content is updated in remote storage incase |
There was a problem hiding this comment.
Typo in comment: "incase" → "in case".
| // Force save to ensure the content is updated in remote storage incase | |
| // Force save to ensure the content is updated in remote storage in case |
| router.afterEach(() => { | ||
| trackPageView() | ||
| }) |
There was a problem hiding this comment.
trackPageView/GTM payload supports page_referrer, but the router hook never supplies a referrer (and always uses window.location.href for the location). Consider using router.afterEach((to, from) => ...) and passing to.fullPath (or a full URL) as path and from.fullPath as referrer so SPA route transitions have meaningful referrer data.
| if (isActiveSubscription.value) { | ||
| const checkoutAttribution = await getCheckoutAttributionForCloud() | ||
| if (userId.value) { | ||
| telemetry?.trackBeginCheckout({ | ||
| user_id: userId.value, |
There was a problem hiding this comment.
getCheckoutAttributionForCloud() is awaited in the active-subscription (change) flow without any best-effort fallback. If the dynamic import/chunk load fails, this will block accessBillingPortal() and prevent users from changing plans. Match performSubscriptionCheckout by wrapping attribution collection in a try/catch and proceeding with {} when it fails.
| throw new FirebaseAuthStoreError( | ||
| t('toastMessages.userNotAuthenticated') | ||
| ) | ||
| } | ||
| const checkoutAttribution = await getCheckoutAttributionForCloud() |
There was a problem hiding this comment.
getCheckoutAttributionForCloud() is awaited before creating the checkout. If the dynamic import/chunk load fails, it will block subscription checkout entirely. Consider treating attribution as best-effort here as well (try/catch and fall back to {}) so purchases aren't prevented by telemetry/attribution loading issues.
| //FIXME: use existing util function | ||
| const executionIdToCurrentId = (id: string) => { | ||
| const executionIdToCurrentId = (id: string): string | undefined => { | ||
| const subgraph = activeSubgraph.value | ||
|
|
||
| // Short-circuit: ID belongs to the parent workflow / no active subgraph |
There was a problem hiding this comment.
executionIdToCurrentId relies on getSubgraphsFromInstanceIds, which mutates the subgraphNodeIds array via shift(). Because executionIdToCurrentId later tries to read subgraphNodeIds.at(-1), this can return undefined even when the active subgraph matches. Also, executionIdToCurrentId appears to pass the full execution id parts (including the terminal node id) into subgraph traversal, which can throw "Subgraph not found" when the last segment is not a subgraph node id. Consider splitting out the final node id first (e.g., const nodeId = parts.pop()) and passing a copy of the remaining parts into traversal.
|
It made it onto cloud/1.39, and cloud is moving there, so we don't need this 1.38 backport. |
Backport of #8354, #8726, and #8688 to
cloud/1.38.The cherry pick of all three commits only had one conflict, with that being the extraction of comfyworkflow.ts out of workflowStore. This is not a material change, please see this diff, with the original text being workflowStore.ts ComfyWorkflow declaration taken from this PR's github Files changed, with the new text being this PR's comfyworkflow.ts:
https://www.diffchecker.com/B5Pe9cz0/
┆Issue is synchronized with this Notion page by Unito