Systems

Architecture overview: how the stack fits together, what runs when, and how data flows.

Stack

LayerTechnologyNotes
Native appReact Native + Expo, TypeScriptexpo-router for navigation
BackendNext.js App RouterDeployed on Vercel
DatabaseSupabase (PostgreSQL)Service role key, no RLS
AIGemini 2.5 Flashtemp 1.0, thinkingBudget 1024
MapsGoogle Maps SDK (native)Google Places API for POIs
AuthNoneuser_id = device UUID in AsyncStorage
PushExpo Push NotificationsBackend built; frontend pending Apple Developer Program

Cron Jobs

All cron jobs are Vercel Cron hitting bare GET endpoints, protected by CRON_SECRET. Times are UTC.

Schedule (UTC)EndpointWhat it does
05:00 daily/api/cleanupExpire old walks, prune orphaned data
00:15 daily/api/taste/batch-updateRecompute taste profiles from recent walk behaviour
14:00 daily/api/notifications/streak-nudgeStreak reminder for active walkers (4pm Madrid summer)
08:00 daily/api/notifications/comeback-nudgeRe-engagement nudge for inactive users (10am Madrid summer)
09:00 daily/api/notifications/quest-reminderRemind users of accepted but incomplete quests (11am Madrid summer)
10:30 daily/api/notifications/weather-windowGood weather walking nudge (12:30pm Madrid summer)
11:00 daily/api/notifications/live-eventNotify users of new live events (1pm Madrid summer)
Timezone note Madrid is UTC+2 in summer (CEST). A 14:00 UTC cron fires at 4pm local time, which is prime walking hour for streak nudges.

Data Flow

Quest generation

  1. App sends lat/lng, timeOfDay, season, weather to /api/quests/daily
  2. Backend queries quest_library filtered by day_types, neighbourhood proximity, and tags
  3. If user has a taste profile, /api/quests/tailored runs Gemini to pick a personalised quest
  4. For non-Barcelona cities, /api/quests/local generates quests via Gemini with live Google Search
  5. Deck returned: [live?] + [daily] + [tailored?] + [library cards]

SNAP flow

  1. User takes a photo via useSnap hook
  2. Image compressed client-side (utils/compress.ts)
  3. Uploaded to Supabase Storage via /api/photos/upload
  4. If quest active: /api/quest/validate-snap runs Gemini vision to classify themes
  5. classified_themes written to photo_snaps row
  6. If themes match active quest's detection_themes, progress increments

Taste memory loop

  1. User completes onboarding: /api/taste/seed creates initial profile
  2. Daily cron /api/taste/batch-update analyses recent walks, snaps, and quest completions
  3. Updated dimensions influence /api/quests/tailored quest selection
  4. User can manually edit taste notes in PROFILE sub-tab

Repo Structure

reroute-app/                  # React Native + Expo (TypeScript)
  app/                        # expo-router screens
    (tabs)/                   # Tab screens: index, walk, diary
    _layout.tsx               # Root layout
  components/                 # UI components
  hooks/                      # 17 custom hooks
  api/                        # Client-side API wrappers
  context/                    # WalkContext.tsx
  config/                     # theme.ts, api.ts, mapStyle.json
  utils/                      # compress.ts, geo.ts, time.ts

Build/                        # Next.js on Vercel (backend)
  src/app/api/                # 52 API route handlers
  src/data/migrations/        # 13 Supabase SQL migrations

Design Rules