V1 Web PWA · Surface walkthrough

Arete · the weekly review

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

walked
9sections5 minread

On this page

Context. surface customer · domain arete · route /arete/review · auth signed-in · source apps/oshun/web/src/app/arete/review/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; 2026-06-30 close/save telemetry re-walked in arete-review-close.spec.ts

Purpose#

"Celebrate · Notice · Choose · Invite. Four acts, twenty minutes, on a Sunday." A guided weekly review with the four acts in order, a sidebar of KPIs, mood thread, and one-per-evening journal lines. Renders <AreteReview data={await getAreteReview()} />.

Entry points#

  • From /arete sub-nav "Weekly review" tab — the LSubNav item carries href="/arete/review" and navigates here
  • Sunday reminder push — inferred from "Sunday. Look back, gently." body
  • From /arete/coaching — Lilith's letter references "the week" and may link back here (no explicit link in source)
  • Direct URL / bookmark — yes (signed-in)

Layout regions#

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

  • Header: LCustomerNav active="today"
  • Masthead (ArMast): left "Arete · sunday review", mid data.weekLabel, right "four acts · twenty minutes", big "Sunday. Look back, gently.", lede about Celebrate · Notice · Choose · Invite
  • Main, left (1.4fr): vertical stack of four act blocks (per data.acts). Each act renders:
    • Header row: "act {num} · {kicker}" eyebrow (accent if a.accent), big italic title, LCode duration on the right
    • Body: mixed text with emphasized (accent italic) and chip (highlight bg) inline spans from a.bodyParts
    • Two-column grid of a.list items in rounded outlined cards
  • Aside, right (1fr):
    • "The week, kept" — data.kpi 3-up: each tuple [k, v, c] renders big serif numeral colored by c ('accent' / 'ink' / muted)
    • "Mood thread" — aria-hidden SVG curve across the week (mon–sun); body renders data.moodCaption
    • "What you wrote" — list of data.days[i] = [day, txt] lines with date code + italic text
  • Footer row (borderTop: 1px solid L.ink):
    • Note: "The review is yours; Lilith never alters a sentence. She might suggest a phrase in the margin."
    • AreteReviewMarginSuggestion reveal button backed by data.marginSuggestion
    • "Save as private letter" (ghost md) + "Close the week" (primary md)

Automation anchors#

  • Root: data-arete-review-page with data-arete-review-week-label, data-arete-review-act-count, data-arete-review-kpi-count, and data-arete-review-journal-day-count
  • Masthead and layout: data-arete-review-masthead, data-arete-review-layout, data-arete-review-acts, and data-arete-review-sidebar
  • Act blocks: each data-arete-review-act={num} carries index/title/kicker/ duration/accent attributes, plus data-arete-review-act-eyebrow, data-arete-review-act-title-text, data-arete-review-act-duration-text, data-arete-review-act-body, per body part kind, data-arete-review-act-list, and per list item index
  • Sidebar: data-arete-review-kpis with per data-arete-review-kpi={label} value/color cells; data-arete-review-mood with data-arete-review-mood-chart and data-arete-review-mood-caption; and data-arete-review-journal with per-row day/text cells
  • Footer: data-arete-review-footer, data-arete-review-footer-note, and data-arete-review-footer-actions

States#

  • Loading — server-awaited; no loading.tsx at this route
  • Populated (default fixture) — four acts, KPI tiles, mood SVG, day-by-day journal lines render
  • Mid-act (partial) — N/A: the page renders all four acts at once; no "active act" progress indicator
  • Empty acts / days / kpi — 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#

Act blocks (per data.acts)#

  • Act block — header, body (mixed emphasis), list items
    • Function: presentational only — no inputs, no completion checks

KPI tiles (per data.kpi)#

  • KPI tile — big numeral + label eyebrow
    • Color: derived from third tuple entry — accentL.accent, inkL.ink, else L.muted (kpiColor helper)
    • Function: presentational only

Mood SVG#

  • Mood thread chartaria-hidden="true"; smooth quadratic curve across mon–sun
    • Function: presentational only
    • Caption renders data.moodCaption

Journal lines (per data.days)#

  • Day row — date code + italic text
    • Function: presentational only — no entry-level link to journal route
  • Margin suggestion (button in AreteReviewMarginSuggestion)
    • Function: toggles a data-backed phrase beside the footer note without mutating the review body
    • Screen reader: button exposes aria-expanded and aria-controls
  • "Save as private letter" (button in AreteReviewClose)
    • Function: POSTs /v1/arete/review/close with action saved-private and renders data-arete-review-close-state="kept-private"
    • Telemetry: after the BFF closure response succeeds, emits arete_weekly_review_closed with action: 'saved-private', state: 'kept-private', weekLabel, closureId, and sourcePath: '/arete/review'
  • "Close the week" (button in AreteReviewClose)
    • Function: POSTs /v1/arete/review/close with action closed and renders data-arete-review-close-state="week-closed"
    • Telemetry: after the BFF closure response succeeds, emits arete_weekly_review_closed with action: 'closed', state: 'week-closed', weekLabel, closureId, and sourcePath: '/arete/review'

Data & contracts#

  • Reads: getAreteReview() from @/lib/lilith-data/arete-depth — returns AreteReviewData ({ weekLabel, acts, kpi, moodCaption, marginSuggestion, days }). BFF mapping comment: getAreteReview ← /arete/reviews/:weekId.
  • Writes: AreteReviewClose POSTs /v1/arete/review/close for saved-private and closed; /arete reads the stored closure through /v1/arete/room
  • Telemetry: apps/oshun/web/src/analytics/areteReviewTelemetry.ts emits arete_weekly_review_closed only after the persisted close/save envelope is returned by the BFF.
  • Realtime: none
  • Caching: server fetch, default Next.js cache
  • Auth/role check: app-level middleware

Cross-references#

Open questions / known gaps#

  • "Save as private letter" and "Close the week" persist through AreteReviewClose, emit arete_weekly_review_closed after BFF success, and read back on /arete
  • No per-act completion state — the user can't mark Celebrate / Notice / Choose / Invite individually
  • No journaling input on this page — data.days is read-only; the "Save as private letter" CTA implies an output that isn't wired here
  • Mood-thread caption is data-driven via AreteReviewData.moodCaption
  • Promise text "Lilith never alters a sentence. She might suggest a phrase in the margin." — AreteReviewMarginSuggestion now reveals the data-backed phrase without altering the review text