Skip to content

feat(frontend): expand onboarding product options - #5633

Open
NicholasKissel wants to merge 4 commits into
mainfrom
NicholasKissel/update-onboarding-options
Open

feat(frontend): expand onboarding product options#5633
NicholasKissel wants to merge 4 commits into
mainfrom
NicholasKissel/update-onboarding-options

Conversation

@NicholasKissel

Copy link
Copy Markdown
Member
  • Add branded onboarding choices for Rivet Actors, agentOS, Workflows, and Dynamic Apps.
  • Tailor quickstarts and coding-agent prompts for each product while preserving flavor-specific availability.
  • Keep product switching, step progress, and responsive card states synchronized.

@NicholasKissel
NicholasKissel requested a review from jog1t September 2, 2026 07:18
@railway-app

railway-app Bot commented Sep 2, 2026

Copy link
Copy Markdown

🚅 Deployed to the actors-pr-5633 environment in rivet-frontend

Service Status Web Updated
frontend-cloud 😴 Sleeping (View Logs) Web Sep 2, 2026 at 11:51 pm UTC
frontend-inspector 😴 Sleeping (View Logs) Web Sep 2, 2026 at 11:50 pm UTC
kitchen-sink 😴 Sleeping (View Logs) Web Sep 2, 2026 at 11:47 pm UTC
ladle ✅ Success (View Logs) Web Sep 2, 2026 at 11:40 pm UTC
website ❌ Build Failed (View Logs) Web Sep 2, 2026 at 11:38 pm UTC
mcp-hub ✅ Success (View Logs) Web Sep 2, 2026 at 7:19 am UTC

@claude

claude Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Review

Reviewed the diff for feat(frontend): expand onboarding product options (branded product picker, agentOS/Workflows/Dynamic Apps onboarding, MCP client-tabs, add-component picker). Overall the change is well-scoped and the new UI code has decent story/test coverage (product-picker.stories.tsx, onboarding-skeleton.stories.tsx, agent-prompts.test.ts). A few things worth a look:

1. product-picker.tsx - "Dynamic Apps" ships with no feature flag

isAvailable: () => true,   // Dynamic Apps, badged "Preview"

agentOS (Beta) is correctly gated behind features.agentOs, but "Dynamic Apps" (Preview) is unconditionally available, so it shows in every deployment flavor (OSS/cloud/enterprise). Per root CLAUDE.md: "Ship a new pack of features behind a feature flag whenever it is significant... or not universally available across flavors" and "If unsure whether a feature needs a flag, confirm the need with the user before adding or omitting one." Given it's badged Preview the same way agentOS is badged Beta, this looks like it may have been meant to be gated too. Worth confirming intentionally rather than assuming.

2. stepper-form.tsx - isLastVisible doesn't apply the same stale-value guard as nextId

The nextId computation explicitly spreads ref.current after the stale useWatch snapshot ({...getValues(), ...ref.current}) to avoid clobbering a field just submitted synchronously in the same action, with a comment describing exactly this hazard. isLastVisible, a few lines up, calls getValues() directly, which does the spread in the opposite order and lacks the same protection.

This is currently inert: in getting-started.tsx the step order is select -> local -> agent -> handoff -> deploy, and deploy has no isVisible gate and is the last array entry, so isLastVisible resolves purely positionally today regardless of field staleness. But it's a latent inconsistency: if a future step is added after deploy, or deploy itself becomes value-gated, this could silently pick the wrong submit branch. Worth applying the same fix for robustness even though it isn't exploitable yet.

3. compute-deploy.tsx - MCP setup computed twice per render on the Rivet Compute path

useAgentInstructionsCode always calls useMcpSetup() internally, but for provider: "rivet" it sets cliDeploy: true, and getAgentInstructionsPrompt suppresses mcpSection whenever cliDeploy === true. So on the Rivet Compute deploy screen, useComputeInstructionsCode calls useAgentInstructionsCode({provider: "rivet", ...}) (whose internal MCP computation is discarded by construction) and then separately calls useMcpSetup() again itself. Minor, but it's extra useParams/data-provider work on every render of a hot onboarding screen for no effect. Could be worth restructuring so useAgentInstructionsCode skips the MCP computation when cliDeploy is true.

