kalei/docs/technical/kalei-architecture-diagrams.md

21 KiB

Kalei — Architecture & User Journey Diagrams

All diagrams below are valid Mermaid syntax. Paste into any Mermaid renderer (mermaid.live, GitHub markdown, VS Code preview) to visualize.


1. Complete User Journey

Shows the full lifecycle from app discovery through onboarding, daily habit loops, conversion, and long-term retention.

flowchart LR
    subgraph Acquisition["Acquisition"]
        A1["App Store Discovery"] --> A2["Download App"]
        A2 --> A3["Open for First Time"]
    end

    subgraph Onboarding["Onboarding Flow"]
        A3 --> O1["Welcome Screen"]
        O1 --> O2["3 Swipeable Intro Cards"]
        O2 --> O3["Choose Reframe Style\n(Brutal / Gentle / Logical / Philosophical / Humor)"]
        O3 --> O4["Set Daily Check-in Time"]
        O4 --> O5["Create Account\n(Email / Google / Apple)"]
        O5 --> O6["First Reframe — WOW Moment"]
        O6 --> O7["Land on Home Screen"]
    end

    subgraph DailyLoop["Daily Habit Loop"]
        O7 --> D1["Push Notification\n'What's weighing on you?'"]
        D1 --> D2{"User Intent"}
        D2 -->|"Quick reframe"| T1["The Turn\n(Kaleidoscope)"]
        D2 -->|"Need to process"| M1["The Mirror\n(Freeform Writing)"]
        D2 -->|"Goal focus"| L1["The Lens\n(Manifestation)"]
        D2 -->|"Review history"| G1["The Gallery"]
        T1 --> D3["Save Reframe / Keepsake"]
        M1 --> D3
        L1 --> D3
        D3 --> D4["Streak Updated"]
        D4 --> D5["Return Tomorrow"]
        D5 --> D1
    end

    subgraph Deepening["Engagement Deepening"]
        G1 --> E1["See Thought Patterns Over Time"]
        E1 --> E2["Weekly Summary\n(Sunday Push)"]
        E2 --> E3["Discover Recurring Fragments"]
        E3 --> E4["Motivation to Continue"]
        E4 --> D1
    end

    subgraph Conversion["Free → Premium"]
        D4 --> C1{"Hit Free Tier Limit?"}
        C1 -->|"Yes"| C2["Soft Paywall\n'Unlock unlimited Turns'"]
        C2 --> C3{"Convert?"}
        C3 -->|"Yes"| C4["Subscribe to Prism\n($4.99/mo)"]
        C3 -->|"Not yet"| D5
        C4 --> C5["Full Access Unlocked"]
        C5 --> D1
    end

    subgraph Retention["Long-Term Retention"]
        C5 --> R1["Spectrum Dashboard\n(Prism+)"]
        R1 --> R2["Monthly AI Insights"]
        R2 --> R3["Growth Trajectory"]
        R3 --> R4["Deep Self-Knowledge"]
        R4 --> D1
    end

2. Backend System Architecture

The full backend from client through edge, API services, AI layer, data stores, and external integrations.

