V1 Web PWA · Surface walkthrough

Habits · Arete · OSHUN (internal habit system)

A per-surface walkthrough of the V1 Web PWA customer surface: layout, states, interactions, data, and cross-references.

walked
8sections4 minread

On this page

Context. surface customer · domain arete · route /domains/arete/habits · auth signed-in · source apps/oshun/web/src/app/domains/arete/habits/page.tsx

Last walked. 2026-05-29 automated runtime walk (Playwright headless) — render, /v1 data (2xx), console/page-errors, expected content, screenshot verified; live screen-reader, touch, offline, and telemetry-delivery checks pending a manual AT pass. Evidence: WALKTHROUGH/results/runtime-sweep-2026-05-29.md; 2026-06-23 wizard create and first check-in re-verified against real /v1/arete/habits + /v1/arete/habits/:habitId/check-in with polished /arete and Home read-back

Purpose#

Internal /domains/arete/* alternate habit-system surface. A multi-view workspace covering the habit loop wizard, the Four Laws panel, habit stacking, identity statements, keystone habits, streak system, analytics, reminders, and per-habit detail. Driven by Atomic-Habits-style scaffolding (cue, craving, response, reward; identity-based habits). Heavier and more app-like than the polished /arete Lilith room which surfaces intentions in a single board. Newly-created wizard habits now share the BFF habit-create and check-in contracts with the polished room; seeded sample habits remain local.

Entry points#

  • Direct URL / bookmark — yes (signed-in)
  • Back navigation — page passes onBack={() => router.back()} to AreteHabitSystem
  • Polished /arete "+ New ritual" — links to /domains/arete/habits?wizard=loop

Layout regions#

page.tsx is a 'use client' page using useRouter(). Renders <AreteHabitSystem onBack={() => router.back()} />. Component at apps/oshun/web/src/components/domains/arete/AreteHabitSystem.tsx.

  • Container: min-height: 100vh; max-width: 800px; padding: tokens.spacing[4]; background: L.bg
  • Top bar (dashboard only): ArrowLeft button + h1 "Habit System" (accent)
  • Dashboard view === 'dashboard': <HabitDashboard habits analytics onNavigate onToggle checkInFeedback />onToggle POSTs the real check-in route for persisted wizard habits and keeps seeded sample rows local
  • Other views routed by view state (HabitView union):
    • loop_wizard<HabitLoopWizard onBack />
    • four_laws<FourLawsPanel habits onBack />
    • stacking<HabitStackingView habits stacks onBack />
    • identity<IdentityPanel identities habits onBack />
    • keystone<KeystoneHabitsView habits onBack />
    • streaks<StreakSystemView habits onBack />
    • analytics<HabitAnalyticsView habits analytics onBack />
    • reminders<RemindersView habits onBack />
    • detail<HabitDetailView habit onBack />

Constants:

  • CATEGORY_CONFIG: 8 habit categories (health / mindfulness / learning / fitness / creativity / social / productivity / custom)
  • DIFFICULTY_CONFIG: trivial / easy / medium / hard / epic
  • DAY_LABELS: ['S','M','T','W','T','F','S']
  • GRADE_COLORS: A+ through F (ConsistencyGrade)

States#

  • Loading — N/A: client component with sync fixture
  • Dashboard (default) — lists sampleHabits plus non-archived habits persisted in useAreteStore
  • Drill-in views — one of nine non-dashboard views; back button returns to dashboard via goBack (sets view to 'dashboard')
  • Habit toggled today — persisted wizard habits POST /v1/arete/habits/:habitId/check-in and update from the BFF streak result; seeded sample rows flip local habit.todayDone
  • Habit detail without selectionview === 'detail' requires selectedHabit to be defined, otherwise nothing renders
  • Error (recoverable) — relies on app-level error boundary
  • Offline — sample dashboard remains client-rendered, but new habit creation and persisted-habit check-ins require the BFF and report inline errors if unavailable
  • Gated — N/A
  • Standalone PWA — no shell chrome around the page

Interactions#

Top bar#

  • Back button (aria-label="Back")
    • Function: calls router.back()
    • Visible only on dashboard when onBack provided

Dashboard (HabitDashboard)#

  • Per-habit toggle — clicking the toggle calls toggleHabit(id). Persisted wizard-created habits POST /v1/arete/habits/:habitId/check-in with status: "done" (or skip when amending to rest), render the server outcome inline, and patch useAreteStore with completedToday / streak. Seeded sample rows still flip local state only.
  • Per-habit drill-inonNavigate('detail', habit.id) sets view to detail with selectedHabitId
  • Framework launches — buttons for Loop Wizard / Four Laws / Stacking / Identity / Keystone / Streaks / Analytics / Reminders; verify which lucide-icon buttons map to which views in HabitDashboard

View-specific interactions#

  • Habit Loop Wizard create — Cue/Routine/Reward submits /v1/arete/habits, stores the returned BFF habitId in useAreteStore, can attach one sampleIdentityStatements id locally, includes that statement in the BFF whyItMatters summary, returns to the dashboard, appears in IdentityPanel, and appears on polished /arete through /v1/arete/room
  • Persisted habit first check-in — the dashboard toggle submits /v1/arete/habits/:habitId/check-in; polished /arete reads the same check-in back through /v1/arete/room, and Home reads the local continuity store as 1d streak • done today
  • Per-view interactions exist (wizard steps, identity-statement binding, identity statement edits, reminder configuration, etc.) — enumerate when individually walking each view

Data & contracts#

  • Reads: import-time sampleHabits, sampleHabitStacks, sampleIdentityStatements, sampleHabitAnalytics from @/lib/arete/arete-simulation-data, plus non-archived local useAreteStore.habits created through the wizard, including optional identityStatements ids for same-browser identity-panel continuity. Types from @/lib/arete/arete-types (HabitData, HabitStack, IdentityStatement, FourLawsScore, HabitCategory, HabitDifficulty, CueType, RewardType, HabitAnalytics, HabitHeatmapCell, ConsistencyGrade).
  • Writes: Habit Loop Wizard POSTs /v1/arete/habits and mirrors the returned BFF id into useAreteStore for same-browser rich metadata. Persisted wizard-habit check-ins POST /v1/arete/habits/:habitId/check-in and mirror the returned streak/today state into useAreteStore; seeded sample toggles remain local-only.
  • Realtime: none
  • Caching: client bundle only
  • Auth/role check: app-level middleware

Cross-references#

  • Domain hub: arete.md
  • Polished counterpart: arete.md — intentions list in the Lilith room is the visible single-board habit surface
  • Sibling internal alternates:
  • Component source: apps/oshun/web/src/components/domains/arete/AreteHabitSystem.tsx
  • Simulation data: apps/oshun/web/src/lib/arete/arete-simulation-data.ts
  • Types: apps/oshun/web/src/lib/arete/arete-types.ts

Open questions / known gaps#

  • No shared chrome with /arete polished surface
  • Seeded sample habit toggles do not persist because those fixture ids do not exist in the member's BFF habit store
  • Nine subviews; each is a sub-flow needing its own walkthrough leaf
  • Identity-based habits (sampleIdentityStatements) draw on Atomic Habits framing — verify whether the V1 voice/tone copy matches the polished Lilith room's gentleness
  • Relationship between created habits on this surface and the polished /arete room — wizard-created habits write /v1/arete/habits and read back through /v1/arete/room; seeded sample habits remain internal scaffolding
  • Document V1-shipping vs internal status for the remaining framework subviews