CU-18: New-user onboarding wizard (4-step) and schedule-setup prompt
Priority: P2 Accounts/sessions: A FRESH throwaway Calendo account created during this run via a Gmail plus-alias (ravikantguptaofficial+onb-<RUNID>@gmail.com). Also requires the persisted Gmail session for ravikantguptaofficial@gmail.com (to read the email-verification message that lands in the plus-alias inbox). No P3/Outlook needed. Parallel-safe: Yes — this suite creates and destroys its own isolated account and touches nothing on P1's real Calendo data, calendars, or bookings. Exclusive (rewrites global host availability?): No. It only edits the throwaway account's own availability, never P1's. Estimated time: ~22 minutes. L3 reality checks: One light L3: confirm the verification email actually arrived in Gmail (required to get past the email-verification gate). No calendar-event L3 (this suite intentionally does NOT connect a real calendar).
Goal
This suite proves that a brand-new person who signs up for Calendo is correctly guided through first-run setup: the email-verification gate, the multi-step onboarding wizard (calendar connect, weekly schedule, meeting location, AI assistant intro), and the "your schedule has no hours" nudge for users with empty availability. It also proves that completing/dismissing each of these is remembered server-side so the user is never nagged twice. This matters because onboarding is the first impression and the highest-drop-off moment of the product; a broken or repeating wizard kills activation.
Preconditions
- The browser must already have an ACTIVE persisted Gmail session for
ravikantguptaofficial@gmail.comat https://mail.google.com (needed to read the verification email sent to the+onb-<RUNID>alias, which is delivered to this single inbox). If Gmail prompts for a password, STOP and flag this as a precondition failure in00-setup-preconditions.md; do NOT attempt a cold login. - No Calendo login session is required at the start — this suite REGISTERS a fresh account from scratch. If a Calendo session for some OTHER user is already active, open https://calendo.dev/auth/logout (or clear it) before starting so the registration flow is clean. If you cannot reach a logged-out state, flag it and stop.
- Calendo production https://calendo.dev must be reachable and the email provider (Resend) must be live so the verification email is actually sent. If registration succeeds but no verification email arrives within 5 minutes, treat it as a precondition/environment failure (flag it) — without the email you cannot pass the verification gate.
- IMPORTANT CONSTRAINT (read before running): the in-dashboard 4-step wizard's Step 1 (Connect calendar) has NO skip button — the only way forward is to actually connect a Google or Outlook calendar via OAuth. Per environment rules you must NEVER perform a cold Google/Microsoft OAuth login mid-run for a throwaway account. Therefore this suite verifies the wizard's appearance, structure, step-1 contents, persistence semantics, and the schedule-setup nudge, and treats "drive steps 2→4 to completion" as conditional: only attempt it if a calendar can be connected WITHOUT a cold credential prompt (it normally cannot for a fresh throwaway). If Step 1 cannot be completed without a cold login, record that the steps 2–4 walk-through is BLOCKED-by-design and verify completion-persistence via the alternate path described in Step 18. Do not improvise a real OAuth login.
- See
00-setup-preconditions.mdfor the canonical list of which sessions must be live and how to confirm them.
Test data
- RUNID: pick a fresh UTC token at execution time, e.g.
20260601-1530. Reuse the SAME RUNID for every value below. - Throwaway account name:
Onb Test <RUNID>(e.g.Onb Test 20260601-1530). - Throwaway email:
ravikantguptaofficial+onb-<RUNID>@gmail.com. - Throwaway password:
OnbTest!<RUNID>(must be ≥8 chars; the RUNID suffix keeps it unique). Record the exact string you used in the results report so the human can log in if needed for forensics. - Generated booking slug: Calendo auto-derives the slug from the name (
onb-test-<RUNID>or similar, made unique server-side). Read the actual value from the dashboard booking link (#bookingLinkValue) and record it — do not assume.
Steps
- Action: Open the registration page at https://calendo.dev/auth/register.html. Expect: A "Create account" form with a Full name field (
#name), Email field (#email), Password field (#password), and a "Create account" submit button (#submitBtn). [L1]
- Action: Fill the form — name =
Onb Test <RUNID>(#name), email =ravikantguptaofficial+onb-<RUNID>@gmail.com(#email), password = the throwaway password (#password). Click "Create account" (#submitBtn). Expect: No inline error appears in the error box (#errorstays hidden). The card content is replaced in-place with a verification gate that reads "Before continuing, we need to verify your email address." and shows your+onb-<RUNID>email in bold, plus a "resend it to you" link (#resendLink). This proves a fresh email/password account is created UNVERIFIED. Capture screenshot: cu18-01-verify-gate.png [L1]
- Action: Confirm the email-verification gate is enforced by trying to reach the dashboard directly: open https://calendo.dev/dashboard/. Expect: You are redirected to https://calendo.dev/auth/verify.html (the dashboard JS blocks unverified users). You do NOT see the dashboard or the onboarding wizard yet. This proves email verification gates onboarding. [L2]
- Action: Open Gmail at https://mail.google.com (already logged in as
ravikantguptaofficial@gmail.com) and search for the verification email using the query:subject:(verify) onb-<RUNID>— if that returns nothing, broaden toto:ravikantguptaofficial+onb-<RUNID>@gmail.com. Expect: A recent Calendo verification email addressed to the+onb-<RUNID>alias, containing a verification link/button (the link targetshttps://calendo.dev/api/auth/verify-email?token=...). Capture screenshot: cu18-02-verify-email.png [L3]
- Action: Click the verification link/button inside that email. Expect: A Calendo "Email Verified" confirmation page appears ("Your email address has been verified successfully") with a "Continue Setup" button, and it auto-redirects after ~2 seconds to https://calendo.dev/onboarding/. [L1]
- Action: Observe the page you land on after the redirect (https://calendo.dev/onboarding/). Expect: The standalone "Connect your calendars" page — heading "Connect your calendars", a "Google Calendar" button (
#googleBtn), an "Outlook / Microsoft 365" button (#outlookBtn), a "Continue to Dashboard" link (#continueBtn), and an "I'll do this later" skip link (.btn-skip). Note: this is the POST-VERIFY standalone onboarding page, distinct from the in-dashboard 4-step wizard you will see next. Do NOT click either calendar button (that would start a cold OAuth login). Capture screenshot: cu18-03-standalone-onboarding.png [L1]
- Action: Click "I'll do this later" (
.btn-skip) to proceed to the dashboard WITHOUT connecting a calendar (this link points to/dashboard/). Expect: The browser navigates to https://calendo.dev/dashboard/. [L1]
- Action: Observe what the dashboard shows. Expect: Because onboarding is not yet marked complete server-side AND no calendar is connected, the dashboard does NOT render the normal app; instead the full-screen in-dashboard onboarding wizard overlay (
#calendarOnboarding) is displayed and the main app layout (#appLayout) is hidden. This is the 4-step wizard. Capture screenshot: cu18-04-wizard-step1.png [L1]
- Action: Read the step indicator at the top of the wizard (
#onboardingSteps). Expect: Exactly four labeled step pills: "1. Calendar", "2. Schedule", "3. Location", "4. AI Assistant" (.onb-stepelements withdata-step1–4). Step 1 pill has theactiveclass. This confirms the wizard is 4 steps with the expected titles. [L1]
- Action: Read the content of the Step 1 panel (
#onb-step-1). Expect: A calendar emoji, heading "Connect your calendar", explanatory copy about preventing double-bookings, a "Connect Google Calendar" button (links to/api/calendars/connect/google), a "Connect Outlook Calendar" button (links to/api/calendars/connect/outlook), and a Privacy Policy link. Confirm there is NO "skip"/"next"/"I'll do this later" control on Step 1 — the only forward path is connecting a calendar. Record this observation. [L1]
- Action (CONDITIONAL — only if a calendar can be connected without a cold password prompt; otherwise SKIP to Step 18): Click "Connect Google Calendar". If you are immediately bounced into a Google account chooser that requires entering a password or approving consent for a brand-new account, STOP — do not enter credentials. Navigate back to https://calendo.dev/dashboard/ and proceed to Step 18, recording "Steps 2–4 walk-through BLOCKED-by-design: Step 1 requires a cold OAuth login for the throwaway account." Expect (only if connect somehow completes without a cold login): You return to the dashboard wizard, which now advances to Step 2 automatically (the dashboard resumes onboarding from step 2 when a calendar exists but onboarding is unfinished). [L1]
- Action (CONDITIONAL — only reached if Step 11 connected a calendar): On Step 2 (
#onb-step-2, "When are you available to meet?"), inspect the weekly-hours editor (#onb-weekly-editor) and the timezone dropdown (#onb-timezone-select). The preset is Mon–Fri 09:00–17:00 already filled in. Leave the preset as-is. Click "Next" (#onb-save-schedule). Expect: The wizard advances to Step 3 and the preset weekly rules are saved to the default availability schedule viaPUT /api/availability-schedules/:id. Capture screenshot: cu18-05-wizard-step2.png [L1]
- Action (CONDITIONAL): On Step 3 (
#onb-step-3, "How would you like to meet with people?"), observe the five location preset cards (.onb-loc-opt): Zoom, Google Meet (preselected, blue border), Microsoft Teams, In-person, Phone call. Leave the default "Google Meet" selected. Click "Next" (#onb-save-location). Expect: The wizard advances to Step 4 and the chosen location is applied to the auto-created event types viaPUT /api/event-types/:id. Capture screenshot: cu18-06-wizard-step3.png [L1]
- Action (CONDITIONAL): On Step 4 (
#onb-step-4, "Meet your AI assistant"), observe the sparkle emoji, the "Meet your AI assistant" heading, the "You're all set! Your booking link is ready to share." copy, and the "Go to Dashboard" button (#onb-finish). Click "Go to Dashboard" (#onb-finish). Expect: The wizard overlay disappears,POST /api/onboarding/completeis called, the page reloads, and the normal dashboard app (#appLayout) renders with the sidebar and an Overview tab. Capture screenshot: cu18-07-wizard-step4-and-dashboard.png [L1]
- Action (CONDITIONAL — completion persistence): Reload https://calendo.dev/dashboard/ (hard refresh). Expect: The 4-step wizard does NOT reappear; the normal dashboard renders directly. This proves
onboarding_completedpersisted server-side. [L2]
- Action (CONDITIONAL — cross-session persistence): In a fresh browser tab/profile (or after clearing only the in-memory state, NOT the session cookie), open https://calendo.dev/api/auth/me. Expect: The JSON includes
"onboarding_completed": 1. This confirms completion is stored on the user record, not in localStorage. [L2]
- Action (CONDITIONAL): If Steps 11–16 ran, you have a fully onboarded throwaway account; proceed to Step 19 (schedule-setup prompt) using this account. If Steps 11–16 were SKIPPED (calendar connect blocked), go to Step 18 first.
- Action (ALTERNATE completion-persistence path — required when Steps 11–16 were skipped): Because the wizard cannot be driven to completion without a cold OAuth login, verify the completion mechanism via the API the wizard itself calls. With the throwaway session active, confirm the PRE-completion state by opening https://calendo.dev/api/auth/me and noting
"onboarding_completed": 0. Then mark onboarding complete exactly as the "Go to Dashboard" button does, by issuing the same authenticated request the app uses: in the browser DevTools console on https://calendo.dev/dashboard/ runawait fetch('/api/onboarding/complete', {method:'POST', credentials:'include'}).then(r=>r.status). Expect: Status200. Reload https://calendo.dev/dashboard/. Expect: The wizard no longer appears; the normal dashboard renders. Re-open https://calendo.dev/api/auth/me. Expect:"onboarding_completed": 1. This proves the completion flag is server-side and gates the wizard, even though the UI steps 2–4 could not be hand-driven. Record clearly in the report that steps 2–4 were verified structurally (Steps 9–10, 12–14 by inspection) but completion was triggered via the app's own endpoint, not by clicking through OAuth. Capture screenshot: cu18-08-onboarding-complete-api.png [L2]
- Action (schedule-setup empty-availability nudge — setup): The nudge only fires when the default schedule has ZERO available hours. A fresh account ships with Mon–Fri 09:00–17:00, so you must first empty it. In the dashboard, click the "Availability" sidebar tab (
a[data-tab="availability"]). In the weekly editor, click each active day circle ([data-day-circle]with classactive, days Mon–Fri) to toggle every day to "inactive", removing all windows. Then click "Save Schedule" (#saveScheduleBtn). Expect: A success toast; all seven days show "Unavailable". This makes the default schedule's rules empty. Capture screenshot: cu18-09-empty-availability.png [L1]
- Action: Reload https://calendo.dev/dashboard/ and wait ~2 seconds on the Overview tab. Expect: A native browser confirm() dialog appears reading approximately "Welcome! Your schedule has no available hours set. Would you like to set up your availability now?" with OK/Cancel. This is the schedule-setup nudge for users with empty availability. Capture screenshot: cu18-10-schedule-prompt.png [L1]
- Action: In that confirm dialog, click Cancel (the "dismiss" path). Expect: The dialog closes, you stay on the dashboard, and
POST /api/schedule-setup/dismissis fired in the background (setsschedule_setup_dismissed = 1). [L1]
- Action (dismissal persistence): Reload https://calendo.dev/dashboard/ again and wait ~2 seconds. Expect: The schedule-setup confirm dialog does NOT reappear (it was dismissed and the flag persisted). [L2]
- Action (dismissal persistence — server-backed): Open https://calendo.dev/api/auth/me. Expect: The JSON includes
"schedule_setup_dismissed": 1. This proves the dismissal is stored on the user record and not just suppressed client-side. Capture screenshot: cu18-11-dismiss-persisted-api.png [L2]
- Action (positive-path sanity for the nudge — optional): If you want to also confirm the OK branch, you cannot re-trigger the dialog for this same user (it's dismissed). Skip re-triggering; the Cancel/persist path above is sufficient for this suite. Note in the report that the "OK → switch to Availability tab" branch was verified by code inspection only (line
switchTab('availability')), not exercised, to avoid creating a second throwaway account. [L1]
L3 reality checks
- Verification email arrival (required): In Gmail (https://mail.google.com, logged in as
ravikantguptaofficial@gmail.com), searchsubject:(verify) onb-<RUNID>, or if emptyto:ravikantguptaofficial+onb-<RUNID>@gmail.com. Confirm a single recent Calendo verification email exists, addressed to the+onb-<RUNID>alias, with a working verify link tohttps://calendo.dev/api/auth/verify-email?token=.... This is the only external-reality check in this suite. Capture screenshot: cu18-02-verify-email.png - No calendar-event L3: This suite deliberately does NOT connect a real Google/Outlook calendar, so there is no calendar event to confirm. See Pass/Fail.
Cleanup
Run cleanup whether the suite passed or failed, to keep the inbox/account list clean.
- Delete the throwaway Calendo account. Log in to https://calendo.dev/dashboard/ as the throwaway account if not already (use the throwaway email/password). Open the "Settings" sidebar tab (
a[data-tab="settings"]), scroll to the red "Danger Zone" card, click "Delete My Account" (#deleteAccountBtn). In the first confirm dialog (#confirmDialog), click "Delete Everything" (#dialogConfirm). In the second confirm dialog, click "Yes, Delete My Account" (#dialogConfirm). Expect: Redirect to https://calendo.dev/auth/login.html?deleted=1. This callsDELETE /api/auth/delete-account, which removes the user and all their event types, bookings, availability schedules, booking pages, calendar connections, and sessions. - Verify deletion: Open https://calendo.dev/api/auth/me. Expect:
401 Unauthorized(session gone / user deleted). Optionally attempt login at https://calendo.dev/auth/login.html with the throwaway email — expect "Invalid email or password". - Gmail tidy (optional, host inbox hygiene): In Gmail, search
onb-<RUNID>and move the verification email (and any onboarding/welcome mail) to Trash so the shared inbox stays clean for future runs. Do NOT delete unrelated mail. - No calendar cleanup needed — no real calendar was connected and no events were created.
Pass/Fail criteria
The run PASSES only if ALL of the following are true:
- Registering a fresh email/password account creates an UNVERIFIED account and shows the verification gate, NOT the dashboard (Steps 2–3). [L1/L2]
- The verification email actually arrived in Gmail for the
+onb-<RUNID>alias and its link verified the email, redirecting to/onboarding/(Steps 4–5). [L3] - The standalone
/onboarding/calendar-connect page renders with Google + Outlook buttons and an "I'll do this later" link (Step 6). [L1] - After skipping the standalone page, the dashboard shows the in-dashboard 4-step wizard (
#calendarOnboarding) with exactly the four steps "1. Calendar", "2. Schedule", "3. Location", "4. AI Assistant", and Step 1 has no skip control (Steps 8–10). [L1] - Onboarding completion persists: after completion (via Steps 11–14 if calendar connect was possible, OR via the app's own
/api/onboarding/completeendpoint in Step 18), reloading the dashboard does NOT re-show the wizard AND/api/auth/mereturnsonboarding_completed: 1(Steps 15–16 or 18). [L2] - The empty-availability schedule-setup confirm prompt appears for a user whose default schedule has no hours (Step 20). [L1]
- Dismissing (Cancel) the prompt persists: it does not reappear on reload AND
/api/auth/mereturnsschedule_setup_dismissed: 1(Steps 21–23). [L2] - Cleanup completed: the throwaway account is deleted and
/api/auth/mereturns 401 (Cleanup 1–2).
The run is recorded as PARTIAL (not a hard FAIL) but still acceptable if: Step 1 of the wizard could not be completed without a cold OAuth login (expected for a throwaway account), provided the structural checks (Steps 9–10) passed and completion-persistence was verified via Step 18. The report MUST state this explicitly.
The run FAILS if: the verification email never arrives; an unverified account can reach the dashboard; the wizard does not appear for a new uncompleted user; the wizard reappears after completion (persistence broken); the empty-availability prompt never appears when availability is empty; the prompt reappears after being dismissed (dismissal not persisted); or /api/auth/me does not reflect the expected flag values.
Evidence to capture
- cu18-01-verify-gate.png — registration verification gate.
- cu18-02-verify-email.png — the verification email in Gmail (L3).
- cu18-03-standalone-onboarding.png —
/onboarding/calendar-connect page. - cu18-04-wizard-step1.png — in-dashboard 4-step wizard, Step 1.
- cu18-05-wizard-step2.png, cu18-06-wizard-step3.png, cu18-07-wizard-step4-and-dashboard.png — wizard steps 2/3/4 + final dashboard (CONDITIONAL; only if calendar connect was possible).
- cu18-08-onboarding-complete-api.png —
/api/auth/meshowingonboarding_completed: 1after the alternate completion path (when steps 2–4 were blocked). - cu18-09-empty-availability.png — availability cleared to all-Unavailable.
- cu18-10-schedule-prompt.png — the schedule-setup confirm() dialog.
- cu18-11-dismiss-persisted-api.png —
/api/auth/meshowingschedule_setup_dismissed: 1. - Notes to record: the exact throwaway email/password/slug used; the exact RUNID; whether the wizard steps 2–4 were hand-driven or verified via Step 18; the precise wording of the schedule-setup confirm() dialog; the booking slug read from
#bookingLinkValue.
Manual residue / cannot-verify
- Driving wizard steps 2→4 by hand requires a real Google/Outlook OAuth login, which is forbidden for a throwaway account mid-run. The agent verifies steps 2–4 structurally (by reading the panels) and verifies completion via the app's own
/api/onboarding/completeendpoint, but a human should periodically run the FULL click-through (connect a real calendar, set schedule preset, pick location, finish) once with a disposable but pre-consented Google account to confirm the end-to-end happy path including that the schedule preset and location actually save and that event types get the chosenlocation_type. - The "OK" branch of the schedule-setup prompt (clicking OK → it switches to the Availability tab) is verified only by code inspection here, not exercised, to avoid creating a second throwaway account. A human can confirm the OK branch manually.
- Verification-email deliverability/timing depends on the live Resend integration; if email is delayed or filtered to Spam, the agent may falsely flag a failure. Human should confirm Resend health if the email does not appear.
- Resend-verification link (
#resendLinkon the gate,POST /api/auth/resend-verification) is not exercised in this suite.