reviewed by skillbay: we re-ran the example ourselves and the skill changed the answer. see our run
log in to download this skill.
Design first-run flows around earned outcomes, fast time-to-value, durable state, and trustworthy activation evidence.
the same prompt, answered without the skill and then with it.
prompt:
Audit and redesign the onboarding for MetricNest, a B2B subscription analytics product. Today: users create an account, verify email, complete a 7-step wizard (role, company size, industry, team name, business goal, Stripe connection, invite teammates), then land on an empty dashboard. After Stripe OAuth, the first sync takes 2–5 minutes; users can navigate away, but the dashboard gives little feedback. Current analytics events are signup_completed, wizard_completed, and integration_connected. We do not yet have trustworthy funnel data. Constraints: email verification is required before accessing customer billing data; Stripe OAuth scopes need a clear explanation; syncs have real infrastructure cost; the implementation team can ship one substantial change this month. Give me a concrete audit and build recommendation. Include the proposed flow, what to cut or defer, edge states, and the measurement plan. Do not invent benchmark data. Keep the answer under 1,200 words.
# MetricNest Onboarding: Audit & Build Recommendation
## 1. Audit
**The core structural flaw: you ask for the most, right before you deliver the least.**
The 7-step wizard extracts seven pieces of information, six of which do nothing for the user in the next five minutes, and then deposits them on an empty dashboard during a 2–5 minute sync with no feedback. Every step is a drop-off opportunity purchased with zero user value.
Step-by-step verdict:
| Step | Verdict | Why |
|---|---|---|
| Role | Defer | Doesn't change what they see on day one. Ask later, in context. |
| Company size | Cut from onboarding | Inferable from Stripe volume; it's a sales-routing field, not a product field. |
| Industry | Cut from onboarding | Same. Enrich from email domain or Stripe business profile. |
| Team name | Auto-derive | Use Stripe account name or email domain; make it editable in settings. |
| Business goal | Keep, but relocate | The only field that can plausibly shape the first dashboard — if it actually does. If it doesn't, cut it. |
| Stripe connection | **Promote to the center** | This is the activation event. Everything else is overhead. |
| Invite teammates | Defer | Nobody invites colleagues to an empty product. Ask after first value. |
**Second flaw: the sync is treated as a backend detail rather than a product moment.** Two to five minutes of silence after the single highest-intent action in the funnel is where trust is lost. Users who navigate away have no reason to come back.
**Third flaw: you cannot see any of this.** Three events — `signup_completed`, `wizard_completed`, `integration_connected` — give you no visibility into per-step wizard drop-off, verification drop-off, OAuth abandonment, sync duration, sync failure, or whether anyone ever saw real data. You have an outcome counter, not a funnel.
**Fourth flaw: cost exposure.** Full syncs fire for every connection, including tire-kickers and test-mode accounts, with no tiering.
---
## 2. The one substantial change: connect-first onboarding with a live sync experience
Collapse the pre-connection wizard, move Stripe OAuth to immediately after email verification, and convert the sync wait into the onboarding itself — showing live progress and streaming partial results as they land, while asking the one or two questions that actually matter *during* the wait.
This attacks the biggest drop-off (pre-value friction), the biggest trust gap (silent sync), and the cost problem (via tiered sync) in a single change.
### Proposed flow
1. **Sign up** — email + password/SSO. Nothing else.
2. **Inline verification** — 6-digit code entered on the same screen, not a click-out to email. Keep the magic link as a fallback for people who switch devices. This keeps the session alive and makes verification measurable as a step rather than a black hole.
3. **Connect screen** — one screen that does three jobs: states what MetricNest will show them, explains the Stripe scopes in plain language (what you read, what you never touch, that it's read-only, how to revoke), and has one primary button. No skip button, but a "Why do you need this?" expander and a quiet "explore with sample data" secondary link.
4. **Stripe OAuth** → return.
5. **Sync experience (the new surface)** — a live progress view, not a spinner:
- Named stages with real state: *Reading account → Importing customers (1,284) → Importing subscriptions → Calculating MRR*.
- Counts increment as objects land. This alone converts dead time into evidence the product works.
- Metric tiles hydrate progressively as each becomes computable, rather than all-or-nothing at the end.
- Below the progress, ask **one** question: "What are you here to track?" with 3–4 options that genuinely select a default dashboard layout. If the answer doesn't change anything, drop the question.
6. **First value** — dashboard with real numbers, plus a banner if backfill is still running.
7. **Post-value prompts** — *then* offer teammate invites ("share this MRR view with your team") and any profile enrichment, as dismissible in-product cards, not gates.
### Enabling backend piece: tiered sync
First sync pulls a recent window (e.g. last 90 days) to reach a displayable MRR/customer count fast; full history backfills in the background with a visible "historical data loading" state. This shortens time-to-value *and* lets you defer expensive full backfills for accounts that never return.
**Scope fallback:** if tiered sync is too large for one month, ship the flow restructure plus honest progress UI (real stages, real counts, revised ETA) and schedule tiering next. The flow change carries most of the value; do not let the backend work block it.
---
## 3. Cut or defer
- **Cut from onboarding:** company size, industry, role. Enrich or ask in-app later.
- **Auto-derive:** team name.
- **Defer to post-value:** teammate invites.
- **Conditionally keep:** business goal — only if it changes the default dashboard. Otherwise cut.
- **Do not build this month:** multi-integration support, onboarding checklists, product tours, email drip redesign.
---
## 4. Edge states to specify
**Verification:** code expired; code mis-entered (rate-limit, don't lock out); wrong email at signup (allow change before resend); corporate mail delay (show "still waiting?" with resend + change-address after ~60s); user clicks magic link on a different device.
**OAuth:** user cancels at Stripe (return to connect screen with a non-blaming message and the explainer expanded, not an error); scopes partially denied; Stripe account already connected to another MetricNest workspace (offer join-request, don't silently create a duplicate); Stripe Connect platform account with connected sub-accounts (pick one, or block with a clear message); test-mode-only account (show it works, label the data as test, don't run a full paid sync).
**Sync:** zero customers/subscriptions (a real state — show an explicit empty state explaining *why* it's empty, not a broken dashboard); very large account exceeding the ETA (switch from progress bar to "this one's big, we'll email you"); rate-limited or partial failure (show what succeeded, retry the rest, never show a blank dashboard); hard failure (specific reason + retry + support path); token revoked mid-sync.
**Session:** user closes the tab during sync — send the "your data is ready" email and deep-link straight to the dashboard; user returns mid-sync — resume the progress view, don't restart.
**Multi-user:** second teammate joining an existing workspace skips verification-to-value entirely and lands on the live dashboard. Never make them connect Stripe.
---
## 5. Measurement plan
**Instrument first — this week, before the redesign ships.** It's small, and without it you can't evaluate the change.
**Events** (all carrying `user_id`, `workspace_id`, `session_id`, `onboarding_variant`, `is_first_member`):
`signup_started` · `signup_completed` · `verification_code_sent` · `verification_submitted {result}` · `email_verified {method, seconds_since_signup}` · `connect_screen_viewed` · `scope_explainer_expanded` · `oauth_started` · `oauth_returned {status, failure_reason}` · `sync_started {mode: fast|full}` · `sync_stage_changed {stage}` · `first_metric_rendered` · `sync_completed {duration_ms, object_counts}` · `sync_failed {reason}` · `backfill_completed` · `dashboard_viewed {has_real_data}` · `invite_sent` · `invite_accepted` · `session_abandoned_during_sync`
**Primary metric — activation:** % of `signup_started` that reach `first_metric_rendered` with real data within 24 hours. Report the full step-by-step funnel, not just the endpoints.
**Secondary:** median time from `signup_started` → `first_metric_rendered`; verification completion rate; OAuth start→success rate; sync abandonment rate.
**Guardrails:** sync compute cost per *activated* workspace (should improve with tiering); sync failure rate; support ticket volume; D30 seats per workspace — invites will drop in onboarding by design, so verify they recover downstream rather than treating the in-onboarding number as a loss.
**Method:** ship behind a flag, 50/50 by workspace, run to a pre-declared duration rather than stopping on a favorable reading. Treat any pre-change numbers as directional only — the current instrumentation can't support a clean baseline.## 1. Activation contract
```
Segment: Operator/founder connecting their own billing data
Situation: Wants to see MRR, churn, and retention without building it in a spreadsheet
Entry: Marketing site → signup (assumed; unverified — no entry-source event exists)
First earned outcome: Their own Stripe revenue metrics rendered on screen
Proof event: first_sync_completed(records > 0) AND metrics_view_rendered(live data)
within 24h of stripe_oauth_granted
Event source: server — sync worker for completion, app server for render
Durable state: Connected Stripe account, synced dataset, workspace that remembers it
Entry → outcome target: p50 ≤ 8 min, p90 ≤ 20 min
Account → outcome target: p50 ≤ 6 min, p90 ≤ 15 min
Counter-metrics: OAuth scope-consent abandonment, sync infra cost per activated
workspace, support tickets about "wrong/missing numbers," week-2 return rate
Disqualifiers: wizard_completed, integration_connected, invite_sent
```
`integration_connected` is your most dangerous event: it fires at OAuth grant, before any sync has succeeded, so it will report success for every user who watches a spinner and leaves. `wizard_completed` measures compliance with a form you built. Neither is value.
## 2. Current path map
| State/action | Why it exists | User-visible consequence | Failure/escape | Event | Decision |
|---|---|---|---|---|---|
| Signup | Identity | Account | — | `signup_completed` | KEEP |
| Email verification | Required before billing data | Blocks everything | None observed | none | KEEP, instrument |
| Role | Unclear | None visible | — | none | DEFER |
| Company size | CRM/sales | None visible | — | none | CUT |
| Industry | Segmentation | None visible | — | none | CUT (enrich from Stripe/domain later) |
| Team name | Workspace label | Header text | — | none | AUTOMATE from email domain, editable |
| Business goal | Personalization intent | None visible | — | none | DEFER → becomes a real choice after data exists |
| Stripe OAuth | The actual prerequisite | Grants scopes | Unknown | `integration_connected` | KEEP, promote to first |
| Invite teammates | Expansion | None pre-value | — | none | DEFER to post-activation |
| Empty dashboard | Default landing | Nothing | None | none | REPLACE |
Four of seven steps change nothing the user sees, and they sit *in front of* the only step that produces value. The 2–5 minute sync then lands on a dashboard with "little feedback," which is where the flow most likely breaks: the user's last impression of a successful connection is an empty screen.
## 3. The one substantial change to ship this month
**Build the connect-and-watch path: verification → Stripe → live sync surface that becomes the first result.** Delete or defer the rest of the wizard as part of the same change; it is removal work, not new build.
Proposed flow:
1. **Signup.** Email + password/SSO. Capture `entry_source`.
2. **Verification, inline.** 6-digit code entered on the same page rather than a link that opens a new tab and loses context. Accept the link too. Explain the gate in one line: *"We verify email before connecting billing data."* Workspace is auto-created from the email domain, editable later.
3. **Scope explanation, then OAuth.** One screen, before the redirect: which Stripe scopes, read-only, what each enables, what MetricNest never does (no charges, no refunds, no customer writes). A "Why read-only?" disclosure. Primary button: Connect Stripe.
4. **Sync progress as a product surface, not a spinner.** Named stages (Customers → Subscriptions → Invoices), running counts, and metric tiles that fill in as each stage lands. Honest partial state. Explicit copy: *"Safe to leave — we'll email you when it's ready."*
5. **First result.** MRR, active subscriptions, and churn from their real data, with the synced date range labeled. Then, and only then, the deferred questions become useful: *"What do you want to track first?"* routes the default view, and *"Invite a teammate to this dashboard"* now shares something real.
**Cost control:** first sync is bounded — most recent 90 days — to produce a real result quickly and cheaply. Full backfill runs in the background with a visible banner: *"Showing last 90 days. Full history syncing."* This is labeled honestly; it is not a fixture. It also caps infra spend on accounts that never return.
**Cut now:** company size, industry. **Defer:** role, business goal, invites. **Automate:** team name.
## 4. Edge states
| State | User sees | System does | Recovery | Evidence |
|---|---|---|---|---|
| Verification email missing | Resend with cooldown, change-address link | Invalidate prior code | Change email without losing account | `verification_resent{attempt}` |
| Scope consent denied | Plain explanation of what cannot work read-only | Preserve workspace | Retry OAuth; contact support | `stripe_oauth_denied{scopes}` |
| OAuth callback fails / user closes tab | "Connection didn't complete" with retry | No partial credential stored | Idempotent retry, no duplicate account | `stripe_oauth_failed{error_family}` |
| Zero-record Stripe account (test/new) | "Connected — no charges yet in this account" + account-switch and labeled demo dataset | Do not report an error | Switch Stripe account; demo clearly marked | `first_sync_completed{records:0}` |
| Partial sync (one stage fails) | Completed tiles stay; failed section named | Preserve successful subset | Retry only the failed stage | `sync_partial{stage,error}` |
| Stripe rate limit | "Stripe is throttling; resuming automatically" + ETA | Backoff, no duplicate billable work | Automatic | `sync_rate_limited` |
| Sync exceeds ~10 min | Same page, email notification sent | Job continues server-side | Resume link returns to live progress | `sync_slow{elapsed}` |
| Reload / return later | Live progress or finished result, never the wizard | Rehydrate from job state, no re-trigger | — | `resume{source}` |
| Duplicate account, same Stripe | "This account is connected to another workspace" | Block silent split | Sign in or request access | `stripe_conflict` |
| Disconnected/revoked token | Banner with stale-data timestamp | Stop syncing | Reconnect | `integration_revoked` |
Accessibility: progress announced via a polite live region at stage boundaries only; keyboard-complete with no traps; focus moves to the first result heading on completion; success conveyed by text, not color or animation alone; tiles readable at 375px.
## 5. Measurement plan
Replace the three events with a boundary funnel. Every event server-emitted, deduped on `workspace_id`, carrying `elapsed_from_signup_ms`.
`signup_completed` → `verification_completed` → `scopes_viewed` → `stripe_oauth_granted` → `first_sync_started` → `first_sync_completed{records, stage_durations, range_days}` → `metrics_view_rendered{live:true}` → `returned_to_metrics{day_7}`
```yaml
name: first_sync_completed
trigger: first sync job for a workspace reaching terminal success
source: sync worker
dedupe: workspace_id
properties: {records, duration_ms, stages_failed, entry_source}
owner: data_platform
```
Readout table per boundary: eligible / attempted / succeeded / failed / p50 / p90, with retries counted separately from users. Segment only by entry source, Stripe account age, and error family.
Counter-metrics tracked weekly: OAuth denial rate, sync cost per activated workspace, "numbers look wrong" tickets, day-7 return.
Because you have no trustworthy funnel today, **instrument before you interpret.** Ship the events with the redesign, hold two weeks of clean baseline, then A/B only if the funnel shows a boundary worth contesting. Do not accept a wizard-completion improvement as evidence; only `first_sync_completed ∧ metrics_view_rendered` counts.
## 6. Rubric — current build
Activation proof 0 · Time-to-value 0 · Interaction cost 0 · Realness 1 · Continuity 0 · Personalization 0 · Failure recovery 0 · Trust/safety 1 · Accessibility 0 · Measurement 0 · Retained value 0 → **2/22: a guided signup, not an activation system.** The zeros in activation proof and failure recovery are blocking, not cosmetic.
**Unresolved assumptions:** that most signups have a live Stripe account with recent charges; that the 2–5 min sync is dominated by Stripe API pagination rather than your own processing; that verification isn't itself a major drop. Each is answerable in two weeks with the events above — and the answer to the second determines whether the 90-day bound actually shortens time-to-value or merely caps cost.what changed: Same prompt and model in isolated Claude Code runs. The baseline is polished but treats OAuth as activation, assumes missing product behavior without flagging it, recommends an auth-method change and a 90-day sync policy without first validating constraints, and lacks an authoritative activation contract. With the skill, the answer defines a server-verifiable earned outcome, maps every interaction to KEEP/CUT/DEFER/AUTOMATE, specifies durable state and counter-metrics, covers recoverable tail states and accessibility, deduplicates events, and labels unresolved assumptions.
we ran the same prompt in an empty directory, once with nothing and once with only this skill installed, and did not let the agent touch the network or the disk. this is what came back, unedited.
**Audit: where the current flow breaks** - **The value moment is buried.** MetricNest's only real "aha" is a dashboard showing the user's own MRR. Today that sits behind verification, seven questions, an OAuth grant, and an unexplained wait. Every step before Stripe is cost without value. - **The wizard asks before it earns.** Role, company size, industry, team name, and goal are collected before the product has shown anything. Most of these are inferable later from Stripe data or the account itself. Invite teammates asks users to vouch for a product they have not yet seen. - **The sync wait is the highest-risk moment and the least designed.** After OAuth the user has just granted access to their billing data, then lands on an empty dashboard for 2 to 5 minutes. An empty dashboard reads as "it did not work." Navigating away is allowed but nothing tells them to come back. - **Scope consent is a trust cliff.** Stripe OAuth surfaces scopes with no product-side framing. A finance-adjacent buyer will hesitate on the Stripe screen if the ask was not explained first. - **You cannot see any of this.** Three events, none on the steps where drop-off most likely occurs. The wizard is one event for seven screens. There is no event for "saw real data." I will not estimate drop-off rates. The instrumentation below is how you get those. **The one change to ship this month** Ship "Connect-first onboarding with a real syncing state." One project, one flag, three pieces that only work together: 1. Move Stripe connection to immediately after verification. Cut the wizard in front of it. 2. Replace the empty dashboard with a syncing state that shows progress and uses the wait. 3. Instrument every step, including the old flow for the first week. Instrumentation is not a separate project. It ships inside this one and gates the flag rollout. **Proposed flow** 1. **Create account.** Email, password or SSO, nothing else. Emit account_created. 2. **Verify email.** Required before any billing data, so it stays. Page shows a resend button and a "wrong address" fix. Emit verification_sent, email_verified. 3. **Connect Stripe.** One screen, one button. Above the button, in plain language: MetricNest asks for read-only access to customers, subscriptions, invoices, and charges. It cannot move money, change plans, or write to your account. Access can be revoked from Stripe at any time. Emit stripe_connect_viewed, oauth_started, oauth_completed with outcome. 4. **Syncing state on the dashboard itself.** Not a spinner on a blank page. Show what is being pulled and what has landed: customers found, subscriptions found, invoices processed, with counts updating as the sync progresses. Show "usually a few minutes" rather than a fake progress bar. State plainly that the user can close the tab and will get an email when data is ready. 5. **Use the wait.** During sync, show at most two optional questions, chosen only if their answers change what the user sees next. Role is the strongest candidate, because it can pick the default view. Everything else is deferred. 6. **First data.** Charts render as data arrives. Emit dashboard_first_data_viewed the first time the user sees a chart with a nonzero synced value. This is your activation event. 7. **Invite teammates.** Prompted after first data, from the dashboard, with a suggested message that includes the actual MRR chart. Emit invite_sent. Team name defaults to the Stripe business name and is editable in settings. Company size and industry come from the synced data and enrichment, not from the user. **What to cut, what to defer** - **Cut:** the seven-step wizard as a gate. Team name, company size, industry as questions. - **Defer to next month:** invite flow polish, welcome checklist, non-Stripe billing sources. - **Defer indefinitely unless data says otherwise:** business goal. If sales or marketing needs these fields, collect them as optional prompts during the sync wait, never as blockers. **Sync cost controls** - One sync per Stripe account, started only on successful OAuth. Reloading the page must not enqueue another. - Idempotent job keyed on the Stripe account id. A second workspace connecting the same account attaches to the existing sync result. - Sync recent data first so the dashboard can render the last 90 days early, then backfill history in the background. This turns "2 to 5 minutes to anything" into "a minute to something." - Test-mode-only accounts get a lightweight sync and a clear label, not the full backfill. **Edge states to design, not discover** - **Verification email missing.** Resend, change address, and a note about spam folders. Track resend count. - **OAuth cancelled or denied.** Return to the connect screen with the scope explanation expanded, not a generic error. - **Stripe account already connected elsewhere.** Tell the user which workspace, offer to request access or contact the owner. - **Multiple Stripe accounts.** Let the user pick one now and add more later from settings. - **No customers or no subscriptions.** Show an honest empty state: "Connected. Stripe has no subscriptions yet." Never render zero MRR as a synced result. - **Sync fails or exceeds a timeout.** Dashboard state switches to "we hit a problem" with a retry button. Retry is rate-limited. Emit sync_failed with the reason. - **User closes the tab mid-sync.** Sync-complete email links straight to the dashboard. Emit sync_complete_email_sent and the click. - **Partial data.** Show what landed with a "still syncing history" banner, dated to the earliest synced record. - **Invite accepted before sync completes.** Invitee sees the same syncing state, not a blank dashboard. - **Non-Stripe billing.** A "we do not support this yet" option that records the provider name. This becomes your roadmap data. **Measurement plan** Events, each with workspace id, user id, flow version, and timestamp: - account_created, verification_sent, email_verified, verification_resend - stripe_connect_viewed, oauth_started, oauth_completed with outcome, oauth_error - sync_started, sync_progress with entity counts, sync_completed with duration and counts, sync_failed with reason - dashboard_syncing_viewed, dashboard_first_data_viewed, dashboard_empty_state_viewed - profile_question_answered with question and value, invite_sent, invite_accepted Funnel to report weekly: | Step | Conversion measured | |---|---| | Account created to verified | within 24 hours | | Verified to OAuth completed | within 24 hours | | OAuth completed to first data viewed | within 24 hours | | First data viewed to second session | within 7 days | Also track time to first data as a distribution, not an average, and sync duration split by customer count. Rollout: ship events on the current flow in week one so you have at least a partial baseline. Ship the new flow behind a flag in weeks two to four. If volume allows, split traffic. If it does not, compare cohorts and state the sample sizes rather than claiming significance. The decision metric is verified-to-first-data conversion. The guardrails are sync failure rate and sync cost per activated workspace. **Assumptions I made** Wizard answers do not currently change the product experience. If they drive sales routing, keep them as optional prompts during the sync wait. Stripe is the only integration today. The verification requirement applies to reading billing data, so showing a static product preview before verification would be allowed but is out of scope this month.
The MetricNest description is the only source here, so the audit works from the stated flow rather than code. Everything below is inference from that description plus the constraints you gave. ## Activation contract **Situation:** a finance or ops person at a subscription business signs up to see their MRR and churn without building spreadsheets. **First earned outcome:** they see their own MRR, churn, and customer count computed from Stripe data. **Proof event:** `first_sync_completed` with `subscriptions_imported > 0`, emitted server-side by the sync worker, deduplicated by organization. `integration_connected` is a disqualifier on its own. So is `wizard_completed`. **Durable state:** a connected Stripe account, a populated dashboard, and one saved view. **Time budget:** account created to proof event within one session. The two-to-five minute sync sets the floor, so the target is that the user is still present when it lands. Report p50 and p90 once instrumented. **Counter-metrics:** sync cost per activated org, sync failure rate, OAuth denials, verification bounce rate, support tickets in the first day. ## Current path audit | Step | Why it exists | Changes what the user sees soon? | Decision | |---|---|---|---| | Account creation | Identity | Yes | KEEP | | Email verification | Legal and security gate on billing data | Yes, required | KEEP, but move to where it gates access | | Role | CRM segmentation | No visible effect today | DEFER, ask after first value | | Company size | Segmentation | No | CUT, derive from imported customer count | | Industry | Segmentation | No | DEFER | | Team name | Workspace label | Marginal | AUTOMATE from Stripe business name, editable | | Business goal | Could route default view | Not wired to anything today | MERGE into the post-sync landing as "what do you want first" | | Stripe connection | The whole product | Yes | KEEP, make it the centerpiece | | Invite teammates | Growth | Nothing to share yet | DEFER until dashboard is populated | | Empty dashboard | Landing | Shows nothing during sync | Replace with a sync-progress state | Largest problems, in rank order: 1. **No proof event exists.** The team cannot tell whether anyone reaches value. Every other finding is a hypothesis until this is fixed. 2. **Six of seven wizard questions do not change anything the user sees.** They sit between signup and the only step that matters. 3. **The sync window is a blind spot.** The user does the hardest step, then stares at an empty screen for minutes with no signal that work is happening, no estimate, and no reason to stay. 4. **Invites happen before there is anything to invite anyone to.** An invited teammate who lands on an empty dashboard is a second wasted activation. ## Proposed flow One substantial build this month. Recommendation: rebuild the path from Stripe connect through first populated dashboard, and instrument it. Leave account creation and the verification mechanism untouched. **Step 1. Signup, then verify.** After account creation, the user reaches a single page explaining what happens next. It states that connecting Stripe requires a verified email because MetricNest will read customer billing records. The verify button is the only action. Resend and change-email links sit beneath it. Verification stays a hard gate before OAuth, which honors the constraint and gives the gate a reason the user can accept. **Step 2. Connect Stripe.** One screen. Plain-language scope explanation before the OAuth redirect: which objects are read, what is never written, and how to disconnect. The read-only nature is the trust argument, so say it in the first line. Store a draft state so an abandoned OAuth returns here with nothing lost. **Step 3. Sync in progress.** The dashboard itself renders in a loading state with honest progress: stage name, records imported so far, and an elapsed timer. Add one sentence stating the sync takes a few minutes and continues if they leave. Offer an email notification on completion. No fixture numbers on screen. The one useful thing to do while waiting is to pick which view lands first, which is where the old business-goal question moves. That answer now routes the first populated view, so it earns its place. **Step 4. Populated dashboard.** The chosen view shows their real numbers with a one-line summary: how many customers and subscriptions were imported and the date range covered. A single next-action panel offers one invite and one saved-view action. Team name defaults to the Stripe business name, editable inline. **Step 5. Deferred questions.** Role and industry appear as an optional two-field prompt on the second visit or after the first saved view. If they will never change product behavior, drop them. ## Edge states - **Verification not completed:** the connect button is disabled with the reason inline. Refresh preserves the page. - **OAuth denied or closed:** return to the connect screen with a message naming the denied scope and a retry. No wizard replay. - **Stripe account already connected to another org:** name the conflict and offer sign-in or support. Never silently create a second sync. - **Sync running, user leaves:** the sync continues. Return lands on the progress state at the current stage. Emails only on completion or failure. - **Sync produces zero subscriptions:** the dashboard says so explicitly, distinguishes "Stripe account has no subscriptions" from "sync failed", and offers a labeled sample dataset. The sample is visually marked and never merged into real data. - **Partial sync:** show the imported portion with a banner naming what is still pending. Retry only the missing range. - **Sync failure or timeout:** preserve the connection, show the failure stage, and offer one retry. Retries are idempotent so infrastructure cost does not double. - **Stripe test mode connected:** label it clearly and offer to connect live mode. - **Narrow viewport:** progress and error states must read without horizontal scroll. ## Cost controls Syncs are billable work. Start sync only after OAuth succeeds, once per org, with retry keyed on the same sync id. Do not pre-sync on signup. Cap retries and surface a support path after the cap. ## Measurement plan Server-emitted, one row per org per event, keyed by organization id and sync id. | Event | Trigger | Source | |---|---|---| | `account_created` | User record written | Auth service | | `email_verified` | Token redeemed | Auth service | | `stripe_oauth_started` | Redirect issued | App server | | `stripe_oauth_completed` | Token stored, or denied with reason | App server | | `sync_started` | Worker picks up job | Sync worker | | `sync_completed` | Job finishes, with record counts and duration | Sync worker | | `sync_failed` | Job errors, with stage and error family | Sync worker | | `dashboard_populated_viewed` | First render with real data | Client, deduplicated by org | | `first_view_saved` | Saved view written | App server | Retire `wizard_completed`. Keep `integration_connected` as an alias of OAuth completed but never report it as activation. Readout is a boundary table: eligible, attempted, succeeded, failed, p50 and p90 per boundary. Segment only by OAuth outcome and error family, since those are the dimensions that lead to fixes. Report time from account creation to sync completed, and time from sync completed to first saved view as the retained-value signal. Because no trustworthy funnel data exists, do not run this as an A/B test. Ship it, collect four weeks of clean events, and compare to the same events replayed against the old flow where possible. Set the first decision rule before launch: if fewer verified users reach a populated dashboard than started OAuth, the sync-progress state is the next fix. ## Rubric and open assumptions Against the review rubric, the current flow scores zero on activation proof, measurement, and retained value. The proposed flow lifts those to two, with time-to-value and failure recovery at two once the events land. Personalization stays at one, since only the first-view choice routes behavior. Assumptions to verify before build: that role and industry are not feeding anything downstream today, that the sync worker can report stage progress, and that Stripe business name is available under the requested scopes.
reviewer's note: without the skill the plan treated the stripe oauth grant as the win; with it the answer opened with an activation contract naming a server-side proof event (first sync completed with subscriptions imported), mapped every wizard step to keep, cut, defer or automate, and listed counter-metrics. clearly better, not just longer.
A rigorous workflow for auditing, specifying, and implementing onboarding for SaaS, developer tools, and product-led apps. It replaces generic wizard advice with an activation contract, current/proposed path map, explicit CUT/DEFER/ENRICH/MERGE/AUTOMATE decisions, a real state-and-recovery system, authoritative event contracts, counter-metrics, and an evidence-based review rubric. Includes specialized guidance for APIs, SDKs, CLIs, infrastructure, and agent products.
--- name: activation-first-onboarding description: Design, audit, or implement first-run onboarding for SaaS, developer tools, and product-led apps around a measurable activation event and fast time-to-value. Use for signup flows, setup wizards, first-run states, empty dashboards, activation funnels, onboarding experiments, and personalized onboarding. Do not use for employee onboarding or high-touch customer implementation plans. --- # Activation-First Onboarding Treat onboarding as the shortest trustworthy path from intent to an earned product outcome. Do not optimize completion of a wizard whose completion is not itself value. ## Begin with reality Inspect the actual product before proposing a replacement. Read the relevant code, routes, docs, analytics events, tests, screenshots, and existing first-run surfaces. Trace what happens from the entry point through the first durable result. Do not infer that a capability is missing from a marketing page or a screenshot. Reuse working primitives. When two onboarding surfaces exist, identify whether they serve distinct entry intents or accidentally split the funnel. For research-only work, separate observed facts, inference, and recommendations. Do not present generic benchmarks as product evidence. ## Establish the activation contract Before discussing screens, write a compact activation contract: - **User and situation:** who arrived, from where, trying to accomplish what now? - **First earned outcome:** what useful result exists because the user acted? - **Proof event:** the observable product-side event that proves the outcome happened. - **Durable state:** what remains for the user after the session: saved data, a configured integration, a successful request, an invited collaborator, or another reusable artifact. - **Time budget:** an explicit target from entry and from account creation to the proof event. - **Counter-metrics:** trust, error rate, support load, cost, abuse, retention, or another measure the shortcut must not damage. Prefer evidence events over proxy events. `report_generated` is stronger than `tour_completed`; a successful authenticated request is stronger than `api_key_copied`. If no single event proves value, define a small event conjunction and say why. Read [activation-contract.md](references/activation-contract.md) when the activation event is disputed, multi-step, or needs a measurement plan. ## Map before redesigning Model onboarding as a path through product states, not a sequence of modal screens. Make the current and proposed paths comparable. For every interaction, record: | State or action | Why it exists | User-visible consequence | Failure/escape | Event | Decision | |---|---|---|---|---|---| | Example: choose language | Tailor setup | Next screen uses that SDK | Skip → curl | `language_selected` | KEEP | Use one decision per row: - **KEEP** — necessary and already earns its cost. - **CUT** — no activation or safety value. - **DEFER** — useful after first value, not before it. - **ENRICH** — derive it from existing data; disclose and allow correction when material. - **MERGE** — combine decisions that can be made in one coherent moment. - **AUTOMATE** — safely provision or configure it on the user's behalf. Count interaction cost, not screens. A single screen with twelve decisions may be worse than three focused steps. Distinguish unavoidable work from product-imposed work. ## Design the shortest trustworthy path Work backward from the proof event. 1. Let users encounter real value before or alongside identity collection when security, abuse, cost, and privacy allow it. Preserve pre-auth work through signup. 2. Pre-provision safe prerequisites such as a workspace, test key, sample project, imported object, or starter configuration. Make destructive or billable actions explicit. 3. Ask only questions whose answers change something the user sees or does soon. Name that downstream consequence. Otherwise cut, defer, or enrich the field. 4. Use the user's own data or a representative sample that demonstrates the same product behavior. Label samples honestly; never present fixtures as live output. 5. Put the activation action inside the product. A docs link, copied snippet, or handoff to another tool is an escape route, not proof of success. 6. Return a real result inline, then offer deeper paths: export, SDK, integration, teammate, template, or docs. 7. Land in a product state that remembers the work already done. Do not send an activated user to an empty dashboard. Keep back, skip, resume, and human-help routes where they reduce abandonment without hiding required decisions. Explain why a sensitive field or permission is needed at the moment it is requested. Personalization must be causal. A collected answer should alter a snippet, template, default, checklist, example, destination, or lifecycle path. Cosmetic personalization can earn attention; behavioral routing is what earns relevance. ## Design the state system, not the happy path Specify loading, empty, partial, success, invalid input, permission denied, timeout, rate limit, offline/retry, enrichment miss, wrong enrichment, duplicate account, refresh/resume, back navigation, and narrow viewport behavior where relevant. Choose the least surprising recovery. Preserve entered work. Never turn a recoverable miss into a dead end. When enrichment confidence is low, omit or ask for confirmation rather than confidently displaying the wrong company or identity. For a complete state/event matrix and event naming rules, read [state-and-event-matrix.md](references/state-and-event-matrix.md). ## Instrument the path Instrument boundaries and outcomes, not every render. Include enough context to reconstruct the path without collecting unnecessary personal data. At minimum measure: - entry intent and source; - attempts, failures, and retries for the value action; - activation proof from the authoritative side of the system; - elapsed time from entry and account creation; - abandonment and resumed sessions; - a retained-value signal appropriate to the product. Report distributions, not only averages: activation rate, time-to-value p50/p90, failure rate, and return-to-value. Segment only by dimensions that can lead to a decision. Define event owner, trigger, deduplication key, and important properties before implementation. Do not fabricate benchmarks or analytics. If product data is unavailable, state the hypothesis and the measurement needed. ## Adapt to the product - For API, SDK, CLI, infrastructure, or agent products, read [developer-products.md](references/developer-products.md). - For an audit, spec, or implementation review, score the result with [review-rubric.md](references/review-rubric.md). Do not force pre-auth value when it would expose customer data, create expensive resources, weaken consent, or invite abuse. Do not remove verification or compliance gates without understanding why they exist. Shorter is useful only while the path remains honest, safe, accessible, and recoverable. ## Deliver at the requested depth ### Audit Lead with the activation contract and the observed current path. Identify the largest delays and false proxies. Rank a small set of changes by expected effect, confidence, effort, and risk. Cite the code, screenshots, events, or product behavior that support each finding. ### Product or design spec Include the proposed activation map, annotated state transitions, exact screen purpose and copy, edge states, data dependencies, event contract, accessibility requirements, and experiment/readout plan. Use wireframes only when they clarify hierarchy or interaction. ### Implementation Follow existing project patterns and reuse working primitives. Build the vertical path to a real proof event before polishing secondary branches. Add or update tests for state transitions, persistence, authoritative activation, and failure recovery. Verify at relevant viewports and capture before/after evidence for visible changes. Finish with the review rubric. Call out unresolved assumptions and risks without disguising them as facts.
activation-first-onboarding/SKILL.md | 8.0 KB |
activation-first-onboarding/agents/openai.yaml | 247 B |
activation-first-onboarding/references/developer-products.md | 3.2 KB |
activation-first-onboarding/references/state-and-event-matrix.md | 3.8 KB |
activation-first-onboarding/references/activation-contract.md | 3.0 KB |
activation-first-onboarding/references/review-rubric.md | 2.4 KB |
post id: m8iqqfytspbct1x8