flowchart TB
    subgraph Client["Client Layer"]
        APP["React Native + Expo App"]
        TURN_UI["Turn Screen"]
        MIRROR_UI["Mirror Screen"]
        LENS_UI["Lens Screen"]
        GALLERY_UI["Gallery Screen"]
        SPECTRUM_UI["Spectrum Dashboard"]
        PROFILE_UI["Profile & Settings"]
    end

    subgraph Edge["Edge Layer"]
        CF["Cloudflare\nDNS / CDN / DDoS"]
        NGINX["Nginx\nReverse Proxy / SSL / Rate Limit"]
    end

    subgraph API["API Layer — Modular Monolith (Fastify)"]
        GW["API Gateway & Auth\n(JWT + Refresh Rotation)"]
        TURN_SVC["Turn Service"]
        MIRROR_SVC["Mirror Service"]
        LENS_SVC["Lens Service"]
        SPECTRUM_SVC["Spectrum Service"]
        SAFETY_SVC["Safety Service\n(Crisis Detection)"]
        ENT_SVC["Entitlement Service\n(Plan Gating)"]
        COST_SVC["Usage Meter &\nCost Guard"]
        JOBS["Job Scheduler\n& Workers"]
        NOTIF["Notification Service"]
    end

    subgraph AI["AI Layer (via OpenRouter Gateway)"]
        AI_GW["AI Gateway\n(OpenRouter Provider Routing)"]
        DEEPSEEK["DeepSeek V3.2\nvia DeepInfra/Fireworks\n(US/EU — Primary)"]
        CLAUDE_FALLBACK["Claude Haiku 4.5\n(Automatic Fallback)"]
    end

    subgraph Data["Data Layer"]
        PG["PostgreSQL 16\n(Source of Truth)"]
        REDIS["Redis\n(Cache / Rate Limits / Counters)"]
        OBJ["Object Storage\n(Spectrum Exports)"]
    end

    subgraph External["External Services"]
        APPLE["Apple App Store\nBilling API"]
        GOOGLE["Google Play\nBilling API"]
        APNS["APNs"]
        FCM["FCM"]
        POSTHOG["PostHog\n(Self-Hosted Analytics)"]
        GLITCHTIP["GlitchTip\n(Error Tracking)"]
    end

    APP --> CF --> NGINX --> GW
    GW --> TURN_SVC
    GW --> MIRROR_SVC
    GW --> LENS_SVC
    GW --> SPECTRUM_SVC
    GW --> ENT_SVC

    TURN_SVC --> SAFETY_SVC
    MIRROR_SVC --> SAFETY_SVC
    LENS_SVC --> SAFETY_SVC

    TURN_SVC --> AI_GW
    MIRROR_SVC --> AI_GW
    LENS_SVC --> AI_GW
    SPECTRUM_SVC --> AI_GW

    AI_GW --> DEEPSEEK
    AI_GW --> CLAUDE_FALLBACK

    TURN_SVC --> COST_SVC
    MIRROR_SVC --> COST_SVC
    LENS_SVC --> COST_SVC
    COST_SVC --> REDIS

    TURN_SVC --> PG
    MIRROR_SVC --> PG
    LENS_SVC --> PG
    SPECTRUM_SVC --> PG
    ENT_SVC --> PG
    JOBS --> PG

    ENT_SVC --> APPLE
    ENT_SVC --> GOOGLE
    NOTIF --> APNS
    NOTIF --> FCM
    GW --> POSTHOG
    GW --> GLITCHTIP

3. The Mirror (Awareness) — Sequence Diagram

Complete sequence from session start through writing, fragment detection, inline reframing, and session reflection.

sequenceDiagram
    participant U as User
    participant App as Mobile App
    participant API as Kalei API
    participant Safety as Safety Service
    participant Ent as Entitlement Service
    participant AI as AI Gateway
    participant Model as DeepSeek V3.2 via OpenRouter
    participant DB as PostgreSQL
    participant R as Redis

    Note over U,R: Session Start
    U->>App: Opens Mirror tab
    App->>API: POST /mirror/sessions
    API->>Ent: Check plan (free: 2/week, prism: unlimited)
    Ent->>R: Read session counter
    R-->>Ent: Counter value
    Ent-->>API: Allowed / Denied
    API->>DB: Create mirror_session row
    API-->>App: Session ID + empty state prompt

    Note over U,R: Writing & Fragment Detection Loop
    U->>App: Writes message freely
    App->>API: POST /mirror/messages
    API->>Safety: Crisis precheck on text
    alt Crisis Detected
        Safety->>DB: Log safety_event
        API-->>App: Crisis resources (hotlines, warm message)
    else Safe Content
        API->>AI: Fragment detection prompt + user text
        AI->>Model: Inference request (cached system prompt)
        Model-->>AI: JSON with fragments + confidence scores
        AI-->>API: Validated structured result
        API->>DB: Save message + fragments (confidence > 0.75)
        API->>R: Increment usage counters
        API-->>App: Message with highlighted fragments (amber glow + ◇ icons)
    end

    Note over U,R: User Taps a Fragment
    U->>App: Taps highlighted fragment ◇
    App->>API: POST /mirror/fragments/{id}/reframe
    API->>AI: Reframe prompt + fragment + surrounding context
    AI->>Model: Inference request
    Model-->>AI: Reframe + distortion explanation
    AI-->>API: Validated reframe response
    API->>DB: Update fragment (was_tapped, was_reframed, reframe_text)
    API-->>App: Mini-card slides up with reframe
    App-->>U: Shows pattern name + alternative angle + Full Turn option

    Note over U,R: Session Close & Reflection
    U->>App: Presses Done / closes Mirror
    App->>API: POST /mirror/sessions/{id}/close
    API->>AI: Generate Reflection from all messages + fragments
    AI->>Model: Batch summary request
    Model-->>AI: Mosaic themes + fragment count + insight
    AI-->>API: Reflection payload
    API->>DB: Update session with reflection + pattern_seed
    API-->>App: Reflection card (Mosaic + fragments found + patterns + one-line insight)
    App-->>U: Reflection saved to Gallery

4. The Turn (Kaleidoscope) — Sequence Diagram

The structured reframing flow with entitlement gating, safety checks, and multi-perspective AI generation.

sequenceDiagram
    participant U as User
    participant App as Mobile App
    participant API as Kalei API
    participant Ent as Entitlement Service
    participant Safety as Safety Service
    participant AI as AI Gateway
    participant Model as DeepSeek V3.2 via OpenRouter
    participant DB as PostgreSQL
    participant Cost as Cost Guard
    participant R as Redis

    Note over U,R: User Submits a Fragment for Turning
    U->>App: Types negative thought + selects style
    App->>API: POST /turns {text, style, context}

    Note over API,R: Validation & Gating
    API->>Ent: Validate tier + daily Turn cap
    Ent->>R: Check daily counter (free: 3/day)
    R-->>Ent: Current count
    Ent-->>API: Allowed / Limit reached

    alt Limit Reached
        API-->>App: Soft paywall prompt
        App-->>U: "Unlock unlimited Turns with Prism"
    else Allowed
        Note over API,R: Safety Gate
        API->>Safety: Crisis precheck on text
        alt Crisis Detected
            Safety->>DB: Log safety_event
            API-->>App: Crisis resources response
            App-->>U: Hotline numbers + warm message
        else Safe Content
            Note over API,R: AI Reframe Generation
            API->>AI: Build prompt (cached system + user style + fragment + history context)
            AI->>Model: Streaming inference request
            Model-->>AI: 3 reframe perspectives + micro-action (if-then)
            AI-->>API: Validated structured response + token count

            Note over API,R: Record & Respond
            API->>Cost: Record token usage + budget check
            Cost->>R: Update per-user + global counters
            API->>DB: Save turn + reframes + metadata
            API-->>App: Stream final Turn result

            Note over U,App: User Sees the Turn Card
            App-->>U: Original fragment quoted
            App-->>U: 3 perspective reframes in chosen style
            App-->>U: Micro-action (Gollwitzer if-then)
            App-->>U: "Why This Works" expandable science drawer

            Note over U,App: Post-Turn Actions
            U->>App: Save as Keepsake / Try Different Style / Share
            App->>API: POST /turns/{id}/save
            API->>DB: Mark as saved keepsake
            API-->>App: Streak counter updated
        end
    end

5. The Lens (Manifestation Engine) — 6-Step Flow

The complete goal creation and daily action system mapped to the 6 research-backed steps.

flowchart TB
    subgraph Step1["Step 1: DECIDE — Clarity"]
        S1A["User taps 'Create a Manifestation'"] --> S1B["AI Conversation (3-5 exchanges)\n'What do you want to achieve?'"]
        S1B --> S1C["SMART Goal Refinement"]
        S1C --> S1D["Output: Clarity Statement Card"]
    end

    subgraph Step2["Step 2: SEE IT — Mental Rehearsal"]
        S1D --> S2A["AI generates personalized\nvisualization script"]
        S2A --> S2B["First-person, sensory-rich,\nprocess-focused imagery"]
        S2B --> S2C["User reads/listens\nMarks as complete"]
        S2C --> S2D["Output: Vision Summary\n(revisitable daily)"]
    end

    subgraph Step3["Step 3: BELIEVE — Self-Efficacy"]
        S2D --> S3A["AI asks:\n'What makes you doubt\nthis is possible?'"]
        S3A --> S3B["User lists doubts"]
        S3B --> S3C["AI addresses each with:\npast successes + transferable skills\n+ role models + small wins"]
        S3C --> S3D["Output: Belief Statement Card\n'You CAN — here's the evidence'"]
    end

    subgraph Step4["Step 4: NOTICE — Attention Training"]
        S3D --> S4A["AI sets up daily\nattention prompts"]
        S4A --> S4B["Daily push:\n'Notice one thing aligned\nwith your goal today'"]
        S4B --> S4C["User logs observations\nBuilds Evidence Journal"]
        S4C --> S4D["AI surfaces patterns:\n'23 alignment instances\nthis month'"]
    end

    subgraph Step5["Step 5: ACT — Implementation Intentions"]
        S4D --> S5A["AI generates weekly\nmicro-actions"]
        S5A --> S5B["Gollwitzer if-then format:\n'If [time/situation],\nthen I will [15-min action]'"]
        S5B --> S5C["User checks off\ncompleted actions"]
        S5C --> S5D["AI adapts difficulty:\n2-min actions → scales up\nas habit solidifies"]
    end

    subgraph Step6["Step 6: REPEAT — Compound"]
        S5D --> S6A["Habit Tracking Dashboard"]
        S6A --> S6B["Visual growth charts\nMilestone celebrations"]
        S6B --> S6C["Weekly AI Summary:\nreframes + patterns +\nprogress + adjustments"]
        S6C --> S6D["Celebrates PROCESS\nnot just outcomes"]
        S6D --> S4B
    end

6. AI Processing Pipeline & Cost Routing

How every AI request flows through the gateway with prompt caching, safety guards, and cost-aware provider routing.

flowchart LR
    subgraph Input["Request Sources"]
        MIRROR["Mirror\n(Fragment Detection)"]
        TURN["Turn\n(3 Reframes)"]
        LENS["Lens\n(Affirmations)"]
        SPECTRUM["Spectrum\n(Weekly Insights)"]
    end

    subgraph Pipeline["AI Gateway Pipeline"]
        PROMPT["Prompt Builder\n(System + User Context + Research Grounding)"]
        CACHE["Prompt Cache Check\n(~20% savings on cached system prompts)"]
        SAFETY_CHECK["Anti-Toxicity Guard\n(No toxic positivity / magical thinking)"]
        ROUTER{"Cost Router\nBudget Check"}
    end

    subgraph PrimaryLane["Primary Lane (via OpenRouter)"]
        DEEPSEEK_RT["DeepSeek V3.2\nvia DeepInfra/Fireworks\n($0.26/$0.38 per MTok)"]
    end

    subgraph FallbackLane["Fallback Lane (Auto-Failover)"]
        CLAUDE_FB["Claude Haiku 4.5\n(Automatic on provider outage)"]
    end

    subgraph TemplateLane["Template Fallback (Budget Pressure)"]
        TEMPLATE["Local Template System\n(Pre-written, no AI cost)"]
    end

    subgraph Output["Response Handling"]
        VALIDATE["Structured Output\nValidation (JSON)"]
        TOKEN_LOG["Token Usage Logger"]
        BUDGET["Budget Tracker\n(Per-user daily + Global monthly)"]
        RESPONSE["Stream Response\nto Client"]
    end

    subgraph Alerts["Cost Controls"]
        ALERT50["50% Budget Alert"]
        ALERT80["80% Budget Alert\nDegrade Lens to templates"]
        ALERT95["95% Budget Alert\nPause Spectrum generation"]
        HARDCAP["100% Hard Cap\nGraceful service message"]
    end

    MIRROR --> PROMPT
    TURN --> PROMPT
    LENS --> PROMPT
    SPECTRUM --> PROMPT

    PROMPT --> CACHE
    CACHE --> SAFETY_CHECK
    SAFETY_CHECK --> ROUTER

    ROUTER -->|"All features\n(primary)"| DEEPSEEK_RT
    ROUTER -->|"Provider outage\n(auto-failover)"| CLAUDE_FB
    ROUTER -->|"Lens / basic content\n(budget pressure)"| TEMPLATE

    DEEPSEEK_RT --> VALIDATE
    CLAUDE_FB --> VALIDATE
    TEMPLATE --> VALIDATE

    VALIDATE --> TOKEN_LOG
    TOKEN_LOG --> BUDGET
    BUDGET --> RESPONSE
    BUDGET --> ALERT50
    BUDGET --> ALERT80
    BUDGET --> ALERT95
    BUDGET --> HARDCAP

7. Safety & Crisis Detection Flow

The multi-stage safety pipeline that ensures crisis content is never reframed.

flowchart TB
    subgraph Input["User Input Arrives"]
        MSG["User text from\nMirror / Turn / Lens"]
    end

    subgraph Stage1["Stage 1: Deterministic Keyword Scan"]
        KW["Keyword & Pattern Matcher\n(Regex-based, zero latency)"]
        KW_YES{"Crisis keywords\ndetected?"}
    end

    subgraph Stage2["Stage 2: AI Confirmation"]
        AI_CHECK["Low-latency AI model\nconfirms severity"]
        AI_YES{"Confirmed\ncrisis?"}
    end

    subgraph CrisisResponse["Crisis Response — NEVER Reframed"]
        CR1["Hardcoded crisis template\n(Not AI-generated)"]
        CR2["Display local hotline numbers\n(988 Lifeline, Crisis Text Line)"]
        CR3["Warm handoff message:\n'You matter. Help is available\nright now.'"]
        CR4["Log safety_event to DB\nfor monitoring"]
    end

    subgraph NormalFlow["Normal Processing"]
        PROCEED["Continue to AI Gateway\nfor reframing / detection"]
    end

    subgraph Monitoring["Safety Monitoring"]
        FP["Track false positives\nfor filter tuning"]
        FN["Track false negatives\nvia user reports"]
        REVIEW["Weekly safety review loop"]
    end

    MSG --> KW
    KW --> KW_YES

    KW_YES -->|"Yes — high confidence\n(explicit phrases)"| CR1
    KW_YES -->|"Maybe — ambiguous"| AI_CHECK
    KW_YES -->|"No"| PROCEED

    AI_CHECK --> AI_YES
    AI_YES -->|"Yes"| CR1
    AI_YES -->|"No — false alarm"| PROCEED

    CR1 --> CR2
    CR2 --> CR3
    CR3 --> CR4

    PROCEED --> FP
    CR4 --> FN
    FP --> REVIEW
    FN --> REVIEW

8. Subscription & Billing State Machine

All user states from anonymous through free, Prism, and Prism+ with renewal, grace period, and downgrade flows.

stateDiagram-v2
    [*] --> Anonymous: App Downloaded

    Anonymous --> FreeUser: Account Created

    state FreeUser {
        [*] --> ActiveFree
        ActiveFree --> LimitHit: Daily/weekly cap reached
        LimitHit --> ActiveFree: Next day/week resets
        LimitHit --> PaywallShown: Soft upgrade prompt
    }

    state PrismSubscriber {
        [*] --> ActivePrism
        ActivePrism --> PrismRenewal: Monthly renewal
        PrismRenewal --> ActivePrism: Payment success
        PrismRenewal --> GracePeriod: Payment failed
        GracePeriod --> ActivePrism: Retry success
        GracePeriod --> Expired: Grace period ends
    }

    state PrismPlusSubscriber {
        [*] --> ActivePrismPlus
        ActivePrismPlus --> PlusMRenewal: Monthly renewal
        PlusMRenewal --> ActivePrismPlus: Payment success
        PlusMRenewal --> PlusGrace: Payment failed
        PlusGrace --> ActivePrismPlus: Retry success
        PlusGrace --> PlusExpired: Grace period ends
    }

    FreeUser --> PrismSubscriber: Subscribe $4.99/mo
    FreeUser --> PrismPlusSubscriber: Subscribe $9.99/mo
    PrismSubscriber --> PrismPlusSubscriber: Upgrade
    PrismPlusSubscriber --> PrismSubscriber: Downgrade

    PrismSubscriber --> FreeUser: Cancel / Expired
    PrismPlusSubscriber --> FreeUser: Cancel / PlusExpired

    state EntitlementCheck {
        [*] --> CheckPlan
        CheckPlan --> FreeTier: No active subscription
        CheckPlan --> PrismTier: Active Prism
        CheckPlan --> PrismPlusTier: Active Prism+
        FreeTier --> ApplyLimits: 3 Turns/day, 2 Mirror/week
        PrismTier --> FullAccess: Unlimited Turns + Mirror
        PrismPlusTier --> FullPlusAccess: Full + Spectrum + Insights
    }

9. Data Entity Relationship Model

All database domains showing how Users connect to every feature, commerce, and ops table.

