V1 Web PWA · Surface walkthrough

Arete · the plan

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 /arete/plan · auth signed-in · source apps/oshun/web/src/app/arete/plan/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; content re-verified 2026-06-03 against current source

Purpose#

The week workspace. "Three goals, the week's habits, what got in the way last week, what to invite this one." Goals · routine · friction & invitations · the week at a glance. Renders <AretePlan data={await getAretePlan()} />.

Entry points#

  • From / home rail "See plan"HomeAretePracticeSection links to /arete/plan
  • From /arete — sub-nav contains a "Weekly review" tab but no explicit "Plan" tab; verify whether plan is linked from the room
  • Direct URL / bookmark — yes (signed-in)
  • From notification — week-kickoff push

Layout regions#

page.tsx is a thin server component: await getAretePlan()<AretePlan>. Component lives at apps/oshun/web/src/components/lilith/arete.tsx line 520 — an LWebShell with LCustomerNav active="today".

  • Header: LCustomerNav active="today"
  • Masthead (ArMast): left "Arete · the plan", mid "week of 5 May · Renata K." (hardcoded), right "goals · habits · friction", big "This week.", lede about three goals plus the week's habits
  • Main, 3-column grid (1.3fr / 1fr / 1fr):
    • I · Goals — "Three, no more.", right meta "of which 1 is for this week". List of data.goals cards; the goal with g.focus === true gets paper fill, accent border, and a "this week's focus" code chip. Each card shows name, sub, progress bar (pct * 100% accent fill), "this week" eyebrow, italic delta. "Open the chapter goal ›" ghost button at the bottom.
    • II · Routine — "Today, in order.", right meta "tue · 6 may". Vertical timeline of data.routine steps with state-driven dot colors (kept / now / pending) and strike-through for kept.
    • III · Friction · invitations — "last week's friction" list (each row is a left-accent-bordered prose paragraph + tag eyebrow from data.friction); "this week's invitations" paper card with hardcoded prose ("phone in the kitchen after nine — book by the bed", "Saturday's sit at any hour…", "block 12:00–12:30 on the calendar…")
  • Footer section (borderTop: 1px solid L.ink):
    • IV · The week, at a glance. Habit-by-habit, day-by-day. Grid: 180px label column + 7 day columns (mon–sun, today=tue highlighted accent). Each row is data.weekHabits[i] — name + 7 cells. Cell color is c === 'k' accent, c === 'n' bg, c === '-' rule2, otherwise paper. Today column gets a 1.5px accent border.

States#

  • Loading — server-awaited; no loading.tsx at this route
  • Populated (default fixture) — three goals (one focused), routine timeline, friction list, week grid
  • Goal with focus — one card highlighted via g.focus === true
  • No focused goal — possible if all g.focus === false; right meta "of which 1 is for this week" is hardcoded and would lie
  • Empty goals / routine / friction / week habits — empty arrays render no rows; no empty-state copy
  • Error (recoverable) — relies on parent error boundary
  • Offline — no explicit handling
  • Gated — N/A
  • Standalone PWA — renders cleanly within LWebShell

Interactions#

Goal cards (per data.goals)#

  • Goal card — name, sub, progress bar, "this week" eyebrow, delta
    • Function: presentational only; no click target

"Open the chapter goal ›"#

  • "Open the chapter goal ›" (button LBtn kind="ghost" size="sm")
    • Function: no onClick handler in source — visual only
    • Likely-intended target: /arete/goal
    • Telemetry: none

Routine timeline (per data.routine)#

  • Routine step row — time LCode + italic event text; struck through when state === 'kept', accent when state === 'now'
    • Function: presentational only

Friction list (per data.friction)#

  • Friction item — italic body + tag eyebrow
    • Function: presentational only

Week grid#

  • Habit row (per data.weekHabits) — label + 7 cells
    • Function: presentational only; cells are colored blocks with no per-cell tap target
    • Today column: 1.5px accent border

Data & contracts#

  • Reads: getAretePlan() from @/lib/lilith-data/arete-depth — returns AretePlanData ({ goals, routine, friction, weekHabits }). BFF mapping comment: getAretePlan ← /arete/plans/:id.
  • Writes: none wired
  • Realtime: none
  • Caching: server fetch, default Next.js cache
  • Auth/role check: app-level middleware

Cross-references#

Open questions / known gaps#

  • The "this week's invitations" prose ("phone in the kitchen after nine…", "Saturday's sit at any hour…", "block 12:00–12:30…") is hardcoded in the component, not driven by AretePlanData
  • "Renata K." (recipient name) and "week of 5 May" (date) appear hardcoded in the masthead
  • "Open the chapter goal ›" has no onClick; presumed target /arete/goal
  • Right meta "of which 1 is for this week" is hardcoded and may not match actual focus-goal count
  • No keyboard / screen-reader treatment for the week grid; cells have no labels or summaries
  • Relationship with /domains/arete/plan-review — that workspace has reflection drafts, assistant handoff, artifact export; this one is static. Document V1-shipping vs internal