Skip to content

Latest commit

 

History

History
620 lines (440 loc) · 27.1 KB

File metadata and controls

620 lines (440 loc) · 27.1 KB

CLAUDE.md

⚠️ CRITICAL: Always read actual code files to verify accuracy. This file may lag behind code changes.

Last Updated: 2026-03-24 Last Verified: 2026-03-24


Verification Checklist

Before making architectural statements, verify by reading actual code:

# 1. AI Provider (OpenAI via Supabase Edge Function proxy)
cat mobile/src/utils/aiClient.ts | head -30

# 2. TypeScript Usage (mobile uses TypeScript)
cat mobile/tsconfig.json

# 3. Database Schema
cat mobile/src/types/schema.json | head -100

# 4. Edge Functions (list all sync functions + orchestrators)
ls -1 supabase/functions/sync-*
# Expected: sync-all-devices, sync-cgm-devices, sync-fitbit, sync-libre, sync-oura, sync-whoop

# 5. Dependencies (check actual versions)
cat mobile/package.json | grep -A 5 '"dependencies"'
cat web/package.json | grep -A 5 '"dependencies"'

Source of Truth: See ARCHITECTURE.md for comprehensive system design, tech stack, and deployment details.


Common Misconceptions

❌ WRONG Assumptions

  • "Mobile uses JavaScript only" → ✅ Mobile uses TypeScript (see mobile/tsconfig.json)
  • "Web uses React 18" → ✅ Web was upgraded to React 19 and reintegrated into workspace

✅ Current Tech Stack

  • Mobile Language: TypeScript + JSX
  • AI Provider: OpenAI (GPT-4o-mini, GPT-4o, gpt-4o-mini-transcribe)
  • Backend: Supabase PostgreSQL + Deno Edge Functions
  • Web: React Router v7 (integrated into workspace)
  • Insight Engine: Pattern Spotter v2 + data-driven metric discovery (Deno Edge Functions)

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

HealthDecoder (formerly DashMobileApp) is a monorepo containing a mobile app (React Native/Expo), web application (React Router), and shared packages for health event tracking. The app allows users to log health-related events (food, glucose, insulin, activity, supplements, etc.) using voice, text, or camera input, with AI-powered parsing to extract structured data.

Repository Structure

HealthDecoder/
├── package.json              # Root workspace configuration
├── packages/
│   └── shared/               # @healthdecoder/shared - Shared utilities and types
│       ├── src/
│       │   ├── database/     # Supabase client initialization
│       │   ├── health/       # Nutrient calculations
│       │   └── types/        # Database types and schemas
│       └── dist/             # Built output (CommonJS + ESM)
├── mobile/                   # React Native (Expo) mobile application
├── web/                      # React Router web application (temporarily excluded from workspace)
├── supabase/                 # Database migrations and seed scripts
└── .husky/                   # Git hooks for pre-commit tests

Project File Organization (MANDATORY)

NEVER create files at the repository root. The only files permitted at root level are:

  • CLAUDE.md — AI assistant guidance
  • ARCHITECTURE.md — System design reference
  • KNOWN_ISSUES.md — Active known issues tracker
  • package.json, tsconfig.json, .gitignore, and other tooling config files

All documentation and generated files MUST be placed in the correct directory. No exceptions.

File Placement Rules

File type Directory Naming convention
Feature plans, architecture proposals, migration plans docs/planning/ <topic>.md
Implementation guides, setup docs, completion reports docs/implementation/ <topic>.md
Code review reports docs/reviews/ YYYY-MM-DD-<scope>.md
Feature documentation docs/features/ <feature-name>.md
Security documentation docs/security/ <topic>.md
Test plans and strategies docs/testing/ <topic>.md
Completed/historical docs docs/archive/<category>/ <topic>.md
SQL scripts and queries scripts/ or supabase/ context-dependent

Rules

  • NEVER place .md files at the repository root (except the three listed above)
  • NEVER place .sql files at the repository root — use scripts/ or supabase/
  • ALWAYS check docs/README.md for the current directory index before creating docs
  • ALWAYS update docs/README.md when adding a new document
  • When asked to create a plan, put it in docs/planning/, not root
  • When asked to create a review, put it in docs/reviews/ with date prefix YYYY-MM-DD-<scope>.md
  • If unsure where a file belongs, ask — do not default to root

Monorepo Workspace

The project uses npm workspaces to manage shared code between mobile and web applications.

