Onboarding
The first 60 seconds after sign-up are a guided wizard that lands the new user with a working agent token + an MCP config they can paste into Claude Code, Cursor, or Claude Desktop.
State machine
Persisted column: users.onboarding_step — Postgres enum onboarding_step = ('org' | 'repo' | 'agent' | 'client' | 'done') (migration 0015). All transitions implemented in packages/core/src/onboarding/state.ts.
┌─────┐ ┌──────┐ ┌───────┐ ┌────────┐ ┌──────┐
│ org │───▶│ repo │───▶│ agent │───▶│ client │───▶│ done │
└─────┘ └──────┘ └───────┘ └────────┘ └──────┘
▲ ▲
│ │
skippable skippable| Step | Skippable | What it does |
|---|---|---|
org | no | Create the user's first organization. Slug auto-derived; collisions resolved with -2, -3, … |
repo | yes | Optional GitHub App / GitLab connect. Skip → go to agent. Can be done later from Settings → Integrations. |
agent | no | Create the default my-agent (scopes tasks:* + knowledge:*) and mint its first token. |
client | yes | MCP config snippet + "Add to Claude Code" copy button + plain MCP URL field. Skip → done. |
done | terminal | Marks the wizard finished. _app loader stops redirecting; user lands in their workspace. |
Enforcement
apps/web/app/routes/_app.tsxloader readsusers.onboarding_stepon
every authenticated app request. Non-done → 302 to /onboarding/<step>.
apps/web/app/routes/_marketing._index.tsxapplies the same redirect on/
for signed-in users (real drobek_session cookie only — not the dev bypass fallback) so a user mid-wizard cannot land on the marketing homepage. After done, authenticated users with a workspace redirect to /o/<slug>.
- The
apps/web/app/routes/onboarding.tsxlayout guards against URL
jumping: visiting /onboarding/agent while still at step org redirects back to org. Visiting an earlier step is allowed (read-only review).
- Step transitions bump the column inside the same flow as the side
effect (org insert, token mint). A crash mid-flow leaves consistent shape.
Token "show once" invariant
The agent token plaintext is rendered exactly once — at /onboarding/client, right after /onboarding/agent posts. The carrier is a one-shot HMAC-signed, HttpOnly cookie minted by the agent step's action and cleared by the client step's loader in the same response that renders the snippet.
Reloading /onboarding/client after the cookie has been read shows the token-prefix placeholder + a callout pointing the user to Settings → Agents to mint a fresh token. This matches the parity with task 36's same-shape invariant.
Welcome email
After the org step succeeds we fire a welcome email via the shared nodemailer transport (Hostinger SMTP in prod, Mailpit in dev). Body lives in apps/web/app/lib/welcome-email.server.ts. Failure is logged and swallowed — the wizard never blocks on email delivery. Task 45 ships the final MJML template; the firing point + payload contract stays stable.
Funnel telemetry
Each step renders + every advance/skip emits a structured pino event:
{ "event": "onboarding.funnel", "user_id": "…", "onboarding_step": "agent", "action": "view" }Once the Sentry SDK is wired (see docs/runbooks/observability.md), the same emit point also adds setTag('onboarding_step', step) so the funnel shows up in the Sentry breadcrumb / event search. Until then, log search over event:onboarding.funnel is the source of truth.
Skip rules
organdagentare MANDATORY. Skipping them isn't surfaced in the UI.reposkip → user can wire a repo later from Settings → Integrations.
No data dependency; plans can be created without a repo (verification engine falls back to needs_review).
clientskip → wizard completes without showing the snippet. User can
rediscover the snippet by minting a fresh token from Settings → Agents (the snippet schema is identical).
/.well-known/mcp
Cheap discovery endpoint for MCP clients that want to auto-find a server:
{
"server": "https://mcp.drobek.app/mcp",
"version": "1",
"auth_scheme": "bearer",
"docs": "https://drobek.app/docs/mcp"
}Source: [apps/web/app/routes/[.]well-known.mcp.tsx](../apps/web/app/routes/%5B.%5Dwell-known.mcp.tsx). Nothing in the runtime depends on this endpoint — it's future-friendly metadata for clients that read it (the convention is proposed-but-not-yet broadly adopted as of 2026).
See also
- ADR 0009 — server-driven onboarding state machine
- Design lock § Onboarding — pixel-perfect rules.
- Source design:
docs/design/final/project/app-screens.jsxOnboarding
component + appshell-screens.css .onboarding* rules.