---
path: /domains/arete/habits
surface: customer
domain: arete
auth: signed-in
source: apps/oshun/web/src/app/domains/arete/habits/page.tsx
status: walked
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'
---

# Habits · Arete · OSHUN (internal habit system)

## 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 selection** — `view === '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-in** — `onNavigate('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`](./arete.md)
- Polished counterpart: [`arete.md`](./arete.md) — intentions list in the Lilith
  room is the visible single-board habit surface
- Sibling internal alternates:
  - [`domains-arete-seven-habits.md`](./domains-arete-seven-habits.md) — Covey's
    seven habits
  - [`domains-arete-goals.md`](./domains-arete-goals.md)
- 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
- [x] 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
