---
path: /arete
surface: customer
domain: arete
auth: signed-in
source: apps/oshun/web/src/app/arete/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; content
  re-verified 2026-06-03 against current source; 2026-06-23 review-closure
  read-back verified through arete-review-close.spec.ts. 2026-06-27 Codex Arete
  hub addendum — signed-in /arete first-run and seeded mixed habit states
  against the real BFF room model, canonical metadata, Lilith subnav links,
  intention row and new-ritual navigation, evening prompt, week stats, coach
  affordances, and standalone PWA relaunch/mobile overflow safety are covered by
  the E2E references below.
---

# Arete · habits

## Purpose

The Arete domain hub — "the small daily rituals — and a humane coach who watches
the patterns." A polished Lilith room with member habits for the week, evening
prompt, week stats, Lilith's weekly coach note, and the latest weekly-review
closure when the member has saved or closed a review. The page awaits
`getArete()` and renders `<AreteRoom>` — analogous to `/tara`. See
[`V1/features.md`](../../../V1/features.md#arete).

## Entry points

- **Shell nav: `LCustomerNav` "Today"** — Arete room mounts with
  `active="today"`
- **Domain card on `/`** — `DomainCardGrid` cell → `/arete`
- **Domain switcher** in shell header / `/switcher`
- **Home rail: Arete practice section** (`HomeAretePracticeSection`) — "See
  plan" / "Continue habit"
- **PWA shortcut** (if defined in `manifest.json` `shortcuts[]`)
- **Direct URL / bookmark** — yes (auth required)
- **Deep link from notification** — Arete habit reminder push

## Layout regions

`page.tsx` is a thin server component: `await getArete()` →
`<AreteRoom data={data} />`. `AreteRoom` (see
`apps/oshun/web/src/components/lilith/rooms.tsx`, line 341) is an `LWebShell`
composition.

- **Header**: `LCustomerNav active="today"` (Today is highlighted)
- **Sub-nav**: `LSubNav active="habits"` with items Habits · Journal · Weekly
  review · Patterns
- **Domain masthead row**: eyebrow "Arete · habits", display title "The small
  daily _rituals_.", AI disclosure "Coaching · patterns, never streaks"
- **Main (left column, 1.5fr desktop; one-column mobile stack)**: intention rows
  (one per habit) — label, streak chip, "Done today" / "Pending" tag, 7-day cell
  bar, kebab arrow `↗`. On tablet/phone the row reflows so the 7-day bar spans
  the row and the page does not create document-level horizontal overflow.
- **Below intentions**: "+ New ritual" ghost button, evening-prompt block
  (headline + citation + journal text card + tag chips)
- **Aside (right column, 1fr desktop; stacked below the main column on
  mobile)**: "This week" card (`completed / total` big numeral, intention count
  line, `bestStreak / atRisk / started` fields)
- **Weekly-review continuity card (right column, conditional)**: renders when
  `data.reviewClosure` exists; selector
  `data-arete-review-continuity-state="week-closed|kept-private"`; links back to
  `/arete/review`
- **Coach card (right column)**: "The coach · this week" with `data.coachNote`
  body, "Approve" / "Not this week" buttons, eyebrow "Two adjustments offered ·
  accept or wait until next review"

## States

- [x] **Loading** — `getArete()` is awaited server-side; no client `loading.tsx`
      under `/arete`, so server blocks until data ready
- [x] **Returning user, populated** — intention rows render from the member's
      real stored habits and check-ins via `/v1/arete/room`
- [x] **Mixed completion** — some intentions show "Done today" (green/ok) others
      "Pending" (muted)
- [x] **Empty intentions** — `data-arete-room-empty` explains that proposed
      habits from the habits page will appear here
- [x] **Review closed/saved** — conditional weekly-review continuity card reads
      the latest `reviewClosure` from `/v1/arete/room`
- [ ] **Error (recoverable)** — failed BFF reads return `areteUnavailable()`;
      the room renders honest empty stats and an unavailable coach note
- [ ] **Offline** — no explicit offline handling in component; relies on SW
      shell cache
- [ ] **Gated** — N/A: no auth or feature-flag gate visible in `page.tsx`
- [x] **Standalone PWA** — cold-launch lands here if relaunch target is
      `/arete`; nav chrome renders within `LWebShell`

## Interactions

Current E2E evidence:

- `apps/oshun/web/e2e/arete-hub.spec.ts` — `/arete` signed-in hub render, real
  BFF room read, canonical metadata, first-run empty state, seeded mixed
  Done/Pending habits, subnav hrefs, intention-row detail links, new-ritual
  navigation, evening prompt/tags, week stats, coach affordances, standalone PWA
  relaunch, and mobile overflow safety
- `apps/oshun/web/e2e/arete-streak-recovery.spec.ts` — humane recovery/streak
  journey, Arete engagement read-back on `/arete`, durable coach decision
  approve/decline/undo, and plan propagation
- `apps/oshun/web/e2e/arete-review-close.spec.ts` — weekly-review close/save
  writes, `arete_weekly_review_closed` browser telemetry,
  `/v1/arete/review/closed` persistence, and `/arete` continuity-card read-back
  from `/v1/arete/room`
- `apps/oshun/web/e2e/arete-create-habit.spec.ts` and
  `apps/oshun/web/e2e/arete-habits-bff.spec.ts` — habit proposal UI and real BFF
  habit/create/check-in persistence

### Sub-nav (`LSubNav`)

- [x] **Habits / Journal / Weekly review / Patterns** (tab links)
  - Function: all four items carry an `href` (lines 351–354) and navigate —
    Habits → `/domains/arete/habits`, Journal → `/domains/arete/journal`, Weekly
    review → `/arete/review`, Patterns → `/arete/patterns`; `active="habits"`
    highlights the current tab
  - Keyboard: tab order matches DOM source order
  - Screen reader: announces label only; no `role="tablist"` on the wrapper
  - Telemetry: none wired in `AreteRoom`

### Intention rows (one per habit)

- [x] **Habit row** (grid row)
  - Function: visually shows habit label, streak eyebrow, today-status text,
    7-day cell bar, and a trailing `↗` link
  - Click target: the `↗` is a `next/link`
    `<Link href="/domains/arete/habits?habit=<label>" aria-label="Open {label} detail">`
    (lines 443–456) — it navigates to the habit detail, encoding the habit label
    as a query param
  - Screen reader: the `↗` link announces "Open {label} detail"; the rest of the
    row has no `role` / `aria-label` and announces as static text
  - Touch target: the `↗` link is small (≤ 14 px font); flag for review

### "+ New ritual"

- [x] **"+ New ritual"**
      (`LBtn kind="ghost" size="md"     href="/domains/arete/habits?wizard=loop"`)
  - Function: navigates to `/domains/arete/habits?wizard=loop` (line 460),
    opening the loop-habit wizard
  - Keyboard: standard link/button focus
  - Telemetry: none

### Evening prompt card

- [x] **Card body** — displays `eveningPrompt.journalText` and tag chips
      (`LChip`)
  - Function: presentational; no click handlers
- [x] **Tag chips** — `data.eveningPrompt.tags` rendered as `LChip` spans

### "This week" stats card

- [x] **`weekStats.completed / total`** (large numeral)
  - Function: presentational
- [x] **`bestStreak`, `atRisk`, `started`** (`LField` rows)
  - Function: presentational key/value lines

### Weekly-review continuity card

- [x] **Latest review closure** (`data-arete-review-continuity`)
  - Function: appears only when `/v1/arete/room` includes `reviewClosure`
    sourced from the member-scoped `areteReviewStore`
  - States: `data-arete-review-continuity-state="week-closed"` renders "Week
    closed"; `kept-private` renders "Private letter saved"
  - Click target: "Review the week" link returns to `/arete/review`
  - Coverage: `arete-review-close.spec.ts` closes the week, verifies
    `arete_weekly_review_closed`, verifies `/v1/arete/review/closed`, returns to
    `/arete`, and asserts this card

### Coach card

- [x] **Coach note body** — renders `data.coachNote` (the at-risk-Sunday-reading
      observation from fixture)
- [x] **Approve / "Not this week"** (live in `<AreteCoachActions />`, line 554 —
      see `apps/oshun/web/src/components/lilith/AreteCoachActions.tsx`)
  - Function: fully wired client component. **"Approve"**
    (`LBtn kind="primary" size="sm"`) has `onClick={approve}`; **"Not this
    week"** (`LBtn kind="ghost" size="sm"`) has `onClick={decline}`. The
    decision POSTs to `/v1/arete/coach/decision` and reads back through
    `/v1/arete/room`.
  - States: once decided, the buttons are replaced by a confirmation eyebrow —
    "Approved · two adjustments queued for the next review" (ok color) or "Set
    aside for now · the letter returns on Sunday" — each with an **undo** button
    that resets the decision (`writeStored('pending')`). The wrapper carries
    `data-coach-decision="pending|approved|declined"` for E2E selectors.
  - Read-on-mount: the server-provided `coachDecision` initializes the island so
    a returning user sees their prior choice.
  - Telemetry: none

## Data & contracts

- **Reads**: `getArete()` from `@/lib/lilith-data/arete` fetches
  `/v1/arete/room` and returns `AreteData` ({ intentions, weekStats, coachNote,
  coachActionLabel, coachDecision, reviewClosure, eveningPrompt }). The BFF
  computes habits and weekly stats from stored habits/check-ins and folds in the
  latest member-scoped review closure when present.
- **Writes**: the coach decision (Approve / "Not this week") POSTs
  `/v1/arete/coach/decision` via `AreteCoachActions`. Weekly-review close/save
  writes originate on `/arete/review` (`/v1/arete/review/close`) and read back
  here through `/v1/arete/room`. "+ New ritual" and the intention-row `↗`
  navigate (no write).
- **Realtime**: none
- **Caching**: authenticated server fetches use `cache: 'no-store'` in `bffGet`;
  anonymous public reads keep a short revalidate window
- **Auth/role check**: relies on app-level middleware (no per-route check in
  `page.tsx`)

## Cross-references

- Shell: [`shell/01-app-shell.md`](../../shell/01-app-shell.md)
- Sibling Arete polished routes:
  - [`arete-coaching.md`](./arete-coaching.md)
  - [`arete-goal.md`](./arete-goal.md)
  - [`arete-offering.md`](./arete-offering.md)
  - [`arete-patterns.md`](./arete-patterns.md)
  - [`arete-plan.md`](./arete-plan.md)
  - [`arete-recovery.md`](./arete-recovery.md)
  - [`arete-review.md`](./arete-review.md)
  - [`arete-streak.md`](./arete-streak.md)
- Domains-namespace alternate (`/domains/arete/*`):
  - [`domains-arete-affirmations.md`](./domains-arete-affirmations.md)
  - [`domains-arete-balance.md`](./domains-arete-balance.md)
  - [`domains-arete-coach.md`](./domains-arete-coach.md)
  - [`domains-arete-gamification.md`](./domains-arete-gamification.md)
  - [`domains-arete-goals.md`](./domains-arete-goals.md)
  - [`domains-arete-habits.md`](./domains-arete-habits.md)
  - [`domains-arete-journal.md`](./domains-arete-journal.md)
  - [`domains-arete-plan-review.md`](./domains-arete-plan-review.md)
  - [`domains-arete-progress.md`](./domains-arete-progress.md)
  - [`domains-arete-seven-habits.md`](./domains-arete-seven-habits.md)
  - [`domains-arete-time.md`](./domains-arete-time.md)
  - [`domains-arete-vision.md`](./domains-arete-vision.md)
- Component source: `apps/oshun/web/src/components/lilith/rooms.tsx` (line 341,
  `AreteRoom`); coach actions in
  `apps/oshun/web/src/components/lilith/AreteCoachActions.tsx`
- Data: `apps/oshun/web/src/lib/lilith-data/arete.ts`
- E2E coverage:
  - `apps/oshun/web/e2e/arete-hub.spec.ts`
  - `apps/oshun/web/e2e/arete-streak-recovery.spec.ts`
  - `apps/oshun/web/e2e/arete-review-close.spec.ts`
  - `apps/oshun/web/e2e/arete-create-habit.spec.ts`
  - `apps/oshun/web/e2e/arete-habits-bff.spec.ts`
- Feature spec: [`V1/features.md`](../../../V1/features.md#arete)
- Cross-domain partner: [`../03-tara/tara.md`](../03-tara/tara.md) (streak/sit
  bridge)

## Open questions / known gaps

- [ ] Add a dedicated `/arete` recoverable-error browser slice that proves the
      `areteUnavailable()` room when the BFF room read fails; the route has no
      local `error.tsx`
- [ ] Relationship between `/arete` (polished Lilith room) and
      `/domains/arete/habits` (large tabbed `AreteHabitSystem`) is unclear — the
      latter looks like a deeper internal workspace; document which ships in V1
