Verdan · new-tenant experience

Verdan Onboarding Fix Plan

A new GC admin should reach a live jobsite in one sitting of about ten minutes and never be asked the same thing twice. Today five separate setup surfaces fire on the same person, read different "done" signals, and leave dead ends. The fix is autosaved drafting, one server-derived readiness checkpoint, one follow-up list.

Findings behind this plan are filed as VER-8526 with 13 sub-issues, each cited to file and line on main.

Who onboards, and what "done" means to them

Three people arrive in the first week. Only one of them should ever see a setup wizard.

PersonHow they arriveWants in the first sessionMust never happen
Sarah
GC owner or safety director (admin)
Activation email after HQ provisioning, or Stripe checkout when self-serve is onA jobsite that is live: project, super assigned, QR poster printedBeing asked for her company name, first project or team invite twice
Mike
Superintendent
Invite email from Sarah during setupLog in on his phone, see his project, approve the first foremanLanding in Sarah's wizard, or on a page telling him to create a project
A sub's foremanProject invite, or the QR poster on day oneSign in his crewAny setup screen at all
The payoff moment

The first worker sign-in on the QR poster. Everything before it is setup cost; everything after it is the product. The jobsite story canon puts this at pre-day-1: the GC sets the project up, the super approves the foreman, the crew self-signs on the QR. Onboarding is done when that chain can start, not when a checklist reads 9 of 9.

That gives one definition of done for the whole tenant: company profile filled, one project created with a super on it, and the QR poster generated. Subs, insurance requirements, templates and orientation are follow-up work the product should nudge, never gate.

What Sarah hits today

Sarah's first session, as main ships it, asks her to set up the company four times and never tells her when she is finished.

