summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-17 07:30:19 +0000
committersoryu <soryu@soryu.co>2026-01-17 07:30:19 +0000
commitcdda39ff51521f1ba8f17195c43e40a008544e50 (patch)
treea5b71c419dfc2e514adb2137219369a8f44d475a
parentf84a7f2d820f6f432be2b1d78d6bf833b5b19380 (diff)
downloadsoryu-makima/task-task-9b78e317-9b78e317.tar.gz
soryu-makima/task-task-9b78e317-9b78e317.zip
Task completion checkpointmakima/task-task-9b78e317-9b78e317
-rw-r--r--docs/mobile-frontend-spec.md1382
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*