User Flows
How the primary persona actually moves through the main job and three related jobs: steps, decision diamonds, states (empty / error / loading) and both ends, success and dead-end. Color is semantic: green reaches the goal, red is a true dead-end, grey is neutral. The concept sitemap derived from these flows lives on Concept map.
User Flows
The main job plus three related jobs, each proving the route: steps, decision diamonds, states, and both ends, success and dead-end. Color is semantic.
Flow 1 - Main job (Priya): run wellbeing without HR training
flowchart TD
Start(["Priya: handed wellbeing, no HR training"]) --> Landing["Landing / home"]
Landing --> Signup["Sign up"]
Signup --> Onboard["Onboarding: company setup"]
Onboard --> Load1["Loading: pre-select program by size"]
Load1 --> Confirm["First program confirm"]
Confirm --> Invite["Invite team: join link / code"]
Invite --> SendOk{"Invites sent?"}
SendOk -->|send failed| InviteErr["Error: invite send failed, retry"]
InviteErr --> Invite
SendOk -->|yes| Dash["Dashboard: waiting for check-ins"]
Dash --> Enough{"Cycle closed, N of 5 met?"}
Enough -->|still open| Waiting["Loading: cycle open, no live count"]
Waiting --> Enough
Enough -->|under 5 at close| EmptyN["Empty: too few responses, nudge team"]
EmptyN --> Roster["Team roster: resend invite"]
Roster --> Dash
Enough -->|team too small| TooSmall["Team below minimum-N: privacy math explained"]
TooSmall --> Grow{"Can invite more to reach 5?"}
Grow -->|yes| Roster
Grow -->|no| DeadN["Dead: under minimum-N, no team signal possible"]
Enough -->|yes| Pulse["Team pulse: score + interpretation + next action"]
Pulse --> Win(["Job closed: team read and next action"])
classDef success fill:#12351f,stroke:#3fb56b,color:#eafff9;
classDef dead fill:#3a1618,stroke:#e5484d,color:#ffd7d7;
classDef neutral fill:#1b1b1b,stroke:#5a5a5a,color:#dddddd;
class Start,Win success;
class DeadN dead;
class Landing,Signup,Onboard,Load1,Confirm,Invite,SendOk,InviteErr,Dash,Enough,Waiting,EmptyN,Roster,TooSmall,Grow,Pulse neutral;
linkStyle 0,1,2,3,4,5,6,9,10,20,21 stroke:#3fb56b,stroke-width:2px;
Decisions: invites sent? cycle closed and N of 5 met? can invite more? States: loading, empty, invite-send error. Dead-end: a team structurally under minimum-N cannot get an aggregate signal (the honest limit of an aggregate-only model). Recovers: under-5-at-close nudges and loops back.
Flow 2 - J3 + J4 (Yemi): private, under-30s check-in
flowchart TD
Start(["Yemi: invite received, is it private?"]) --> Open["Check-in via invite link / join code"]
Open --> Valid{"Invite link valid?"}
Valid -->|expired| Expired["Error: invite expired, ask operator to resend"]
Valid -->|ok| Dup{"Already checked in this cycle?"}
Dup -->|yes| Already["Already submitted this cycle, thank-you"]
Dup -->|no| Disc["Pre-Q1 privacy disclosure"]
Disc --> Believe{"Do I believe my answer stays private?"}
Believe -->|not sure| Learn["See exactly what your manager sees"]
Learn --> Believe
Believe -->|no| DeadSkip["Dead: skips or non-answer, trust lost"]
Believe -->|yes| Q["Questions: 1 to 3, emoji / slider"]
Q --> Submit["Loading: submitting response"]
Submit --> Ok{"Submitted?"}
Ok -->|network error| Err["Error: submit failed, retry"]
Err --> Q
Ok -->|yes| Done["Check-in complete: thank-you, no individual data"]
Done --> Win(["Job closed: honest response joined the team aggregate"])
classDef success fill:#12351f,stroke:#3fb56b,color:#eafff9;
classDef dead fill:#3a1618,stroke:#e5484d,color:#ffd7d7;
classDef neutral fill:#1b1b1b,stroke:#5a5a5a,color:#dddddd;
class Start,Win success;
class DeadSkip dead;
class Open,Valid,Expired,Dup,Already,Disc,Believe,Learn,Q,Submit,Ok,Err,Done neutral;
linkStyle 0,1,3,5,6,10,11,12,15,16 stroke:#3fb56b,stroke-width:2px;
Decisions: invite valid? already checked in? believe it stays private? submitted? States: error (expired, submit failed), loading, already-submitted. Recovery: an unsure employee opens "see exactly what your manager sees" and returns. Dead-end: disbelief in privacy, trust lost, no in-flow path back to an honest answer.
Flow 3 - J1 + J5 + S2 (Priya to Marcus): prove it works, share and upgrade
flowchart TD
Start(["Priya: wants to show the owner it works"]) --> Milestone["Dashboard: positive pulse milestone"]
Milestone --> Share["Share with owner"]
Share --> Gate{"On a paid plan?"}
Gate -->|no| Upgrade["Invite owner + upgrade"]
Upgrade --> Pay{"Upgrade completed?"}
Pay -->|payment failed| PayErr["Error: payment failed, retry or stay free"]
PayErr --> Upgrade
Pay -->|not now| StayFree["Stay on free tier, retry later"]
StayFree --> Milestone
Pay -->|yes| Sent["Confirmation: owner invite sent"]
Gate -->|yes| Sent
Sent --> Wait["Loading: waiting for owner to open"]
Wait --> Seen{"Owner opened dashboard or digest?"}
Seen -->|not yet| Nudge2["Empty: owner has not opened, resend digest"]
Nudge2 --> Wait
Seen -->|never opens| DeadUnseen["Dead: owner never opens, share never lands"]
Seen -->|yes| OwnerDash["Owner dashboard: one number, trend, what I cannot see"]
OwnerDash --> Ready{"Team score ready, N of 5 met?"}
Ready -->|no| OwnerEmpty["Empty: score not available yet, privacy minimum not met"]
Ready -->|yes| Win(["Job closed: owner sees an honest signal, renewal confidence"])
classDef success fill:#12351f,stroke:#3fb56b,color:#eafff9;
classDef dead fill:#3a1618,stroke:#e5484d,color:#ffd7d7;
classDef neutral fill:#1b1b1b,stroke:#5a5a5a,color:#dddddd;
class Start,Win success;
class DeadUnseen dead;
class Milestone,Share,Gate,Upgrade,Pay,PayErr,StayFree,Sent,Wait,Seen,Nudge2,OwnerDash,Ready,OwnerEmpty neutral;
linkStyle 0,1,2,10,11,12,16,17,19 stroke:#3fb56b,stroke-width:2px;
Decisions: on a paid plan? upgrade completed? owner opened it? score ready? States: payment error, loading, two empties (owner not opened; score not ready under minimum-N). Recoverable exit: declining the upgrade stays on free tier and retries later. Dead-end: owner never opens despite resends, the share never lands.
Flow 4 - J2 (Priya): know if the team is struggling early
flowchart TD
Start(["Brio: a pulse cycle closes"]) --> Compute["Loading: compute aggregate, check N of 5"]
Compute --> N{"N of 5 met?"}
N -->|no| Hold["Empty: too few responses, no score, no alert"]
Hold --> Wait2["Wait for next cycle"]
Wait2 --> Start
N -->|yes| Trend{"Aggregate dropped past threshold?"}
Trend -->|no| Steady["Dashboard: steady, no action needed"]
Steady --> OkEnd(["Job closed: reassured, nothing needed"])
Trend -->|yes| Alert["Threshold alert detail"]
Alert --> Act{"Operator acts?"}
Act -->|ignores once| Digest["Weekly digest re-surfaces the alert"]
Digest --> Alert
Act -->|keeps ignoring| DeadMiss["Dead: alert ignored, someone resigns"]
Act -->|yes| NextAction["Team pulse: suggested next action"]
NextAction --> Win(["Job closed: acted before a resignation"])
classDef success fill:#12351f,stroke:#3fb56b,color:#eafff9;
classDef dead fill:#3a1618,stroke:#e5484d,color:#ffd7d7;
classDef neutral fill:#1b1b1b,stroke:#5a5a5a,color:#dddddd;
class Start,OkEnd,Win success;
class DeadMiss dead;
class Compute,N,Hold,Wait2,Trend,Steady,Alert,Act,Digest,NextAction neutral;
linkStyle 0,1,5,6,7,8,9,13,14 stroke:#3fb56b,stroke-width:2px;
Decisions: N of 5 met? dropped past threshold? operator acts? States: loading, empty (no score, no alert). Escalation: a single ignore does not equal a resignation, the weekly digest re-surfaces the alert. Two success ends (both close J2): steady equals reassured, and alert-then-acted. Dead-end: alert kept ignored, someone resigns.