Context. surface customer · domain arete · route /arete/coaching · auth signed-in · source apps/oshun/web/src/app/arete/coaching/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#
Lilith's letter to the practitioner — "twice a month, on a Sunday, Lilith writes
you a short letter." Named patterns, two adjustments awaiting a nod, and a
privacy note. Marked robots: { index: false, follow: false } in metadata.
Renders <AreteCoaching data={await getAreteCoaching()} /> against the Lilith
design system.
Entry points#
- From
/aretecoach card — the weekly coach note links here (verify; in sourceAreteRoom's Approve button is not wired) - From
/arete/review— the weekly review references "Lilith might suggest a phrase in the margin" - From notification / email — a "your letter has arrived" deep link
- Direct URL / bookmark — yes (signed-in)
- Not indexed —
robots.index = false, so no search-engine surface
Layout regions#
page.tsx is a thin server component: await getAreteCoaching() →
<AreteCoaching>. Component lives at
apps/oshun/web/src/components/lilith/arete.tsx line 1347 — an LWebShell with
LCustomerNav active="today".
- Header:
LCustomerNav active="today" - Masthead (
ArMast): left "Arete · the letter", midfrom Lilith · ${letterDate}, right "private to you · always", big "A letter, after the week.", lede about twice-a-month Sunday letters - Main, left (1.4fr): the letter — paper-card with
letterCodewatermark, recipient name, paragraphs (with optionalemphasizedaccent andchiphighlight spans),— Lilithsignoff - Main, right (1fr):
- "Patterns Lilith named" section — list of
data.patternsrows (name +conf · 0.NNchip + evidence body) - "Two adjustments — your nod" section — list of
data.adjustmentsrows with action buttons (Not now / Tell me more / Apply for one week) for non-accepted, "accepted · 11:42" code chip when accepted - "Privacy of the letter" dashed-border card
- "Patterns Lilith named" section — list of
States#
- Loading — server-awaited; no
loading.tsxat this route - Populated (default) — Sunday letter with paragraphs, 2+ patterns, and 2+ adjustments (fixture provides all)
- Adjustment accepted —
adjustments[i].accept === truerenders "accepted · 11:42" code chip and hides the button row - Adjustment pending —
adjustments[i].accept !== truerenders Not now / Tell me more / Apply for one week buttons - Empty patterns / adjustments — N/A: component renders the section headers and maps over the arrays; an empty array would yield empty space (no empty state coded)
- Error (recoverable) — relies on parent error boundary (none here)
- Offline — no offline handling in component
- Gated — N/A
- Standalone PWA — renders cleanly inside
LWebShell
Interactions#
Pattern row (one per data.patterns)#
- Pattern card — name, confidence chip (
conf · 0.NN), evidence body- Function: presentational only
- Keyboard / SR: no interactive role
Adjustment row (one per data.adjustments)#
- "Not now" (button
LBtn kind="ghost" size="sm")- Function: no
onClickhandler in source — visual only - Telemetry: none
- Function: no
- "Tell me more" (button
LBtn kind="ghost" size="sm")- Function: no
onClickhandler in source — visual only
- Function: no
- "Apply for one week" (button
LBtn kind="primary" size="sm")- Function: no
onClickhandler in source — visual only
- Function: no
These are hidden when a.accept === true; instead a static "accepted · 11:42"
LCode chip renders. The "11:42" timestamp is hardcoded in the component, not
driven by data.
Data & contracts#
- Reads:
getAreteCoaching()from@/lib/lilith-data/arete-depth— returnsAreteCoachingData({ letterDate, letterCode, recipientName, letterParagraphs, patterns, adjustments }). BFF mapping comment:getAreteCoaching ← /arete/letters/:id. - Writes: none wired today (no handlers on Not now / Tell me more / Apply for one week)
- Realtime: none
- Caching: server fetch, default Next.js cache; metadata also disables indexing
- Auth/role check: relies on app-level middleware
Cross-references#
- Shell:
shell/01-app-shell.md - Domain hub:
arete.md - Sibling Arete routes (see
arete.mdfor full list) - Cross-domain: Lilith / Sophia are the AI substrate; this surface narrates pattern detection rather than wiring to a model in code
- Component source:
apps/oshun/web/src/components/lilith/arete.tsx(line 1347,AreteCoaching) - Data:
apps/oshun/web/src/lib/lilith-data/arete-depth.ts(getAreteCoachingFixture) - Feature spec:
V1/features.md
Open questions / known gaps#
- All adjustment-action buttons (Not now / Tell me more / Apply for one
week) are wired as static
LBtnwith noonClick— confirm whether this is intentional pre-V1 staging - The "accepted · 11:42" timestamp is hardcoded in the component instead of
pulled from
adjustment.acceptedAt— verify when wiring real data - Where does the "letter" actually originate — Sophia (AI substrate) or a domain-specific Lilith service? No imports cite Sophia in this file
- Confirm the cadence ("every other Sunday" in metadata vs. "twice a month" in body lede) is consistent with feature spec