flowchart TD
  A[Activation email] --> B["/activate then /login"]
  B --> C["/projects: Getting Started card
4 steps, 'Your account is set up'"] C --> D["Top-bar pill unlocks
'0 of 4'"] C --> E["Guard redirects to
9-step wizard"] E --> F["Wizard inside dashboard shell
pill still visible"] F --> G["Playbook step links out
to /admin/users"] G --> E F --> H["Complete: redirect to
8-step project setup checklist"] H --> I["/admin/getting-started
6-step guide"]

The card and the wizard fire on the same landing. Leaving the wizard by its own links can bounce her back in. Finishing it drops her into a second checklist, with a third guide page one click away.

Screen by screen

  1. Payment or activation. Self-serve checkout ends on a page whose only link is the homepage. HQ-provisioned tenants get an activation link that ends at login. Either way Sarah finds her own way in. VER-8543
  2. First landing on /projects. A Getting Started card says "Your account is set up" and lists four tasks. Seeing it unlocks a "0 of 4" pill in the top bar. In the same second the auth guard reads the tenant's onboarding row, sees "in progress", and redirects her to the wizard. VER-8535 VER-8534
  3. The wizard. Nine steps: account, company profile, playbook, first project, team, billing, compliance, mobile, review. It renders inside the dashboard shell, so the sidebar and the pill surround it. The billing step asks a self-serve tenant to pay again. The playbook step's "Do it now" links go to admin pages; following one can send her back to step one on the next page load. VER-8536 VER-8537
  4. After Review and Launch. She is redirected into an 8-step project setup checklist for the project she just created. Two of its required steps, subcontractors and compliance requirements, repeat what the wizard asked. VER-8537
  5. Back on the dashboard. The card still says "Create your first project" because it never renders ticks. The pill counts from a different endpoint whose invite check looks at the users table, so people she invited do not count until they accept. The guide page's Mark as Complete forgets on reload. VER-8538 VER-8539 VER-5473
  6. Her super, Mike. His invite email links to /invite/{id}, a page that does not exist. If he signs out mid-wizard he is sent to /signup, not /login. VER-8540 VER-8541

None of these is a hard bug on its own. Together they are why the experience reads as broken.

The target flow: draft, then finalize

Product ruling, 2026-09-23: jobsite creation splits into drafting and finalization. Drafting saves itself and asks for nothing. Finalization is an operational-readiness checkpoint that verifies the jobsite can actually run before it goes live and before billing starts.

flowchart LR
  P["Activation or checkout
lands in the workspace"] --> C["Company profile
autosaved, no gate"] C --> D["Jobsite draft
autosaved, leave and return"] D --> R["Readiness check
blockers vs optional"] R -- "blocker missing" --> D R -- "all blockers clear" --> A["Activate
jobsite live, billing starts"] A --> S["First sign-in on the QR"]

One loop, not a wizard. The draft is a real project row in draft status from the first keystroke. The readiness check is computed on the server from what exists, and the same list drives the activate button and the follow-up card.

Drafting

Finalization: the readiness checklist

The server computes this from real rows. Nothing here is a hand-ticked checkbox (VER-8569).

RequirementDerived fromKindFix shown when missing
Company details completetenant name, address, timezone presentBlocker"Finish your company profile" with a link to the fields
Site details completeproject name, address, geofence, start dateBlocker"Add the site address" with the field focused
Superintendent assigned with accessa project lead row with role super whose invite is accepted, or the admin naming themselfBlocker"Invite a superintendent" or "Resend Mike's invite, not accepted yet"
Foreman approval and crew sign-in flow workingsign-in gate configuration resolves for the project without an error; at least one approval path exists (super assigned or admin fallback)BlockerNames the setting that breaks it
QR poster workinga qr_codes row for the project that resolves to the sign-in routeBlocker"Generate the poster" button inline
Required safety and orientation settingsthe compliance-requirements and orientation settings the tenant marks required, using the new tenant defaultsBlockerLists the missing setting by name
Billing or pilot status correctactive subscription with a payment method, or prepaid checkout, or pilot billing profileBlocker"Add a payment method" or nothing for pilots and prepaid tenants
Subcontractors invitedat least one sub besides the GC self-subOptionalNudge on the project page after activation
Templates, notificationstenant settingsOptionalFollow-up card items

Activate refuses while any blocker is open and returns the same list the page shows, so the button and the checklist can never disagree. Prepaid customers and pilots pass the billing row without a second charge; the check reads status, it never creates one.

Walkthrough of the target flow

Sarah builds the jobsite over two days

Mike's first login

Where a blocker refuses

Rules the build keeps

Drafts are free

Creating and editing a draft project never touches billing. The billing check moves from project creation to the readiness list and the activate endpoint. Prepaid and pilot tenants satisfy it by status; nothing in the flow creates a charge.

Readiness is derived, once, on the server

One endpoint returns blockers and optional items computed from rows that exist for another reason: lead rows, QR rows, settings, billing status. The page, the activate button, the follow-up card and the tenant-level redirect all read it. Hand-ticked checklist rows go away.

Redirect rule

Redirect an admin only while the company profile is missing or no project exists, and only to the draft, never to a wizard step. Never redirect any other role. The two stored signals (tenant_onboarding_state, tenants.onboarding_completed) stop being read by anything.

Ask once, in one vocabulary

Requirement ids and labels live in one shared list. The card, the setup page and the API return the same ids, so a requirement met in one place is met everywhere. The wizard, the top-bar pill and the guide page retire.

Build plan

Five phases. Phase 0 and 1 are dispatched. Phases 2 and 3 now have their ruling and can be dispatched next; phase 4 rides with them.

Phase 0 · Stop the bleeding

in progress
VER-8540VER-8541VER-8543VER-8542
Fix the invite link target, the sign-out redirect to /signup, the payment-success dead end, and remove the bare /onboarding public route.
Verify: a Playwright live check that follows a real invite link to activation; contract test that no email template links to a route the app does not serve.

Phase 1 · One onboarding signal

in progress
VER-8534VER-8538
Fix the onboarding-status predicates (pending invites count, GC self-sub excluded), make the redirect guard and the server redirect read one source, and render real ticks on the card.
Verify: API unit tests per predicate against fixtures; a red-then-green contract test that exactly one predicate decides the onboarding redirect.

Phase 2 · Drafts and the readiness endpoint

ready to dispatch
VER-8568VER-8569VER-8547
Remove the billing gate from project creation; add GET /api/projects/:id/readiness returning blockers and optional items derived from data; make activate refuse with that list; add pilot billing profile at HQ provisioning; autosave on the setup page fields.
Verify: API tests for each readiness predicate (no super, invite pending, invite accepted, self-named; no QR; pilot; prepaid; unpaid); an integration test that activate returns the same blockers the readiness endpoint returns; a test that a draft create never calls the billing middleware.

Phase 3 · Setup page becomes the checkpoint; wizard retires

after phase 2
VER-8535VER-8536VER-8537VER-8539VER-8544VER-5473
The project setup page renders blockers vs optional from the readiness endpoint with inline fixes. The 9-step wizard, the pill and the guide page are deleted; the Getting Started card becomes the follow-up list driven by the same ids, dismissal stored on the tenant.
Verify: dev-build screenshots of the setup page at phone and desktop width with 7, 3 and 0 blockers, approved before merge; e2e that abandons a draft, returns on another session, and activates; contract test that exactly one component renders setup progress.

Phase 4 · Canon, SPECs, tests

with phase 3
VER-8545VER-8546
Amend the canon's onboarding ruling to draft-then-finalize with the blocker list. Rewrite tools/onboarding-wizard-steps/SPEC.md (retires) and tools/get-started-topbar-pill/SPEC.md (retires); the project setup SPEC gains the readiness contract. Un-quarantine the two expired onboarding contracts.
Verify: canon-lint green; quarantine.json has no onboarding entries.

Decisions

Phases 2 and 3 can dispatch on the answered ruling; the three open items only shape their details.