Root package.json workspaces:

  • packages/* - Shared packages (currently @healthdecoder/shared)
  • mobile - React Native app
  • web - React Router web application

Common Commands (from root):

npm install              # Install all workspace dependencies
npm run test:mobile      # Run mobile tests
npm run build:shared     # Build shared package
npm run test:shared      # Run shared package tests

Shared Package (packages/shared/)

The @healthdecoder/shared package contains platform-agnostic code used by both mobile and web.

Current Exports

  • Database: initializeSupabase(), getSupabase(), isSupabaseInitialized()
  • Types: Database, Tables, TablesInsert, TablesUpdate, Json
  • Health: calculateConsumedNutrients()

Building

npm run build:shared     # Build to dist/ (CJS + ESM + types)

Usage in Mobile

import {
  initializeSupabase,
  calculateConsumedNutrients,
} from "@healthdecoder/shared";

Mobile App (mobile/)

Technology Stack

  • Language: TypeScript + JSX (see mobile/tsconfig.json)
  • Framework: Expo 54 with React Native 0.81
  • Routing: Expo Router (file-based routing)
  • State Management: Zustand for auth state
  • Data Fetching: @tanstack/react-query
  • Backend: Supabase (authentication and database)
  • Testing: Jest with @testing-library/react-native
  • AI Processing: OpenAI API (GPT-4o-mini for text, GPT-4o for vision, gpt-4o-mini-transcribe for transcription)
  • Shared Code: @healthdecoder/shared workspace package

Project Structure

mobile/
├── src/
│   ├── app/              # File-based routes (Expo Router)
│   │   ├── (tabs)/       # Tab navigation routes
│   │   │   ├── discover.tsx   # Main discovery feed
│   │   │   ├── lab.tsx        # My Lab (experiments)
│   │   │   ├── my-body.tsx    # My Body (health metrics)
│   │   │   ├── playbook.tsx   # Playbook (recommendations)
│   │   │   ├── profile.tsx    # User profile (hidden tab)
│   │   │   ├── insights.tsx   # Metabolic insights (hidden tab)
│   │   │   └── home.tsx       # Legacy event logging (hidden tab)
│   │   ├── (auth)/       # Auth routes (login, signup, verify)
│   │   ├── (onboarding)/ # Onboarding flow
│   │   ├── discovery/    # Discovery detail screens
│   │   ├── event/        # Event detail screens
│   │   └── _layout.tsx   # Root layout with providers
│   ├── components/       # Reusable UI components
│   ├── hooks/            # React hooks (glucose, experiments, etc.)
│   ├── types/            # TypeScript types (synced with shared package)
│   └── utils/            # Utilities and hooks
│       ├── auth/              # Authentication logic (Zustand store, hooks)
│       ├── experiments/       # Experiment framework (A/B testing engine)
│       ├── fitnessTrackers/   # Device connection/sync hooks
│       ├── aiClient.ts        # AI API abstraction layer (OpenAI via proxy)
│       ├── eventParser.ts     # AI event parsing with structured output
│       ├── timezoneRegistration.ts # Auto-detect + push device IANA timezone
│       ├── pushTokenRegistration.ts # Push notification token management
│       ├── logger.ts          # Structured logging to app_logs table
│       └── supabaseClient.ts  # Mobile-specific Supabase initialization
├── __tests__/           # Test files
├── jest.setup.js        # Jest configuration with mocks
├── metro.config.js      # Metro bundler config (includes workspace support)
└── app.json            # Expo configuration

Common Commands

Development:

cd mobile
npm run android          # Run on Android
npm run ios              # Run on iOS

Testing:

cd mobile
npm test                 # Run all tests
npm run test:watch       # Watch mode
npm run test:coverage    # Generate coverage report
npm run test:ci          # CI mode (used in pre-commit hook)

Key Architecture Patterns

Supabase Client:

  • Mobile initializes the shared Supabase client in src/utils/supabaseClient.js
  • Uses initializeSupabase() from @healthdecoder/shared
  • Auth handled separately with Expo SecureStore (not managed by Supabase client)

Authentication:

  • Zustand store in src/utils/auth/store.js manages auth state
  • Token stored in Expo SecureStore
  • useAuth hook in src/utils/auth/useAuth.js provides auth methods

Event Processing Flow:

  1. User inputs via voice/text/camera on home screen
  2. Voice input → voiceRecording.js → audio file
  3. Audio → aiClient.js (transcription) → text
  4. Text → eventParser.js → AI API (structured output) → parsed event
  5. Events saved to Supabase user_health_events table
  6. History screen fetches events with React Query

Environment Variables: Required in mobile/.env:

  • EXPO_PUBLIC_SUPABASE_URL - Supabase project URL
  • EXPO_PUBLIC_SUPABASE_KEY - Supabase anon key
  • EXPO_PUBLIC_OPENAI_TEXT_MODEL - OpenAI model for text parsing (e.g., gpt-4o-mini)
  • EXPO_PUBLIC_OPENAI_VISION_MODEL - OpenAI model for image analysis (e.g., gpt-4o-mini)
  • EXPO_PUBLIC_OPENAI_TRANSCRIPTION_MODEL - OpenAI model for audio transcription (e.g., gpt-4o-mini-transcribe)

Note: The OpenAI API key is stored server-side in Supabase Edge Function secrets, not in the mobile app. All AI calls are proxied through Edge Functions (openai-proxy-chat, openai-proxy-vision, openai-proxy-transcribe) using JWT authentication.

Path Aliases:

  • @/* maps to src/* (configured in tsconfig.json)
  • @healthdecoder/shared resolves via npm workspace

Metro Configuration:

  • metro.config.js includes watchFolders for the shared package
  • nodeModulesPaths includes both mobile and root node_modules

Testing:

  • Tests use Jest with jest-expo preset
  • Extensive mocking in jest.setup.js for Expo modules
  • Tests located alongside source files in __tests__/ directories
  • Pre-commit hook runs full test suite

Event Types Schema

The app supports multiple event types defined in mobile/src/utils/eventParser.ts:

  • food - Nutrition tracking (calories, macros)
  • glucose - Blood glucose readings
  • insulin - Insulin administration
  • activity - Exercise/movement
  • supplement - Supplement intake
  • sauna - Sauna sessions
  • medication - Medication tracking
  • symptom - Symptom tracking

Each event type has required and optional fields validated during parsing.

Web App (web/)

Note: Reintegrated into npm workspaces after upgrading to React 19 (matching mobile).

Technology Stack

  • Framework: React Router v7 with SSR
  • UI: Chakra UI, Tailwind CSS
  • Backend: Hono API routes (file-based in src/app/api/)
  • Database: To be migrated to Supabase (currently Neon PostgreSQL)
  • State Management: Zustand
  • Data Fetching: @tanstack/react-query

Project Structure

web/
├── src/
│   └── app/             # React Router routes
├── plugins/             # Vite plugins for custom functionality
├── __create/            # Route building utilities
└── react-router.config.ts

Common Commands

Development:

cd web
npm run dev              # Start dev server with SSR
npm run typecheck        # Type checking

Path Aliases:

  • @/* maps to ./src/*

Build System Notes

Mobile:

  • Uses Metro bundler with workspace support
  • Expo prebuild for native projects (iOS/Android)
  • Supports web via expo-web-browser

Shared Package:

  • Uses tsup for building
  • Outputs both CommonJS and ESM formats
  • Generates TypeScript declaration files

Web:

  • Vite bundler
  • Custom plugins in plugins/ directory
  • SSR enabled by default

Git Workflow

Pre-commit Hook:

  • Validates schema.json is in sync with Supabase
  • Runs mobile test suite
  • Builds shared package (if it exists)
  • Configured in .husky/pre-commit
  • Tests must pass for commit to succeed

Committing Code: Run tests from the repository root or the pre-commit hook will handle it automatically.

Data Flow Architecture

Mobile App Data Flow:

  1. Input Layer: Home screen captures voice/text/camera input
  2. Processing Layer: Voice/text sent to AI API for parsing (with structured output)
  3. Validation Layer: eventParser.js validates against event schemas
  4. Storage Layer: Supabase stores events in user_health_events table
  5. Display Layer: React Query fetches and caches events for history screen

Device Sync (Dual-Cron Architecture):

  • Fitness trackers (Whoop, Fitbit, Oura): synced hourly via sync-all-devices (0 * * * *)
  • CGM devices (Libre, Dexcom): synced every 5 minutes via sync-cgm-devices (*/5 * * * *)
  • Both orchestrators use vault RPC for auth, trace ID propagation, and rate-limited batching
  • ingestion_log provider: cron-orchestrator (fitness) vs cgm-orchestrator (CGM)

