Skip to content

feat(desktop): restore onboarding funnel instrumentation - #6122

Closed
Kitenite wants to merge 1 commit into
mainfrom
hoakiet98/super-1729-onboarding-instrumentation
Closed

Kitenite wants to merge 1 commit into
mainfrom
hoakiet98/super-1729-onboarding-instrumentation

Conversation

@Kitenite

@Kitenite Kitenite commented Aug 2, 2026 •

Copy link
Copy Markdown
Member

Part of SUPER-1729 — https://linear.app/superset-sh/issue/SUPER-1729

Restores renderer-side onboarding funnel instrumentation for the 2-step flow. Analytics only — no UX, navigation, gating, or experiment changes (useNewWorkspaceScreenVariant and all new-workspace-screen exposure logic untouched). All events go through the existing track() helper in renderer/lib/analytics.

Events emitted

Event Properties When
onboarding_started — First entry into the onboarding flow; module-level guard prevents double-fire on remount/route bounce (once per renderer session)
onboarding_step_started step Entering a step; deduped against the current step, re-fires on re-entry after abandonment
onboarding_step_completed step Step 1: Continue click. Step 2: in finish() immediately before onboarding_finished
onboarding_abandoned step Best-effort on layout unmount (deferred one tick so StrictMode double-mounts and route bounces don't count) and synchronously on beforeunload; suppressed once finished
onboarding_error step, action, reason Clone failure (action: clone), completeOnboarding failure (action: complete_onboarding), gh auth exiting unauthenticated (action: gh_auth), provider API-key save failure (action: provider_connect)
onboarding_finished outcome: completed Unchanged — same call site, name, and timing as before
onboarding_provider_connected provider, method Pre-existing, unchanged

reason is always a machine-readable code mapped from the error (repo_not_found, auth_failed, network_error, already_exists, clone_failed, host_not_ready, api_unreachable, request_failed, auth_incomplete, api_key_failed) — never URLs, repo names, paths, or messages.

Step slugs

  • Step 1 (tools/providers checklist) → providers — the historical providers step is the closest ancestor of this surface; the old gh-cli/permissions/adopt-worktrees steps no longer exist as separate screens, and gh-CLI issues remain distinguishable via onboarding_error with action: gh_auth.
  • Step 2 (create/add a project) → project — exact match with the historical slug.

Verification

  • bun run lint:fix → bun run lint exits 0; apps/desktop bun run typecheck clean. No colocated tests in the touched files.
  • Verified end-to-end via CDP against the real dev app (renderer on this workspace's DESKTOP_VITE_PORT=3225, CDP 9615): posthog.capture was wrapped via Page.addScriptToEvaluateOnNewDocument so no test events reached production PostHog; onboarded_at was nulled for the dev user, the flow was walked with real UI clicks, then onboarded_at restored.

Captured sequence (in order, from window.__t5Events):

[{"event":"onboarding_started","props":null},
 {"event":"onboarding_step_started","props":{"step":"providers"}},
 {"event":"onboarding_step_completed","props":{"step":"providers"}},   // Continue click
 {"event":"onboarding_step_started","props":{"step":"project"}},
 {"event":"onboarding_error","props":{"step":"project","action":"clone","reason":"repo_not_found"}},  // clone of nonexistent repo
 {"event":"onboarding_abandoned","props":{"step":"project"}},          // beforeunload path
 {"event":"onboarding_step_started","props":{"step":"providers"}},     // re-entry via Back — note: no second onboarding_started
 {"event":"onboarding_step_completed","props":{"step":"providers"}},
 {"event":"onboarding_step_started","props":{"step":"project"}},
 {"event":"onboarding_step_completed","props":{"step":"project"}},
 {"event":"onboarding_finished","props":{"outcome":"completed"}}]      // real clone; app then redirected to #/v2-workspaces

The abandonment leg was triggered with a dispatched beforeunload event (synthetic — a real reload wipes the in-page capture buffer); the layout-unmount path uses the same flush and its timer-cancel behavior is exercised by the Back/Continue route changes above, which correctly did not emit onboarding_abandoned. Everything else was end-to-end UI interaction on the live app.

Kept intentionally additive for easy rebasing against #6116/#6117.


Summary by cubic

Restores renderer-side onboarding funnel analytics for the 2-step flow to meet SUPER-1729. No UX, navigation, gating, or experiment changes; all events go through track().

  • New Features
    • Emits: onboarding_started, onboarding_step_started, onboarding_step_completed, onboarding_abandoned, onboarding_error, and keeps onboarding_finished/onboarding_provider_connected as-is. Deduped per session and step.
    • Abandon tracking: flushed on beforeunload; scheduled on layout unmount and canceled on re-entry to avoid false positives.
    • Error tracking: { step, action, reason } with machine-readable reasons only (e.g., repo_not_found, auth_failed, api_unreachable, clone_failed, api_key_failed).
    • Step slugs: providers (tools/providers checklist) and project (create/add project).
    • Integrated at key points: provider API key save failures, GH auth exit unauthenticated, clone failures/host not ready, and completeOnboarding failures.

Written for commit 05d5212. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Analytics
    • Added comprehensive onboarding progress tracking, including step entry, completion, abandonment, and overall completion.
    • Added error tracking for provider API-key connections, GitHub authentication, project setup, cloning, and host readiness.
    • Error reporting uses sanitized reason categories without exposing sensitive messages or paths.

@coderabbitai

coderabbitai Bot commented Aug 2, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The onboarding flow now records entry, step completion, abandonment, and categorized errors for provider authentication, API-key connections, project completion, and repository cloning.

Changes

Onboarding analytics

Layer / File(s) Summary
Analytics state and event API
apps/desktop/src/renderer/routes/_authenticated/onboarding/utils/onboardingAnalytics/*
Adds typed analytics functions for onboarding entry, step completion, errors, completion, and deferred abandonment.
Onboarding lifecycle wiring
apps/desktop/src/renderer/routes/_authenticated/onboarding/layout.tsx
Adds step slugs, entry tracking, completion tracking, unload flushing, and cleanup-based abandonment scheduling.
Provider and project failure tracking
apps/desktop/src/renderer/routes/_authenticated/onboarding/components/ProviderConnectModal/ProviderConnectModal.tsx, apps/desktop/src/renderer/routes/_authenticated/onboarding/page.tsx, apps/desktop/src/renderer/routes/_authenticated/onboarding/project/page.tsx
Records categorized failures for API-key connections, GitHub authentication, project completion, host readiness, and repository cloning. Error reasons use fixed codes instead of raw messages or paths.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant OnboardingLayout
  participant OnboardingPage
  participant ProjectPage
  participant onboardingAnalytics
  OnboardingLayout->>onboardingAnalytics: track onboarding entry and step completion
  OnboardingPage->>onboardingAnalytics: track GitHub authentication errors
  ProjectPage->>onboardingAnalytics: track project completion and failure reasons
Loading

Possibly related PRs

Suggested reviewers: saddlepaddle

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the restored onboarding funnel instrumentation.
Description check ✅ Passed The description covers the change, rationale, events, testing, issue reference, and verification details, despite omitting the template's checklist section.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch hoakiet98/super-1729-onboarding-instrumentation

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/desktop/src/renderer/routes/_authenticated/onboarding/project/page.tsx`:
- Around line 65-66: Update the onboarding completion flow around
completeOnboarding.mutate and markOnboardingFinished so the finished state is
set only after the mutation resolves successfully. Keep
trackOnboardingStepCompleted in the existing completion path, but ensure failed
persistence leaves onboarding unfinished so abandonment tracking can run.

In
`@apps/desktop/src/renderer/routes/_authenticated/onboarding/utils/onboardingAnalytics/onboardingAnalytics.ts`:
- Around line 27-29: Update trackOnboardingStepCompleted to maintain
module-level state of already completed OnboardingStep values, returning without
tracking when the supplied step was previously recorded; otherwise record the
step and emit exactly one onboarding_step_completed event.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 94581ef9-071b-43d5-8f58-5c5ffe80295a

📥 Commits

Reviewing files that changed from the base of the PR and between d1ea13e and 05d5212.

📒 Files selected for processing (6)
  • apps/desktop/src/renderer/routes/_authenticated/onboarding/components/ProviderConnectModal/ProviderConnectModal.tsx
  • apps/desktop/src/renderer/routes/_authenticated/onboarding/layout.tsx
  • apps/desktop/src/renderer/routes/_authenticated/onboarding/page.tsx
  • apps/desktop/src/renderer/routes/_authenticated/onboarding/project/page.tsx
  • apps/desktop/src/renderer/routes/_authenticated/onboarding/utils/onboardingAnalytics/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/onboarding/utils/onboardingAnalytics/onboardingAnalytics.ts

Comment on lines +65 to +66
trackOnboardingStepCompleted("project");
markOnboardingFinished();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Mark onboarding finished only after persistence succeeds.

Line 66 sets finished before completeOnboarding succeeds. If the mutation fails, flushOnboardingAbandoned skips abandonment tracking even though the user remains in onboarding. Set this state after completeOnboarding.mutate() resolves.

Proposed fix
 const finish = async (projectId: string) => {
 	trackOnboardingStepCompleted("project");
-	markOnboardingFinished();
 	track("onboarding_finished", { outcome: "completed" });
 	try {
 		await apiTrpcClient.user.completeOnboarding.mutate();
+		markOnboardingFinished();
 		await refetchSession({ query: { disableCookieCache: true } });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/routes/_authenticated/onboarding/project/page.tsx`
around lines 65 - 66, Update the onboarding completion flow around
completeOnboarding.mutate and markOnboardingFinished so the finished state is
set only after the mutation resolves successfully. Keep
trackOnboardingStepCompleted in the existing completion path, but ensure failed
persistence leaves onboarding unfinished so abandonment tracking can run.

Comment on lines +27 to +29
export function trackOnboardingStepCompleted(step: OnboardingStep): void {
track("onboarding_step_completed", { step });
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Deduplicate step-completion events.

trackOnboardingStepCompleted emits an event on every call. A repeated activation of the Continue control can record duplicate providers completion events. Store completed steps and emit one completion event per step.

Proposed fix
 let currentStep: OnboardingStep | null = null;
+const completedSteps = new Set<OnboardingStep>();
 let abandonTimer: ReturnType<typeof setTimeout> | null = null;
 
 export function trackOnboardingStepCompleted(step: OnboardingStep): void {
+	if (completedSteps.has(step)) return;
+	completedSteps.add(step);
 	track("onboarding_step_completed", { step });
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/onboarding/utils/onboardingAnalytics/onboardingAnalytics.ts`
around lines 27 - 29, Update trackOnboardingStepCompleted to maintain
module-level state of already completed OnboardingStep values, returning without
tracking when the supplied step was previously recorded; otherwise record the
step and emit exactly one onboarding_step_completed event.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

3 issues found across 6 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/desktop/src/renderer/routes/_authenticated/onboarding/project/page.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/onboarding/project/page.tsx:66">
P2: The newly added markOnboardingFinished() (and trackOnboardingStepCompleted("project")) run before completeOnboarding.mutate() succeeds. If that mutation (or the subsequent refetchSession) rejects, the code returns early with an error toast, but the module-level finished flag has already been set to true. As a result flushOnboardingAbandoned() will now silently skip emission of onboarding_abandoned even though the user never actually finished onboarding, and onboarding_finished/step-completed are recorded for a flow that failed. Move the finish/step-completed marks (and the onboarding_finished event) to the success path after the try block completes so failed completions don't get reported as finished and abandonment stays trackable.</violation>

<violation number="2" location="apps/desktop/src/renderer/routes/_authenticated/onboarding/project/page.tsx:154">
P2: V1 clone failures never emit the new `onboarding_error` event because `cloneFromUrl` swallows them before this outer catch runs. Expose the failure through an error callback or typed result and track it at the V1 mutation boundary.</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/onboarding/layout.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/onboarding/layout.tsx:63">
P2: Window-close abandonment events can be lost because this `beforeunload` handler uses a normal asynchronous PostHog capture rather than an unload-safe `sendBeacon` capture. An unload-specific analytics path should pass `transport: "sendBeacon"` and `send_instantly: true` so `onboarding_abandoned` is sent before the renderer exits.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant UI as Onboarding UI Components
    participant Layout as Onboarding Layout (layout.tsx)
    participant Analytics as onboardingAnalytics Module
    participant PH as PostHog (via track())
    participant Auth as authClient / Session
    participant Backend as Backend API
    participant IPC as Electron IPC (clone, gh auth)

    Note over UI,IPC: Onboarding Funnel Instrumentation Flow

    Layout->>Auth: Get session (onboardedAt)
    Auth-->>Layout: session data
    alt User is onboarded
        Layout->>UI: Redirect to root (no tracking)
    else User is NOT onboarded
        Layout->>Analytics: trackOnboardingEntered("providers")
        Analytics->>Analytics: Check module-level flags
        alt First entry (started === false)
            Analytics->>PH: track("onboarding_started")
            Analytics->>Analytics: Set started = true
        end
        alt Step changed (currentStep !== "providers")
            Analytics->>PH: track("onboarding_step_started", {step: "providers"})
            Analytics->>Analytics: Set currentStep = "providers"
        end
        Analytics-->>Layout: done
    end

    Layout->>Layout: Add beforeunload event listener
    Layout->>Analytics: scheduleOnboardingAbandoned()
    Analytics->>Analytics: setTimer setTimeout(flushOnboardingAbandoned, 0)

    Note over UI,IPC: Step 1: Provider Connection

    UI->>UI: User clicks Continue
    UI->>Analytics: trackOnboardingStepCompleted("providers")
    Analytics->>PH: track("onboarding_step_completed", {step: "providers"})
    UI->>Layout: Navigate to /onboarding/project

    Layout->>Analytics: trackOnboardingEntered("project")
    alt Step changed (currentStep !== "project")
        Analytics->>PH: track("onboarding_step_started", {step: "project"})
        Analytics->>Analytics: Set currentStep = "project"
    end

    Note over UI,IPC: Step 2: Clone/Add Project (error paths)

    UI->>IPC: cloneRepository(repoUrl)
    alt Clone fails
        IPC-->>UI: Error
        UI->>Analytics: trackOnboardingError("project", "clone", reason)
        alt repo_not_found
            Analytics->>PH: track("onboarding_error", {step:"project", action:"clone", reason:"repo_not_found"})
        else auth_failed
            Analytics->>PH: track("onboarding_error", {step:"project", action:"clone", reason:"auth_failed"})
        else network_error
            Analytics->>PH: track("onboarding_error", {step:"project", action:"clone", reason:"network_error"})
        else already_exists
            Analytics->>PH: track("onboarding_error", {step:"project", action:"clone", reason:"already_exists"})
        else clone_failed
            Analytics->>PH: track("onboarding_error", {step:"project", action:"clone", reason:"clone_failed"})
        end
    else Clone succeeds
        UI->>UI: finish(projectId)
        UI->>Analytics: trackOnboardingStepCompleted("project")
        Analytics->>PH: track("onboarding_step_completed", {step: "project"})
        UI->>Analytics: markOnboardingFinished()
        Analytics->>Analytics: Set finished = true
        UI->>PH: track("onboarding_finished", {outcome: "completed"})
        UI->>Backend: completeOnboarding.mutate()
        alt API call fails
            Backend-->>UI: Error
            UI->>Analytics: trackOnboardingError("project", "complete_onboarding", reason)
            alt api_unreachable
                Analytics->>PH: track("onboarding_error", {step:"project", action:"complete_onboarding", reason:"api_unreachable"})
            else auth_failed
                Analytics->>PH: track("onboarding_error", {step:"project", action:"complete_onboarding", reason:"auth_failed"})
            else request_failed
                Analytics->>PH: track("onboarding_error", {step:"project", action:"complete_onboarding", reason:"request_failed"})
            end
        end
    end

    Note over UI,IPC: GH Auth Dialog (error path)

    UI->>Auth: refetchGh()
    Auth-->>UI: Result
    alt auth incomplete (not installed or not authenticated)
        UI->>Analytics: trackOnboardingError("providers", "gh_auth", "auth_incomplete")
        Analytics->>PH: track("onboarding_error", {step:"providers", action:"gh_auth", reason:"auth_incomplete"})
    end

    Note over UI,IPC: Provider API Key Save (error path)

    UI->>IPC: setApiKey.mutateAsync()
    alt API key save fails
        IPC-->>UI: Error
        UI->>Analytics: trackOnboardingError("providers", "provider_connect", "api_key_failed")
        Analytics->>PH: track("onboarding_error", {step:"providers", action:"provider_connect", reason:"api_key_failed"})
    end

    Note over UI,IPC: Abandonment Tracking

    alt User closes tab (beforeunload)
        Layout->>Analytics: flushOnboardingAbandoned()
        Analytics->>PH: track("onboarding_abandoned", {step: currentStep})
    else User unmounts layout (route change)
        Layout->>Analytics: scheduleOnboardingAbandoned() [timer]
        alt Re-entry before timer fires
            Layout->>Analytics: trackOnboardingEntered() [cancels timer]
        else Timer fires (true abandonment)
            Analytics->>Analytics: flushOnboardingAbandoned()
            Analytics->>PH: track("onboarding_abandoned", {step: currentStep})
        end
    end
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

if (projectId) await finish(projectId);
}
} catch (err) {
trackOnboardingError("project", "clone", cloneFailureReason(err));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: V1 clone failures never emit the new onboarding_error event because cloneFromUrl swallows them before this outer catch runs. Expose the failure through an error callback or typed result and track it at the V1 mutation boundary.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/onboarding/project/page.tsx, line 154:

<comment>V1 clone failures never emit the new `onboarding_error` event because `cloneFromUrl` swallows them before this outer catch runs. Expose the failure through an error callback or typed result and track it at the V1 mutation boundary.</comment>

<file context>
@@ -138,6 +151,7 @@ function OnboardingProjectPage() {
 				if (projectId) await finish(projectId);
 			}
 		} catch (err) {
+			trackOnboardingError("project", "clone", cloneFailureReason(err));
 			toast.error(
 				err instanceof Error ? err.message : "Failed to clone repository",
</file context>

}, [isPending, onboarded, pathname]);

useEffect(() => {
window.addEventListener("beforeunload", flushOnboardingAbandoned);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: Window-close abandonment events can be lost because this beforeunload handler uses a normal asynchronous PostHog capture rather than an unload-safe sendBeacon capture. An unload-specific analytics path should pass transport: "sendBeacon" and send_instantly: true so onboarding_abandoned is sent before the renderer exits.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/onboarding/layout.tsx, line 63:

<comment>Window-close abandonment events can be lost because this `beforeunload` handler uses a normal asynchronous PostHog capture rather than an unload-safe `sendBeacon` capture. An unload-specific analytics path should pass `transport: "sendBeacon"` and `send_instantly: true` so `onboarding_abandoned` is sent before the renderer exits.</comment>

<file context>
@@ -43,6 +51,22 @@ function OnboardingFlowLayout() {
+	}, [isPending, onboarded, pathname]);
+
+	useEffect(() => {
+		window.addEventListener("beforeunload", flushOnboardingAbandoned);
+		return () => {
+			window.removeEventListener("beforeunload", flushOnboardingAbandoned);
</file context>

// dashboard's new-workspace modal pre-selected to the project just added.
const finish = async (projectId: string) => {
trackOnboardingStepCompleted("project");
markOnboardingFinished();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: The newly added markOnboardingFinished() (and trackOnboardingStepCompleted("project")) run before completeOnboarding.mutate() succeeds. If that mutation (or the subsequent refetchSession) rejects, the code returns early with an error toast, but the module-level finished flag has already been set to true. As a result flushOnboardingAbandoned() will now silently skip emission of onboarding_abandoned even though the user never actually finished onboarding, and onboarding_finished/step-completed are recorded for a flow that failed. Move the finish/step-completed marks (and the onboarding_finished event) to the success path after the try block completes so failed completions don't get reported as finished and abandonment stays trackable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/onboarding/project/page.tsx, line 66:

<comment>The newly added markOnboardingFinished() (and trackOnboardingStepCompleted("project")) run before completeOnboarding.mutate() succeeds. If that mutation (or the subsequent refetchSession) rejects, the code returns early with an error toast, but the module-level finished flag has already been set to true. As a result flushOnboardingAbandoned() will now silently skip emission of onboarding_abandoned even though the user never actually finished onboarding, and onboarding_finished/step-completed are recorded for a flow that failed. Move the finish/step-completed marks (and the onboarding_finished event) to the success path after the try block completes so failed completions don't get reported as finished and abandonment stays trackable.</comment>

<file context>
@@ -57,6 +62,8 @@ function OnboardingProjectPage() {
 	// dashboard's new-workspace modal pre-selected to the project just added.
 	const finish = async (projectId: string) => {
+		trackOnboardingStepCompleted("project");
+		markOnboardingFinished();
 		track("onboarding_finished", { outcome: "completed" });
 		try {
</file context>

@github-actions

Copy link
Copy Markdown
Contributor

This PR has had no activity for 53 days and will close in 7 days unless there is new activity. If this contribution is still important or relevant, please leave a comment to keep it open. If it closes, you are welcome to reopen it or ask a maintainer to reopen it. Thank you for contributing!

@github-actions github-actions Bot added the stale label Sep 27, 2026
@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

This PR was automatically closed due to inactivity to help keep the review queue manageable. This does not mean the contribution was rejected. If this contribution is still important or relevant, please reopen it or leave a comment asking a maintainer to reopen it. Thank you for contributing!

@github-actions github-actions Bot closed this Oct 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant