GMI · TECHNOLOGY OBSERVATORY // ALL SYSTEMS NOMINAL
ENGINEERED BY LEOPARD DATA

User Onboarding: Teach by Doing

GMI's first onboarding (April 2026) explained the product: a Getting Started page, an eight-slide mobile carousel and a 22-step guided tour. Two beginners in live demos showed that reading gets skipped. The September 2026 redesign (Wave 20, the Guided First Report) replaces explanation with one real report and four short callouts, and it needed no schema change.

BUILT · SEPTEMBER 2026 ClientWeb · MAUI · AdminWeb · public walkthrough

What the Demos Showed

In September 2026 two beginners were walked through GMI live, and both stalled at the same first step: type a ticker. Neither knew one offhand, and neither knew what a grade would tell them once they had one. The existing onboarding answered with reading material, and reading is what people skip.

The fix is deliberately small: a new account's home page doesn't say "add a symbol". It says "Pick a company you know" and shows eight names a beginner recognizes. One tap builds a real report. The first time that report opens, four short callouts explain the grade on the page where it appears. Then the user is invited to add something they actually own. There's no "beginner or pro?" branching, no onboarding engine and no new tables. It only grows if the funnel data says it should.

THE JOURNEY
rendering diagram…
flowchart TB
    subgraph PUB[Public site - no account]
      W[Walkthrough<br/>4 beats over a dated,<br/>real Apple grade] --> PK[Pick a company you know<br/>stored in browser storage]
    end
    PK --> SI[Sign in, create account,<br/>verify email]
    SI --> PICK
    subgraph APP[In the app - onboarding flag false]
      PICK[Home card: PICK<br/>8 recognizable names] -->|one tap| START[Track symbol, price check,<br/>queue the real report]
      START -->|price above 0| ASK[Ask first:<br/>this costs X, build it?]
      ASK --> BUILD
      START --> BUILD[Home card: BUILDING<br/>live phase, 5 s poll]
      BUILD --> READY[Home card: READY<br/>open your report]
      READY --> CO[First view only:<br/>4 callouts on the report]
    end
    CO -->|Done, close, or Add my holdings| DONE[Flag set true<br/>normal dashboard forever]
From the public walkthrough to understanding one grade, with nobody on a call. The home card has no state of its own: its stage is derived from the account's jobs and one existing onboarding flag.

A Home Card With No State of Its Own

One pure function, GuidedFirstReport.DetermineStage, shared by ClientWeb and MAUI, decides what the card shows from two facts that already exist: the account's jobs and the platform's onboarding flag. Nothing extra is stored, so the card can't disagree with what actually happened. A failed job, or a report built from the regular Generate page instead of the card, shows up correctly.

Onboarding flagJobs on the accountStageCard says
trueanythingNone(no card: normal dashboard)
falsenonePick"Pick a company you know" + 8 buttons
falseonly failed or scheduledPicksame, plus "your last report didn't finish, try again"
falseone queued or runningBuildinglive phase line + progress, polled every 5 s
falseat least one finishedReady"Your report is ready: Open it"

The eight picks (Apple, Amazon, Costco, Microsoft, Nike, Coca-Cola, Disney, Starbucks) are chosen in code, not queried. "Recognizable" is an editorial call, not a filter on 2,400 public tickers, and all eight are S&P 500 members that already have public /grade/{TICKER} pages. Every stage has a quiet "Skip, I know my way around" link.

One Tap, and No Surprise Charges

GuidedFirstReportService lives in the shared REST client library, so web and mobile run the identical sequence. It calls the same list and report endpoints the Generate Report page uses, so every server-side rule still applies: email verification, the free tier's 8-symbol cap and one-report-a-month allowance, billing, and ad attribution.

  1. Track the symbol in a one-symbol list named First pick: Apple, reused if an earlier tap created it, so a cancelled confirmation doesn't leave duplicate lists behind.
  2. Notify one address: the sign-up email gets "your report is ready", which brings back anyone who closed the tab.
  3. Check the price. For free and staff accounts it's $0. If an account with a card on file would be charged, nothing is queued until the card asks, "This report costs $0.30, billed to your card on file. Build it?" A one-tap flow shouldn't be able to charge someone by surprise, and unit tests cover that.
  4. Queue the report and flip the card to Building. The polling uses a PeriodicTimer with a CancellationToken, because Blazor WebAssembly ignores HttpClient.Timeout (a lesson from an earlier spinner that never stopped).

Each refusal gets its own message rather than a generic error: the symbol cap ("remove a symbol or add a card"), the monthly allowance (the existing upgrade wording, with the symbol still tracked so its live grade appears anyway), or the server's own message, such as an unverified email.

Four Callouts, Using the Report's Own Numbers

