8.8 KiB
Onboarding — Day-2 Backlog
Authored: 2026-04-30 (post-investigation b34897c + Option A2 implementation)
Owner: Engineering + Product
Priority: Day-2 (post-launch). Day-0 unblock = ?forceWizard=true URL param shipped.
Why this exists
The onboarding investigation (docs/ONBOARDING-INVESTIGATION-2026-04-30.md) surfaced three distinct issues that are best handled separately from the immediate launch:
-
The auto-complete heuristic is too coarse.
useOnboarding.ts:91-120flips the wizard to "completed" whenever/api/workspaces.length > 0. But the backend ALWAYS createsdefault-workspaceat boot viawsManager.ensureDefault(), so the heuristic fires even for genuinely-fresh installs. -
Production users have no "redo onboarding" affordance. Once the wizard is completed (or auto-completed), there is no in-product way to re-trigger it short of editing
~/.waggle/on disk — friction for support, demos, persona-evaluation marathons, and product education. -
The wizard's actual production behavior is undocumented. Support team / new hires / partners need to know: "the wizard appears at most once per machine, even after browser cache clear, even after Tauri webview reset". That assumption isn't written down anywhere user-facing.
A3 — Smarter auto-complete heuristic
Current code (apps/web/src/hooks/useOnboarding.ts:91-120):
const workspaces = await adapter.getWorkspaces();
if (Array.isArray(workspaces) && workspaces.length > 0) {
// auto-complete the wizard
}
Problem: workspaces.length > 0 is true even when the only workspace is the boot-time stub from wsManager.ensureDefault(). So:
- Genuine first-launch → wizard would render… but
ensureDefault()runs BEFORE the hook's first render, so wizard is auto-completed before the user ever sees it. - Tauri webview switch on real user → correct (workspaces exist with content).
- localStorage clear on real user → correct (workspaces exist with content).
- localStorage clear after manual workspace deletion → wizard auto-completes again because
ensureDefault()recreates the stub.
Net effect: the wizard only shows up if ~/.waggle/ doesn't exist at all when the backend boots. After that, it's invisible until a developer manually deletes the default-workspace directory (and even then, see Option A2 — backend recreates it). For 99%+ of installs, the wizard fires exactly once in the user's lifetime.
That might be desirable. But if not, the heuristic should be content-aware:
Proposed:
const [workspaces, health] = await Promise.all([
adapter.getWorkspaces(),
adapter.getSystemHealth().catch(() => null),
]);
if (cancelled) return;
// Treat "zero memory across all workspaces" as fresh-state regardless of stubs.
// The default-workspace shell from wsManager.ensureDefault has frameCount=0
// until the user actually does something. Distinguish that from a real
// returning user who has built up memory.
const frameCount = health?.memoryStats?.frameCount ?? 0;
const isReturningUser = Array.isArray(workspaces)
&& workspaces.length > 0
&& frameCount > 0;
if (isReturningUser) {
// auto-complete (existing logic)
}
Side effects:
- Genuine first-launch → wizard renders (frameCount=0).
- Tauri webview switch on real user → still auto-completes (frames > 0).
- localStorage clear on real user → still auto-completes (frames > 0).
- After "reset onboarding" (proposed below) clears memory → wizard renders again.
Cost: ~10 LOC, single file. One extra /health call per hook mount (cheap, idempotent).
Risk: if a returning user has somehow lost all memory (recovery-from-corruption, mind file deleted manually), they'd see the wizard again — but that's actually the right UX in that scenario.
Test plan:
- Add
apps/web/src/hooks/useOnboarding.test.tswith mocked adapter:- workspaces=[], frames=0 → wizard renders
- workspaces=[{id:'default-workspace'}], frames=0 → wizard renders (the new behavior)
- workspaces=[{id:'default-workspace'}], frames=5 → auto-completes
- workspaces=[], frames=0, sidecar throws → wizard renders (unchanged fallback)
Status: deferred to Day-2. The Option A2 URL bypass is sufficient for PM walkthrough. Production behavior with the current heuristic is "wizard fires once per fresh install" which is plausibly the right default. Worth a product call before changing.
Reset Onboarding affordance
Where it lives: Settings → Advanced → "Reset onboarding wizard" (button).
What it does:
async function resetOnboarding() {
// 1. Confirm via dialog (destructive action — clears workspace + memory).
if (!await confirmDestructive('Reset onboarding will clear all workspaces and memory. Continue?')) return;
// 2. Backend: drop default-workspace + clear personal mind.
// New endpoint POST /api/admin/reset-onboarding does the disk wipe
// server-side (rm -rf workspaces/default-workspace, drop personal.mind,
// drop sessions/*, drop awareness/preferences). Atomic. Logs to audit.
await adapter.resetOnboarding();
// 3. Clear localStorage onboarding state.
localStorage.removeItem('waggle:onboarding');
localStorage.removeItem('waggle:tooltips_done');
// 4. Reload to ?forceWizard=true so the wizard renders even before the
// backend has a chance to re-ensureDefault.
window.location.assign(window.location.pathname + '?forceWizard=true');
}
Use cases:
- Demo prep: reset to clean state before showing the product.
- Support escalation: "let's start over" when the user's workspace is in a weird state.
- Persona-evaluation marathon: between persona swaps, reset to baseline.
- New-hire training: walk through the wizard as designed.
Threat model: destructive button. Must be:
- Behind Settings → Advanced (not surfaced casually).
- Confirmation dialog with strong copy ("This will erase your memory. Continue?").
- Requires the user to type "RESET" or click two distinct buttons (defense against fat-finger).
- Audit-logged via
auditStoreso it's traceable.
Cost: ~80 LOC across:
apps/web/src/components/os/apps/SettingsApp.tsx— Advanced tab + button + dialogapps/web/src/lib/adapter.ts—resetOnboarding()methodpackages/server/src/local/routes/admin.ts— newPOST /api/admin/reset-onboardingendpoint (or existing admin route)- One vitest spec exercising the disk-side wipe
Status: Day-2. Not needed for launch. PM can use the URL bypass for walkthrough.
Document the production behavior
Audience: Support team, customer success, product education content authors.
Content (rough draft):
Onboarding wizard frequency
The Waggle OS onboarding wizard appears at most once per install. After the user dismisses it (either via "Let's go!" on the Ready step or via the Skip option), the wizard does not re-trigger automatically.
The wizard does NOT re-appear on:
- Browser cache / localStorage clear (the backend has workspace data).
- Tauri webview profile reset (same — the backend has workspace data).
- Computer reboot (data persists in
~/.waggle/).- App upgrade (data persists across versions).
The wizard DOES re-appear on:
- Fresh install on a new machine (no
~/.waggle/directory exists).- User explicit "Reset onboarding" via Settings → Advanced (Day-2 feature, see backlog).
- Developer URL bypass
?forceWizard=true(DEV mode only).Why
A returning user who clears their browser cookies or switches Tauri profiles still has all their memory and workspaces on the sidecar (
~/.waggle/). Forcing them through the wizard again would be jarring ("why does Waggle want me to re-pick a template I already have?"). The wizard's job is to teach + configure on the very first encounter; after that, the same surfaces are reachable through the normal UI (Settings, dock, persona switcher, harvest tab, etc.).
Where it goes:
docs/user-guide/onboarding.md(or whatever the user-facing docs entry point is)- Internal Notion page for support team
Cost: ~30 min of writing.
Status: Day-2. Not blocking launch. Should land before any large support cohort hits production (week 2-3 post-launch).
Summary
| Item | Cost | Day-X | Notes |
|---|---|---|---|
| A2 URL bypass (this commit) | ~12 LOC + tests | Day-0 ✅ | Shipped. PM uses ?forceWizard=true for walkthrough. |
| A3 smarter heuristic | ~10 LOC + 4 tests | Day-2 | Awaiting product decision: keep "once per machine" or move to "once until memory exists". |
| Reset Onboarding button | ~80 LOC + admin route + 1 test | Day-2 | Demo / support / training affordance. |
| Document production behavior | ~30 min writing | Day-2 | User-facing docs + support runbook. |
No work scheduled today beyond the A2 ship + this backlog doc. Marko reviews Day-2 ordering when launch dust settles.