Authentication Flow:

  1. User initiates OAuth via GoogleAuthWebView component
  2. Supabase handles OAuth redirect
  3. JWT token stored in SecureStore
  4. Zustand store updates auth state globally
  5. Protected routes check auth state from store

Development Notes

When modifying event types:

  1. Update schemas in mobile/src/utils/eventParser.ts (OpenAI strict mode format)
  2. Update prompts in event parsing utilities (mobile/src/utils/eventParser.ts, voiceEventParser.js)
  3. Add tests for new event type validation
  4. Update Supabase table schema if needed

When adding shared utilities:

  1. Add code to packages/shared/src/
  2. Export from packages/shared/src/index.ts
  3. Run npm run build:shared to rebuild
  4. Import in mobile/web using @healthdecoder/shared

When adding new screens:

  • Mobile: Add files to mobile/src/app/ following Expo Router conventions
  • Web: Add files to web/src/app/ following React Router conventions
  • Both use file-based routing with automatic route generation

Testing best practices:

  • Mock Expo modules are configured in jest.setup.js
  • Use @testing-library/react-native for component testing
  • Keep tests close to source files in __tests__/ directories
  • Run tests before committing (enforced by pre-commit hook)

Code Standards

React Native Compatibility (mobile/)

All code in the mobile/ directory must use React Native-compatible APIs only.