#TitleAnchored to (web)Says
1This is the gradethe AI Grade badgeone letter, A+ to F, describing the company's financials today: not a prediction, not a recommendation
2Here's what it's based onthe 0–100 scorestart at 50, six factors from the company's own statements add or subtract points, "AAPL scored 93"
3What a low mark on debt meansthe grade badgethe company's actual debt reading, how that factor runs from +15 to −10, and its weakest factor if one scores ≤ 0
4Now grade what you ownSymbol Lists in the naveverything tracked gets a live grade, 8 free, with an Add my holdings button
  • One source for the words. FirstReportCallouts.Build(symbol, isMobile) generates the text for both platforms. It uses the report's real numbers when available and generic wording when not (an older report without a breakdown, a symbol with no grade). A unit test fails the build if the text contains buy/sell language.
  • Web reuses Shepherd.js, which was already loaded for the nav tour, as a third named tour (first-report) fed the steps from C#. If an anchor is missing or hidden (the row was sorted away, or the nav is collapsed on a phone), the step falls back to a centered callout instead of pointing at nothing.
  • MAUI shows the same steps as a card at the top of the report (Next · Done · ✕). Mobile has no hover and no side nav to anchor to, so the callouts adapt to the platform instead of copying the web layout.
  • No "remind me later". The callouts are four sentences, and showing them twice would be nagging. Done, ✕, Escape and "Add my holdings" all mark onboarding complete.

Decision: Change What the Flag Means, Not the Schema

A new HasCompletedGuidedFirstReport column would have been cleaner on paper and risky in practice. Migrations here are applied by hand, and a column the code expects but prod lacks breaks every account read. That's a real incident from August 2026. So the existing per-platform flags (HasCompletedWebOnboarding / HasCompletedMobileOnboarding) carry the whole feature, and what changed is their meaning:

Before (April 2026)After (Wave 20)
false meanshasn't clicked through Getting Started / the carouselhasn't finished the guided first report
Set true byGetting Started, the carousel, or finishing the tourfinishing or dismissing the first-report callouts; "Skip" on the card
Dashboard when falseredirects to /getting-started (web) / the carousel (mobile)shows the guided card, no redirect
Getting Started / carouselforced on first visitone tap away in the nav / More tab, never forced, doesn't touch the flag
22-step nav tour (web)auto-started; completion set the flagonly via "Replay Guided Tour"; doesn't touch the flag

Existing accounts need no migration either. Anyone who already clicked through has true and sees nothing new. An older account that never did sees the Ready stage once and the callouts once, which is intended. Per-platform independence is kept on purpose: finishing on web still shows the callouts once on the first mobile report, where the layout is different. AdminWeb's Manage Accounts page shows "Guided first report: done / not yet" per platform with a reset button, so the flow can be re-tested without SQL.

The Public Walkthrough: A Real Company, With a Date

/walkthrough on the public site has four short beats over a real graded company: the Apple worked example from Anatomy of a Grade (FY2025 10-K, Aug 21, 2026 close, 50 + 10 + 10 + 5 + 15 + 0 + 3 = 93 → A+), shown with its date and linked to Apple's live page for today's number. A real, dated, checkable example beats a mock-up that drifts from the product. It even shows a weak spot honestly: Apple's current ratio of 0.89 earns zero points.

It ends with the same eight buttons. A tap stores the pick in browser storage and goes to sign-in, and after sign-up the dashboard highlights "You picked Apple". Only the curated eight are accepted (FirstReportPicks.Find), so a hand-edited value can't queue anything, and nothing is generated until the user taps inside the app. The page exists in both the static-SSR project (for crawlers and first visits) and the WASM app (for in-app navigation), following the platform's static-SSR rule.

How Success Is Measured

Success means a beginner gets from sign-up to understanding one grade with nobody on a call, and when they don't, the data shows where they dropped off. New GA4 events fill the gap between "signed up" and "understood a grade", building an open funnel:

walkthrough_pick → sign_up → email_verified → guided_pick → first_report_generated → first_report_ready → first_report_viewed → first_report_callouts_done

Side events (guided_cost_prompt, guided_blocked with a reason, guided_skipped, guided_next_step) explain the drop-offs. MAUI has no GA4 SDK, so it logs the same moments to the existing activity log that AdminWeb already reports on. Known next steps, only if the data calls for them: a priority queue for an account's first report if first_report_ready lags; a follow-up "add a fund you own" stage if guided_next_step is low; and making the picks admin-editable if the names turn out to matter.

The Reference Material Is Still There, Just Not Forced

The April 2026 onboarding wasn't deleted. It became optional reference material, one tap away:

Web (Blazor WASM)Mobile (.NET MAUI)
Guided first reportGuidedFirstReportCard on the Dashboard; first-report Shepherd tour on the Report DashboardGuided card on DashboardPage; callout card on ReportDashboardPage
Getting StartedPage with video, 4-step workflow, pricing and billing explained; linked in the navCarouselView of swipeable slides; linked from More
Guided tour22-step Shepherd.js nav tour, "Replay Guided Tour" onlySyncfusion SfPopup tour, replay only
Completion flagHasCompletedWebOnboardingHasCompletedMobileOnboarding
Funnel telemetryGA4 events (ga4-events.js)Activity log (app source "MAUI")

Account guards are unchanged on both platforms: no account → Create Account; unverified email → Verify Email; the verification link activates the free account. The onboarding endpoints are the existing IDOR-protected PATCH /accounts/{id}/complete-{web|mobile}-onboarding and reset-{web|mobile}-onboarding. The guided flow added no endpoints and no migration. Tests: 33 unit tests on the stage function, the callout builder and the one-tap starter (billing safety, caps, entitlement walls, voice).