Skip to content

[backport cloud/1.38] telemetry: route GTM, reactive begin_checkout user_id, and Impact attribution#8813

Closed
benceruleanlu wants to merge 4 commits intocloud/1.38from
backport-telemetry-8354-8726-8688-to-cloud-1.38
Closed

[backport cloud/1.38] telemetry: route GTM, reactive begin_checkout user_id, and Impact attribution#8813
benceruleanlu wants to merge 4 commits intocloud/1.38from
backport-telemetry-8354-8726-8688-to-cloud-1.38

Conversation

@benceruleanlu
Copy link
Member

@benceruleanlu benceruleanlu commented Feb 12, 2026

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

## 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)
@benceruleanlu benceruleanlu requested a review from a team as a code owner February 12, 2026 00:21
Copilot AI review requested due to automatic review settings February 12, 2026 00:21
@dosubot dosubot bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Feb 12, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 12, 2026

Important

Review skipped

Auto reviews are limited based on label configuration.

🚫 Excluded labels (none allowed) (1)
  • backport

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch backport-telemetry-8354-8726-8688-to-cloud-1.38

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

github-actions bot commented Feb 12, 2026

🎭 Playwright Tests: ⚠️ Passed with flaky tests

Results: 501 passed, 0 failed, 3 flaky, 8 skipped (Total: 512)

❌ Failed Tests

📊 Browser Reports
  • chromium: View Report (✅ 489 / ❌ 0 / ⚠️ 3 / ⏭️ 8)
  • chromium-2x: View Report (✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • chromium-0.5x: View Report (✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • mobile-chrome: View Report (✅ 9 / ❌ 0 / ⚠️ 0 / ⏭️ 0)

@benceruleanlu benceruleanlu added backport Backporting a PR onto a release candidate and removed size:XXL This PR changes 1000+ lines, ignoring generated files. labels Feb 12, 2026
@github-actions
Copy link

github-actions bot commented Feb 12, 2026

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 02/12/2026, 12:40:28 AM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@dosubot dosubot bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Feb 12, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I want to fix this in main, then backport that PR instead of fixing it here:

  1. It's a race/intermittent
  2. 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
  3. I don't want to block the entirety of telemetry based on this, since the impact isn't high
  4. 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.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in comment: "incase" → "in case".

Suggested change
// 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

Copilot uses AI. Check for mistakes.
Comment on lines +105 to +107
router.afterEach(() => {
trackPageView()
})
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines 431 to +435
if (isActiveSubscription.value) {
const checkoutAttribution = await getCheckoutAttributionForCloud()
if (userId.value) {
telemetry?.trackBeginCheckout({
user_id: userId.value,
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +243 to +247
throw new FirebaseAuthStoreError(
t('toastMessages.userNotAuthenticated')
)
}
const checkoutAttribution = await getCheckoutAttributionForCloud()
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines 576 to 580
//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
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
@benceruleanlu
Copy link
Member Author

It made it onto cloud/1.39, and cloud is moving there, so we don't need this 1.38 backport.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport Backporting a PR onto a release candidate size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants