# Makima Mobile Frontend Specification **Version:** 1.0 **Date:** 2026-01-17 **Status:** Draft **Contract Phase:** Specify --- ## Table of Contents 1. [Overview](#1-overview) 2. [Technical Specifications](#2-technical-specifications) 3. [Feature Specifications](#3-feature-specifications) 4. [UI/UX Specifications](#4-uiux-specifications) 5. [API Integration](#5-api-integration) 6. [Timeline and Resources](#6-timeline-and-resources) 7. [Appendices](#appendices) --- ## 1. Overview ### 1.1 Project Goals The Makima mobile frontend enables developers to monitor and interact with AI-powered coding agents (Claude Code) from their mobile devices. The application addresses the critical need for real-time task monitoring, supervisor question responses, and push notifications when away from the desktop. **Primary Goals:** - Enable monitoring of running Claude Code tasks from mobile devices - Provide instant push notifications for supervisor questions requiring user input - Allow quick responses to AI agents without returning to desktop - Support basic contract and task management on-the-go **Success Criteria:** | Metric | Target | Measurement | |--------|--------|-------------| | Push notification delivery | < 5 seconds | Time from question raised to device notification | | Supervisor question response rate | > 80% | Questions answered via mobile vs ignored | | App startup time | < 3 seconds | Cold start to usable dashboard | | Task output streaming latency | < 1 second | Server-to-screen delay | | Monthly active users | 30% of web users | Mobile users / total active users | | App store rating | > 4.5 stars | iOS App Store and Google Play | ### 1.2 Target Platforms | Platform | Minimum Version | Notes | |----------|-----------------|-------| | iOS | iOS 15.0+ | Required for modern notification features | | Android | Android 8.0 (API 26)+ | Required for notification channels | **Device Support:** - iPhone 8 and newer - iPad (universal app support planned for V2) - Android phones with minimum 3GB RAM - Tablets planned for V2 (adaptive layouts) ### 1.3 Technology Decision: React Native with Expo **Chosen Approach:** React Native with Expo SDK **Rationale:** 1. **Code Sharing** (Critical) - Makima's web frontend is React 19 + TypeScript - Can share: API client (`lib/api.ts`), TypeScript interfaces, WebSocket handlers - Estimated 40-60% code reuse with web codebase - React Native for Web enables future cross-platform component sharing 2. **Push Notifications** (Critical) - Full native push notification support on iOS and Android - Required for supervisor questions, task completion, merge conflicts - Works in background (unlike PWA on iOS) 3. **Team Productivity** - Zero ramp-up time - team already knows React + TypeScript - Expo EAS simplifies build and deployment - OTA updates for rapid iteration without app store review 4. **Real-time Features** - Native WebSocket support for task output streaming - Proven in chat applications with sub-100ms latency - New Architecture (JSI) eliminates historical bridge bottleneck 5. **Long-term Sustainability** - Mature 10+ year framework with strong community - JavaScript developers abundant (20:1 vs Dart) - Single team can maintain web + mobile **Alternatives Considered:** | Option | Reason Not Chosen | |--------|-------------------| | Flutter | Cannot share code with existing React/TypeScript codebase | | PWA | iOS push notification limitations are a dealbreaker | | Capacitor | WebView-based performance ceiling for real-time streaming | --- ## 2. Technical Specifications ### 2.1 Monorepo Structure ``` makima/ ├── apps/ │ ├── web/ # Next.js/React web frontend (existing) │ │ └── src/ │ │ ├── routes/ │ │ ├── components/ │ │ └── lib/ │ └── mobile/ # Expo/React Native app (new) │ ├── app/ # Expo Router file-based routes │ │ ├── (tabs)/ # Tab navigator screens │ │ │ ├── index.tsx # Home/Dashboard │ │ │ ├── contracts.tsx │ │ │ ├── tasks.tsx │ │ │ ├── files.tsx │ │ │ └── settings.tsx │ │ ├── contract/ │ │ │ └── [id].tsx # Contract detail │ │ ├── task/ │ │ │ └── [id].tsx # Task detail with output │ │ └── _layout.tsx # Root layout │ ├── components/ # Mobile-specific components │ ├── hooks/ # Mobile-specific hooks │ ├── assets/ # Images, fonts │ ├── app.json # Expo config │ └── package.json └── packages/ ├── shared/ # Shared TypeScript interfaces │ ├── types/ │ │ ├── api.ts # API response types │ │ ├── contracts.ts # Contract types │ │ ├── tasks.ts # Task/Mesh types │ │ └── websocket.ts # WebSocket message types │ └── utils/ │ ├── formatting.ts # Date, duration formatting │ └── validation.ts # Input validation ├── api-client/ # Shared API client │ ├── client.ts # Base fetch wrapper │ ├── contracts.ts # Contract API functions │ ├── tasks.ts # Task/Mesh API functions │ ├── files.ts # File API functions │ └── auth.ts # Authentication helpers └── ui/ # Shared UI components (future) └── components/ ``` ### 2.2 Shared Packages #### 2.2.1 API Client (`packages/api-client/`) Extract and refactor from `makima/frontend/src/lib/api.ts`: ```typescript // packages/api-client/src/client.ts export interface ApiConfig { baseUrl: string; wsBaseUrl: string; getAuthToken: () => Promise; getApiKey: () => string | null; } export function createApiClient(config: ApiConfig) { async function authFetch(url: string, options: RequestInit = {}): Promise { const headers: HeadersInit = { 'Content-Type': 'application/json', }; // Try JWT token first const token = await config.getAuthToken(); if (token) { headers['Authorization'] = `Bearer ${token}`; } else { // Fall back to API key const apiKey = config.getApiKey(); if (apiKey) { headers['X-Makima-API-Key'] = apiKey; } } return fetch(`${config.baseUrl}${url}`, { ...options, headers: { ...headers, ...options.headers }, }); } return { authFetch }; } ``` **Key API Functions to Share:** | Function | Description | |----------|-------------| | `listTasks()` | Get all tasks with status | | `getTask(id)` | Get task details with subtasks | | `startTask(id)` | Start a task | | `stopTask(id)` | Stop a task | | `sendTaskMessage(id, message)` | Send message to running task | | `getTaskOutput(id)` | Get task output history | | `listContracts()` | Get all contracts | | `getContract(id)` | Get contract with relations | | `changeContractPhase(id, phase)` | Change contract phase | | `listPendingQuestions()` | Get supervisor questions | | `answerQuestion(id, response)` | Answer a supervisor question | #### 2.2.2 TypeScript Interfaces (`packages/shared/`) Extract from `lib/api.ts` types section: ```typescript // packages/shared/src/types/tasks.ts export type TaskStatus = | "pending" | "initializing" | "starting" | "running" | "paused" | "blocked" | "done" | "failed" | "merged"; export interface TaskSummary { id: string; contractId: string | null; contractName: string | null; name: string; status: TaskStatus; priority: number; progressSummary: string | null; isSupervisor: boolean; createdAt: string; updatedAt: string; } // packages/shared/src/types/contracts.ts export type ContractPhase = "research" | "specify" | "plan" | "execute" | "review"; export type ContractStatus = "active" | "completed" | "archived"; export interface ContractSummary { id: string; name: string; description: string | null; phase: ContractPhase; status: ContractStatus; fileCount: number; taskCount: number; createdAt: string; } ``` #### 2.2.3 WebSocket Handlers (`packages/api-client/`) ```typescript // packages/api-client/src/websocket.ts export interface TaskSubscriptionMessage { type: 'task_status' | 'task_output' | 'supervisor_question'; taskId: string; data: unknown; } export function createTaskSubscription( wsBaseUrl: string, authToken: string, onMessage: (msg: TaskSubscriptionMessage) => void, onError: (error: Error) => void ): WebSocket { const ws = new WebSocket(`${wsBaseUrl}/api/v1/mesh/tasks/subscribe`); ws.onopen = () => { ws.send(JSON.stringify({ type: 'auth', token: authToken })); }; ws.onmessage = (event) => { const message = JSON.parse(event.data) as TaskSubscriptionMessage; onMessage(message); }; ws.onerror = (event) => { onError(new Error('WebSocket error')); }; return ws; } ``` ### 2.3 Authentication Flow #### 2.3.1 Supabase Integration ``` ┌─────────────────────────────────────────────────────────────┐ │ Authentication Flow │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────┐ ┌──────────┐ ┌────────┐ ┌─────────┐ │ │ │ User │───▶│ Login │───▶│Supabase│───▶│ JWT │ │ │ │ │ │ Screen │ │ Auth │ │ Token │ │ │ └─────────┘ └──────────┘ └────────┘ └────┬────┘ │ │ │ │ │ ▼ │ │ ┌──────────┐ │ │ │ Secure │ │ │ │ Storage │ │ │ │(Keychain)│ │ │ └────┬─────┘ │ │ │ │ │ ┌───────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────┐ ┌──────────┐ ┌────────┐ │ │ │ API │◀───│ Auth │◀───│ Token │ │ │ │ Request │ │ Header │ │Refresh │ │ │ └─────────┘ └──────────┘ └────────┘ │ │ │ └───────────────────────────────────────────────────────────┘ ``` **Implementation:** ```typescript // apps/mobile/src/lib/auth.ts import * as SecureStore from 'expo-secure-store'; import { createClient } from '@supabase/supabase-js'; const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, { auth: { storage: { getItem: (key) => SecureStore.getItemAsync(key), setItem: (key, value) => SecureStore.setItemAsync(key, value), removeItem: (key) => SecureStore.deleteItemAsync(key), }, autoRefreshToken: true, persistSession: true, }, }); export async function signIn(email: string, password: string) { const { data, error } = await supabase.auth.signInWithPassword({ email, password, }); if (error) throw error; return data; } export async function getSession() { const { data: { session } } = await supabase.auth.getSession(); return session; } ``` **Biometric Authentication (V2):** - Face ID / Touch ID for quick app unlock - Stored session remains valid, biometric just unlocks UI - Falls back to password if biometric fails ### 2.4 Push Notification Architecture ``` ┌────────────────────────────────────────────────────────────────┐ │ Push Notification Architecture │ ├────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────┐ ┌────────────┐ │ │ │ Makima │ │ Mobile │ │ │ │ Backend │ │ App │ │ │ │ (Rust) │ │ (Expo) │ │ │ └────┬─────┘ └──────┬─────┘ │ │ │ │ │ │ │ 1. Event occurs │ │ │ │ (supervisor question, │ │ │ │ task complete, etc) │ │ │ │ │ │ │ ▼ │ │ │ ┌────────────┐ │ │ │ │Notification│ 2. Push via FCM/APNs │ │ │ │ Service │────────────────────────────────▶ │ │ │ │ │ │ │ │ └────────────┘ │ │ │ │ │ │ │ │ 3. Store push token │ │ │ │◀────────────────────────────────────────────│ │ │ │ │ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ Notification Categories │ │ │ ├─────────────────────────────────────────────────────────┤ │ │ │ Category │ Priority │ Actions │ │ │ ├───────────────────┼──────────┼──────────────────────────┤ │ │ │ SUPERVISOR_QN │ High │ Quick reply, View │ │ │ │ TASK_COMPLETE │ Medium │ View output │ │ │ │ TASK_FAILED │ Medium │ View error, Retry │ │ │ │ MERGE_CONFLICT │ High │ View conflict │ │ │ │ CONTRACT_UPDATE │ Low │ View contract │ │ │ └───────────────────┴──────────┴──────────────────────────┘ │ └────────────────────────────────────────────────────────────────┘ ``` **Expo Push Notifications Setup:** ```typescript // apps/mobile/src/lib/notifications.ts import * as Notifications from 'expo-notifications'; import * as Device from 'expo-device'; import { Platform } from 'react-native'; export async function registerForPushNotifications() { if (!Device.isDevice) { return null; // Push doesn't work in simulator } const { status: existingStatus } = await Notifications.getPermissionsAsync(); let finalStatus = existingStatus; if (existingStatus !== 'granted') { const { status } = await Notifications.requestPermissionsAsync(); finalStatus = status; } if (finalStatus !== 'granted') { return null; } const token = await Notifications.getExpoPushTokenAsync({ projectId: 'your-project-id', }); // Register token with backend await registerPushToken(token.data); return token.data; } // Configure notification channels (Android) if (Platform.OS === 'android') { Notifications.setNotificationChannelAsync('supervisor-questions', { name: 'Supervisor Questions', importance: Notifications.AndroidImportance.HIGH, vibrationPattern: [0, 250, 250, 250], }); Notifications.setNotificationChannelAsync('task-updates', { name: 'Task Updates', importance: Notifications.AndroidImportance.DEFAULT, }); } ``` **Backend Integration Required:** ```rust // New endpoint needed: POST /api/v1/users/me/push-token #[derive(Deserialize)] struct RegisterPushTokenRequest { token: String, platform: String, // "ios" | "android" } // New table needed: push_tokens // - id: UUID // - user_id: UUID (FK to users) // - token: String // - platform: String // - created_at: Timestamp // - last_used_at: Timestamp ``` --- ## 3. Feature Specifications ### 3.1 MVP (Phase 1) - 4-6 Weeks #### 3.1.1 Task Monitoring **User Stories:** - As a developer, I want to see all my running tasks so I can monitor their progress - As a developer, I want to view real-time task output so I can follow what Claude is doing - As a developer, I want to see task completion/failure status so I know when work is done **Screens:** 1. **Task List Screen** - List all tasks with status indicators - Filter by status (running, pending, done, failed) - Sort by updated time - Pull-to-refresh 2. **Task Detail Screen** - Task name, status, progress summary - Real-time output streaming (WebSocket) - Elapsed time and turn count - Start/Stop controls - Send message input **Acceptance Criteria:** - [ ] Task list loads within 2 seconds - [ ] Real-time output updates within 1 second of server event - [ ] WebSocket reconnects automatically on network change - [ ] Offline state shows cached task list with "offline" indicator - [ ] Start/stop operations provide haptic feedback #### 3.1.2 Push Notifications **User Stories:** - As a developer, I want push notifications for supervisor questions so I can respond quickly - As a developer, I want notifications when tasks complete or fail so I stay informed - As a developer, I want to configure which notifications I receive **Notification Types:** | Type | Priority | Payload Example | |------|----------|-----------------| | Supervisor Question | High | `{ taskId, question, choices[], context }` | | Task Completed | Medium | `{ taskId, taskName, duration }` | | Task Failed | Medium | `{ taskId, taskName, errorMessage }` | | Merge Conflict | High | `{ taskId, conflictFiles[] }` | **Acceptance Criteria:** - [ ] Push notifications arrive within 5 seconds of event - [ ] Tapping notification navigates to relevant screen - [ ] Notification preferences persist across app restarts - [ ] Silent/vibration mode respected #### 3.1.3 Supervisor Question Responses **User Stories:** - As a developer, I want to answer supervisor questions from my phone so tasks aren't blocked - As a developer, I want quick-reply buttons for common responses - As a developer, I want to see question context before answering **Flow:** ``` Push Notification ↓ [View Full Question] or [Quick Reply Option] ↓ Question Detail Screen (if View) ├── Question text ├── Context (truncated with "expand") ├── Choice buttons └── Custom text input ↓ Submit Answer → Loading → Success Toast → Return to Dashboard ``` **Acceptance Criteria:** - [ ] Quick reply from notification completes in < 3 seconds - [ ] Full question context viewable with expand/collapse - [ ] Answer submission shows loading state - [ ] Success/failure feedback via toast - [ ] Answered questions disappear from pending list ### 3.2 V1 (Phase 2) - 4-6 Weeks #### 3.2.1 Full Task Control **Additional Features:** - Create new tasks (simplified form) - Edit task plan/description - Delete tasks - Reassign failed tasks to new daemon - View task checkpoints #### 3.2.2 Contract Management **Screens:** 1. **Contract List Screen** - List contracts with phase badges - Filter by phase and status - Contract progress indicators 2. **Contract Detail Screen** - Phase stepper visualization - Associated files list - Associated tasks list - Phase transition controls - Chat history viewer **Acceptance Criteria:** - [ ] Contract list shows phase progression - [ ] Phase change requires confirmation - [ ] Linked files and tasks navigable #### 3.2.3 Offline Viewing **Cached Data:** - Last 20 contracts - Last 50 tasks - Task output (last 1000 lines per task) - User preferences **Sync Strategy:** - Background sync on app foreground - Optimistic UI updates with server reconciliation - Conflict resolution: server wins for task state ### 3.3 V2 (Phase 3) - 2-4 Weeks #### 3.3.1 Home Screen Widgets **iOS Widgets (WidgetKit):** - **Small Widget**: Running task count + attention badge - **Medium Widget**: Top 3 active tasks with status - **Large Widget**: Dashboard summary **Android Widgets:** - Similar functionality using React Native's widget support - Glance-able active task status #### 3.3.2 Biometric Authentication - Face ID / Touch ID for app unlock - Optional: require biometric for sensitive operations - Graceful fallback to password #### 3.3.3 Tablet Layouts **iPad / Android Tablet:** - Split view: list on left, detail on right - Floating panels for modals - Landscape-optimized layouts - Multi-window support --- ## 4. UI/UX Specifications ### 4.1 Navigation Structure ``` ┌─────────────────────────────────────────────────────────────┐ │ Navigation Structure │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Status Bar │ │ │ ├─────────────────────────────────────────────────────┤ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ Main Content │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├─────────────────────────────────────────────────────┤ │ │ │ │ │ │ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │ │ │ Home │ │Tasks │ │Mesh │ │Files │ │More │ │ │ │ │ │ 🏠 │ │ 📋 │ │ 🤖 │ │ 📁 │ │ ⚙️ │ │ │ │ │ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ │ │ │ │ Bottom Tab Navigation │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘ Tab Details: ───────────────────────────────────────────────────────────── │ Tab │ Primary Screen │ Badge │ ├──────────┼─────────────────────┼────────────────────┤ │ Home │ Dashboard │ Attention count │ │ Tasks │ Contract List │ Active task count │ │ Mesh │ Task/Agent List │ Running count │ │ Files │ File List │ None │ │ More │ Settings │ Update available │ ───────────────────────────────────────────────────────────── ``` ### 4.2 Core Screens with Wireframes #### 4.2.1 Dashboard/Home Screen ``` ┌─────────────────────────────────────────┐ │ Good morning, Alex 🔔 (3) │ ← Header with notification badge ├─────────────────────────────────────────┤ │ │ │ ⚠️ 2 items need attention │ ← Attention banner (tappable) │ ┌─────────────────────────────────────┐ │ │ │ 🤔 Supervisor Question │ │ │ │ Task: Auth System Refactor │ │ │ │ "Which auth method should I use?" │ │ │ │ ┌────────┐ ┌────────┐ ┌────────┐ │ │ │ │ │ OAuth │ │ JWT │ │ More │ │ │ ← Quick action buttons │ │ └────────┘ └────────┘ └────────┘ │ │ │ └─────────────────────────────────────┘ │ ├─────────────────────────────────────────┤ │ Active Tasks See All │ │ ┌─────────────────────────────────────┐ │ │ │ 🟢 Auth System Refactor │ │ │ │ ●●●●●●●○○○ 70% • 23 min │ │ ← Progress bar and time │ │ Writing OAuth handler... │ │ ← Progress summary │ └─────────────────────────────────────┘ │ │ ┌─────────────────────────────────────┐ │ │ │ 🟡 Mobile API Endpoints Paused │ │ │ │ ●●●●○○○○○○ 40% │ │ │ └─────────────────────────────────────┘ │ ├─────────────────────────────────────────┤ │ Recent Activity │ │ ┌─────────────────────────────────────┐ │ │ │ ✅ Task "DB Migration" done 5m │ │ │ │ 📝 Contract "Mobile" → specify 15m │ │ │ │ ❌ Task "Tests" failed 1h │ │ │ └─────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────┘ ``` #### 4.2.2 Task List Screen ``` ┌─────────────────────────────────────────┐ │ Tasks [🔍] [Filter] │ ├─────────────────────────────────────────┤ │ ┌─────────────────────────────────────┐ │ │ │ Running (2) │ │ ← Collapsible section │ │ ├───────────────────────────────────┤ │ │ │ │ 🟢 Auth System Refactor │ │ │ │ │ Mobile Version • 23 min • 70% │ │ │ │ ├───────────────────────────────────┤ │ │ │ │ 🟢 API Documentation │ │ │ │ │ API Docs Contract • 8 min • 30% │ │ │ └─────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────┐ │ │ │ Pending (3) │ │ │ │ ├───────────────────────────────────┤ │ │ │ │ ⏳ Database Migrations │ │ │ │ │ Backend Contract • Queued │ │ │ │ ├───────────────────────────────────┤ │ │ │ │ ⏳ Test Suite Updates │ │ │ │ │ Backend Contract • Queued │ │ │ └─────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────┐ │ │ │ Completed Today (5) │ │ │ │ ├───────────────────────────────────┤ │ │ │ │ ✅ Config Refactor 12:30 PM │ │ │ │ │ ✅ Bug Fix #234 11:15 AM │ │ │ └─────────────────────────────────────┘ │ │ │ │ ┌─────────────┐ │ │ │ + New │ │ ← FAB for new task (V1) │ └─────────────┘ │ └─────────────────────────────────────────┘ ``` #### 4.2.3 Task Detail Screen ``` ┌─────────────────────────────────────────┐ │ ← Auth System Refactor │ ├─────────────────────────────────────────┤ │ │ │ Status: 🟢 Running • 23 min elapsed │ │ ●●●●●●●○○○ 70% • 47 turns │ │ │ │ Contract: Mobile Version │ │ Branch: feature/auth-oauth │ │ │ ├─────────────────────────────────────────┤ │ ┌───────┐ ┌───────┐ ┌───────┐ │ │ │ Stop │ │Restart│ │Message│ │ ← Action buttons │ │ ⏹️ │ │ 🔄 │ │ 💬 │ │ │ └───────┘ └───────┘ └───────┘ │ ├─────────────────────────────────────────┤ │ Live Output [⇕ Full] │ ├─────────────────────────────────────────┤ │ │ Reading src/auth/provider.ts... │ │ │ │ │ │ Found OAuth implementation at L45 │ │ │ │ │ │ Edit: src/auth/config.ts │ │ │ + export const AUTH_PROVIDERS = { │ │ │ + google: { ... }, │ │ │ + github: { ... } │ │ │ + } │ │ │ │ │ │ Running tests... │ │ │ ▌ │ ← Live cursor │ │ ├─────────────────────────────────────────┤ │ ┌─────────────────────────────────┐ ⬆️ │ │ │ Send a message to the agent... │ │ │ └─────────────────────────────────┘ │ └─────────────────────────────────────────┘ ``` #### 4.2.4 Supervisor Question Screen ``` ┌─────────────────────────────────────────┐ │ ← Supervisor Question │ ├─────────────────────────────────────────┤ │ │ │ Task: Auth System Refactor │ │ Contract: Mobile Version │ │ Asked 2 minutes ago │ │ │ ├─────────────────────────────────────────┤ │ │ │ 🤔 Question │ │ ───────────────────────────────────── │ │ Which authentication method should │ │ I implement for the mobile API? │ │ │ ├─────────────────────────────────────────┤ │ │ │ 📋 Context [Expand] │ │ ───────────────────────────────────── │ │ The API currently uses session-based │ │ auth. Mobile apps typically need... │ │ │ ├─────────────────────────────────────────┤ │ │ │ Choose an option: │ │ │ │ ┌─────────────────────────────────┐ │ │ │ OAuth 2.0 │ │ │ │ Industry standard, supports │ │ │ │ Google, GitHub, etc. │ │ │ └─────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────┐ │ │ │ JWT Tokens │ │ │ │ Stateless, good for mobile │ │ │ └─────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────┐ │ │ │ API Keys │ │ │ │ Simple, good for internal use │ │ │ └─────────────────────────────────┘ │ │ │ │ Or write a custom response: │ │ ┌─────────────────────────────────┐ │ │ │ │ │ │ │ │ │ │ └─────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────┐ │ │ │ Submit Answer │ │ │ └─────────────────────────────────┘ │ │ │ └─────────────────────────────────────────┘ ``` ### 4.3 Touch Targets and Accessibility #### 4.3.1 Minimum Touch Target Sizes | Element Type | Minimum Size | Recommended Size | |--------------|--------------|------------------| | Primary buttons | 44×44pt | 48×48pt | | Icon buttons | 44×44pt | 44×44pt | | List item height | 44pt | 56-72pt | | Input fields | 44pt height | 48pt height | | Tab bar items | 44×44pt | 49×49pt (iOS standard) | | Spacing between tappable elements | 8pt | 12pt | #### 4.3.2 Accessibility Features **VoiceOver / TalkBack Support:** - All interactive elements have accessibility labels - Task status announced with context ("Auth task, running, 70% complete") - Supervisor questions announced with urgency **Reduced Motion:** - Respect system preference `prefers-reduced-motion` - Disable animations when enabled - Instant transitions instead of animated **Dynamic Type:** - Support iOS Dynamic Type scaling - Support Android font scaling - Test with 200% scale **Color Contrast:** - All text meets WCAG 2.1 AA (4.5:1 minimum) - Status indicators have text labels, not color alone ### 4.4 Color System ``` ┌─────────────────────────────────────────────────────────────┐ │ Color System │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Brand │ │ ┌──────────┐ │ │ │ Primary │ #6366F1 (Indigo) │ │ │ │ Actions, links, active states │ │ └──────────┘ │ │ │ │ Semantic │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Success │ │ Warning │ │ Error │ │ Info │ │ │ │ #22C55E │ │ #F59E0B │ │ #EF4444 │ │ #3B82F6 │ │ │ │ Green │ │ Amber │ │ Red │ │ Blue │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ Task Status Colors │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Running │ │ Pending │ │ Done │ │ Failed │ │ │ │ #22C55E │ │ #F59E0B │ │ #6366F1 │ │ #EF4444 │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ Neutral (Slate) │ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │ 50 │ │ 100 │ │ 200 │ │ 500 │ │ 800 │ │ 900 │ │ │ │F8FAFC│ │F1F5F9│ │E2E8F0│ │64748B│ │1E293B│ │0F172A│ │ │ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ │ │ │ │ Dark Mode │ │ ┌────────────────────────────────────────────────────┐ │ │ │ Background: #0F172A (Slate 900) │ │ │ │ Surface: #1E293B (Slate 800) │ │ │ │ Text: #F1F5F9 (Slate 100) │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ Light Mode │ │ ┌────────────────────────────────────────────────────┐ │ │ │ Background: #F8FAFC (Slate 50) │ │ │ │ Surface: #FFFFFF (White) │ │ │ │ Text: #1E293B (Slate 800) │ │ │ └────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘ ``` ### 4.5 Typography ``` ┌─────────────────────────────────────────────────────────────┐ │ Typography │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Font Families │ │ ───────────────────────────────────────────────────────── │ │ Display/Headings: Inter (600-700 weight) │ │ Fallback: SF Pro Display (iOS) │ │ Roboto (Android) │ │ │ │ Body: Inter (400-500 weight) │ │ Fallback: SF Pro Text (iOS) │ │ Roboto (Android) │ │ │ │ Monospace: JetBrains Mono │ │ Fallback: SF Mono (iOS) │ │ Roboto Mono (Android) │ │ │ │ Type Scale │ │ ───────────────────────────────────────────────────────── │ │ │ Name │ Size │ Weight │ Line Height │ Use Case │ │ ├────────────┼───────┼────────┼─────────────┼─────────────│ │ │ Display │ 28pt │ 700 │ 36pt │ Page titles │ │ │ Title │ 22pt │ 600 │ 28pt │ Section │ │ │ Headline │ 18pt │ 600 │ 24pt │ Card titles │ │ │ Body │ 16pt │ 400 │ 24pt │ Main text │ │ │ Callout │ 15pt │ 500 │ 20pt │ Emphasis │ │ │ Caption │ 14pt │ 400 │ 18pt │ Secondary │ │ │ Micro │ 12pt │ 400 │ 16pt │ Timestamps │ │ │ Code │ 14pt │ 400 │ 20pt │ Output │ │ │ └─────────────────────────────────────────────────────────────┘ ``` --- ## 5. API Integration ### 5.1 Endpoints Required for Mobile #### 5.1.1 Core Endpoints (Existing) | Endpoint | Method | Mobile Usage | |----------|--------|--------------| | `/api/v1/mesh/tasks` | GET | Task list | | `/api/v1/mesh/tasks/{id}` | GET | Task detail | | `/api/v1/mesh/tasks/{id}/start` | POST | Start task | | `/api/v1/mesh/tasks/{id}/stop` | POST | Stop task | | `/api/v1/mesh/tasks/{id}/message` | POST | Send message | | `/api/v1/mesh/tasks/{id}/output` | GET | Fetch output history | | `/api/v1/contracts` | GET | Contract list | | `/api/v1/contracts/{id}` | GET | Contract detail | | `/api/v1/contracts/{id}/phase` | POST | Change phase | | `/api/v1/mesh/questions` | GET | Pending questions | | `/api/v1/mesh/questions/{id}/answer` | POST | Answer question | | `/api/v1/files` | GET | File list | | `/api/v1/files/{id}` | GET | File detail | #### 5.1.2 New Endpoints Required | Endpoint | Method | Purpose | |----------|--------|---------| | `/api/v1/users/me/push-token` | POST | Register push notification token | | `/api/v1/users/me/push-token` | DELETE | Unregister push token | | `/api/v1/users/me/notification-preferences` | GET, PUT | Notification settings | | `/api/v1/mobile/dashboard` | GET | Optimized dashboard data | **Dashboard Endpoint Response:** ```typescript interface DashboardResponse { attentionItems: AttentionItem[]; activeTasks: TaskSummary[]; recentActivity: ActivityItem[]; stats: { runningTasks: number; pendingTasks: number; completedToday: number; pendingQuestions: number; }; } ``` ### 5.2 WebSocket Subscriptions #### 5.2.1 Task Subscription **Endpoint:** `wss://api.makima.jp/api/v1/mesh/tasks/subscribe` **Message Types:** ```typescript // Incoming messages interface TaskStatusMessage { type: 'task_status'; taskId: string; status: TaskStatus; progressSummary: string | null; } interface TaskOutputMessage { type: 'task_output'; taskId: string; messageType: string; content: string; toolName?: string; createdAt: string; } interface SupervisorQuestionMessage { type: 'supervisor_question'; questionId: string; taskId: string; contractId: string; question: string; choices: string[]; context: string | null; } ``` **Mobile-Specific Considerations:** - Reconnect with exponential backoff on network change - Queue messages while reconnecting - Request full state on reconnect (fetch missed output) ### 5.3 Push Notification Backend Requirements #### 5.3.1 Database Schema ```sql -- Push notification tokens CREATE TABLE push_tokens ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, token TEXT NOT NULL, platform VARCHAR(10) NOT NULL, -- 'ios' | 'android' | 'expo' device_id VARCHAR(255), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), last_used_at TIMESTAMP WITH TIME ZONE, UNIQUE(user_id, token) ); -- Notification preferences CREATE TABLE notification_preferences ( user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, supervisor_questions BOOLEAN DEFAULT true, task_completions BOOLEAN DEFAULT true, task_failures BOOLEAN DEFAULT true, merge_conflicts BOOLEAN DEFAULT true, contract_updates BOOLEAN DEFAULT false, quiet_hours_start TIME, quiet_hours_end TIME, updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); ``` #### 5.3.2 Push Service Integration **Recommended: Expo Push Notification Service** ```rust // Backend integration with Expo Push API async fn send_push_notification( tokens: Vec, title: String, body: String, data: serde_json::Value, category: NotificationCategory, ) -> Result<(), PushError> { let messages: Vec = tokens.iter().map(|token| { ExpoPushMessage { to: token.clone(), title: title.clone(), body: body.clone(), data: data.clone(), sound: "default", priority: category.priority(), channel_id: category.android_channel(), category_id: category.ios_category(), } }).collect(); let client = reqwest::Client::new(); client.post("https://exp.host/--/api/v2/push/send") .json(&messages) .send() .await?; Ok(()) } ``` --- ## 6. Timeline and Resources ### 6.1 Phase 1: MVP - 4-6 Weeks **Goal:** Basic task monitoring and supervisor question responses | Week | Deliverables | |------|--------------| | 1 | Project setup, monorepo structure, shared packages extraction | | 2 | Authentication flow, API client integration | | 3 | Task list, task detail screens with real-time output | | 4 | Push notifications setup, supervisor question flow | | 5 | Polish, bug fixes, internal testing | | 6 | Beta testing, App Store/Play Store submission | **Resources:** - 1 mobile developer (full-time) - 0.5 backend developer (push notification infrastructure) - 0.25 designer (design review) **Risks:** | Risk | Mitigation | |------|------------| | Push notification delivery issues | Test early with Expo push tools | | WebSocket reliability on mobile | Implement robust reconnection logic | | App Store review delays | Submit early, prepare for feedback | ### 6.2 Phase 2: V1 - 4-6 Weeks **Goal:** Full task and contract management | Week | Deliverables | |------|--------------| | 1-2 | Contract list and detail screens | | 2-3 | Task creation and editing | | 3-4 | Offline caching with SQLite | | 4-5 | File viewing (read-only) | | 5-6 | Polish, testing, release | **Resources:** - 1 mobile developer (full-time) - 0.25 backend developer (API optimizations) ### 6.3 Phase 3: Polish - 2-4 Weeks **Goal:** Premium experience with widgets and biometrics | Week | Deliverables | |------|--------------| | 1 | iOS widgets (WidgetKit) | | 2 | Android widgets | | 3 | Biometric authentication | | 4 | Tablet layouts, performance optimization | **Resources:** - 1 mobile developer (full-time) ### 6.4 Total Timeline Summary ``` ┌─────────────────────────────────────────────────────────────┐ │ Development Timeline │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Phase 1: MVP │ │ ├─────────────────────────────────┤ │ │ Week 1 Week 4 Week 6 │ │ │ │ │ │ │ Setup ────▶ Core ────▶ Polish ────▶ Release │ │ │ │ Phase 2: V1 │ │ ├─────────────────────────────┤ │ │ Week 7 Week 10 Week 12 │ │ │ │ │ │ │ Contracts ──▶ Offline ──▶ Release │ │ │ │ Phase 3: Polish │ │ ├─────────────┤ │ │ Week 13 Week 16 │ │ │ │ │ │ Widgets ─▶ Release │ │ │ │ Total: 14-16 weeks to full feature set │ │ │ └─────────────────────────────────────────────────────────────┘ ``` --- ## Appendices ### Appendix A: Expo Configuration ```json // app.json { "expo": { "name": "Makima", "slug": "makima-mobile", "version": "1.0.0", "orientation": "portrait", "icon": "./assets/icon.png", "userInterfaceStyle": "automatic", "splash": { "image": "./assets/splash.png", "resizeMode": "contain", "backgroundColor": "#0F172A" }, "ios": { "supportsTablet": true, "bundleIdentifier": "jp.makima.mobile", "infoPlist": { "NSFaceIDUsageDescription": "Use Face ID to unlock Makima" } }, "android": { "adaptiveIcon": { "foregroundImage": "./assets/adaptive-icon.png", "backgroundColor": "#0F172A" }, "package": "jp.makima.mobile" }, "plugins": [ "expo-router", "expo-secure-store", [ "expo-notifications", { "icon": "./assets/notification-icon.png", "color": "#6366F1" } ] ] } } ``` ### Appendix B: Dependencies ```json // package.json (apps/mobile) { "dependencies": { "expo": "~50.0.0", "expo-router": "~3.4.0", "expo-secure-store": "~12.8.0", "expo-notifications": "~0.27.0", "expo-device": "~5.9.0", "expo-local-authentication": "~13.8.0", "@supabase/supabase-js": "^2.90.0", "react": "18.2.0", "react-native": "0.73.0", "react-native-safe-area-context": "4.8.0", "@react-navigation/native": "^6.0.0", "@react-navigation/bottom-tabs": "^6.0.0", "react-native-reanimated": "~3.6.0", "react-native-gesture-handler": "~2.14.0", "@tanstack/react-query": "^5.0.0", "zustand": "^4.5.0" }, "devDependencies": { "@types/react": "~18.2.0", "typescript": "^5.3.0", "jest": "^29.0.0", "@testing-library/react-native": "^12.0.0" } } ``` ### Appendix C: State Management ```typescript // apps/mobile/src/stores/taskStore.ts import { create } from 'zustand'; import { persist, createJSONStorage } from 'zustand/middleware'; import AsyncStorage from '@react-native-async-storage/async-storage'; interface TaskState { tasks: TaskSummary[]; selectedTaskId: string | null; taskOutputs: Record; // Actions setTasks: (tasks: TaskSummary[]) => void; updateTask: (taskId: string, update: Partial) => void; appendOutput: (taskId: string, output: TaskOutputEntry) => void; selectTask: (taskId: string | null) => void; } export const useTaskStore = create()( persist( (set) => ({ tasks: [], selectedTaskId: null, taskOutputs: {}, setTasks: (tasks) => set({ tasks }), updateTask: (taskId, update) => set((state) => ({ tasks: state.tasks.map((t) => t.id === taskId ? { ...t, ...update } : t ), })), appendOutput: (taskId, output) => set((state) => ({ taskOutputs: { ...state.taskOutputs, [taskId]: [...(state.taskOutputs[taskId] || []), output].slice(-1000), }, })), selectTask: (taskId) => set({ selectedTaskId: taskId }), }), { name: 'makima-tasks', storage: createJSONStorage(() => AsyncStorage), partialize: (state) => ({ tasks: state.tasks.slice(0, 50), // Cache last 50 tasks }), } ) ); ``` ### Appendix D: Testing Strategy **Unit Tests:** - Shared package functions (API client, utilities) - State management logic (Zustand stores) - Component rendering (React Native Testing Library) **Integration Tests:** - API client with mock server - WebSocket message handling - Authentication flow **E2E Tests (Detox):** - Login flow - Task list navigation - Supervisor question response - Push notification handling **Manual Testing Checklist:** - [ ] App launches in < 3 seconds - [ ] Task list shows real-time updates - [ ] Push notifications arrive promptly - [ ] Supervisor questions can be answered - [ ] App handles network changes gracefully - [ ] Dark mode works correctly - [ ] VoiceOver/TalkBack navigable --- *Document generated for Makima Mobile Frontend contract - Specify phase* *Last updated: 2026-01-17*