WARNING: Jest runs in Node.js, NOT React Native. Tests will NOT catch React Native runtime-specific failures. Code that passes all tests can still crash on device. Always verify API compatibility before using any web-standard API in mobile code.

UI Constraints:

  • accessibilityRole: Only use valid React Native values: none, button, link, search, image, keyboardkey, text, adjustable, imagebutton, header, summary, alert, checkbox, combobox, menu, menubar, menuitem, progressbar, radio, radiogroup, scrollbar, spinbutton, switch, tab, tablist, timer, toolbar. Do NOT use web-only values like dialog, navigation, main, article, etc.
  • Styling: Use StyleSheet.create() or inline styles, not CSS classes or web-only style properties
  • Components: Use React Native components (View, Text, TouchableOpacity, etc.), not HTML elements
  • APIs: Use React Native / Expo APIs, not browser APIs (window, document, localStorage, etc.)
  • Modals: Use accessibilityViewIsModal={true} on Modal components instead of accessibilityRole="dialog"

Network & File API Constraints:

  • File uploads MUST use XMLHttpRequest: React Native has three constraints that eliminate all other approaches:
    1. fetch() does NOT support FormData.append('file', {uri, name, type}) — throws "Unsupported FormDataPart implementation"
    2. Blob does NOT support ArrayBuffer or Uint8Array — throws "Creating blobs from ArrayBuffer not supported"
    3. XMLHttpRequest DOES support {uri, name, type} in FormData natively — this is the ONLY working approach
  • Never use fetch() for file uploads in React Native. Use XMLHttpRequest with the {uri, name, type} FormData pattern. All three constraints above pass silently in Jest/Node.js, making them invisible to tests.

Module & Import Constraints:

  • No dynamic require() for already-imported modules: Never use require('@/utils/foo') inside a function if the module is already imported at the top of the file via import. Dynamic require() can return incomplete module objects in Hermes production bundles, causing "undefined is not a function" errors that Jest cannot catch.
  • Prefer top-level import statements: For lazy loading, use React.lazy() or dynamic import() (not require()).
  • Hermes vs Node.js: The production JS engine (Hermes) has different module evaluation behavior than Node.js (Jest). Circular dependencies and partial module evaluation affect them differently.

Bottom Tab Bar Labels (CRITICAL)

The bottom tab bar uses a custom label renderer to prevent label truncation on Android. Do NOT modify this without understanding the root cause:

Problem: React Navigation's default Label component (@react-navigation/elements) has numberOfLines={1} hardcoded, which truncates labels to "Ho...", "Hist...", "Prof..." on Android devices.

Solution: We use tabBarLabel: renderTabLabel with a custom Text component that:

  1. Has NO numberOfLines constraint
  2. Has explicit width: 80 to ensure labels have enough horizontal space
  3. Uses allowFontScaling={false} to prevent accessibility settings from breaking layout

Rules:

  • NEVER remove or replace the custom tabBarLabel function in mobile/src/app/(tabs)/_layout.jsx
  • NEVER add tabBarLabelStyle - it's handled by the custom renderer
  • NEVER add numberOfLines to the custom label Text component
  • ALWAYS keep explicit width on the label style (minimum 70px)
  • Tests in mobile/__tests__/components/TabBar.test.jsx verify this - they MUST pass

AI Integration (OpenAI Proxy)

All AI calls are routed through Supabase Edge Functions for security:

  • Proxy Functions: openai-proxy-chat, openai-proxy-vision, openai-proxy-transcribe
  • Authentication: User's JWT token (from Supabase Auth), not a client-side API key
  • API Key Storage: Server-side only (Supabase Edge Function secrets)
  • Rate Limiting: Per-user rate limits enforced by proxy
  • Usage Logging: All calls logged to openai_usage_logs table

Client-side model configuration (for logging/metadata only):

  • EXPO_PUBLIC_OPENAI_TEXT_MODEL - Text parsing model name
  • EXPO_PUBLIC_OPENAI_VISION_MODEL - Vision model name
  • EXPO_PUBLIC_OPENAI_TRANSCRIPTION_MODEL - Transcription model name

Implementation:

  • All AI calls go through mobile/src/utils/aiClient.ts abstraction layer
  • OpenAI strict mode requires: lowercase types, additionalProperties: false, all fields in required array, anyOf for nullable fields

Insight Engine (Pattern Spotter v2)

The pattern-spotting pipeline runs server-side as a Deno Edge Function, analyzing 60-90 days of wearable data to find statistically significant behavioral patterns.

Pipeline: FETCH → CLEANSE → AGGREGATE → MERGE → DISCOVER → SCAN → CORRECT → RANK → FILTER → NARRATE → PERSIST

Key files:

  • supabase/functions/ai-engine/engines/pattern-spotter.ts — main pipeline orchestrator
  • supabase/functions/_shared/daily-aggregation.ts — aggregates raw data into daily metrics
  • supabase/functions/_shared/local-time.ts — timezone-aware date/hour extraction (closure-based LocalTimeExtractors factory)
  • supabase/functions/_shared/metric-registry.ts — canonical 62-metric registry (SERVER_METRIC_REGISTRY)
  • supabase/functions/_shared/metric-discovery.ts — data-driven metric + segment discovery
  • supabase/functions/_shared/statistical-tests.ts — Mann-Whitney U, Cohen's d, BH correction
  • supabase/functions/spot-patterns-cron/index.ts — cron trigger

Admin dashboard: web/src/app/routes/admin/insights.tsx (listing) + insights.$runId.tsx (run detail with 11-step pipeline visualization)

Web imports from supabase shared: The web app imports SERVER_METRIC_REGISTRY from @supabase-shared/metric-registry via a Vite path alias (web/vite.config.ts + web/tsconfig.json). This avoids hardcoding the metric registry in the admin dashboard.

Timezone Strategy

All data aggregation is timezone-aware. The system auto-detects the user's IANA timezone and uses it for all date/hour extraction in the insight engine pipeline.

How timezone is populated (no user action required):

  1. Email signup: device timezone passed as user metadata → handle_new_user() trigger writes it to user_profiles
  2. Every app launch: timezoneRegistration.ts calls .update() on user_profiles with Intl.DateTimeFormat().resolvedOptions().timeZone
  3. Fitbit sync: fetches IANA timezone from Fitbit profile API
  4. Oura sync: fetches from Personal Info API
  5. WHOOP sync: preserves timezone_offset in vendor_metadata, computes local activity_date
  6. Libre sync: derives offset from display_time - glucose_timestamp

Aggregation layer: Functions in daily-aggregation.ts accept an optional LocalTimeExtractors parameter (defaults to UTC). The pattern spotter creates extractors once per pipeline run via createLocalTimeExtractors(timezone) — a closure-based factory that captures 2 pre-built Intl.DateTimeFormat instances for the entire run (~54ms vs ~1.8s naive per-call construction).

Glucose special case: Glucose data uses display_time (already user-local wall-clock time) for date grouping and overnight/daytime classification. No IANA conversion needed.

Design doc: docs/planning/timezone-strategy.md

Resilient Token Refresh (Fitness Tracker Sync)

All three fitness tracker sync functions (Fitbit, WHOOP, Oura) use a shared token refresh utility with retry and error classification:

