Skip to content

style(web): refine onboarding menus - #5371

Closed
t3-code[bot] wants to merge 12 commits into
mainfrom
t3bot/onboarding-menu-ui
Closed

style(web): refine onboarding menus#5371
t3-code[bot] wants to merge 12 commits into
mainfrom
t3bot/onboarding-menu-ui

Conversation

@t3-code

@t3-code t3-code Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

summary

  • remove the uppercase monospace app label from onboarding steps
  • let the available connection choices evenly fill the selector
  • mute complete pairing instruction rows
  • replace individual project borders with one subdued list surface

verification

  • vp run --filter @t3tools/web typecheck
  • pnpm --dir apps/web test (204 files, 1777 tests)
  • exercised the real /welcome workflow in light and dark modes

screenshots

before, light

before light

after, light

after light

before, dark

before dark

after, dark

after dark


Note

Medium Risk
Touches first-load routing (blank screen until gate resolves) and new filesystem reads of agent home directories, though the scan is read-only and gated behind existing orchestration read authorization.

Overview
Adds a first-run onboarding path for fresh installs: FirstRunGate blocks the authenticated shell until client settings and workspace state are known, then sends empty workspaces to a full-screen /welcome wizard and records completion via onboardingCompletedAt.

The wizard walks through connection choice (local / T3 Connect / direct pair), optional machine pairing, Claude Code & Codex setup with an inline PTY for install/login, and a project import step that calls a new server scan. Onboarding UI uses an even-width connection card row, softer pairing copy, and a single subdued surface for the import checklist instead of per-row borders.

On the server, AgentSessionScanner reads Claude and Codex transcript prefixes to infer cwds, merges and sorts candidates, skips missing dirs and T3 worktree sandboxes (including symlink targets), marks paths already imported, and exposes results through agentSessions.scan (orchestration read scope) with contract types and web query wiring.

Reviewed by Cursor Bugbot for commit 05f1086. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add multi-step onboarding wizard and agent-session project scanner

  • Adds FirstRunGate in __root.tsx that withholds the app tree for authenticated sessions until onboarding state resolves, routing fresh workspaces to /welcome and timing out to the app after 4 seconds.
  • Adds the full WelcomeWizard flow in WelcomeWizard.tsx with steps for connection selection, T3 Connect machine setup, direct server pairing, agent install/login via inline terminals, and project import.
  • Adds AgentSessionScanner service in AgentSessionScanner.ts that reads Claude and Codex transcript directories, groups candidates by workspace cwd, excludes T3-managed worktrees, marks already-imported projects, and returns newest-first results within bounded read/stat budgets.
  • Adds the agentSessions.scan WebSocket RPC and AgentSessionProjectCandidate / AgentSessionScanResult contracts in agentSessions.ts, plus a nullable onboardingCompletedAt field on client settings.
  • Behavioral Change: authenticated users without a onboardingCompletedAt timestamp and a fresh workspace will now see the wizard instead of the app on first load; the gate falls back to the app after FIRST_RUN_DECISION_TIMEOUT_MS (4 s) if onboarding checks remain unresolved.

Macroscope summarized 05f1086.

t3dotgg and others added 11 commits August 4, 2026 14:35
New onboarding flow for fresh installs: choose how to connect (Local Only /
T3 Connect / Direct), verify Claude Code and Codex with live probe status and
an inline install terminal, then import existing projects discovered from
Claude/Codex home directories.

- FirstRunGate at the root holds back the entire authenticated tree until the
  first-run decision is known, so fresh installs see nothing before /welcome
  (no shell flash, no EventRouter thread navigation)
- New read-only agentSessions.scan RPC discovers project candidates from
  ~/.claude/projects and Codex session rollouts (cwd from transcript first
  lines, never the lossy dir slug; T3-managed worktrees excluded)
- Import creates projects via existing project.create dispatch; default
  window is last 30 days, full checklist behind Choose
- onboardingCompletedAt client setting gates the wizard; installs that
  predate the field only qualify when the workspace is fresh

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Scanner: expand ~ in CLAUDE_CONFIG_DIR; sort transcripts by mtime before
  applying the per-source cap; check alreadyImported against both recorded
  and realpath spellings; derive the worktree-sandbox filter from the
  configured worktreesDir instead of hard-coding .t3
- Contracts: AgentSessionScanError carries the structural operation
  discriminator and derives its message (drops redundant failure literal)
