feat: enterprise onboarding wizard#3079
Conversation
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 4de0405 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
4 issues found across 63 files
Partial review: This PR has more than 50 files, so cubic reviewed the highest-priority files first. During the trial, paid plans get a higher file limit.
You can try an ultrareview to bypass the file limit, comment @cubic-dev-ai ultrareview. Learn more.
Fix all with cubic | Re-trigger cubic
…m setup Add multi-step setup wizard for enterprise organizations: - SSO connection via WorkOS admin portal with provider selection - Directory sync step for user/role confirmation - Agent platform instrumentation with per-platform setup wizards (Claude, Codex, Cursor) - MCP sources configuration step - Traffic confirmation step Backend: onboarding status endpoint, WorkOS admin portal link generation with expanded intent options (SSO provider type, domain verification, DSYNC).
URL now shows ?step=connect-idp instead of ?step=1. All steps are accessible without requiring SSO completion first — users can skip and return later. Server callback also emits slug-based redirects.
d86099b to
e1ccd5a
Compare
This comment has been minimized.
This comment has been minimized.
…alidate redirect URLs - confirm-traffic-step: render AlertTriangle on attention-required banner instead of Check - connect-idp-step: remove unreachable isConnected state; flow advances via WorkOS success_url -> server callback redirect, not local state - organizations design: add Format(FormatURI) to return_url and success_url
…y gating, Claude managed-settings flow
- Move per-platform setup steps into a Sheet drawer with horizontal wipe animation between steps
- Add eligibility step type: yes/no gate with blocked-state when ineligible (Claude requires Teams/Enterprise plan)
- Auto-issue Gram API key (scope: hooks) on drawer open for platforms with requiresApiKey steps; substitute {{GRAM_API_KEY}} in code blocks
- Append timestamp to key name to avoid (organization_id, name) unique-index conflicts on rerun
- Add helpLink step field rendering moonshine Link with external-link icon
- Add screenshot step field with figcaption legend
- Shiki JSON syntax highlighting with wrap and 400px max-height in code blocks
- Provider search box on connect-idp step
- Remove add-sources wizard step entirely
- Rename mock-data.ts -> setup-data.ts; drop dead MOCK_DIRECTORY_USERS and unused McpSource type
GET /rpc/organizations.verifyOnboardingHooksSetup returns recent ClickHouse hook events for the active organization's projects. The onboarding wizard's confirm-traffic step polls this every 2s with a moving since_unix_nano cursor to verify Claude Code / Cursor / Codex instrumentation is delivering events to Gram. - New OnboardingHookEvent + VerifyOnboardingHooksSetupResult types - HookEventReader interface decouples the handler from the telemetry repo - ListRecentHookEventsForOnboarding + CountRecentHookEventsForOnboarding ClickHouse queries against telemetry_logs - Add ListRoles to MockOrganizationProvider to satisfy interface (rebase leftover) - Dashboard confirm-traffic-step now consumes the real polling hook; drop MOCK_TRAFFIC_METRICS + TrafficMetric type
…g feed - Add `mise run seed:hook-events` (calls server/cmd/seedhooks) to insert randomized bursts of synthetic hook events into local ClickHouse for demoing the confirm-traffic step. Weighted source mix, jittered cadence (rapid-fire / lull / normal), uniformly 1-3 events per burst. - Rewrite the activity feed as a marquee-style sliding window: absolute-positioned rows translate by index * ROW_HEIGHT, AnimatePresence handles enter (from above) and exit (off the bottom). Polled events queue client-side and play back one per 400ms so the slide reads cleanly. - Drop status dots from each row; add provider icons (Claude/Cursor/Codex) inline. - Brighter "Live" indicator (emerald with ping ring). - Remove now-unused MOCK_TRAFFIC_METRICS, TrafficMetric type, and "events received" banner.
- Rename continue button "Go to Dashboard" -> "Complete setup" - Center empty-state vertically + horizontally; add spinner above the message - "Live" -> "Live tail" on activity header - Seed: swap to speakeasy.com user pool (adam, quinn, sagar, brian, daniel, thomas) - Seed: 1-3 events per burst (uniform), faster default interval (800ms), wider cadence randomness
|
|
||||||||||||||||
|
|
||||||||||||||||
There was a problem hiding this comment.
4 issues found across 50 files (changes from recent commits).
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="client/dashboard/src/pages/setup/components/steps/instrument-agents-step.tsx">
<violation number="1" location="client/dashboard/src/pages/setup/components/steps/instrument-agents-step.tsx:354">
P2: When the API key is still loading or has errored, the code block displays the raw `{{GRAM_API_KEY}}` placeholder and the copy button remains active. Users can copy non-functional configuration. Consider hiding the code block or disabling copy while `isPending` is true or `error` is set for steps that require a key.</violation>
</file>
<file name="server/cmd/seedhooks/main.go">
<violation number="1" location="server/cmd/seedhooks/main.go:149">
P2: The `-burst-size` flag is advertised in usage docs and parsed, but silently ignored — the actual size is always `1 + rng.IntN(3)`. Either use the flag value (e.g., as a max for the random range) or remove the flag and update the usage comment to avoid misleading callers.</violation>
</file>
<file name="client/dashboard/src/pages/setup/setup-data.ts">
<violation number="1" location="client/dashboard/src/pages/setup/setup-data.ts:97">
P2: Codex setup copies a fake API key string because this step is not marked for API-key substitution.</violation>
<violation number="2" location="client/dashboard/src/pages/setup/setup-data.ts:125">
P2: Cursor setup also uses a hard-coded sample key without enabling API-key substitution.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
| step.code && needsKey && platformKey | ||
| ? step.code.replaceAll(API_KEY_PLACEHOLDER, platformKey) | ||
| : step.code; |
There was a problem hiding this comment.
P2: When the API key is still loading or has errored, the code block displays the raw {{GRAM_API_KEY}} placeholder and the copy button remains active. Users can copy non-functional configuration. Consider hiding the code block or disabling copy while isPending is true or error is set for steps that require a key.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At client/dashboard/src/pages/setup/components/steps/instrument-agents-step.tsx, line 354:
<comment>When the API key is still loading or has errored, the code block displays the raw `{{GRAM_API_KEY}}` placeholder and the copy button remains active. Users can copy non-functional configuration. Consider hiding the code block or disabling copy while `isPending` is true or `error` is set for steps that require a key.</comment>
<file context>
@@ -94,11 +237,301 @@ export function InstrumentAgentsStep({
+ const error = apiKeyError[activePlatform.id];
+ const needsKey = !!step.requiresApiKey;
+ const displayCode =
+ step.code && needsKey && platformKey
+ ? step.code.replaceAll(API_KEY_PLACEHOLDER, platformKey)
+ : step.code;
</file context>
| step.code && needsKey && platformKey | |
| ? step.code.replaceAll(API_KEY_PLACEHOLDER, platformKey) | |
| : step.code; | |
| step.code && needsKey && platformKey | |
| ? step.code.replaceAll(API_KEY_PLACEHOLDER, platformKey) | |
| : needsKey | |
| ? null | |
| : step.code; |
|
|
||
| rng := rand.New(rand.NewPCG(uint64(time.Now().UnixNano()), 0xC0FFEE)) //nolint:gosec // demo seed | ||
|
|
||
| _ = burstSize // size is uniformly sampled in [1,3] below regardless of flag |
There was a problem hiding this comment.
P2: The -burst-size flag is advertised in usage docs and parsed, but silently ignored — the actual size is always 1 + rng.IntN(3). Either use the flag value (e.g., as a max for the random range) or remove the flag and update the usage comment to avoid misleading callers.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/cmd/seedhooks/main.go, line 149:
<comment>The `-burst-size` flag is advertised in usage docs and parsed, but silently ignored — the actual size is always `1 + rng.IntN(3)`. Either use the flag value (e.g., as a max for the random range) or remove the flag and update the usage comment to avoid misleading callers.</comment>
<file context>
@@ -0,0 +1,306 @@
+
+ rng := rand.New(rand.NewPCG(uint64(time.Now().UnixNano()), 0xC0FFEE)) //nolint:gosec // demo seed
+
+ _ = burstSize // size is uniformly sampled in [1,3] below regardless of flag
+ burstIdx := 0
+ for {
</file context>
| title: "Add your API key", | ||
| description: | ||
| "Set the Speakeasy API key in your environment or Cursor settings.", | ||
| code: `export SPEAKEASY_API_KEY="sk_live_speakeasy_xxxxxxxxxxxxx"`, |
There was a problem hiding this comment.
P2: Cursor setup also uses a hard-coded sample key without enabling API-key substitution.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At client/dashboard/src/pages/setup/setup-data.ts, line 125:
<comment>Cursor setup also uses a hard-coded sample key without enabling API-key substitution.</comment>
<file context>
@@ -0,0 +1,135 @@
+ title: "Add your API key",
+ description:
+ "Set the Speakeasy API key in your environment or Cursor settings.",
+ code: `export SPEAKEASY_API_KEY="sk_live_speakeasy_xxxxxxxxxxxxx"`,
+ language: "bash",
+ },
</file context>
| title: "Configure environment", | ||
| description: | ||
| "Set environment variables for Codex to route through Speakeasy.", | ||
| code: `export SPEAKEASY_API_KEY="sk_live_speakeasy_xxxxxxxxxxxxx"\nexport SPEAKEASY_CODEX_HOOK=true`, |
There was a problem hiding this comment.
P2: Codex setup copies a fake API key string because this step is not marked for API-key substitution.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At client/dashboard/src/pages/setup/setup-data.ts, line 97:
<comment>Codex setup copies a fake API key string because this step is not marked for API-key substitution.</comment>
<file context>
@@ -0,0 +1,135 @@
+ title: "Configure environment",
+ description:
+ "Set environment variables for Codex to route through Speakeasy.",
+ code: `export SPEAKEASY_API_KEY="sk_live_speakeasy_xxxxxxxxxxxxx"\nexport SPEAKEASY_CODEX_HOOK=true`,
+ language: "bash",
+ },
</file context>
… Onboarding tab - New POST /rpc/organizations.sendEnterpriseAdminOnboardingEmail endpoint (org:admin scope) takes a list of recipient emails and dispatches the enterprise admin onboarding email via Loops template cmpqyxnzl00hj0jwtkibhyjdz. The setup_link variable is built server-side as siteURL/<org-slug>/setup so super admins can't redirect recipients to arbitrary URLs. - Email template registered as EnterpriseAdminOnboarding (single SetupLink field). - Dashboard: new "Onboarding" tab on OrgAdminSettings with comma-separated email input, mutation hook, sent-count toast, and display of the embedded setup link for verification.
There was a problem hiding this comment.
1 issue found across 33 files (changes from recent commits).
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="server/internal/organizations/impl.go">
<violation number="1" location="server/internal/organizations/impl.go:1152">
P2: The batch send aborts on the first delivery error, which leaves earlier recipients already emailed but returns a full failure. This creates partial side effects and duplicate emails on retry.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
| if recipient == "" { | ||
| continue | ||
| } | ||
| if err := s.email.Send(ctx, recipient, tmpl); err != nil { |
There was a problem hiding this comment.
P2: The batch send aborts on the first delivery error, which leaves earlier recipients already emailed but returns a full failure. This creates partial side effects and duplicate emails on retry.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/internal/organizations/impl.go, line 1152:
<comment>The batch send aborts on the first delivery error, which leaves earlier recipients already emailed but returns a full failure. This creates partial side effects and duplicate emails on retry.</comment>
<file context>
@@ -1120,6 +1120,47 @@ func (s *Service) handleSetupCallback(w http.ResponseWriter, r *http.Request) {
+ if recipient == "" {
+ continue
+ }
+ if err := s.email.Send(ctx, recipient, tmpl); err != nil {
+ return nil, oops.E(oops.CodeUnexpected, err, "failed to send onboarding email to %s", recipient).Log(ctx, s.logger)
+ }
</file context>
Summary
/:orgSlug/setupwith stepper navigationBackend
GetOnboardingStatusendpoint returning SSO/DSYNC configuration stateGenerateWorkOSAdminPortalLinkexpanded with intent options (SSO provider type, domain verification, DSYNC)Dashboard
public/icons/platforms/Test plan
/:orgSlug/setupand verify wizard rendersSummary by cubic
Adds a full-screen enterprise onboarding wizard at
/:orgSlug/setupto guide orgs through SSO, directory sync, agent setup, and traffic verification. Adds a super‑admin Onboarding tab to email enterprise admins a setup link to the wizard.New Features
?step=<slug>.intentOptions(SSOproviderType, domain verification),successUrl/returnUrl, anditContactEmails.GET /rpc/organizations.verifyOnboardingHooksSetupevery 2s; React Query hooks in@gram/client. Seed demo data viamise run seed:hook-events.GET /rpc/organizations.getOnboardingStatuswith React Query support in@gram/client; WorkOS dev mock lists connections/directories; OpenAPI/SDK regenerated.POST /rpc/organizations.sendEnterpriseAdminOnboardingEmailto dispatch the Loops email linking to/:orgSlug/setup; includes React Query mutation in@gram/client.Bug Fixes
success_urlcallback.return_urlandsuccess_urlas URIs; avoid auth skeleton flash on/setup.Written for commit 4de0405. Summary will update on new commits.