Skip to content

feat(onboarding): force session replay for SCM onboarding flow#114329

Merged
jaydgoss merged 6 commits intomasterfrom
jaygoss/scm-onboarding-replay
Apr 30, 2026
Merged

feat(onboarding): force session replay for SCM onboarding flow#114329
jaydgoss merged 6 commits intomasterfrom
jaygoss/scm-onboarding-replay

Conversation

@jaydgoss
Copy link
Copy Markdown
Member

@jaydgoss jaydgoss commented Apr 29, 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:

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.

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.
@github-actions github-actions Bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Apr 29, 2026
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.
@jaydgoss jaydgoss marked this pull request as ready for review April 29, 2026 18:24
@jaydgoss jaydgoss requested review from a team as code owners April 29, 2026 18:24
@jaydgoss jaydgoss requested a review from a team April 29, 2026 18:24
@scttcper
Copy link
Copy Markdown
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.
@jaydgoss
Copy link
Copy Markdown
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.

@jaydgoss jaydgoss merged commit e03abd2 into master Apr 30, 2026
65 checks passed
@jaydgoss jaydgoss deleted the jaygoss/scm-onboarding-replay branch April 30, 2026 17:37
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`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants