V1 Web PWA · Surface walkthrough

Arete · the humane streak

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/streak · auth signed-in · source apps/oshun/web/src/app/arete/streak/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#

"A practice, gently kept — rest days are part of it; quiet days do not break it." The streak surface: day count, six-week heatmap, an explicit promise about rest and check-ins, and today's invitation. Rendered as a phone shell inside a desktop frame.

Entry points#

  • From / home rail Arete practice sectionHomeAretePracticeSection shows streak indicator; verify whether it deep-links here
  • From /arete intention rows — each row shows a "{streakDays}-day streak" eyebrow but no href is wired
  • Cross-domain from /arete/streak itself — the "Begin" CTA links into the Tara sit player (/tara/sit/when-the-mind-wanders); note TaraRoom has no streak/continuity panel that links back here
  • Direct URL / bookmark — yes (signed-in)

Layout regions#

page.tsx is not a thin server component — it wraps a PhoneFrame inside an LWebShell with a masthead and lede. Layout:

  • Outer: LWebShell with top={<LCustomerNav active="today" />}
  • Outer body: padding: 32px 56px 80px; max-width: 720px
  • Masthead: LMasthead — left "Specimen · Arete mobile", right "375 × 812", kicker "A practice, gently kept", title "The streak."
  • Lede block: LEyebrow "The promise" + italic body "A streak should never punish a Sunday off. Two rest days a week are part of the practice. Three quiet days in a row, and Lilith will check in — never before."
  • Phone frame: PhoneFrame with label="Arete · streak" and caption="Day 47, gently. Two quiet days, folded in."
  • Phone contents: <AreteStreak data={data} />apps/oshun/web/src/components/lilith/arete.tsx line 172
    • Status bar (LStatusBar)
    • Top row: "Arete · today" eyebrow + "day {dayNumber}" accent eyebrow (fixture: day 109)
    • Italic lede "A practice, gently kept."
    • Huge data.streakCount numeral (fixture: 47) + smaller "days" accent italic
    • Italic muted data.streakNote ("Two quiet days are folded into the count, as we agreed.")
    • "Last six weeks" heatmap — 42-cell grid (data.weekHeatmap) exposed as role="list" with per-cell role="listitem" labels. Cell color is derived per state: kept = accent, half = #d99777, rest = rule color, today = ink with 2px accent border, else rule2; each cell's accessible label includes its fixture date and state.
    • Legend row: "● kept · ● partial · ○ rest · · quiet"
    • "The promise" callout card (light paper bg) with data.promise
    • "Today's invitation" italic body from data.todaysInvitation
    • Bottom action row (absolute-positioned): "Adjust" ghost lg (flex 1, href /arete/plan?intent=adjust) + "Begin" primary lg (flex 2, href /tara/sit/when-the-mind-wanders)
    • Home indicator (LHomeIndicator)

States#

  • Loading — server-awaited; no loading.tsx at this route
  • Populated (default fixture) — day 109, streak 47, full heatmap, promise + invitation
  • Recovery state (3+ quiet days) — N/A here; recovery is a separate route /arete/recovery. The streak surface still renders even when quiet — see Open questions
  • Streak == 0 — N/A: streakCount is shown as a big numeral; "0 days" would render literally with the small italic "days" suffix
  • Promise tone (V1 feature requirement) — body and promise read "gently", "never punish a Sunday off", "Lilith will check in — never before"; no shaming copy ✓ matches V1/features.md humane-recovery requirement
  • Error (recoverable) — relies on parent error boundary
  • Offline — no explicit handling
  • Gated — N/A
  • Standalone PWA — page is a desktop frame around a phone shell; verify on actual mobile viewport (page hardcodes max-width: 720px)

Interactions#

Heatmap cells#

  • Heatmap cell — square cell colored by state from data.weekHeatmap[i]
    • Function: presentational only — no click
    • Keyboard / SR: parent grid is labelled "Last six weeks of Arete practice"; each cell has role="listitem", aria-label, and title with date + state
    • Today cell gets a 2px accent border

"Adjust"#

  • "Adjust"LBtn kind="ghost" size="lg" (href="/arete/plan?intent=adjust", flex 1)
    • Function: navigates to /arete/plan?intent=adjust (line 341) — the plan/adjust surface

"Begin"#

  • "Begin"LBtn kind="primary" size="lg" (href="/tara/sit/when-the-mind-wanders", flex 2)
    • Function: cross-domain link — navigates to the Tara sit player at /tara/sit/when-the-mind-wanders (line 344), starting today's session

Data & contracts#

  • Reads: getAreteStreak() from @/lib/lilith-data/arete-depth — returns AreteStreakData ({ dayNumber, streakCount, streakNote, weekHeatmapStartDateIso, weekHeatmap, promise, todaysInvitation }). BFF mapping comment: getAreteStreak ← /arete/streak.
  • Writes: none — "Adjust" and "Begin" are navigation links (to /arete/plan?intent=adjust and /tara/sit/when-the-mind-wanders), not mutations
  • Realtime: none
  • Caching: server fetch, default Next.js cache
  • Auth/role check: app-level middleware

Cross-references#

  • Domain hub: arete.md
  • Recovery partner: arete-recovery.md — what triggers when "three quiet days in a row" condition is met
  • Cross-domain: ../03-tara/tara.md — this surface's "Begin" CTA links into the Tara sit player (TaraRoom itself has no streak panel)
  • Component source: apps/oshun/web/src/components/lilith/arete.tsx (line 172, AreteStreak)
  • Data: apps/oshun/web/src/lib/lilith-data/arete-depth.ts (getAreteStreakFixture)
  • Phone frame: apps/oshun/web/src/components/lilith/mobile.tsx
  • Feature spec: V1/features.md

Open questions / known gaps#

  • "Adjust" and "Begin" CTAs are wired as navigation links (/arete/plan?intent=adjust and the cross-domain /tara/sit/when-the-mind-wanders); no streak-mutation write exists yet
  • The 42-cell heatmap is generated from arithmetic in the fixture (generateStreakHeatmap in arete-depth.ts) and a fixed weekHeatmapStartDateIso, not from real per-day data
  • The page wraps a phone shell inside a desktop frame ("Specimen · Arete mobile" label) — same specimen pattern as /arete/recovery. Verify whether this is the actual mobile customer surface or a design specimen
  • Relationship between /arete/streak (presentational, single practice) and the /arete room intention rows (per-habit streak eyebrows) — verify which streaks roll up here