feat(onboarding): force session replay for SCM onboarding flow#114329
Merged
feat(onboarding): force session replay for SCM onboarding flow#114329
Conversation
Adds a useReplayForCriticalFlow hook that calls replay.flush() to upgrade the existing buffer/sample-rate replay into a full session replay, and tags it with `critical_flow` for later filtering. Wires it into the new-org onboarding flow gated on the `onboarding-scm-experiment` cohort so we capture every SCM funnel attempt, not just the ones that error.
At ~600-700 SCM funnel users per day, forcing a replay on every mount would be ~700/day on top of getsentry's 5% baseline. 30% gives a representative ~200/day for funnel debugging without paying storage on near-duplicate happy paths. Error replays still fire at 100% regardless via the existing on-error sample rate. The sampleRate is decided once per mount via useState lazy init so the effect is stable across re-renders.
The replay integration is only registered by gsApp's useReplayInit, so forcing replays in the SCM funnel is fundamentally a getsentry concern. Move the implementation behind the existing HookStore pattern: OSS onboarding renders <Hook name="onboarding:scm-flow-replay-tracker" /> gated on hasScmOnboarding, and gsApp owns the flow name, sample rate, and tag. Adds a new ScmFlowReplayTracker component in gsApp that calls the useReplayForCriticalFlow hook, registered alongside the existing onboarding hooks.
Mirrors the existing useExperiment pattern: OSS exports a thin
useReplayForCriticalFlow hook that delegates to a HookStore-registered
implementation, with a noop fallback when nothing is registered. gsApp
provides the implementation and registers it as
react-hook:use-replay-for-critical-flow.
Avoids the wrapper component plus <Hook> render dance for what is
fundamentally a function call. Call site goes back to a plain
useReplayForCriticalFlow({...}) invocation in onboarding.tsx.
Math.random() returns [0, 1), so sampleRate=0 deterministically skips and sampleRate=1 deterministically forces. Use those endpoints to cover both gate paths instead of overwriting Math.random — a pattern not used anywhere else in the codebase, and one that previously broke source-map stack traces under jest.spyOn.
Member
|
imo doesn't need to be in getsentry since i assume the sdk isn't configured to capture replays when self hosted |
Self-hosted Sentry doesn't register the Replay integration, so Sentry.getReplay() returns undefined there and the existing early-return already makes the hook a no-op. The HookStore indirection through gsApp isn't needed. Move the implementation back into static/app, drop the gsApp hook and its registration, and remove the Hooks type entry.
Member
Author
|
Good call, inlined the implementation in 48ee2e9. Self-hosted's getReplay() returns undefined, so the existing early-return covers it without the HookStore detour. |
ryan953
approved these changes
Apr 30, 2026
3 tasks
cleptric
pushed a commit
that referenced
this pull request
May 5, 2026
## TL;DR
Force a session replay for ~30% of users in SCM onboarding, tagged
`critical_flow=scm_onboarding` for filtering.
## Why
getsentry runs Session Replay at 5% session sample / 100% on-error
globally. For the SCM onboarding funnel that means we miss the success
paths, which are the ones we most want to debug. Sampling at 30% gives
roughly 200 replays/day at expected funnel volume without paying storage
on near-duplicate happy paths.
## How it works
`useReplayForCriticalFlow` calls `replay.flush()` from `@sentry/react`,
which per the SDK docstring covers all three states: starts a session
replay if not yet recording, upgrades buffer mode to session, or no-ops
if already in session. The decision to force is made once per mount via
`Math.random() < sampleRate` so the effect is stable across re-renders.
On unmount the hook flushes a final segment and clears the tag, but does
not stop recording (matches the existing pattern in `useReplayInit`).
Self-hosted doesn't register the Replay integration, so
`Sentry.getReplay()` returns undefined there and the hook's early-return
makes it a no-op.
The call site in `onboarding.tsx` is gated on the
`onboarding-scm-experiment` cohort:
```tsx
useReplayForCriticalFlow({
flowName: 'scm_onboarding',
enabled: hasScmOnboarding,
sampleRate: 0.3,
});
```
## Verifying after deploy
Enroll into the SCM onboarding cohort, run the funnel three ways
(success, validation failure, backend failure), and confirm replays
appear in the Replays product filtered by
`critical_flow:scm_onboarding`.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR
Force a session replay for ~30% of users in SCM onboarding, tagged
critical_flow=scm_onboardingfor filtering.Why
getsentry runs Session Replay at 5% session sample / 100% on-error globally. For the SCM onboarding funnel that means we miss the success paths, which are the ones we most want to debug. Sampling at 30% gives roughly 200 replays/day at expected funnel volume without paying storage on near-duplicate happy paths.
How it works
useReplayForCriticalFlowcallsreplay.flush()from@sentry/react, which per the SDK docstring covers all three states: starts a session replay if not yet recording, upgrades buffer mode to session, or no-ops if already in session. The decision to force is made once per mount viaMath.random() < sampleRateso the effect is stable across re-renders. On unmount the hook flushes a final segment and clears the tag, but does not stop recording (matches the existing pattern inuseReplayInit).Self-hosted doesn't register the Replay integration, so
Sentry.getReplay()returns undefined there and the hook's early-return makes it a no-op.The call site in
onboarding.tsxis gated on theonboarding-scm-experimentcohort:Verifying after deploy
Enroll into the SCM onboarding cohort, run the funnel three ways (success, validation failure, backend failure), and confirm replays appear in the Replays product filtered by
critical_flow:scm_onboarding.