- FirstRunGate: require server config before judging workspace freshness
  (fixes both the offline-install-sees-wizard and config-race cases); only
  gate authenticated sessions
- Wizard: T3 Connect "connected" view requires a live connection; the
  no-machine wait gets a Skip; partial import failures keep the step open
  and report the count; disabled providers show "Enable in Settings" instead
  of Ready/Install; failed terminal opens retry
- Docs: match actual first-run and terminal sign-in behavior

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…l reattach

Windows resolves paths with backslashes, so the configured-worktreesDir
prefix match failed there; both sides now normalize to forward slashes.
Reopening the onboarding install terminal reattaches to the still-live PTY,
so the pre-typed command is only written into a session with empty history.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A symlink whose spelling looks harmless can resolve into the worktrees
directory, so the filter now also runs against the realpath. Windows
comparisons case fold, using the injectable HostProcessPlatform reference.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sharing one terminal id between install and sign-in meant reattach either
skipped the login pre-type (history guard) or duplicated the command
(without it). Each drawer mount now opens a uniquely-id'd session that Done
closes, so pre-typing is always into a brand-new PTY.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nvironment

The server-local error was a field-for-field clone of the contract
AgentSessionScanError, so the ws.ts mapping was an identity re-tag; the
service now fails with the contract error directly. CLAUDE_CONFIG_DIR reads
through the injectable HostProcessEnvironment reference.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Continue/Skip, switching agent cards, and session exit all unmount the
drawer without hitting Done, which left uniquely-id'd PTYs running behind
the wizard. Cleanup now lives in the unmount effect so every exit path
kills the session.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A silently failed write left a blank prompt under copy telling the user to
press Enter; the header now falls back to showing the command to run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…or cause

The read cap now has a companion stat budget so a pathological Claude home
can't trigger unbounded sequential stats. Claude project directories group
transcripts by recorded cwd like the Codex scan — the slug is lossy, so one
directory can hold sessions from several distinct paths. Contract error
cause becomes required since every construction wraps a real failure.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 5, 2026
: (AGENT_INSTALL_COMMANDS[driver] ?? "");
const [preTypeFailed, setPreTypeFailed] = useState(false);

useEffect(() => {

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.

🟡 Medium onboarding/WelcomeWizard.tsx:668

When openTerminal fails, AgentInstallTerminal never retries. The async task returns without changing any state or ref, and because the useEffect dependencies don't change, React has no reason to re-run the effect on a subsequent render — so a transient RPC failure leaves the inline terminal permanently unattached despite the inline comment claiming it retries on the next render. The user must close and reopen the card to recover. Consider tracking the attempt (e.g., with a retry counter in state) so a failed open triggers a re-render and re-invocation, or remove the retry claim if retries are not intended.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/web/src/components/onboarding/WelcomeWizard.tsx around line 668:

When `openTerminal` fails, `AgentInstallTerminal` never retries. The async task returns without changing any state or ref, and because the `useEffect` dependencies don't change, React has no reason to re-run the effect on a subsequent render — so a transient RPC failure leaves the inline terminal permanently unattached despite the inline comment claiming it retries on the next render. The user must close and reopen the card to recover. Consider tracking the attempt (e.g., with a retry counter in state) so a failed open triggers a re-render and re-invocation, or remove the retry claim if retries are not intended.

Comment on lines +13 to +14
code (`npx t3 connect`) and advances when it appears.
- **Direct** — connect to a server by URL. Works over LAN and Tailscale. Run

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.

🟢 Low user/welcome-wizard.md:13

The docs say the wizard "advances when" a connected machine appears, but the user must press Continue after the machine shows up. Users will wait for an automatic transition that never happens. Update the sentence to mention the required Continue action.

+-  connected yet, the wizard shows the command to run on the machine with your
+-  code (`npx t3 connect`) and advances when it appears.
++-  connected yet, the wizard shows the command to run on the machine with your
++-  code (`npx t3 connect`). When the machine appears in the list, press
++-  **Continue** to proceed.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @docs/user/welcome-wizard.md around lines 13-14:

The docs say the wizard "advances when" a connected machine appears, but the user must press **Continue** after the machine shows up. Users will wait for an automatic transition that never happens. Update the sentence to mention the required **Continue** action.

Comment on lines +610 to +617
) : disabled ? (
<span className="text-xs text-muted-foreground">Enable in Settings</span>
) : (
<Button size="xs" variant="outline" onClick={onOpenTerminal} disabled={terminalOpen}>
<TerminalIcon className="size-3.5" />
{needsLogin ? "Sign in" : "Install"}
</Button>
)}

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.

