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" |
| 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" |
| 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
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
-- 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?" |