# Kalei — User Journey Technical Map

> Version 1.0 — February 2026
> Maps every user-facing flow to backend API endpoints, database operations, frontend components, and AI calls

---

## Architecture Summary

**Backend:** Fastify 5.x (Node.js 22 LTS), Drizzle ORM, PostgreSQL 16, Redis 7
**Frontend:** React Native + Expo SDK 54+, Expo Router, TanStack Query v5, Zustand, MMKV v4
**AI:** Claude Haiku 4.5 (primary), Groq/Qwen fallback, provider-agnostic AI Gateway
**Auth:** JWT with refresh token rotation, Apple/Google SSO
**Billing:** Direct App Store / Google Play webhook integration

---

## 1. Authentication & Onboarding

### 1.1 Account Registration

**User Action:** Enters email + password or taps Apple/Google SSO

| Layer | Detail |
|-------|--------|
| **API** | `POST /auth/register` — body: `{ email, password, provider? }` |
| **Validation** | Zod: email format, password 8+ chars, provider enum |
| **DB Write** | `INSERT INTO users (id, email, password_hash, created_at)` |
| **DB Write** | `INSERT INTO profiles (user_id, display_name, coaching_style, onboarding_complete)` |
| **DB Write** | `INSERT INTO subscriptions (user_id, plan, status, started_at)` — defaults to `free` |
| **Redis** | Set `rate:auth:{ip}` with TTL for brute-force protection |
| **Response** | `{ access_token, refresh_token, user: { id, email, profile } }` |
| **Frontend** | `AuthStore.setTokens()` → MMKV encrypted storage → navigate to onboarding |

### 1.2 Token Refresh

| Layer | Detail |
|-------|--------|
| **API** | `POST /auth/refresh` — body: `{ refresh_token }` |
| **DB Read** | `SELECT FROM refresh_tokens WHERE token = $1 AND revoked = false` |
| **DB Write** | Revoke old token, issue new pair (rotation) |
| **Redis** | Invalidate old session cache |

### 1.3 Onboarding Completion

**User Action:** Completes screens 2-9 (style selection, first Turn)

| Layer | Detail |
|-------|--------|
| **API** | `PATCH /me/profile` — body: `{ coaching_style, notification_time, onboarding_complete: true }` |
| **DB Write** | `UPDATE profiles SET coaching_style = $1, notification_time = $2, onboarding_complete = true` |
| **Push** | Schedule first notification via push service at chosen time |
| **Frontend** | `OnboardingStore.complete()` → navigate to main tab navigator |

---

## 2. The Turn (Reframing)

### 2.1 Submit a Turn

**User Action:** Types thought → taps "Turn it"

| Layer | Detail |
|-------|--------|
| **Frontend** | Validate non-empty input, show Turn animation (1.5s kaleidoscope rotation) |
| **API** | `POST /turns` — body: `{ input_text, coaching_style? }` |
| **Rate Check** | Redis: `INCR rate:turns:{user_id}:{date}` → reject if > 3 (free) or > 100 (prism) |
| **Entitlement** | `SELECT plan FROM subscriptions WHERE user_id = $1` → gate check |
| **Safety** | Deterministic keyword scan → if flagged: `INSERT INTO safety_events`, return crisis response |
| **AI Call** | AI Gateway → Claude Haiku 4.5: system prompt (coaching style + reframe instructions) + user input |
| **AI Response** | JSON: `{ perspectives: [{ style, text, emotion_before, emotion_after }], micro_action: { if_clause, then_clause }, fragments_detected: [{ type, phrase, confidence }] }` |
| **DB Write** | `INSERT INTO turns (id, user_id, input_text, perspectives, micro_action, fragments, emotion_vector, created_at)` |
| **DB Write** | `INSERT INTO ai_usage_events (user_id, feature, model, input_tokens, output_tokens, latency_ms, cost_usd)` |
| **Redis** | Increment daily counter, update streak cache |
| **Response** | `{ turn_id, perspectives, micro_action, fragments, pattern_seed }` |
| **Frontend** | Dismiss animation → render 3 perspective cards + micro-action card |

### 2.2 Save a Turn / Keepsake

**User Action:** Taps save on a perspective card

| Layer | Detail |
|-------|--------|
| **API** | `POST /turns/{id}/save` — body: `{ perspective_index, save_type: "keepsake" }` |
| **DB Write** | `UPDATE turns SET saved = true, saved_perspective_index = $1` |
| **DB Write** | `INSERT INTO evidence_wall_tiles (user_id, tile_type, source_feature, source_id, color_accent, created_at)` — type: `saved_keepsake` |
| **Response** | `{ saved: true, gallery_id, evidence_tile_id }` |
| **Frontend** | Success toast "Turn saved" → update Gallery cache via TanStack Query invalidation |

### 2.3 Get Turn History

| Layer | Detail |
|-------|--------|
| **API** | `GET /turns?limit=20&offset=0&date=2026-02-21` |
| **DB Read** | `SELECT id, input_text, perspectives, fragments, created_at FROM turns WHERE user_id = $1 ORDER BY created_at DESC LIMIT $2 OFFSET $3` |
| **Cache** | Redis: cache recent 20 turns per user, 5 min TTL |

---

## 3. The Mirror (Journaling + Fragment Detection)

### 3.1 Start Mirror Session

**User Action:** Opens Mirror tab → starts new session

| Layer | Detail |
|-------|--------|
| **API** | `POST /mirror/sessions` — body: `{ prompt_id? }` |
| **Rate Check** | Redis: `INCR rate:mirror:{user_id}:{week}` → reject if > 2 (free) |
| **DB Write** | `INSERT INTO mirror_sessions (id, user_id, status, started_at)` — status: `active` |
| **Response** | `{ session_id, opening_prompt }` |
| **Frontend** | Navigate to session view, show opening prompt in AI bubble |

### 3.2 Send Message in Mirror

**User Action:** Types and sends a message

| Layer | Detail |
|-------|--------|
| **Frontend** | Append user message to local state, show AI thinking animation |
| **API** | `POST /mirror/sessions/{id}/messages` — body: `{ content, message_type: "user" }` |
| **DB Write** | `INSERT INTO mirror_messages (id, session_id, role, content, created_at)` |
| **AI Call #1** | Fragment Detection: system prompt (10 distortion types + detection rules) + session context + new message |
| **AI Response #1** | `{ fragments: [{ type, phrase, start_index, end_index, confidence }] }` |
| **Entitlement Gate** | Free: filter to 3 types (catastrophizing, black_and_white, should_statements). Prism: all 10 |
| **DB Write** | `INSERT INTO mirror_fragments (id, session_id, message_id, distortion_type, phrase, start_idx, end_idx, confidence)` |
| **AI Call #2** | Reflective Response: system prompt (warm, non-directive, Mirror voice) + session history + detected fragments |
| **AI Response #2** | `{ response_text, suggested_prompts: [] }` |
| **DB Write** | `INSERT INTO mirror_messages (id, session_id, role: "assistant", content)` |
| **Response** | `{ message_id, ai_response, fragments: [{ type, phrase, indices }] }` |
| **Frontend** | Render AI response bubble, apply amber highlight underlines to user's message at fragment positions |

### 3.3 Tap Fragment Highlight → Inline Reframe

**User Action:** Taps highlighted text in their message

| Layer | Detail |
|-------|--------|
| **Frontend** | Open half-sheet modal with distortion info (local data from fragment detection response) |
| **API** | `POST /mirror/fragments/{id}/reframe` — body: `{ fragment_id }` |
| **Rate Check** | Free: 1 inline reframe per session |
| **AI Call** | Quick reframe: system prompt + fragment context + distortion type |
| **AI Response** | `{ reframes: [{ text, style }], can_turn: true }` |
| **Response** | `{ reframes, turn_prefill }` — `turn_prefill` is the fragment phrase ready for Turn |
| **Frontend** | Render reframes in half-sheet. "Take to Turn" button navigates to Turn tab with `input_text` pre-filled |

### 3.4 Close Mirror Session → Generate Reflection

**User Action:** Taps "End Session" or navigates away

| Layer | Detail |
|-------|--------|
| **API** | `POST /mirror/sessions/{id}/close` |
| **DB Read** | Fetch all messages + fragments for session |
| **AI Call** | Reflection generation: full session transcript + all fragments → themes, insight, pattern seed |
| **AI Response** | `{ themes: [], fragment_summary: { total, by_type }, insight: "string", pattern_seed: "hash" }` |
| **DB Write** | `UPDATE mirror_sessions SET status = "closed", reflection = $1, pattern_seed = $2, closed_at = NOW()` |
| **DB Write** | `INSERT INTO evidence_wall_tiles (user_id, tile_type: "mirror_reflection", source_id, ...)` |
| **Response** | `{ reflection, pattern_seed, fragment_summary }` |
| **Frontend** | Show reflection card with generated kaleidoscope pattern thumbnail → auto-saved to Gallery |

---

## 4. The Lens (Goals + Actions)

### 4.1 Create Goal

| Layer | Detail |
|-------|--------|
| **API** | `POST /lens/goals` — body: `{ title, description, target_date?, category? }` |
| **AI Call** | Goal refinement: make SMART, suggest metrics, generate initial visualization description |
| **DB Write** | `INSERT INTO lens_goals (id, user_id, title, description, target_date, visualization_text, status, created_at)` |
| **Response** | `{ goal_id, refined_title, visualization, suggested_actions }` |

### 4.2 Get Daily Actions + Affirmation

| Layer | Detail |
|-------|--------|
| **API** | `GET /lens/today` |
| **DB Read** | Active goals + incomplete actions + today's affirmation |
| **AI Call** (if no affirmation cached) | Generate daily affirmation based on active goals + recent progress |
| **Redis** | Cache today's affirmation, 24h TTL |
| **Response** | `{ goals: [...], today_actions: [...], affirmation: { text, goal_id } }` |

### 4.3 Complete Action

| Layer | Detail |
|-------|--------|
| **API** | `POST /lens/actions/{id}/complete` |
| **DB Write** | `UPDATE lens_actions SET completed = true, completed_at = NOW()` |
| **DB Write** | `INSERT INTO evidence_wall_tiles (tile_type: "completed_action", source_id, color_accent: "emerald")` |
| **Redis** | Update streak counter |
| **Response** | `{ completed: true, goal_progress_pct, streak_count, evidence_tile_id }` |

### 4.4 Start Rehearsal Session

**User Action:** On goal detail → taps "Rehearse"

| Layer | Detail |
|-------|--------|
| **API** | `POST /lens/goals/{id}/rehearsal` |
| **Rate Check** | Free: 1/week, Prism: unlimited |
| **DB Read** | Fetch goal details + user's recent Mirror/Turn data for personalization |
| **AI Call** | Visualization script generation: first-person, multi-sensory, process-oriented, obstacle rehearsal |
| **AI Response** | `{ script_segments: [{ text, duration_seconds, breathing_cue? }], total_duration }` |
| **DB Write** | `INSERT INTO rehearsal_sessions (id, user_id, goal_id, script, duration, created_at)` |
| **Response** | `{ session_id, script_segments, total_duration }` |
| **Frontend** | Enter Rehearsal mode: timer ring, text card sequence with breathing animation pacing |

### 4.5 Complete Rehearsal

| Layer | Detail |
|-------|--------|
| **API** | `POST /lens/rehearsals/{id}/complete` |
| **DB Write** | `UPDATE rehearsal_sessions SET completed = true, completed_at = NOW()` |
| **DB Write** | `INSERT INTO evidence_wall_tiles (tile_type: "rehearsal_complete", source_id, color_accent: "amethyst")` |
| **Response** | `{ completed: true, evidence_tile_id }` |
| **Frontend** | Success burst animation → navigate back to goal detail |

---

## 5. The Ritual (Daily Habit Sequences)

### 5.1 Start Ritual

**User Action:** Taps "Start Ritual" on Turn tab or from notification

| Layer | Detail |
|-------|--------|
| **API** | `POST /rituals/start` — body: `{ template: "morning" | "evening" | "quick" }` |
| **Rate Check** | Free: only `quick` template allowed |
| **DB Write** | `INSERT INTO ritual_sessions (id, user_id, template, status, started_at)` |
| **DB Read** | Fetch user's active goals, recent fragments, streak data for personalization |
| **AI Call** | Personalize ritual prompts based on user context |
| **Response** | `{ session_id, steps: [{ type, prompt, duration_seconds }] }` |
| **Frontend** | Enter Ritual mode: fragment-shaped step progress bar, step-by-step flow |

### 5.2 Ritual Step Completion

Each step may trigger its own API call:

| Step | API Calls |
|------|-----------|
| Mirror check-in | `POST /mirror/sessions` + `POST /mirror/sessions/{id}/messages` (lightweight, 1-2 exchanges) |
| Turn | `POST /turns` (with ritual context flag) |
| Lens review | `POST /lens/actions/{id}/complete` (for each completed action) |
| Affirmation | `GET /lens/today` (cached) |
| Gratitude | `POST /rituals/{id}/gratitude` — body: `{ text }` |

### 5.3 Complete Ritual

| Layer | Detail |
|-------|--------|
| **API** | `POST /rituals/{id}/complete` |
| **DB Write** | `UPDATE ritual_sessions SET status = "completed", completed_at = NOW()` |
| **DB Write** | `INSERT INTO evidence_wall_tiles (tile_type: "ritual_complete", color_accent: "amber")` |
| **Redis** | Update ritual streak: `INCR streak:ritual:{user_id}`, check context consistency (same time ± 30 min) |
| **DB Write** | `UPDATE profiles SET ritual_streak = $1, ritual_consistency_score = $2` |
| **Response** | `{ completed: true, streak_count, consistency_score, evidence_tile_id }` |
| **Frontend** | Prismatic ring completion animation → streak view |

---

## 6. Gallery

### 6.1 Get All Patterns

| Layer | Detail |
|-------|--------|
| **API** | `GET /gallery?view=all&limit=20&offset=0` |
| **DB Read** | `SELECT id, source_feature, pattern_seed, preview_text, created_at FROM gallery_items WHERE user_id = $1 ORDER BY created_at DESC` |
| **Cache** | Redis: cache first page, 2 min TTL |

### 6.2 Get Pattern Detail

| Layer | Detail |
|-------|--------|
| **API** | `GET /gallery/{id}` |
| **DB Read** | Full gallery item with source content, fragments, pattern seed |
| **Frontend** | Render hero kaleidoscope pattern (deterministic from seed), source content, metadata |

### 6.3 Search / Filter

| Layer | Detail |
|-------|--------|
| **API** | `GET /gallery/search?q=text&feature=turn&distortion=catastrophizing&from=2026-01-01` |
| **DB Read** | Full-text search on gallery content + filter joins |

### 6.4 Generate Pattern Card (Share)

| Layer | Detail |
|-------|--------|
| **API** | `POST /gallery/{id}/share` |
| **Backend** | Generate Pattern Card image: kaleidoscope pattern + reframe text overlay + Kalei watermark |
| **Response** | `{ share_url, image_url }` |
| **Frontend** | Native share sheet with generated image |

---

## 7. Evidence Wall

### 7.1 Get Evidence Wall

| Layer | Detail |
|-------|--------|
| **API** | `GET /evidence-wall?limit=50` |
| **DB Read** | `SELECT * FROM evidence_wall_tiles WHERE user_id = $1 ORDER BY created_at DESC` |
| **Entitlement** | Free: 30-day window (`WHERE created_at > NOW() - INTERVAL '30 days'`). Prism: all history |
| **Response** | `{ tiles: [...], connections: [...], stage: "empty" | "early" | "mid" | "full" }` |
| **Frontend** | Render mosaic view based on stage, assign tile shapes (diamond, hex, rect, pentagon, triangle) based on tile_type |

### 7.2 Get Tile Detail

| Layer | Detail |
|-------|--------|
| **API** | `GET /evidence-wall/tiles/{id}` |
| **DB Read** | Tile + source data (join to turns/mirror_sessions/lens_actions/ritual_sessions/rehearsal_sessions) |
| **Response** | `{ tile, source_content, source_feature, created_at }` |
| **Frontend** | Half-sheet with tile detail, source content, link to source feature |

### 7.3 Contextual Evidence Surfacing

**Trigger:** AI detects low self-efficacy language in Mirror or Turn

| Layer | Detail |
|-------|--------|
| **AI Detection** | During Mirror fragment detection or Turn processing, flag self-efficacy score < threshold |
| **DB Read** | `SELECT * FROM evidence_wall_tiles WHERE user_id = $1 ORDER BY relevance_score DESC LIMIT 2` |
| **Response** | Included in Mirror/Turn response: `{ evidence_nudge: { tiles: [...], message: "..." } }` |
| **Frontend** | Render gentle card below main content with 1-2 evidence tiles |

---

## 8. Spectrum (Phase 2)

### 8.1 Weekly Aggregation (Background Job)

| Layer | Detail |
|-------|--------|
| **Trigger** | Cron: Sunday 6pm UTC |
| **DB Read** | All turns + mirror_sessions + lens_actions for user's week |
| **AI Call** | Batch API (50% cost): analyze emotional vectors, fragment patterns, Turn impact |
| **DB Write** | `INSERT INTO spectrum_weekly (user_id, week_start, river_data, glass_data, impact_data, rhythm_data, growth_score, insight_text)` |
| **Push** | Notification: "Your weekly Spectrum insight is ready" |

### 8.2 Get Spectrum Dashboard

| Layer | Detail |
|-------|--------|
| **API** | `GET /spectrum/dashboard` |
| **Entitlement** | Free: simplified `{ weekly_insight_text, basic_fragment_count }`. Prism: full dashboard |
| **DB Read** | Latest weekly + monthly aggregates |
| **Response** | `{ river, glass, impact, rhythm, growth, weekly_insight, monthly_insight? }` |
| **Frontend** | Render 5 visualization components from spectrum-visualizations.svg data |

### 8.3 Monthly Deep Dive (Background Job)

| Layer | Detail |
|-------|--------|
| **Trigger** | Cron: 1st of month |
| **DB Read** | All weekly aggregates for the month |
| **AI Call** | Batch API: month-over-month narrative generation |
| **DB Write** | `INSERT INTO spectrum_monthly (user_id, month_start, narrative, growth_trajectory, milestone_events)` |

---

## 9. Billing & Entitlements

### 9.1 Entitlement Check (Middleware)

Every rate-limited endpoint runs this check:

```
1. Redis: GET entitlement:{user_id} → if cached, return
2. DB: SELECT plan, status FROM subscriptions WHERE user_id = $1 AND status = 'active'
3. Redis: SET entitlement:{user_id} = plan, EX 300 (5 min cache)
4. Return plan → middleware applies feature gates
```

### 9.2 App Store Webhook

| Layer | Detail |
|-------|--------|
| **API** | `POST /billing/webhooks/apple` |
| **Validation** | Verify Apple JWT signature |
| **DB Write** | `INSERT/UPDATE subscriptions SET plan = $1, status = $2, apple_transaction_id = $3` |
| **Redis** | Invalidate `entitlement:{user_id}` cache |
| **DB Write** | `INSERT INTO entitlement_snapshots (user_id, plan, event_type, timestamp)` |

### 9.3 Google Play Webhook

Same pattern as Apple, with Google-specific JWT validation and `google_purchase_token`.

---

## 10. Safety System

### 10.1 Crisis Detection Pipeline

Every AI-processed input runs through:

```
Stage 1: Deterministic keyword scan (regex, ~1ms)
  → If match: flag, skip AI, return crisis template

Stage 2: AI confirmation (during normal processing)
  → AI output includes safety_flag: boolean
  → If flagged: return hardcoded crisis response (never AI-generated)

Stage 3: Logging
  → INSERT INTO safety_events (user_id, input_hash, detection_stage, action_taken)
  → Alert: send to safety dashboard
```

### 10.2 Crisis Response

| Layer | Detail |
|-------|--------|
| **Response** | Hardcoded template: empathetic acknowledgment + 988 Suicide & Crisis Lifeline + Crisis Text Line |
| **UI** | Full-screen modal with prominent crisis resource links, "I'm OK" dismiss button |
| **Logging** | All crisis events logged, never the content itself |

---

## Database Schema Summary

### Core Tables

| Table | Key Columns | Indexes |
|-------|------------|---------|
| `users` | id (uuid), email, password_hash, created_at | email (unique) |
| `profiles` | user_id (FK), display_name, coaching_style, notification_time, onboarding_complete, ritual_streak, ritual_consistency_score | user_id (unique) |
| `subscriptions` | user_id (FK), plan (enum), status, started_at, expires_at, apple_transaction_id?, google_purchase_token? | user_id, status |
| `refresh_tokens` | id, user_id (FK), token_hash, revoked, expires_at | token_hash, user_id |

### Feature Tables

| Table | Key Columns | Indexes |
|-------|------------|---------|
| `turns` | id, user_id, input_text (encrypted), perspectives (jsonb), micro_action (jsonb), fragments (jsonb), emotion_vector (jsonb), saved, pattern_seed, created_at | user_id + created_at |
| `mirror_sessions` | id, user_id, status, reflection (jsonb), pattern_seed, started_at, closed_at | user_id + status |
| `mirror_messages` | id, session_id (FK), role, content (encrypted), created_at | session_id + created_at |
| `mirror_fragments` | id, session_id (FK), message_id (FK), distortion_type (enum), phrase, start_idx, end_idx, confidence | session_id, distortion_type |
| `lens_goals` | id, user_id, title, description, target_date, visualization_text, status, created_at | user_id + status |
| `lens_actions` | id, goal_id (FK), text, if_clause, then_clause, completed, completed_at | goal_id + completed |
| `rehearsal_sessions` | id, user_id, goal_id (FK), script (jsonb), duration, completed, started_at, completed_at | user_id + goal_id |
| `ritual_sessions` | id, user_id, template (enum), status, steps_completed (jsonb), started_at, completed_at | user_id + created_at |
| `gallery_items` | id, user_id, source_feature (enum), source_id, content_preview, pattern_seed, distortion_types (text[]), created_at | user_id + source_feature, full-text on content_preview |
| `evidence_wall_tiles` | id, user_id, tile_type (enum), source_feature, source_id, color_accent, metadata (jsonb), created_at | user_id + created_at, user_id + tile_type |

### Analytics Tables

| Table | Key Columns | Indexes |
|-------|------------|---------|
| `spectrum_weekly` | id, user_id, week_start, river_data (jsonb), glass_data (jsonb), impact_data (jsonb), rhythm_data (jsonb), growth_score, insight_text | user_id + week_start |
| `spectrum_monthly` | id, user_id, month_start, narrative (text), growth_trajectory (jsonb), milestone_events (jsonb) | user_id + month_start |
| `ai_usage_events` | id, user_id, feature, model, input_tokens, output_tokens, latency_ms, cost_usd, created_at | user_id + feature, created_at |
| `safety_events` | id, user_id, input_hash, detection_stage, action_taken, created_at | user_id, created_at |

