v4.16 — Guided Product Tour
New here? We'll show you around — a skippable, replayable, never-blocking guided tour now covers the dashboard and every major surface, plus a lightweight spotlight that re-introduces new features to existing users.
Summary
Objectuve had grown deep — dashboard, streaks, AI Coach, goals, communities, achievements, an activity feed, allies — but nothing in the product actually introduced any of it. An orphaned tour component sat unused in communities/OnboardingTour.vue, its steps hardcoded to a single page and its anchor targets pointing at elements that no longer existed anywhere in the app. Discovery was entirely left to users stumbling onto features on their own.
v4.16 closes that gap with a reusable FeatureTour engine — a dimmed backdrop, an animated spotlight/coach-mark tooltip positioned via @floating-ui/dom, and centered intro/outro dialogs, all gated on useReducedMotion() and fully keyboard/focus-trap accessible — generalized from that orphaned component rather than rebuilt from scratch. Completion and dismissal state is server-authoritative, written into the existing UserDetail.data['feature_tours'] jsonb column with no migration required, so a tour a user finishes or skips on one device never re-fires on another.
Phase 1 shipped the MVP: the engine itself, plus a flagship 8-step first-run tour that fires once when a new user lands on the dashboard straight out of onboarding. Phase 2 extended the same, unchanged engine to five more surfaces — Goals, Coach, Communities, Achievements, and the Activity Feed — and added a "Tours" section to Settings where any of the six tours can be replayed on demand. Phase 3, a stretch layer that was completed rather than deferred, added a lighter-weight re-engagement mechanism for existing users: a version-aware "What's New" spotlight that surfaces at most one newly-shipped feature per session, plus an admin-only completion funnel so the team can finally measure whether any of this tour work is actually landing.
Every tour, across all three phases, is skippable, never destructively blocks the UI, and is replayable — a deliberate brand-philosophy constraint (no dark patterns) that shaped every design and engineering decision in the milestone, not an afterthought applied at review time.
Goal
Give users a comprehensive, polished, in-app feature tour that turns the app's now-deep functionality (dashboard, streaks, AI Coach, goals, communities/challenges, achievements, activity feed, allies) into something users can discover — not just something that exists. Deliver a reusable spotlight/coach-mark + intro-dialog FeatureTour engine with server-authoritative completion state, a flagship first-run dashboard tour that fires once after onboarding, contextual per-surface mini-tours with a Settings replay entry, and (stretch) a re-engagement "What's New" spotlight layer for existing users.
Scope — What Shipped
FeatureTour.vue+useFeatureTour.ts(ionic_frontend/src/components/tour/,ionic_frontend/src/composables/) — the reusable engine: backdrop, animated spotlight ring, coach-mark tooltip card (@floating-ui/dom-positioned with collision handling), centered intro/outro dialogs, step-progress dots, Skip/Back/Next/Done controls, focus-trap,useReducedMotion()gating throughout. Generalized from the orphanedcommunities/OnboardingTour.vue, not rebuilt.storeFeatureTourStatemutation +UserIdentity::StoreFeatureTourStateinteraction (rails_api/app/interactions/user_identity/) +featureTourStatusresolver onUserType— server-authoritative completion state in the existingUserDetail.data['feature_tours']jsonb column,deep_merged so sibling keys (data['onboarding']) are never touched. No migration.- Flagship dashboard tour — an 8-step walkthrough (intro → streak → check-in → coach → today's actions → goals → nav → outro), firing exactly once at
/dashboard?source=wizard, gated on the newfeature_tour_enabledflag. - Five per-surface mini-tours — Goals, Coach, Communities, Achievements, Activity Feed — as step composables under
ionic_frontend/src/composables/tours/, each auto-starting once behindfeature_tour_enabled(Communities' ally-request step additionally gated onally-request-flow-enabled). - Settings → Tours replay —
ToursSettingsSection.vue+ a canonicalTOUR_REGISTRY(ionic_frontend/src/composables/tourRegistry.ts) lists all six tours with per-row completion status and a Start/Replay control, resetting the tour's persisted state (and its Apollo cache entry) on replay. - "What's New" re-engagement spotlight —
WhatsNewSpotlight.vue/WhatsNewWatcher.vue/useWhatsNew.ts, a lighter-weight, non-blocking callout (not the fullFeatureTouroverlay) — version-aware against a per-user "epoch," capped at one per session, gated on its ownwhats_new_enabledflag, persisted through the samefeature_toursmap keyed by feature id.WHATS_NEW_FEATURESships empty by design — the mechanism is live but inert until a future feature opts in. - Admin-only tour completion funnel —
Admin::ComputeTourFunnel(rails_api/app/interactions/admin/), exposed viaQuery.tourFunnelStats,require_admin!-gated — aggregatesstarted/completed/dismissed/completion_rateper tour. - QA + a11y — Playwright smoke (dashboard tour + What's New spotlight), a reduced-motion/a11y audit across
FeatureTour.vueandWhatsNewSpotlight.vue, and a watcher-test backfill for the four surfaces (Coach/Communities/Achievements/ActivityFeed) that Phase 2 had left uncovered. - Docs — new
docs/features/feature-tour.mdcovering the architecture, persistence model, and every tour added across all three phases;docs/product/user-guide.md§16 (Tours);docs/features/onboarding-timeline.mdDay-1 update;ionic_frontend/STYLE_GUIDE.mdoverlay pattern note;CHANGELOG.mdandionic_frontend/CHANGELOG.mdentries.
Phases
| Phase | Name | Status | Plans | Highlights |
|---|---|---|---|---|
| 1 | Engine, persistence & flagship dashboard tour | Shipped | 1 | FeatureTour/useFeatureTour engine, server-authoritative persistence, 8-step dashboard tour — the MVP, ships value alone |
| 2 | Full surface coverage + Settings replay | Shipped | 1 | Five more mini-tours on the unchanged Phase 1 engine, Settings "Tours" replay section; Roy caught a stale-cache replay no-op, fixed pre-merge |
| 3 | Re-engagement "What's New" + analytics/polish | Shipped | 1 | Version-aware spotlight layer, admin completion funnel, QA/a11y sweep — stretch phase, completed rather than deferred |
Key Decisions
- Generalize the orphaned tour, don't rebuild (D-1) —
communities/OnboardingTour.vuealready had the right visual base (backdrop, spotlight, glass tooltip, progress dots, keyboard-accessible controls); its steps were just hardcoded to one page. Turning it into a props-driven engine reused proven, on-brand UI instead of starting over. @floating-ui/domfor anchor positioning (D-2, Josh-approved) — replaces hardcoded 300px/400px position estimates with real viewport-collision handling, the single new frontend dependency this milestone introduced.- Server-authoritative completion state, not PostHog or localStorage (D-3) — PostHog is dev-disabled and best-effort;
useDismissedActions's 30-day TTL is the wrong shape for a tour that should stay completed forever.UserDetail.data['feature_tours'](existing jsonb,deep_merge) needed no migration and no new table. - Every tour is always skippable, never blocking, always replayable (D-4) — a brand-philosophy constraint, not a nice-to-have; it shaped the engine's control cluster, the Settings replay surface, and the What's New spotlight's non-blocking (halo/docked, not full-overlay) presentation.
- What's New deliberately does not reuse the
FeatureTouroverlay — a re-engagement callout for an existing user browsing normally needs to be lighter-weight and non-blocking, unlike a first-run walkthrough. Same persistence and analytics plumbing, different UI treatment. - Phase 3 (stretch) completed, not deferred — flagged at kickoff as deferrable without stranding Phases 1–2; Orion recommended shipping it anyway since the funnel closes a "shipped a retention feature with no measurement" gap, and it landed in full.
.planning/milestone artifacts must merge tomastervia their own PR before any milestone branch is deleted — the one durable process lesson from this milestone (see Tech Debt / near-miss below), now recorded for future milestones.
Requirements Coverage
14 / 14 requirements satisfied (per the Milestone Audit; this milestone has no standalone REQUIREMENTS.md — coverage is derived from the ROADMAP's own task checklist per this repo's planning convention).
| Category | Count | Status |
|---|---|---|
| Phase 1 (TOUR-BE-1, UISPEC-1, ENGINE-1, DASH-1, DOCS-1) | 5 | All satisfied |
| Phase 2 (TOUR-UISPEC-2, SURF-2, REPLAY-2, DOCS-2) | 4 | All satisfied |
| Phase 3 (TOUR-UISPEC-3, WHATSNEW-3, FUNNEL-3, QA-3, DOCS-3) | 5 | All satisfied |
Full detail: v4.16-guided-product-tour-MILESTONE-AUDIT.md.
Outcomes
A new user finishing onboarding now sees a guided dashboard tour exactly once, pointing out their streak, check-in, Coach, today's actions, goals, and navigation. Any user can discover and take a mini-tour on Goals, Coach, Communities, Achievements, or the Activity Feed, and can replay any of the six tours at any time from Settings → Tours. An existing user on a new app version can see at most one dismissible "What's New" spotlight per session for a newly-shipped feature — live but currently inert, since no feature has opted into the registry yet. The team can now query an admin-only completion funnel to measure tour effectiveness, closing a "shipped a retention feature with no measurement" gap that existed through Phase 2.
Not yet true for any real user: no production tag has been cut for this milestone — it rides the next Wednesday release train, per the beta release cadence. All merges landed on master → staging only.
Tech Debt
- (Phase 1, near-miss, resolved) The integration branch
gsd/v4.16-guided-product-tourwas deleted mid-Phase-1 before its planning docs (ROADMAP, UI-SPEC, mockups) had ever merged tomaster. They survived only in a dangling commit and were recovered and durably landed via a follow-up PR (OBJ-1785, #1809) before Phase 2 dispatch — no data was actually lost, but it's the reason every phase after Phase 1 cut its branch fresh offorigin/masterinstead of reusing a milestone integration branch. - (Phase 2/3, open, non-blocking) The
Capture UI EvidenceCI gate only ever screenshots/dashboard— it never covered any of the five Phase 2 surfaces or the Phase 3 What's New spotlight, a pre-existing scope limit ofscripts/capture-ui-evidence.mjs's default route list. Worth a standalone backlog item to extendUI-Evidence-Routesper-PR. - (Phase 3)
ionic_frontend/CHANGELOG.mdintentionally has no Phase 3 entry —WHATS_NEW_FEATURESships empty and the funnel is admin-only, so nothing user-visible shipped from Phase 3 yet. Not a gap; will need an entry once the first feature opts into the What's New registry.
Related Artifacts
- Roadmap: v4.16-guided-product-tour-ROADMAP.md
- Milestone Audit: v4.16-guided-product-tour-MILESTONE-AUDIT.md
- Phase artifacts: .planning/phases/v4.16-guided-product-tour-phase-1/, .planning/phases/v4.16-guided-product-tour-phase-3/
- Feature docs: docs/features/feature-tour.md
- Merge PRs: #1802, #1803, #1812, #1814, #1815, #1817, #1821, #1809 (planning-doc recovery)
Related Commits
4a0924eaf— docs(tour): document What's New spotlight + tour funnel, backfill in-app CHANGELOG (TOUR-DOCS-3, OBJ-1793) (#1821) — milestone-complete commit (master head at close)58712cd7e— [Codi] feat(tour): add per-surface mini-tours + Settings Tours replay (TOUR-SURF-2, TOUR-REPLAY-2, OBJ-1776) (#1812)b1fc5207e— [Codi] feat(tour): add FeatureTour engine + flagship dashboard tour (TOUR-ENGINE-1, TOUR-DASH-1, OBJ-1775) (#1803)47f2f22f6— [Codi] feat(tour): add feature-tour completion state (TOUR-BE-1, OBJ-1775) (#1802)
Last updated: 2026-07-27