Search

Search components, tokens, patterns, architecture

Architecture · Part 03

User Flows

Every flow in the shipped product, documented from code — goals, entry points, decision points, branches, success and failure states. Screens are referenced S1–S23 from the canonical inventory in the Information Architecture.

Flow index

#Flow familyFrequencyEntry surface
F1Onboarding & account creationonceS1 Splash
F2Daily experience (dashboard · check-in · insight review)dailyS3 Today
F3Cycle trackingcycle-pacedS10 Log Period
F4Trainingper workoutS9 Log Workout
F5Sleep (manual · wearable · analysis)daily / weeklyS11 / S19 / S21
F6Nutritiondaily, passiveS3 FuelingCard
F7Weekly reviewweeklyS4 Insights
F8Historical explorationoccasionalWeekStrip / S19 / S20
F9Settings & profilerareS19 Profile drawer
F10Permissionsevent-drivensplash / banners / drawers
F11Error recoveryevent-drivenany

F1 · Onboarding & account creation

Goal · Start using Peri — with an account, without one, or just look around.

Entry points · Unauthenticated launch → Splash.

Success · Dashboard reached; new users meet the learning-state stack — the threshold ladder is the onboarding's second act.

Failure · OAuth denied → stay on splash, retriable. Drive unreachable → 'working offline', app proceeds local-only.

Decision points

Returning detection (hasProfile || hasData) skips onboarding on a new device. Every step is skippable — an all-null profile is valid. The demo path never marks onboarding complete, so browsing leaves the real flow untouched. Google OAuth is in Testing mode: only allow-listed users can sign in.

F2 · Daily experience

F2a — Dashboard read (0 taps). Engines run synchronously on render for navDate; the card stack is the priority order. Success is sub-10-second comprehension; no failure state exists — every card degrades to a learning form, never an error.

Goal · F2b — record today in seconds through the single write funnel.

Entry points · The floating + (only door); assumption actions from drawers.

Success · Toast confirms scope ('Drive ✓' / 'locally ✓'); dashboard recomputes immediately.

Failure · Drive write fails → entry persists locally, red dot, auto-retry. Data loss is structurally impossible.

F2c — Insight review. Card face → drawer → “based on…” → red assumptions → tapping an action closes the sheet and opens the fixing log screen after 320ms. After saving, the assumption line disappears — the explainability loop closes on itself.

F3 · Cycle tracking

Goal · Record bleeding and period symptoms; keep cycle context honest as cycles become irregular.

Entry points · + → Log Period; Cycle Overview assumption 'Log Period'.

Success · Correct cycle day everywhere; predictions re-anchor instantly.

Failure · Bleed >40d after last start: saves fine, cycle mode absorbs the gap — no error surfaced. Duplicates resolved by merge.

The anchor and the modes

The Active-flow toggle is the cycle anchor: ON at a new bleed writes cd = 1. The system then reacts to gaps, not just logs: ≥35 days since last bleed → irregular; ≥60 → late_peri (phase labels removed, “Xd since last bleed”); none → no_data. The product sheds its cycle layer gracefully as cycles fade.

F4 · Training

Goal · Log a session and its perceived effort; understand its recovery cost.

Entry points · + → Log Workout; DailyFocus assumption 'Log Workout'.

Success · Seconds to log; tomorrow's recommendation visibly reflects the session.

Failure · None beyond storage; legacy ⚡ Hulk tags accepted as ⚡ Peak.

Three tag sections — type body state (incl. “Trained in the heat”) → how it felt (Very easy → Had to stop, ⚡ Peak). The return loop: recovery strain rises, capacity may drop, DailyFocus may flip to “Train lighter”, Fueling floors carbs and protein, WhatNext matches similar past days, and weekly Training Insights accumulate playbook patterns (back-to-back cost, heat+sleep interactions, yoga/walks as recovery boosters).

F5 · Sleep

Goal · Get sleep's effect on capability — manual or imported.

