# 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<string | null>;
getApiKey: () => string | null;
}
export function createApiClient(config: ApiConfig) {
async function authFetch(url: string, options: RequestInit = {}): Promise<Response> {
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<String>,
title: String,
body: String,
data: serde_json::Value,
category: NotificationCategory,
) -> Result<(), PushError> {
let messages: Vec<ExpoPushMessage> = 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<string, TaskOutputEntry[]>;
// Actions
setTasks: (tasks: TaskSummary[]) => void;
updateTask: (taskId: string, update: Partial<TaskSummary>) => void;
appendOutput: (taskId: string, output: TaskOutputEntry) => void;
selectTask: (taskId: string | null) => void;
}
export const useTaskStore = create<TaskState>()(
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*