🟡 Medium onboarding/WelcomeWizard.tsx:610

The AgentCard shows an enabled "Install" button whenever provider is undefined, which is the normal state before the provider probe completes (the summary headline reads "Checking provider status"). A user who clicks during this window opens AgentInstallTerminal with installed=false, so it pre-types the global install command (npm install -g ...) even when the CLI is already installed and only needs a login. The action should be disabled while provider status is unknown, or the card should distinguish "unknown" from "not installed".

Suggested change
) : disabled ? (
<span className="text-xs text-muted-foreground">Enable in Settings</span>
) : (
<Button size="xs" variant="outline" onClick={onOpenTerminal} disabled={terminalOpen}>
<TerminalIcon className="size-3.5" />
{needsLogin ? "Sign in" : "Install"}
</Button>
)}
) : disabled ? (
<span className="text-xs text-muted-foreground">Enable in Settings</span>
) : provider === undefined ? (
<Button size="xs" variant="outline" disabled>
<TerminalIcon className="size-3.5" />
Checking…
</Button>
) : (
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/web/src/components/onboarding/WelcomeWizard.tsx around lines 610-617:

The `AgentCard` shows an enabled "Install" button whenever `provider` is `undefined`, which is the normal state before the provider probe completes (the summary headline reads "Checking provider status"). A user who clicks during this window opens `AgentInstallTerminal` with `installed=false`, so it pre-types the global install command (`npm install -g ...`) even when the CLI is already installed and only needs a login. The action should be disabled while provider status is unknown, or the card should distinguish "unknown" from "not installed".

Comment on lines +90 to +94
useEffect(() => {
if (decision !== "pending") return;
const timer = window.setTimeout(() => setDecision("app"), FIRST_RUN_DECISION_TIMEOUT_MS);
return () => window.clearTimeout(timer);
}, [decision]);

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.

🟡 Medium onboarding/FirstRunGate.tsx:90

The fallback timer fires even while client settings are still unhydrated, so a slow hydration (>4s) permanently sets decision to "app" before the workspace-freshness check ever runs. On a genuinely fresh install this skips the welcome wizard purely because settings hydration was slow, even when the server and shells subsequently load successfully. The second useEffect guards the "app" transition on hydrated, but the timeout effect does not, so it resolves the decision out from under it. Start the timer only after hydrated is true so it cannot fire during the hydration window.

  useEffect(() => {
-    if (decision !== "pending") return;
+    if (decision !== "pending" || !hydrated) return;
    const timer = window.setTimeout(() => setDecision("app"), FIRST_RUN_DECISION_TIMEOUT_MS);
    return () => window.clearTimeout(timer);
-  }, [decision]);
+  }, [decision, hydrated]);
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/web/src/components/onboarding/FirstRunGate.tsx around lines 90-94:

The fallback timer fires even while client settings are still unhydrated, so a slow hydration (>4s) permanently sets `decision` to `"app"` before the workspace-freshness check ever runs. On a genuinely fresh install this skips the welcome wizard purely because settings hydration was slow, even when the server and shells subsequently load successfully. The second `useEffect` guards the `"app"` transition on `hydrated`, but the timeout effect does not, so it resolves the decision out from under it. Start the timer only after `hydrated` is true so it cannot fire during the hydration window.

connected yet, the wizard shows the command to run on the machine with your
code (`npx t3 connect`) and advances when it appears.
- **Direct** — connect to a server by URL. Works over LAN and Tailscale. Run
`npx t3 pair` on the server and paste the pairing URL it prints.

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.

🟢 Low user/welcome-wizard.md:15

The Direct instructions tell users to run npx t3 pair to connect over Tailscale, but plain npx t3 pair produces a non-Tailscale endpoint. The correct command for Tailscale is npx t3 pair --tailscale; without the flag, remote Tailscale users get an unreachable pairing URL.

Suggested change
`npx t3 pair` on the server and paste the pairing URL it prints.
+ `npx t3 pair --tailscale` on the server and paste the pairing URL it prints.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @docs/user/welcome-wizard.md around line 15:

The Direct instructions tell users to run `npx t3 pair` to connect over Tailscale, but plain `npx t3 pair` produces a non-Tailscale endpoint. The correct command for Tailscale is `npx t3 pair --tailscale`; without the flag, remote Tailscale users get an unreachable pairing URL.