Other notes

  • No security concerns found in the new components (no dangerouslySetInnerHTML/innerHTML, no hardcoded secrets in client-tabs.tsx, add-component-card.tsx, product-picker.tsx).
  • Good test/story coverage on the new pieces (agent-prompts.test.ts, product-picker.stories.tsx, onboarding-skeleton.stories.tsx).
  • Since this touches onboarding across flavors, worth double-checking OSS explicitly (per frontend/CLAUDE.md) given the new Dynamic Apps card and MCP client-tabs changes. The flag question above is the main risk there.

Nothing here is a hard blocker; #1 is the one I'd want a clear answer on before merge, #2 and #3 are minor robustness/perf cleanups.

@jog1t

jog1t commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Onboarding after the MCP + prompt changes, OSS flavor (FEATURE_FLAGS=mcp).

Step 1 — product picker

OSS onboarding step 1

Step 2 — deploy. MCP is folded into the existing "Use your coding agent" prompt rather than a separate card, so the flow keeps one agent affordance. The banner notes it, and the copied prompt carries a ## Connect the Rivet MCP server section.

OSS onboarding step 2

Cloud flavor screenshots to follow.

@jog1t

jog1t commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Cloud flavor onboarding

Captured against a local k3d platform stack with the cloud feature-flag set (compute,platform,acl,auth,branding,support,billing,datacenter,danger-zone,multitenancy,mcp,agent-os). Companion to the OSS shots above.

Step 1 — Run locally. Product selector with all four targets.

Step 2 — Deploy to Rivet Compute. Two changes from the review round are visible here:

  • The agent banner now carries the MCP line ("The prompt also connects Rivet to your editor over MCP") instead of the flow growing a second, separate MCP card.
  • The manual @rivetkit/cli deploy command now passes --namespace and --env PORT=3000 for every target, not just Dynamic Apps. Previously actor/workflows deployed into production rather than the onboarding namespace.

On this flavor useMcpSetup takes the platform branch, so the copied prompt instructs the agent to hand the browser OAuth approval back to the user rather than running the local stdio server itself.

cloud-1-local

cloud-2-deploy

@jog1t

jog1t commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Updated: build-target cards

  • Dropped the Beta badge from the agentOS card.
  • Renamed the Actors card from "Rivet Actors" to "Actors".

Also visible here: the top-of-flow banner now carries the MCP line, matching the deploy-step banner. Previously only the lower banners mentioned it, so the first banner under-promised what the copied prompt actually does.

cloud-1-local

@jog1t

jog1t commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Updated: step 1 heading

Collapsed the duplicated headings on the first step. It previously stacked a step title ("Run locally"), a description ("Choose a product and get it running on your machine."), and then a second label right above the cards ("What are you building?").

Now it's a single heading: Select a product.

Also dropped the step's titleFor, which existed only to swap the heading to "What are you building?" on the agentOS path. Both paths now read the same, so the override no longer earned its keep.

cloud-1-local

@jog1t

jog1t commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Updated: composability note under the product cards

Added a small line under the grid so picking one product doesn't read as a lock-in decision:

Rivet is composable. Start with one product and add the rest to the same project whenever you need them.

cloud-1-local

@jog1t

jog1t commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Product picker is now a launcher

The first screen is just the product grid. Clicking a card submits the step and advances, so the copy-prompt, quickstart banner, and Continue button are gone from it.

  • New select step holds only the cards; Run locally (prompt + quickstart) is its own step behind it, hidden on the agentOS path since agentOS has dedicated agent/handoff steps. Flow is now Select a product -> Run locally -> Deploy.
  • Cards no longer render a selected state. Selection advances immediately, so a persistent highlight described a state the user never sits in. They now highlight on hover only, and the aria-pressed toggle semantics were dropped since these act as buttons, not a radio group.
  • Actors description shortened to two lines: "The primitive for realtime, stateful workloads".

Two bugs found while wiring this up

Stepper computed next-step visibility from stale values. handleSubmit merged the submitted values into its accumulator, then resolved the next step through getValues(), which spreads the previous render's useWatch snapshot over that accumulator. Any step gated on a field submitted by the current step therefore evaluated against the pre-submit value. Picking agentOS landed on Run locally (which is hidden for agentOS) and the progress counter read "Step 1 of 4". Fixed by letting the accumulated values win when resolving the next step.