Key files:

  • supabase/functions/_shared/token-refresh.tsTokenRefreshError (with permanent flag), classifyTokenError(), refreshWithRetry(), fetchTokenEndpoint()
  • Each client (fitbit-client.ts, oura-client.ts, whoop-client.ts) calls refreshWithRetry(fetchTokenEndpoint(...)) for token refresh

Error classification: HTTP 400 invalid_grant/invalid_token/invalid_client → permanent (set needs_reauth). HTTP 5xx, 429, network errors → transient (do NOT set needs_reauth, return 503, retry next sync). Handles both standard OAuth format ({ error: "invalid_grant" }) and Fitbit's nested format ({ errors: [{ errorType: "invalid_grant" }] }).

Sync function catch blocks: TokenRefreshError.permanent === true → set needs_reauth=true, log as failure, return 401. TokenRefreshError.permanent === false → do NOT set needs_reauth, log as warning, return 503.

Personalized Health Snapshot

The health snapshot tab uses a three-layer scoring system instead of static population ranges:

Layer 1 — Personal Baseline: P10/P50/P90 computed from 90-day trailing window of user's own data (30-day minimum). Computed by compute-baselines Edge Function, triggered after each sync via sync-all-devices.

Layer 2 — Cycle-Aware Baselines: For women who opt in, separate follicular/luteal baselines. Cycle data collected in-context on the health snapshot tab (not during onboarding) via CycleInsightCard after 30 days of data.

Layer 3 — Trend Direction: Cycle-aware trend computation compares like-to-like phases (luteal vs luteal). Uses last 14 days vs prior 14 days.

Key files:

  • mobile/src/utils/experiments/cyclePhase.ts — phase estimation pure function
  • mobile/src/utils/experiments/personalBaseline.ts — direction-aware status scoring
  • mobile/src/utils/experiments/trendComputation.ts — cycle-aware trends
  • mobile/src/utils/experiments/computeWins.ts — personal bests, back-in-range, baselines-ready
  • mobile/src/utils/experiments/buildMetricScorecard.ts — enriches scorecard with personal data
  • mobile/src/hooks/usePersonalBaselines.ts — fetches from user_personal_baselines table
  • mobile/src/hooks/useCyclePhase.ts — combines profile data with phase estimation
  • supabase/functions/compute-baselines/index.ts — server-side baseline computation
  • mobile/src/components/Experiments/ — HeroSummary, WinsSection, ExpertContext, CycleInsightCard, CycleConfirmationPrompt

Database tables: user_personal_baselines (P10/P50/P90 per metric per phase), user_cycle_reports (period confirmations for cycle length refinement)

Color logic: Direction-aware — outside_good (e.g., HRV above P90) = green, outside_bad (e.g., RHR above P90) = amber. Never red.

Privacy: Cycle data used solely for range personalization. Never sold, shared, or sent to external APIs. Fully deletable via Profile.

Design doc: docs/planning/personalized-health-snapshot.md

Shared Package Usage

  • Platform-agnostic utilities should be added to @healthdecoder/shared
  • Mobile-specific code (Expo modules, React Native APIs) stays in mobile/src/utils/
  • Web-specific code stays in web/src/
  • Types and schemas should be defined once in the shared package

Documentation Maintenance

When Architecture Changes

  1. Update ARCHITECTURE.md first (source of truth)
  2. Update CLAUDE.md (Claude Code guidance)
  3. Update mobile/.env and web/.env (if environment variables change)
  4. Update package.json scripts (if workflow changes)

Preventing Inaccuracies

DO:

  • ✅ Read actual code files when making architectural statements
  • ✅ Use verification checklist (top of this file)
  • ✅ Reference ARCHITECTURE.md for comprehensive details
  • ✅ Update "Last Verified" timestamp when reviewing

DON'T:

  • ❌ Make assumptions about tech stack without verification
  • ❌ Trust outdated documentation
  • ❌ Rely on variable/file naming alone
  • ❌ State absolute facts without reading source code

Quick Verification Commands

# What AI provider is actually used? (OpenAI via proxy)
grep -r "openai-proxy" mobile/src/utils/aiClient.ts | head -5

# What language is mobile written in?
test -f mobile/tsconfig.json && echo "TypeScript" || echo "JavaScript"

# What integrations exist?
ls -1 supabase/functions/sync-* | sed 's/.*sync-//'
# Expected: all-devices, cgm-devices, fitbit, libre, oura, whoop

# What's in the shared package?
cat packages/shared/src/index.ts | grep "^export"

For comprehensive architecture details, see ARCHITECTURE.md