V1 Web PWA · Surface walkthrough

AI Coach · Arete · OSHUN

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

walked
9sections4 minread

On this page

Context. surface customer · domain arete · route /domains/arete/coach · auth signed-in · source apps/oshun/web/src/app/domains/arete/coach/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; live BFF chat submit + 5xx recovery re-walked 2026-06-27

Purpose#

Internal /domains/arete/* alternate surface for an AI-coach chat workspace: chat / starters / history / insights / settings tabs. The chat composer now calls the live BFF coach composer (POST /v1/arete/coach/responses) with the typed prompt; starters/history/insights/settings still read fixture constants (COACH_SESSIONS, COACH_CONVERSATION_STARTERS). Distinct from the polished /arete/coaching (Lilith's bi-weekly letter) — this is conversational and active, that is editorial and asynchronous.

Entry points#

  • Direct URL / bookmark — yes (signed-in)
  • No link from /arete/coaching — verify; the polished letter surface does not link here in source

Layout regions#

page.tsx is a 'use client' page: SR-only "AI Coach · Arete · OSHUN" heading + <AreteAICoach />. Component at apps/oshun/web/src/components/domains/arete/AreteAICoach.tsx.

  • Container: max-width: 900px; padding: 24px
  • Header: Brain icon + h2 "AI Coach" + subtitle "Your personal coaching companion for growth, reflection, and insight"
  • Tab bar (5 tabs):
    • chat — Chat (MessageCircle icon)
    • starters — Starters (Lightbulb icon)
    • history — History (Clock icon)
    • insights — Insights (Sparkles icon)
    • settings — Settings (Settings icon)
  • Content area (per active tab):
    • chat<ChatView /> — session header with title and framework / personality chips; empty scrollable messages region until the user submits; browser message bubbles are tagged by [data-arete-ai-coach-message="user|coach"]
    • starters<StartersView />
    • history<HistoryView />
    • insights<InsightsView />
    • settings<SettingsView />

ChatView constants:

  • FRAMEWORK_LABELS: motivational-interviewing, cbt, act, positive-psychology, solution-focused
  • FRAMEWORK_COLORS: corresponding accent variables
  • PERSONALITY_LABELS: supportive, challenging, analytical, playful
  • PERSONALITY_ICONS: Heart / Brain / BarChart3 / Smile

States#

  • Loading — N/A: client component with sync fixture import
  • Empty chat (initial) — chat starts with no message bubbles; the active session metadata still renders above the empty message pane
  • Typing indicatorisTyping === true toggled by handleSend; animation ac-typing defined in STYLES
  • User message — submitted text renders as [data-arete-ai-coach-message="user"]
  • Coach message (live BFF) — the real BFF response renders as [data-arete-ai-coach-message="coach"]
  • Suggested prompts — when the BFF returns suggestions, prompt chips render as [data-arete-ai-coach-suggestion] and clicking one fills the composer
  • Tab switching — instant
  • Coach BFF error (recoverable) — a non-2xx or failed BFF call renders "The coach could not be reached right now — your message was not processed. Please try again in a moment." as a coach bubble
  • Offline — no offline queue or retry state; failed fetch uses the same recoverable message as BFF 5xx
  • Gated — N/A
  • Standalone PWA — no shell chrome around the page

Interactions#

Tab bar (5 tabs)#

  • Chat / Starters / History / Insights / Settings (buttons)
    • Function: setActiveTab(tab.id)
    • Telemetry: none

Chat composer#

  • Input (verify in lower lines of source — composer is referenced via inputValue, setInputValue)
    • Function: typed text becomes userMsg on send; immediately appended to messages
  • Send (verify the send button rendering — handleSend is the handler)
    • Function: appends a user message, POSTs { framework: 'reflective', userMessage } to /v1/arete/coach/responses, and appends the BFF content plus suggestion chips when the response is ok
    • Failure: non-2xx or failed fetch appends the honest recoverable failure bubble and does not render fake suggestion chips
    • Telemetry: none

Suggested prompt chips#

  • Pill prompt — clicking calls handleSuggestedPrompt(prompt) which sets the input value to the prompt text (does not auto-send)

Data & contracts#

  • Reads: import-time COACH_SESSIONS, COACH_CONVERSATION_STARTERS from @/lib/arete/arete-extended-simulation for session metadata, starters, history, insights, and settings fixtures
  • Writes: browser POST to /v1/arete/coach/responses for chat replies; message state remains local and is not persisted
  • Realtime: none
  • Caching: client bundle only
  • Auth/role check: app-level middleware

Cross-references#

  • Domain hub: arete.md
  • Polished counterpart: arete-coaching.md — the letter surface (different intent: editorial, asynchronous, bi-weekly)
  • Cross-domain: Sophia (AI substrate) — AreteAICoach calls the Arete BFF coach composer; it does not directly import Sophia
  • Component source: apps/oshun/web/src/components/domains/arete/AreteAICoach.tsx
  • Simulation data: apps/oshun/web/src/lib/arete/arete-extended-simulation.ts
  • Types: apps/oshun/web/src/lib/arete/arete-extended-types.ts (CoachingFramework, CoachPersonality, CoachMessage)

E2E coverage#

  • apps/oshun/web/e2e/arete-coach-ui.spec.ts — direct /domains/arete/coach browser coverage: typed prompt POSTs to the real /v1/arete/coach/responses BFF route as { framework: 'reflective', userMessage }, renders the reflective coach response and suggestion chips, verifies suggestion click fills the composer, and forces a 503 to assert the honest recoverable message with no fake suggestions.

Open questions / known gaps#

  • Chat response now uses the real Arete BFF coach route instead of the older hardcoded simulated reply
  • The direct page always sends framework: 'reflective'; the Settings framework picker is local UI state and does not influence chat requests
  • No real persistence — messages reset on reload
  • No safety / disclosure copy in the chat interface, despite the framework selector (e.g., CBT, ACT) implying clinical-adjacent framing — verify whether AI-disclosure / boundary copy needs adding
  • No shared chrome with /arete polished surface
  • How does this relate to Sophia / Lilith infrastructure? The page does not import from those domain libs directly
  • No telemetry events fired anywhere in the component