Skip to content

fix(desktop): stop auto-opting users into v2 + onboarding#4177

Merged
saddlepaddle merged 3 commits into
mainfrom
disable-v2-onboarding
May 7, 2026
Merged

fix(desktop): stop auto-opting users into v2 + onboarding#4177
saddlepaddle merged 3 commits into
mainfrom
disable-v2-onboarding

Conversation

@saddlepaddle
Copy link
Copy Markdown
Collaborator

@saddlepaddle saddlepaddle commented May 7, 2026

Summary

  • Fresh installs were being auto-flipped to optInV2: true and then force-walked through the experimental /setup/* onboarding flow on first launch. Both behaviors are removed — v2 and onboarding are explicit-opt-in only via Settings → Experimental.
  • Deletes V2DefaultResolver, useIsFreshInstall, the isFreshInstall store field, the redirect block in _authenticated/layout.tsx, and the projects.hasAny / workspaces.hasAny tRPC procedures (all unused once the auto-flip is gone).
  • Preserves the v2 toggle and "Restart onboarding" action in Settings → Experimental, the full /setup/* route tree, and any persisted optInV2: true from users who explicitly toggled.

Test plan

  • Fresh install (clear v2-local-override-v2 localStorage): launches into v1 dashboard, no /setup/* redirect.
  • Existing v1 user (projects/workspaces in localdb, no persisted override): stays on v1.
  • User who toggled v2 on under Settings → Experimental: stays on v2 across launches, lands on the dashboard (not setup).
  • Settings → Experimental → "Restart onboarding" still walks through /setup/providers → ... when v2 is on.
  • Toggle v2 off in Experimental: surface flips back to v1 immediately.

Summary by cubic

Stops auto-opting fresh installs into v2 and removes the forced /setup/* onboarding. v2 and onboarding are now explicit opt-in via Settings → Experimental; all users land on the dashboard.

  • Bug Fixes

    • Removed first-launch redirect to /setup/* and the fresh-install auto-set of v2.
    • Kept existing optInV2 values in v2-local-override-v2 to avoid resetting explicit opt-ins; recently auto-flipped installs will remain on v2 unless toggled off.
  • Refactors

    • Deleted V2DefaultResolver, useIsFreshInstall, and the isFreshInstall field from the v2-local-override store.
    • Removed unused tRPC procedures projects.hasAny and workspaces.hasAny.

Written for commit fb55ffc. Summary will update on new commits.

Summary by CodeRabbit

  • Refactor

    • Removed fresh-install detection and its initialization flow
    • Persisted workspace list now respects tab order
    • Local defaults state simplified to only store opt-in choice
    • Removed several unused internal query endpoints and re-exports
  • Bug Fix

    • Eliminated an onboarding redirect that relied on the removed fresh-install logic

Two recurring complaints on top of each other: fresh installs were
being flipped to v2 without explicit opt-in, and v2 users were then
force-walked through the experimental onboarding flow on first launch.
v2 + onboarding aren't ready to be defaults — only users who explicitly
opt in via Settings → Experimental should land there.

- Delete V2DefaultResolver. Its only job was to auto-set optInV2=true
  on detected-fresh installs. With it gone, optInV2 stays null until
  the user toggles it, and useIsV2CloudEnabled returns false.
- Remove the /setup/* redirect in _authenticated/layout.tsx so fresh
  v2 users land on the dashboard like v1 users.
- Drop isFreshInstall from the store + the useIsFreshInstall hook
  (only consumer was the redirect gate).
- Drop the projects.hasAny / workspaces.hasAny tRPC procedures
  (only consumer was V2DefaultResolver).

Preserved: the optInV2 toggle and "Restart onboarding" action in
Settings → Experimental, the full /setup/* route tree + onboarding
store, and any persisted optInV2=true from explicit toggles.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 7, 2026

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3d992ebd-4874-4e1c-9d0e-22f811414607

📥 Commits

Reviewing files that changed from the base of the PR and between 3ca8703 and fb55ffc.

📒 Files selected for processing (1)
  • apps/desktop/src/renderer/stores/v2-local-override.ts
💤 Files with no reviewable changes (1)
  • apps/desktop/src/renderer/stores/v2-local-override.ts

📝 Walkthrough

Walkthrough

This PR removes fresh-install detection: it strips persisted isFreshInstall from the v2-local-override store, removes backend hasAny tRPC queries, deletes the useIsFreshInstall hook and the V2DefaultResolver re-export, and removes onboarding redirect wiring that depended on fresh-install checks.

Changes

Fresh Install Detection Removal

Layer / File(s) Summary
State & Store
apps/desktop/src/renderer/stores/v2-local-override.ts
V2LocalOverrideState removes isFreshInstall and setIsFreshInstall; persist key bumped to v2-local-override-v3; store now persists only optInV2.
tRPC Query Endpoints
apps/desktop/src/lib/trpc/routers/projects/projects.ts, apps/desktop/src/lib/trpc/routers/workspaces/procedures/query.ts
Removed hasAny query from projects and workspaces; workspaces getAll updated to return tabOrder-sorted non-deleting workspaces.
Hooks & Component Logic
apps/desktop/src/renderer/hooks/useIsFreshInstall.ts, apps/desktop/src/renderer/components/V2DefaultResolver/...
Deleted useIsFreshInstall hook; V2DefaultResolver effect logic remains but its re-export from the index was removed.
Layout Integration
apps/desktop/src/renderer/routes/-layout.tsx, apps/desktop/src/renderer/routes/_authenticated/layout.tsx
Root layout stops importing/rendering V2DefaultResolver; authenticated layout removes onboarding redirect that used useIsFreshInstall and updates imports (adds Paywall, useUpdateListener, env).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through stores and routes today,
Removed an old flag that used to stay,
No hasAny calls, no fresh-install cue,
The layout moves on, the code feels new,
Tiny rabbit grin — release the dew.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly summarizes the main change: stopping auto-opt-in to v2 and onboarding for fresh installs.
Description check ✅ Passed Description includes a comprehensive summary, test plan, and explanation of what was removed and preserved.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch disable-v2-onboarding

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.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 7, 2026

Greptile Summary

This PR removes the automatic opt-in to the v2 UI and forced /setup/* redirect that were triggering on fresh installs. The V2DefaultResolver component, useIsFreshInstall hook, isFreshInstall Zustand field, the authenticated-layout redirect block, and the hasAny tRPC procedures for projects and workspaces (which were only consumed by V2DefaultResolver) are all deleted.

  • v2 opt-in is now explicit only: optInV2 starts as null (falsy), so fresh installs land on v1; users who previously toggled v2 on in Settings → Experimental retain their optInV2: true in localStorage.
  • Onboarding preserved but voluntary: The /setup/* route tree, useOnboardingStore, and the "Restart onboarding" action in Settings → Experimental remain intact — they are just no longer auto-triggered.
  • Cleanup is consistent: All call-sites of the deleted procedures and hooks are removed; the store interface is narrowed accordingly.

Confidence Score: 4/5

Safe to merge — the auto-opt-in and redirect are cleanly removed across all call-sites, v2 stays on for users who explicitly enabled it, and the onboarding flow itself is untouched.

The change is a well-scoped removal: V2DefaultResolver, its two hasAny tRPC procedures, the isFreshInstall store field, and the authenticated-layout redirect are all deleted consistently. The only minor gap is that the Zustand persist store drops isFreshInstall without bumping its version, so the orphaned key lingers in existing users localStorage — harmless at runtime but worth a one-line cleanup.

apps/desktop/src/renderer/stores/v2-local-override.ts — the persist config could use a version bump to evict the stale isFreshInstall key from localStorage.

Important Files Changed

Filename Overview
apps/desktop/src/renderer/stores/v2-local-override.ts Removes isFreshInstall field and its setter from the Zustand persist store; no version bump or migration added for existing persisted state.
apps/desktop/src/renderer/routes/_authenticated/layout.tsx Removes the isFreshInstall/onboarding redirect block and associated imports cleanly; authenticated layout logic is otherwise unchanged.
apps/desktop/src/renderer/routes/-layout.tsx Removes V2DefaultResolver from the root layout; straightforward import and JSX cleanup.
apps/desktop/src/renderer/components/V2DefaultResolver/V2DefaultResolver.tsx File deleted — the auto-detect-and-flip component is entirely removed.
apps/desktop/src/lib/trpc/routers/projects/projects.ts Removes the hasAny tRPC procedure that was only consumed by the now-deleted V2DefaultResolver.
apps/desktop/src/lib/trpc/routers/workspaces/procedures/query.ts Removes the hasAny tRPC procedure that was only consumed by the now-deleted V2DefaultResolver.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[App launch] --> B{optInV2 in localStorage?}
    B -- "null - fresh install" --> C[v1 dashboard]
    B -- "true - user enabled" --> D[v2 dashboard]
    D --> E{Restart onboarding in Settings}
    E --> F[onboardingStore.reset]
    F --> G[Navigate to /setup/providers]
    C --> H{User toggles v2 on in Settings}
    H --> I[setOptInV2 true to localStorage]
    I --> D
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
apps/desktop/src/renderer/stores/v2-local-override.ts:11-17
The `isFreshInstall` field is being removed from the persisted store, but there is no version bump or `migrate` function to clean the orphaned key out of existing users' localStorage. Zustand's `persist` middleware will simply ignore the unknown `isFreshInstall` key on rehydration, so behaviour is correct — but the stale value will accumulate in localStorage indefinitely for anyone who ran the old build. A version bump with a trivial migration would clean it up.

```suggestion
		persist(
			(set) => ({
				optInV2: null,
				setOptInV2: (optInV2) => set({ optInV2 }),
			}),
			{
				name: "v2-local-override-v2",
				version: 1,
				migrate: (persisted) => {
					const state = persisted as Record<string, unknown>;
					const { isFreshInstall: _dropped, ...rest } = state;
					return rest;
				},
			},
		),
```

Reviews (1): Last reviewed commit: "fix(desktop): stop auto-opting users int..." | Re-trigger Greptile

Comment on lines 11 to 17
persist(
(set) => ({
optInV2: null,
isFreshInstall: null,
setOptInV2: (optInV2) => set({ optInV2 }),
setIsFreshInstall: (isFreshInstall) => set({ isFreshInstall }),
}),
{ name: "v2-local-override-v2" },
),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 The isFreshInstall field is being removed from the persisted store, but there is no version bump or migrate function to clean the orphaned key out of existing users' localStorage. Zustand's persist middleware will simply ignore the unknown isFreshInstall key on rehydration, so behaviour is correct — but the stale value will accumulate in localStorage indefinitely for anyone who ran the old build. A version bump with a trivial migration would clean it up.

Suggested change
persist(
(set) => ({
optInV2: null,
isFreshInstall: null,
setOptInV2: (optInV2) => set({ optInV2 }),
setIsFreshInstall: (isFreshInstall) => set({ isFreshInstall }),
}),
{ name: "v2-local-override-v2" },
),
persist(
(set) => ({
optInV2: null,
setOptInV2: (optInV2) => set({ optInV2 }),
}),
{
name: "v2-local-override-v2",
version: 1,
migrate: (persisted) => {
const state = persisted as Record<string, unknown>;
const { isFreshInstall: _dropped, ...rest } = state;
return rest;
},
},
),
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/stores/v2-local-override.ts
Line: 11-17

Comment:
The `isFreshInstall` field is being removed from the persisted store, but there is no version bump or `migrate` function to clean the orphaned key out of existing users' localStorage. Zustand's `persist` middleware will simply ignore the unknown `isFreshInstall` key on rehydration, so behaviour is correct — but the stale value will accumulate in localStorage indefinitely for anyone who ran the old build. A version bump with a trivial migration would clean it up.

```suggestion
		persist(
			(set) => ({
				optInV2: null,
				setOptInV2: (optInV2) => set({ optInV2 }),
			}),
			{
				name: "v2-local-override-v2",
				version: 1,
				migrate: (persisted) => {
					const state = persisted as Record<string, unknown>;
					const { isFreshInstall: _dropped, ...rest } = state;
					return rest;
				},
			},
		),
```

How can I resolve this? If you propose a fix, please make it concise.

…opt-ins

V2DefaultResolver wrote optInV2=true to v2-local-override-v2 for every
fresh-detected install in the ~24h window since #4176 shipped. Those
writes are indistinguishable from explicit user toggles in the store,
so without bumping the key those users would silently stay on v2 even
though they never made a real choice.

Bumping to v2-local-override-v3 clears the field for everyone:
- existing v1 users: no change (their key was already null/absent)
- recently auto-flipped users: land back on v1, can opt in if they want
- explicit opt-ins: have to toggle once more

Acceptable trade-off vs leaving a small group force-stuck on v2.
…le auto-opt-ins"

This reverts 3ca8703. The bump also clears optInV2 for users who
explicitly toggled v2 on and went through onboarding — they'd silently
land back on v1 with no projects visible (v2 workspaces live in cloud,
not v1 local), looking broken. Worse trade-off than leaving the small
~24h auto-flip cohort stuck on v2.
@saddlepaddle saddlepaddle merged commit 6f713bc into main May 7, 2026
10 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch

Thank you for your contribution! 🎉

MocA-Love pushed a commit to MocA-Love/superset that referenced this pull request May 8, 2026
…h#4177)

* fix(desktop): stop auto-opting users into v2 + onboarding

Two recurring complaints on top of each other: fresh installs were
being flipped to v2 without explicit opt-in, and v2 users were then
force-walked through the experimental onboarding flow on first launch.
v2 + onboarding aren't ready to be defaults — only users who explicitly
opt in via Settings → Experimental should land there.

- Delete V2DefaultResolver. Its only job was to auto-set optInV2=true
  on detected-fresh installs. With it gone, optInV2 stays null until
  the user toggles it, and useIsV2CloudEnabled returns false.
- Remove the /setup/* redirect in _authenticated/layout.tsx so fresh
  v2 users land on the dashboard like v1 users.
- Drop isFreshInstall from the store + the useIsFreshInstall hook
  (only consumer was the redirect gate).
- Drop the projects.hasAny / workspaces.hasAny tRPC procedures
  (only consumer was V2DefaultResolver).

Preserved: the optInV2 toggle and "Restart onboarding" action in
Settings → Experimental, the full /setup/* route tree + onboarding
store, and any persisted optInV2=true from explicit toggles.

* fix(desktop): bump v2-local-override persist key to clear stale auto-opt-ins

V2DefaultResolver wrote optInV2=true to v2-local-override-v2 for every
fresh-detected install in the ~24h window since superset-sh#4176 shipped. Those
writes are indistinguishable from explicit user toggles in the store,
so without bumping the key those users would silently stay on v2 even
though they never made a real choice.

Bumping to v2-local-override-v3 clears the field for everyone:
- existing v1 users: no change (their key was already null/absent)
- recently auto-flipped users: land back on v1, can opt in if they want
- explicit opt-ins: have to toggle once more

Acceptable trade-off vs leaving a small group force-stuck on v2.

* Revert "fix(desktop): bump v2-local-override persist key to clear stale auto-opt-ins"

This reverts 3ca8703. The bump also clears optInV2 for users who
explicitly toggled v2 on and went through onboarding — they'd silently
land back on v1 with no projects visible (v2 workspaces live in cloud,
not v1 local), looking broken. Worse trade-off than leaving the small
~24h auto-flip cohort stuck on v2.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant