815 lines
38 KiB
Markdown
815 lines
38 KiB
Markdown
|
|
# 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:** DeepSeek V3.2 via OpenRouter (primary, hosted on DeepInfra/Fireworks US/EU), Claude Haiku 4.5 (automatic 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
|
||
|
|
|
||
|
|
### 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
|
||
|
|
├── guide/
|
||
|
|
│ ├── checkin.tsx — Check-in conversation (nested in Lens goal detail)
|
||
|
|
│ ├── checkin-summary.tsx — Post check-in summary
|
||
|
|
│ ├── bridge-card.tsx — Cross-feature bridge card (shared component)
|
||
|
|
│ ├── attention-prompt.tsx — Daily attention prompt card
|
||
|
|
│ ├── moment-log.tsx — Log a noticed moment
|
||
|
|
│ ├── evidence-card.tsx — Evidence intervention card (used in Mirror + Turn)
|
||
|
|
│ └── pulse/
|
||
|
|
│ ├── index.tsx — Weekly Pulse container (3-step flow)
|
||
|
|
│ ├── self-report.tsx — Step 1: self-report
|
||
|
|
│ ├── ai-read.tsx — Step 2: AI observations
|
||
|
|
│ └── next-focus.tsx — Step 3: next week focus
|
||
|
|
└── (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` |
|
||
|
|
| `GuideCard` | Bridge cards, evidence interventions, Guide notices | — (CSS-only prismatic border) |
|
||
|
|
| `GuideChat` | Check-in conversation interface | — (reuses Mirror chat styling) |
|
||
|
|
| `PulseScale` | Weekly Pulse self-report (5-point fragment scale) | `fragment-icons.svg` |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 8. The Guide — Technical Specification
|
||
|
|
|
||
|
|
### 8.1 Database Schema
|
||
|
|
|
||
|
|
```sql
|
||
|
|
-- Guide check-in conversations
|
||
|
|
CREATE TABLE guide_checkins (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
user_id UUID REFERENCES users(id),
|
||
|
|
goal_id UUID REFERENCES lens_goals(id),
|
||
|
|
started_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
ended_at TIMESTAMPTZ,
|
||
|
|
plan_adjustments JSONB, -- { old_plan, new_plan, reason }
|
||
|
|
evidence_surfaced JSONB, -- array of proof point IDs shown
|
||
|
|
summary TEXT,
|
||
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Guide check-in messages (conversation history)
|
||
|
|
CREATE TABLE guide_checkin_messages (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
checkin_id UUID REFERENCES guide_checkins(id),
|
||
|
|
role VARCHAR(10) NOT NULL CHECK (role IN ('guide', 'user')),
|
||
|
|
content TEXT NOT NULL,
|
||
|
|
sequence_order INTEGER,
|
||
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Cross-feature bridge events
|
||
|
|
CREATE TABLE guide_bridges (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
user_id UUID REFERENCES users(id),
|
||
|
|
bridge_type VARCHAR(20) NOT NULL CHECK (bridge_type IN ('discovery', 'reinforcement', 'integration')),
|
||
|
|
source_feature VARCHAR(20) NOT NULL, -- 'mirror', 'turn', 'lens'
|
||
|
|
target_feature VARCHAR(20), -- what feature the bridge suggests
|
||
|
|
trigger_data JSONB, -- { session_ids, turn_ids, theme, confidence }
|
||
|
|
bridge_content TEXT, -- the displayed bridge text
|
||
|
|
was_shown BOOLEAN DEFAULT FALSE,
|
||
|
|
was_acted_on BOOLEAN DEFAULT FALSE,
|
||
|
|
action_taken VARCHAR(50), -- 'opened_lens', 'started_rehearsal', 'dismissed', etc.
|
||
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Daily attention prompts
|
||
|
|
CREATE TABLE guide_attention_prompts (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
user_id UUID REFERENCES users(id),
|
||
|
|
goal_id UUID REFERENCES lens_goals(id),
|
||
|
|
prompt_type VARCHAR(20) NOT NULL CHECK (prompt_type IN ('vision', 'capability', 'awareness', 'action', 'reflection')),
|
||
|
|
manifestation_step INTEGER NOT NULL CHECK (manifestation_step BETWEEN 2 AND 6),
|
||
|
|
prompt_text TEXT NOT NULL,
|
||
|
|
delivered_at TIMESTAMPTZ,
|
||
|
|
was_acknowledged BOOLEAN DEFAULT FALSE,
|
||
|
|
moment_logged BOOLEAN DEFAULT FALSE,
|
||
|
|
moment_text TEXT,
|
||
|
|
moment_logged_at TIMESTAMPTZ,
|
||
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Evidence interventions
|
||
|
|
CREATE TABLE guide_evidence_interventions (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
user_id UUID REFERENCES users(id),
|
||
|
|
trigger_source VARCHAR(20) NOT NULL, -- 'mirror', 'turn'
|
||
|
|
trigger_session_id UUID, -- mirror_session_id or turn_id
|
||
|
|
trigger_signals JSONB, -- { signals detected: helplessness_language, etc. }
|
||
|
|
evidence_shown JSONB, -- array of proof point IDs surfaced
|
||
|
|
intervention_text TEXT,
|
||
|
|
was_shown BOOLEAN DEFAULT FALSE,
|
||
|
|
was_acted_on BOOLEAN DEFAULT FALSE,
|
||
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Weekly pulse check-ins
|
||
|
|
CREATE TABLE guide_pulses (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
user_id UUID REFERENCES users(id),
|
||
|
|
week_start DATE NOT NULL,
|
||
|
|
self_report_score INTEGER CHECK (self_report_score BETWEEN 1 AND 5),
|
||
|
|
self_report_text TEXT,
|
||
|
|
ai_observations JSONB, -- array of { observation, source, accent_color }
|
||
|
|
divergence_note TEXT, -- when self-report != AI read
|
||
|
|
next_week_focus JSONB, -- array of { suggestion, feature, reasoning }
|
||
|
|
completed_at TIMESTAMPTZ,
|
||
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
UNIQUE(user_id, week_start)
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 8.2 API Endpoints
|
||
|
|
|
||
|
|
| Endpoint | Method | Description | Auth | Rate Limit |
|
||
|
|
|----------|--------|-------------|------|------------|
|
||
|
|
| `/guide/checkins/:goalId` | GET | Get check-in history for a goal | JWT | — |
|
||
|
|
| `/guide/checkins/:goalId/start` | POST | Start a new check-in conversation | JWT | Free: 1/mo/goal, Prism: unlimited |
|
||
|
|
| `/guide/checkins/:checkinId/message` | POST | Send a message in check-in conversation | JWT | — |
|
||
|
|
| `/guide/checkins/:checkinId/complete` | POST | End check-in, generate summary | JWT | — |
|
||
|
|
| `/guide/bridges` | GET | Get pending bridge cards for user | JWT | — |
|
||
|
|
| `/guide/bridges/:id/action` | POST | Record bridge action (shown, acted_on, dismissed) | JWT | — |
|
||
|
|
| `/guide/bridges/scan` | POST | Trigger cross-feature pattern scan (background job) | JWT | Auto: daily |
|
||
|
|
| `/guide/prompts/today` | GET | Get today's attention prompt | JWT | — |
|
||
|
|
| `/guide/prompts/:id/acknowledge` | POST | Mark prompt as seen | JWT | — |
|
||
|
|
| `/guide/prompts/:id/log-moment` | POST | Log a noticed moment | JWT | — |
|
||
|
|
| `/guide/prompts/generate` | POST | Generate next prompt (background job) | JWT | Auto: daily |
|
||
|
|
| `/guide/evidence/check` | POST | Check for evidence intervention triggers | JWT | Auto: after Mirror/Turn |
|
||
|
|
| `/guide/evidence/:id/action` | POST | Record intervention action | JWT | — |
|
||
|
|
| `/guide/pulse/current` | GET | Get current week's pulse (or create if needed) | JWT | — |
|
||
|
|
| `/guide/pulse/:id/self-report` | POST | Submit self-report score + text | JWT | 1/week |
|
||
|
|
| `/guide/pulse/:id/ai-read` | GET | Generate AI observations for the week | JWT | Prism only |
|
||
|
|
| `/guide/pulse/:id/focus` | GET | Generate next-week focus suggestions | JWT | Prism only |
|
||
|
|
| `/guide/pulse/:id/complete` | POST | Mark pulse as complete | JWT | — |
|
||
|
|
|
||
|
|
### 8.3 AI Pipeline
|
||
|
|
|
||
|
|
The Guide requires a cross-feature AI analysis pipeline that differs from single-feature calls:
|
||
|
|
|
||
|
|
**Cross-Feature Context Window:**
|
||
|
|
|
||
|
|
For check-ins, bridges, and the weekly pulse, the AI needs context from multiple features simultaneously:
|
||
|
|
|
||
|
|
```
|
||
|
|
Guide AI Context = {
|
||
|
|
user_profile: { coaching_style, member_since, preferences },
|
||
|
|
active_goals: [ { goal, milestones, if_then_plans, progress } ],
|
||
|
|
recent_mirror_sessions: [ last 7 days — themes, fragment types, emotional tone ],
|
||
|
|
recent_turns: [ last 7 days — topics, distortions, saved keepsakes ],
|
||
|
|
evidence_wall: [ last 30 days — proof points by type and source ],
|
||
|
|
ritual_data: { streak, consistency_score, last_completed },
|
||
|
|
previous_checkins: [ last 2 check-ins per goal — summaries only ],
|
||
|
|
previous_pulse: { last week's scores and focus areas }
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Token estimate for cross-feature context:** ~1,500-2,000 input tokens. With prompt caching (system prompt + stable user context cached), effective billable input drops to ~800-1,200 tokens per Guide call.
|
||
|
|
|
||
|
|
**Background Jobs:**
|
||
|
|
|
||
|
|
| Job | Schedule | Purpose | AI Model |
|
||
|
|
|-----|----------|---------|----------|
|
||
|
|
| Bridge Pattern Scan | Daily (2am user-local) | Analyze 7-day Mirror/Turn window for cross-feature patterns | Haiku 4.5 Batch |
|
||
|
|
| Attention Prompt Generation | Daily (5am user-local) | Generate personalized prompt based on goal progress and manifestation step | Haiku 4.5 (cached) |
|
||
|
|
| Evidence Trigger Check | After each Mirror session / Turn | Check session language for self-efficacy dip signals | Haiku 4.5 (inline, fast) |
|
||
|
|
| Weekly Pulse AI Read | On pulse open (lazy) | Generate weekly observations from all feature data | Haiku 4.5 |
|
||
|
|
|
||
|
|
**Prompt Engineering Notes:**
|
||
|
|
|
||
|
|
The Guide's system prompt must enforce:
|
||
|
|
- Evidence-first framing (always lead with what went well)
|
||
|
|
- Specific data references (numbers, dates, not vague encouragement)
|
||
|
|
- Collaborative tone (propose adjustments, don't dictate)
|
||
|
|
- Forward momentum (always end with next action)
|
||
|
|
- Cross-feature awareness (reference Mirror/Turn patterns when coaching on Lens goals)
|
||
|
|
- The user's selected coaching style (brutal honesty, gentle guidance, logical analysis, etc.)
|
||
|
|
|
||
|
|
### 8.4 Frontend Component Tree
|
||
|
|
|
||
|
|
```
|
||
|
|
screens/guide/
|
||
|
|
├── GuideCheckinScreen.tsx — Check-in conversation (within Lens goal detail)
|
||
|
|
├── GuideCheckinSummary.tsx — Post check-in summary card
|
||
|
|
├── GuideBridgeCard.tsx — Reusable bridge card component (3 variants)
|
||
|
|
├── GuideAttentionPrompt.tsx — Daily prompt card (within Lens dashboard)
|
||
|
|
├── GuideMomentLog.tsx — Log a noticed moment screen
|
||
|
|
├── GuideEvidenceCard.tsx — Evidence intervention card (used in Mirror + Turn)
|
||
|
|
├── GuidePulseScreen.tsx — 3-step weekly pulse flow
|
||
|
|
│ ├── PulseSelfReport.tsx — Step 1: self-report scale
|
||
|
|
│ ├── PulseAIRead.tsx — Step 2: AI observations
|
||
|
|
│ └── PulseNextFocus.tsx — Step 3: next week suggestions
|
||
|
|
└── shared/
|
||
|
|
├── GuideBorder.tsx — Prismatic gradient border component
|
||
|
|
├── GuideIcon.tsx — Faceted diamond with directional pulse
|
||
|
|
└── FragmentScale.tsx — 5-point fragment glow scale (for Pulse)
|
||
|
|
```
|
||
|
|
|
||
|
|
### 8.5 Rate Limiting (Guide-Specific)
|
||
|
|
|
||
|
|
| Feature | Free | Prism |
|
||
|
|
|---------|------|-------|
|
||
|
|
| Goal check-ins | `rate:guide:checkin:{userId}:{goalId}` — 1/month | Unlimited |
|
||
|
|
| Cross-feature bridges | Discovery only. `rate:guide:bridge:{userId}` — 1/day | All types, 1/day |
|
||
|
|
| Attention prompts | `rate:guide:prompt:{userId}` — 3/week | Daily |
|
||
|
|
| Evidence interventions | Blocked (Prism feature) | `rate:guide:evidence:{userId}` — 1/session |
|
||
|
|
| Weekly pulse | Self-report only | Full 3-step with AI read |
|
||
|
|
|
||
|
|
### 8.6 Push Notifications (Guide-Specific)
|
||
|
|
|
||
|
|
| Notification | Trigger | Copy |
|
||
|
|
|---|---|---|
|
||
|
|
| Check-in reminder | Scheduled per goal settings | "Time to check in on [goal name]. How's it going?" |
|
||
|
|
| Attention prompt | Daily at user's chosen time | "Your Lens has a focus for today." |
|
||
|
|
| Bridge surfaced | After daily bridge scan finds match | "◇ A pattern is forming. Take a look." |
|
||
|
|
| Weekly pulse | User's chosen day (default Sunday 7pm) | "Your weekly Pulse is ready." |
|
||
|
|
| Moment log reminder | 6 hours after prompt acknowledgment | "Did you notice anything today?" |
|