### Enum Types

```sql
CREATE TYPE plan_type AS ENUM ('free', 'prism');
CREATE TYPE subscription_status AS ENUM ('active', 'expired', 'canceled', 'trial', 'grace_period');
CREATE TYPE distortion_type AS ENUM ('catastrophizing', 'black_and_white', 'mind_reading', 'fortune_telling', 'personalization', 'discounting_positives', 'emotional_reasoning', 'should_statements', 'labeling', 'overgeneralization');
CREATE TYPE tile_type AS ENUM ('saved_keepsake', 'mirror_reflection', 'completed_action', 'self_correction', 'streak_milestone', 'goal_completion', 'reframe_echo', 'rehearsal_complete', 'ritual_complete');
CREATE TYPE ritual_template AS ENUM ('morning', 'evening', 'quick');
CREATE TYPE source_feature AS ENUM ('turn', 'mirror', 'lens', 'rehearsal', 'ritual');
```

---

## Redis Key Patterns

| Key | Type | TTL | Purpose |
|-----|------|-----|---------|
| `rate:turns:{user_id}:{date}` | counter | 24h | Daily Turn rate limit |
| `rate:mirror:{user_id}:{week}` | counter | 7d | Weekly Mirror rate limit |
| `rate:rehearsal:{user_id}:{week}` | counter | 7d | Weekly Rehearsal rate limit |
| `entitlement:{user_id}` | string | 5 min | Cached subscription plan |
| `streak:daily:{user_id}` | hash | — | Current daily streak count + last active date |
| `streak:ritual:{user_id}` | hash | — | Ritual streak + consistency data |
| `affirmation:{user_id}:{date}` | string | 24h | Today's cached affirmation |
| `turns:recent:{user_id}` | list | 5 min | Cached recent turns |
| `session:{session_id}` | hash | 2h | Active Mirror session context |

---

## AI Gateway Call Patterns

### Cost Tiers

| Feature | Model | Est. Cost/Call | Caching |
|---------|-------|----------------|---------|
| Turn (reframe) | Claude Haiku 4.5 | ~$0.003 | System prompt cached (40% saving) |
| Mirror (fragment detection) | Claude Haiku 4.5 | ~$0.002 | System prompt cached |
| Mirror (reflective response) | Claude Haiku 4.5 | ~$0.003 | System prompt cached |
| Mirror (session reflection) | Claude Haiku 4.5 | ~$0.005 | — |
| Lens (goal refinement) | Claude Haiku 4.5 | ~$0.004 | — |
| Rehearsal (script gen) | Claude Haiku 4.5 | ~$0.008 | — |
| Ritual (personalization) | Claude Haiku 4.5 | ~$0.003 | System prompt cached |
| Spectrum (weekly) | Claude Batch API | ~$0.010 | Batch (50% off) |
| Spectrum (monthly) | Claude Batch API | ~$0.015 | Batch (50% off) |
| Safety (confirmation) | Included in feature call | $0 | Part of existing call |

### Prompt Template Versioning

All prompts stored as versioned templates: `prompts/{feature}/{version}.json`

| Prompt | Current Version |
|--------|----------------|
| turn_reframe | v1.0 |
| mirror_fragment_detect | v1.0 |
| mirror_reflect | v1.0 |
| mirror_session_close | v1.0 |
| lens_goal_refine | v1.0 |
| rehearsal_script | v1.0 |
| ritual_personalize | v1.0 |
| spectrum_weekly | v1.0 |
| spectrum_monthly | v1.0 |
| safety_check | v1.0 |

---

## Frontend Component Architecture

### Navigation