Rivet Compute could not be re-selected on the deploy step. deployOptions in @rivetkit/shared-data lists self-host platforms only and has no rivet entry, so the switcher's filter(o => features.compute || o.name !== "rivet") was a no-op over a list that never contained it. Once the user switched to any other platform, provider could never return to rivet and the deploy step was stuck on the self-host UI. Added a local Rivet option, prepended only when features.compute, and reset the dependent runner/custom-name fields on switch. Verified the Rivet -> Railway -> Rivet round trip.

Worth a follow-up outside this PR: adding "rivet" to the Provider union and to deployOptions would clear the pre-existing TS2367/TS2322 errors in this file.

cloud-1-local

@jog1t

jog1t commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Actors page: create button out, add-component picker in

The Create Actor button is gone from both the section header and the empty state, in both flavors. The cloud CreateMenu (with its agentOS dropdown) and the OSS openCreateActor helper were left with no callers and are deleted. The create-actor modal itself stays: it is still reached from the runners table, the context switcher, and create-actor-button.

In its place, a dashed Add a component tile sits as the last cell of the grid, matching ActorBuildCard's shape so it reads as part of the grid rather than a control parked beside it. It opens a dialog with the onboarding product picker; choosing a product opens that product's docs and closes the dialog. The empty state gets the same action as a button rather than a lone 110px tile.

To keep the dialog and the onboarding step from drifting, the picker moved to a shared components/products/product-picker.tsx holding one catalog: label, description, badge, feature-flag gate, and docs URL per product. Onboarding's BuildTargetSelector is now a thin react-hook-form wrapper over it.

Also here: the progress line reads "Step 1 of 3" instead of "Step 1 of 3 · Local setup", and the Dynamic Apps card no longer wraps to two lines in the dialog's narrow column (the flex row was letting the label shrink below its content width to make room for the Preview badge).

Onboarding skeleton no longer flashes the wrong shape

Entering a namespace bound for the onboarding wizard briefly rendered the actors-grid skeleton first. The namespace route's pendingComponent was hardcoded to that skeleton, but the route's own loader is what decides onboarding vs grid, so the pending UI always guessed "grid".

The pending component now derives the destination synchronously from the query cache and renders an onboarding-shaped skeleton when that says onboarding. Since displayOnboarding || displayFrontendOnboarding reduces to !hasActors, the guess only needs the namespace record and the actor count, both already cached on the reported path. With nothing cached, it falls back to the grid skeleton rather than risk introducing the opposite wrong-skeleton flash on a cold load.

This also turned up a flavor divergence: the OSS route had no pendingComponent at all and fell through to the router-wide fullscreen spinner. Both flavors now use the same two skeletons.

Verification

Exercised in the browser on the cloud flavor: grid with the tile, the dialog, product selection opening the right docs URL, and the full onboarding flow. The grid needed MSW dev mocks (?mock=1) to populate builds, since the local stack has no deployed actors. OSS is type- and lint-clean and shares the same components, but was not exercised in a browser.

grid-tile

add-dialog2

@jog1t

jog1t commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

OSS verification

Verified both commits on the OSS flavor against the local RocksDB engine, with real builds rather than mocks.

  • Actors grid: no Create Actor button, Add a component tile last in the grid alongside the five real builds.
  • Dialog opens and correctly omits the agentOS card, since features.agentOs is off on this flavor. Selecting Workflows opened https://rivet.dev/workflows/docs/quickstart/ and closed the dialog.
  • Onboarding wizard reachable on the engine route and rendering "Step 1 of 3" with three product cards.

Two notes on the skeleton commit:

The engine pending component reads its data provider from useRouteContext, which is correct here because the engine route creates the provider in context(). The cloud route creates its provider in beforeLoad, whose return value never merges back into match.context, hence the separate peekCloudNamespaceContext cache lookup on that side. Both pending components mirror their own loader's gates exactly: onboarding display name, the skip-welcome flag, and a cached actor count of zero.

Fixed a mismatch while verifying: the skeleton drew four progress segments while the wizard now has three steps, so the bar would have visibly re-flowed on mount. Now three, with a note that agentOS only adds steps after a product is picked, which is past this skeleton.

I could not force the exact pending window in a live browser. Warming the cache also satisfies the loader, so the route resolves without ever entering the pending state, which is the intended end result. The skeleton itself was verified through its Ladle story.

oss-grid

oss-dialog

oss-onboarding-skeleton

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.

2 participants