diff options
| author | soryu <soryu@soryu.co> | 2026-01-17 07:30:19 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-01-17 07:30:19 +0000 |
| commit | cdda39ff51521f1ba8f17195c43e40a008544e50 (patch) | |
| tree | a5b71c419dfc2e514adb2137219369a8f44d475a | |
| parent | f84a7f2d820f6f432be2b1d78d6bf833b5b19380 (diff) | |
| download | soryu-cdda39ff51521f1ba8f17195c43e40a008544e50.tar.gz soryu-cdda39ff51521f1ba8f17195c43e40a008544e50.zip | |
Task completion checkpointmakima/task-task-9b78e317-9b78e317
| -rw-r--r-- | docs/mobile-frontend-spec.md | 1382 |
1 files changed, 1382 insertions, 0 deletions
diff --git a/docs/mobile-frontend-spec.md b/docs/mobile-frontend-spec.md new file mode 100644 index 0000000..21425e9 --- /dev/null +++ b/docs/mobile-frontend-spec.md @@ -0,0 +1,1382 @@ +# 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* |