flowchart LR
    subgraph Identity["Identity Domain"]
        USERS["USERS\nid, email, password_hash"]
        PROFILES["PROFILES\nuser_id, display_name\nreframe_style, checkin_time"]
        AUTH["AUTH_SESSIONS\nid, user_id, device_info"]
        REFRESH["REFRESH_TOKENS\nid, user_id, token_hash"]
    end

    subgraph Mirror["Mirror Domain"]
        M_SESS["MIRROR_SESSIONS\nid, user_id, started_at\nreflection_summary"]
        M_MSG["MIRROR_MESSAGES\nid, session_id\ncontent (encrypted)"]
        M_FRAG["MIRROR_FRAGMENTS\nid, message_id\ndistortion_type, confidence\nwas_tapped, reframe_text"]
    end

    subgraph Turn["Turn Domain"]
        TURNS["TURNS\nid, user_id, input_text\nreframe_style, reframes\nmicro_action, is_saved"]
    end

    subgraph Lens["Lens Domain"]
        GOALS["LENS_GOALS\nid, user_id, goal_text\nclarity_statement\nbelief_statement"]
        ACTIONS["LENS_ACTIONS\nid, goal_id, action_text\nif_then_format, completed"]
    end

    subgraph Spectrum["Spectrum"]
        S_WEEK["SPECTRUM_WEEKLY\nuser_id, week_start\naggregates, insight"]
        S_MONTH["SPECTRUM_MONTHLY\nuser_id, month\ngrowth_trajectory"]
    end

    subgraph Commerce["Commerce"]
        SUBS["SUBSCRIPTIONS\nid, user_id, plan\nstore, status, expires_at"]
    end

    subgraph Ops["Safety and Ops"]
        SAFE["SAFETY_EVENTS\nid, user_id, trigger_text\ndetection_stage"]
        AI_USE["AI_USAGE_EVENTS\nid, user_id, feature\nmodel, tokens, cost"]
    end

    USERS --> PROFILES
    USERS --> AUTH
    USERS --> REFRESH
    USERS --> M_SESS
    M_SESS --> M_MSG
    M_MSG --> M_FRAG
    USERS --> TURNS
    USERS --> GOALS
    GOALS --> ACTIONS
    USERS --> S_WEEK
    USERS --> S_MONTH
    USERS --> SUBS
    USERS --> SAFE
    USERS --> AI_USE

10. Deployment Topology & Scaling Path

How the infrastructure evolves from a single EUR 8.45/mo VPS to a multi-node architecture as DAU grows.

flowchart TB
    subgraph Launch["Launch: Single VPS — 50 DAU — ~EUR 16/mo"]
        P1_CF["Cloudflare (Free)"] --> P1_NGINX["Nginx"]
        P1_NGINX --> P1_API["Node.js API + Workers"]
        P1_API --> P1_PG["PostgreSQL 16"]
        P1_API --> P1_REDIS["Redis"]
        P1_API --> P1_AI["DeepSeek V3.2\nvia OpenRouter/DeepInfra"]
    end

    subgraph Traction["Traction: Split DB — 200 DAU — ~EUR 53/mo"]
        P2_CF["Cloudflare"] --> P2_NGINX["Nginx"]
        P2_NGINX --> P2_API["Node.js API + Workers"]
        P2_API --> P2_PG["PostgreSQL (Separate VPS)"]
        P2_API --> P2_REDIS["Redis"]
        P2_API --> P2_AI["DeepSeek V3.2 via OpenRouter\n+ Claude Haiku fallback"]
    end

    subgraph Growth["Growth: Scale API — 1000 DAU — ~EUR 216/mo"]
        P3_CF["Cloudflare"] --> P3_LB["Load Balancer"]
        P3_LB --> P3_API1["API Replica 1"]
        P3_LB --> P3_API2["API Replica 2"]
        P3_API1 --> P3_PG["PostgreSQL (Dedicated)"]
        P3_API2 --> P3_PG
        P3_API1 --> P3_REDIS["Redis Cluster"]
        P3_API2 --> P3_REDIS
        P3_WORKER["Spectrum Workers"] --> P3_PG
        P3_API1 --> P3_AI["OpenRouter AI Gateway\n(DeepSeek + Claude fallback)"]
        P3_API2 --> P3_AI
    end

    Launch -->|"p95 latency > 120ms\nor storage > 70%"| Traction
    Traction -->|"CPU > 70% sustained\nor p95 > SLO"| Growth