Entry points · + → Log Sleep (manual); Profile drawer → Wearables → Sync (import); Sleep Overview (analysis).

Success · Stages and overnight recovery feed DailyFocus; 'sleep missing' notices disappear.

Failure · Per-step failure shown in the modal; save_failed keeps imported data in memory with explicit wording.

Merge rule

Wearable sleep never overwrites a manually logged night — manual wins; the import fills gaps and adds stages. Every attempt, successful or not, lands in the sync history view.

F6 · Nutrition

Goal · Know when fueling needs increase — without logging anything extra.

Entry points · Passive: FuelingCard is card 2 on Today.

Success · Glanceable dots matching today's reality; the sentence names the driver.

Failure · None possible — missing inputs reduce confidence and are listed (max 3), never errored.

Baselines 2/2/1; floors only raise. Signal priority: training load > sleep <6h > capacity <30% > crash risk ≥70 > fatigue/heavy bleeding > symptoms > appetite > cycle phase (lowest, skipped at low confidence).

F7 · Weekly review

Goal · Is anything changing? What has my data taught me?

Entry points · Insights tab, typically weekly.

Success · A narrative of direction plus at least one personally-earned insight.

Failure · Degrades only: <10 entries → low confidence labels; <14 → playbooks gated with the threshold named.

The designed reading order: Trends over time (1M/3M/6M or “Since [Treatment]”) → What My Data Says → the four domain overviews with playbooks → the stats row (Days logged · Patterns found · Insights learned). Range switches recompute in place; assumption actions hand off to log screens.

F8 · Historical exploration

Three entries by depth: WeekStrip/calendar pick navDate changes and the same dashboard recomputes for that day (the calendar stays open for hopping); Profile drawer → Log History — full list → PastEntryModal for read/edit through the same merge-safe save; and the data coverage indicator — % of the last 90 days logged, color-banded (≥75% green / ≥40% orange / red). No failure states; future dates just show predictions.

F9 · Settings & profile

ItemPathOutcome
Edit Profiledrawer → Edit Profile viewonboarding answers editable; saves to peri-profile
Log Historydrawer → History viewF8 — list → PastEntryModal
Wearablesdrawer → Wearables viewF5b sync; manage devices; sync history sub-view
Privacy modetoggleblurs content text for the session
Legaldrawer → document viewsread-only, back returns
Log outbuttonsigned-in → revoke token → splash; demo → clear demo → splash
Delete logged data / account datadouble-confirmed modalsdestructive: wipes local → splash. Drive copy from last sync may persist (documented limitation)

F10 · Permissions

PermissionAskedGrantedDenied / absent
Google sign-inSplash; quiet auth banner when signed outDrive sync, photo, cross-devicefull app local-only; 'Saved locally ✓'
Drive (appdata + readonly)bundled with sign-indata file + Health Connect export readablered dot; local persistence continues
Wearable / Health Connect'Sync' assumption actions, in contextimport pipelineabsent-but-invited; no nagging
Demo consent'I just want to browse'read-only tour, real enginesexit returns to splash

Re-ask policy

Invitations live where their value is visible and recur only while the gap exists — once sleep data flows, the “Sync” action disappears because the assumption it fixes is gone.

F11 · Error recovery

Goal · Never lose data, never be blocked.

Entry points · Any failure, anywhere.

Success · Every branch ends in an automatic or one-tap recovery.

Failure · None terminal — the worst case is 'saved locally, will sync later', and the UI says exactly that.

The structural guarantees

Local-first writes (localStorage before network) · merge-never-discard sync (Drive ∪ local, field-count tiebreak, cd-repair) · engines as pure functions (no loading or error states possible on the dashboard) · the read path is never blocked by any failure.

Divergence touched by this part

Ruling #10 — Treatments export

Figma shows a PDF export flow (generating → export-ready sheet with share/download). The shipped app exports CSV per category (peri-{category}-export.csv) with a confirmation toast. App canonical (CSV); the PDF flow is visual intent for a future iteration. Recorded in the divergence register.