```
AppNavigator (Expo Router)
├── (auth)
│   ├── login.tsx
│   ├── register.tsx
│   └── onboarding/
│       ├── welcome.tsx
│       ├── metaphor.tsx
│       ├── turn-demo.tsx
│       ├── style-select.tsx
│       ├── notifications.tsx
│       └── first-turn.tsx
├── (tabs)
│   ├── turn/
│   │   ├── index.tsx          — Turn home + input
│   │   ├── results.tsx        — Turn results display
│   │   ├── ritual-select.tsx  — Ritual template picker
│   │   └── ritual-flow.tsx    — Active ritual flow
│   ├── mirror/
│   │   ├── index.tsx          — Session list + new session
│   │   └── session.tsx        — Active Mirror session
│   ├── lens/
│   │   ├── index.tsx          — Goal dashboard
│   │   ├── create-goal.tsx    — 6-step goal creation
│   │   ├── goal-detail.tsx    — Goal detail + actions
│   │   └── rehearsal.tsx      — Rehearsal session
│   ├── gallery/
│   │   ├── index.tsx          — Pattern grid + filters
│   │   └── detail.tsx         — Pattern detail + share
│   └── you/
│       ├── index.tsx          — Profile + stats
│       ├── evidence-wall.tsx  — Evidence Wall mosaic
│       ├── spectrum.tsx       — Spectrum dashboard
│       ├── settings.tsx       — App settings
│       └── subscription.tsx   — Plan management
└── (modals)
    ├── fragment-detail.tsx    — Half-sheet for fragment info
    ├── upgrade.tsx            — Prism upgrade prompt
    ├── crisis.tsx             — Crisis response (safety)
    ├── share-card.tsx         — Pattern card share sheet
    └── rate-limit.tsx         — Rate limit notice
```

### Shared Components

| Component | Usage | SVG Asset |
|-----------|-------|-----------|
| `FragmentIcon` | Everywhere — the core ◇ | `fragment-icons.svg` |
| `TabBar` | Main navigation | `icons-tab-bar.svg` |
| `DistortionBadge` | Mirror highlights, Gallery filters | `icons-distortions.svg` |
| `ActionIcon` | Buttons, list items, settings | `icons-actions.svg` |
| `KaleidoscopePattern` | Gallery, Turn result, share card | `patterns-kaleidoscope.svg` |
| `ProgressRing` | Lens goals, Rehearsal timer | `progress-indicators.svg` |
| `StepDots` | Lens 6-step, Ritual flow | `progress-indicators.svg` |
| `StreakCalendar` | Ritual tracking, You stats | `progress-indicators.svg` |
| `EvidenceMosaic` | Evidence Wall | `evidence-wall.svg` |
| `LoadingSpinner` | All loading states | `loading-animations.svg` |
| `SkeletonShimmer` | Data loading | `loading-animations.svg` |
| `TurnAnimation` | Turn processing | `loading-animations.svg` |
| `AIThinkingBubble` | Mirror AI processing | `loading-animations.svg` |
| `SuccessBurst` | Completion celebrations | `loading-animations.svg` |
| `BreathingLogo` | Splash, idle states | `loading-animations.svg` |
| `StatusBar` | Device chrome | `device-chrome.svg` |
| `NavHeader` | All screens | `device-chrome.svg` |
| `TabBarFrame` | Tab bar container | `device-chrome.svg` |
| `Toast` | Success/error feedback | `device-chrome.svg` |
| `InputAccessory` | Mirror, Turn text input | `device-chrome.svg` |
| `ShardCluster` | Empty states, backgrounds | `decorative-shards.svg` |
| `PrismaticDivider` | Section separators | `decorative-shards.svg` |
| `CornerAccent` | Card decorations | `decorative-shards.svg` |
| `SpectrumRiver` | Spectrum dashboard | `spectrum-visualizations.svg` |
| `SpectrumGlass` | Spectrum dashboard | `spectrum-visualizations.svg` |
| `SpectrumImpact` | Spectrum dashboard | `spectrum-visualizations.svg` |
| `SpectrumRhythm` | Spectrum dashboard | `spectrum-visualizations.svg` |
| `SpectrumGrowth` | Spectrum dashboard | `spectrum-visualizations.svg` |