return caseFold ? normalized.toLowerCase() : normalized;
}

function isT3ManagedWorktree(

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.

🟡 Medium project/AgentSessionScanner.ts:102

isT3ManagedWorktree compares the realpath-resolved candidate path against the unresolved spelling of worktreesDir. When worktreesDir is reached through a symlink, the candidate's realpath points at the physical target, which won't share the prefix of the un-resolved worktreesDir spelling. If that target also doesn't contain /.t3/worktrees/, the filter fails and the app's own disposable worktree is incorrectly returned as an import candidate. Canonicalize worktreesDir via realPath before the prefix comparison.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/server/src/project/AgentSessionScanner.ts around line 102:

`isT3ManagedWorktree` compares the realpath-resolved candidate path against the unresolved spelling of `worktreesDir`. When `worktreesDir` is reached through a symlink, the candidate's realpath points at the physical target, which won't share the prefix of the un-resolved `worktreesDir` spelling. If that target also doesn't contain `/.t3/worktrees/`, the filter fails and the app's own disposable worktree is incorrectly returned as an import candidate. Canonicalize `worktreesDir` via `realPath` before the prefix comparison.

*/
const MAX_TRANSCRIPTS_PER_SOURCE = 5000;

/**

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.

🟡 Medium project/AgentSessionScanner.ts:53

MAX_STATS_PER_SOURCE is consumed in scanClaude while iterating projectDirectories in filesystem-enumeration order, not mtime order. A single early Claude project with enough transcripts exhausts the budget, so every later project is skipped regardless of recency. Even within that project, transcripts.slice(0, statBudget) can omit newer transcripts because readDirectory returns unsorted entries. The scan can therefore hide recent, importable projects instead of only dropping stale sessions as the cap's documentation states. Consider statting all transcripts across all project directories before applying the budget, or at least sorting project directories by their newest transcript mtime so the budget drops the oldest directories first.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/server/src/project/AgentSessionScanner.ts around line 53:

`MAX_STATS_PER_SOURCE` is consumed in `scanClaude` while iterating `projectDirectories` in filesystem-enumeration order, not mtime order. A single early Claude project with enough transcripts exhausts the budget, so every later project is skipped regardless of recency. Even within that project, `transcripts.slice(0, statBudget)` can omit newer transcripts because `readDirectory` returns unsorted entries. The scan can therefore hide recent, importable projects instead of only dropping stale sessions as the cap's documentation states. Consider statting all transcripts across all project directories before applying the budget, or at least sorting project directories by their newest transcript mtime so the budget drops the oldest directories first.

defaultModelSelection,
},
});
if (result._tag === "Failure" && !isAtomCommandInterrupted(result)) {

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.

🟡 Medium onboarding/WelcomeWizard.tsx:806

runImport treats interrupted createProject results as success: when result._tag === "Failure" and isAtomCommandInterrupted(result) is true, the project is not created, but failures is not incremented. After the loop, failures can be zero and onDone() fires, marking onboarding complete even though selected projects were never created. The interrupt branch falls through the loop alongside successful results. Consider detecting interrupted results and bailing out (or counting them as failures) so a disconnection during import doesn't silently advance the wizard.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/web/src/components/onboarding/WelcomeWizard.tsx around line 806:

`runImport` treats interrupted `createProject` results as success: when `result._tag === "Failure"` and `isAtomCommandInterrupted(result)` is true, the project is not created, but `failures` is not incremented. After the loop, `failures` can be zero and `onDone()` fires, marking onboarding complete even though selected projects were never created. The interrupt branch falls through the loop alongside successful results. Consider detecting interrupted results and bailing out (or counting them as failures) so a disconnection during import doesn't silently advance the wizard.

@macroscopeapp macroscopeapp 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.

Effect service conventions review of the new AgentSessionScanner service, its contract error, and the ws wiring. One finding on error context; everything else (namespace imports, Context.Service + inline interface, make/layer order and naming, Foo["Service"] references, environment-based dependency acquisition, no hidden runtimes) matches the conventions.

Posted via Macroscope — Effect Service Conventions

Comment on lines +417 to +419
Effect.mapError(
(cause) => new AgentSessionScanError({ operation: "read-projects", cause }),
),

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.

AgentSessionScanError carries only operation + cause, but the resource that failed is known right here (lookupPath). Consider capturing it so the error identifies the resource structurally, as sibling services in this directory do (ProjectSetupScriptOperationError keeps projectCwd/worktreePath, the favicon resolver error keeps workspaceRoot).

In packages/contracts/src/agentSessions.ts:

     operation: Schema.Literals(["read-settings", "read-projects"]),
+    workspaceRoot: Schema.optional(TrimmedNonEmptyString),
     cause: Schema.Defect(),

and at this call site:

-              (cause) => new AgentSessionScanError({ operation: "read-projects", cause }),
+              (cause) =>
+                new AgentSessionScanError({
+                  operation: "read-projects",
+                  workspaceRoot: lookupPath,
+                  cause,
+                }),

The message getter can then include the path when present, still derived purely from structural attributes.

Posted via Macroscope — Effect Service Conventions

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1457866. Configure here.


const readCwd = Effect.fn("AgentSessionScanner.readCwd")(function* (filePath: string) {
const line = yield* readFirstLine(filePath);
return line === null ? null : extractCwd(line.trim());

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.

Claude cwd read skips sessions

High Severity

readCwd only inspects the first JSONL line, but Claude Code transcripts often start with records like file-history-snapshot that have no cwd. Those sessions are dropped even when a later line in the already-read prefix has the path, so the import step undercounts or misses Claude projects.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1457866. Configure here.

@macroscopeapp

macroscopeapp Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This change introduces a substantial first-run onboarding workflow with new routing, pairing and terminal interactions, filesystem scanning, project creation, and an auth-protected RPC rather than merely refining menu styling. Unresolved correctness findings also affect scanning, importing, worktree exclusion, and onboarding state transitions.

Not approved because:

  • 7 blocking correctness issues found at or above your repo's Minimum Blocking Severity

Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more.

}

if (choosing) {
const selectedCount = candidates.length - deselected.size;

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.

🟠 High onboarding/WelcomeWizard.tsx:858

selectedCount subtracts the entire persistent deselected set size from candidates.length instead of counting how many current candidates are in deselected. When the onboarding target environment changes (e.g. the current machine disconnects and another connected environment becomes the target), stale paths from the old environment remain in deselected even though they are not in the new candidates. This makes selectedCount subtract entries that are no longer present, so the count can drop to zero or go negative while every checkbox still appears checked — disabling the Import button despite all candidates being selected. Consider computing the count by filtering current candidates against deselected, or resetting deselected when candidates changes.

Suggested change
const selectedCount = candidates.length - deselected.size;
const selectedCount = candidates.filter((candidate) => !deselected.has(candidate.path)).length;
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/web/src/components/onboarding/WelcomeWizard.tsx around line 858:

`selectedCount` subtracts the entire persistent `deselected` set size from `candidates.length` instead of counting how many current candidates are in `deselected`. When the onboarding target environment changes (e.g. the current machine disconnects and another connected environment becomes the target), stale paths from the old environment remain in `deselected` even though they are not in the new `candidates`. This makes `selectedCount` subtract entries that are no longer present, so the count can drop to zero or go negative while every checkbox still appears checked — disabling the Import button despite all candidates being selected. Consider computing the count by filtering current candidates against `deselected`, or resetting `deselected` when `candidates` changes.

@t3dotgg
t3dotgg force-pushed the t3code/overhaul-onboarding-flow branch 2 times, most recently from 92c434a to 0b680a7 Compare August 7, 2026 09:03
@t3dotgg
t3dotgg force-pushed the t3code/overhaul-onboarding-flow branch 3 times, most recently from 9c93f3f to 612c2d3 Compare August 19, 2026 01:33
@t3dotgg
t3dotgg force-pushed the t3code/overhaul-onboarding-flow branch 5 times, most recently from d02d817 to 8468b95 Compare August 25, 2026 09:56
@t3dotgg
t3dotgg force-pushed the t3code/overhaul-onboarding-flow branch from 5047c7b to 50f2b7f Compare August 31, 2026 12:03
@t3dotgg
t3dotgg force-pushed the t3code/overhaul-onboarding-flow branch 12 times, most recently from 5c1b667 to a0c0a2b Compare September 5, 2026 09:16
Base automatically changed from t3code/overhaul-onboarding-flow to main September 5, 2026 09:22
@juliusmarminge

Copy link
Copy Markdown
Member

Superseded by #10465, which rewrote onboarding into a shared multi-computer wizard (same FirstRunGate / WelcomeWizard / welcome route surfaces this PR was styling). Closing as superseded.

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

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants