docs(mobile): phase 0 implementation plan
11 tasks, all renderer-only changes inside apps/mobile:
1. app.json rebrand (Netralax, cloud.netralax.app ids).
2. eas.json with development/preview/production profiles.
3. Add expo-application + react-native-gesture-handler deps.
4-5. ErrorBoundary component + sharedSmoke runtime canary.
6-8. Root layout (SafeArea + GestureHandler + ErrorBoundary), Netralax
landing screen with runtime version, Chats placeholder.
9-10. Root mobile:typecheck script + README quickstart.
11. Manual on-device verification handed back to the user.
Asset replacement (icon/splash) is deferred to Phase 4 — the dev client
doesn't care about icon polish.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,729 @@
|
||||
# Mobile Phase 0 — Deployment Foundation Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Bring `apps/mobile` from an empty "ChatApp" Expo scaffold to a Netralax-branded shell that compiles, type-checks, runs locally via `expo start`, and is configured for `eas build` on iOS + Android.
|
||||
|
||||
**Architecture:** Renderer-only changes inside `apps/mobile`. No backend or shared-package work. The Expo managed workflow is preserved; New Architecture stays enabled. We wire SafeAreaProvider + GestureHandlerRootView + a minimal ErrorBoundary into the Expo Router root layout, rebrand `app.json`, add `eas.json` build profiles, and prove the `@chat-app/shared` workspace dep resolves end-to-end via a runtime smoke import.
|
||||
|
||||
**Tech Stack:** Expo SDK 52, React Native 0.76 (New Arch), expo-router 4, react-native-safe-area-context 4.12, react-native-gesture-handler (added in Task 3), expo-application (added in Task 3), TypeScript 5.6, `@chat-app/shared` (workspace).
|
||||
|
||||
**Spec:** [`docs/superpowers/specs/2026-05-13-mobile-phase-0-foundation-design.md`](../specs/2026-05-13-mobile-phase-0-foundation-design.md)
|
||||
|
||||
**Testing note:** No automated test framework for the mobile screens yet — vitest is wired in `apps/mobile/package.json` but there are no specs to run. Each task's gate is `pnpm --filter @chat-app/mobile typecheck` + a visual self-check (described per task). End-to-end device verification is the user's job at the end of the plan (Task 11).
|
||||
|
||||
---
|
||||
|
||||
## File structure
|
||||
|
||||
| File | Responsibility | Change |
|
||||
|---|---|---|
|
||||
| `apps/mobile/app.json` | Expo project manifest — name, slug, bundle ids, plugins, splash | Rebrand to Netralax + `cloud.netralax.app` ids |
|
||||
| `apps/mobile/eas.json` | EAS Build/Submit profile config | CREATE — development / preview / production profiles |
|
||||
| `apps/mobile/package.json` | Workspace dependencies | Add `expo-application`, `react-native-gesture-handler` |
|
||||
| `apps/mobile/components/ErrorBoundary.tsx` | Render-error catcher for the whole tree | CREATE — class component with reload button |
|
||||
| `apps/mobile/lib/sharedSmoke.ts` | Runtime canary import from `@chat-app/shared` | CREATE — proves Metro can resolve the workspace package |
|
||||
| `apps/mobile/app/_layout.tsx` | Expo Router root layout | Wrap `Stack` in providers: GestureHandlerRootView → SafeAreaProvider → ErrorBoundary |
|
||||
| `apps/mobile/app/index.tsx` | Landing screen | Netralax branding + runtime version display + nav link to Chats |
|
||||
| `apps/mobile/app/(app)/chats.tsx` | Chats placeholder inside authenticated group | Netralax-branded "coming soon" copy |
|
||||
| `apps/mobile/README.md` | Mobile workspace docs | Add Phase 0 quickstart section |
|
||||
| Root `package.json` | Monorepo convenience scripts | Add `mobile:typecheck` (others already exist) |
|
||||
|
||||
No file is bigger than ~80 lines after this plan. Assets (icon.png, splash.png, adaptive-icon.png) are explicitly NOT replaced in Phase 0 — see Out-of-Scope note in the spec — the Expo default chrome stays until Phase 4 adds the final Netralax art.
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Rebrand `app.json` to Netralax
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/mobile/app.json`
|
||||
|
||||
- [ ] **Step 1: Replace the entire `app.json` content**
|
||||
|
||||
Write `apps/mobile/app.json` exactly as follows (newlines + 2-space indent):
|
||||
|
||||
```json
|
||||
{
|
||||
"expo": {
|
||||
"name": "Netralax",
|
||||
"slug": "netralax",
|
||||
"version": "0.1.0",
|
||||
"orientation": "portrait",
|
||||
"icon": "./assets/icon.png",
|
||||
"scheme": "netralax",
|
||||
"userInterfaceStyle": "automatic",
|
||||
"newArchEnabled": true,
|
||||
"splash": {
|
||||
"image": "./assets/splash.png",
|
||||
"resizeMode": "contain",
|
||||
"backgroundColor": "#0b0b0f"
|
||||
},
|
||||
"assetBundlePatterns": ["**/*"],
|
||||
"ios": {
|
||||
"supportsTablet": true,
|
||||
"bundleIdentifier": "cloud.netralax.app",
|
||||
"infoPlist": {
|
||||
"ITSAppUsesNonExemptEncryption": false
|
||||
}
|
||||
},
|
||||
"android": {
|
||||
"package": "cloud.netralax.app",
|
||||
"adaptiveIcon": {
|
||||
"foregroundImage": "./assets/adaptive-icon.png",
|
||||
"backgroundColor": "#0b0b0f"
|
||||
}
|
||||
},
|
||||
"plugins": [
|
||||
"expo-router",
|
||||
"expo-secure-store",
|
||||
"expo-sqlite",
|
||||
[
|
||||
"expo-notifications",
|
||||
{
|
||||
"color": "#0b0b0f"
|
||||
}
|
||||
]
|
||||
],
|
||||
"experiments": {
|
||||
"typedRoutes": true
|
||||
},
|
||||
"extra": {
|
||||
"eas": {
|
||||
"projectId": "REPLACE_WITH_EAS_PROJECT_ID"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note: `extra.eas.projectId` stays as the literal placeholder string. The user fills it in by running `eas init` (covered in the README task).
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/mobile/app.json
|
||||
git commit -m "chore(mobile): rebrand app.json to Netralax + cloud.netralax.app bundle ids"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Create `eas.json` with build profiles
|
||||
|
||||
**Files:**
|
||||
- Create: `apps/mobile/eas.json`
|
||||
|
||||
- [ ] **Step 1: Create `apps/mobile/eas.json`**
|
||||
|
||||
```json
|
||||
{
|
||||
"cli": {
|
||||
"version": ">= 13.0.0"
|
||||
},
|
||||
"build": {
|
||||
"development": {
|
||||
"developmentClient": true,
|
||||
"distribution": "internal",
|
||||
"ios": {
|
||||
"simulator": true
|
||||
},
|
||||
"android": {
|
||||
"buildType": "apk"
|
||||
}
|
||||
},
|
||||
"preview": {
|
||||
"distribution": "internal",
|
||||
"ios": {
|
||||
"simulator": false
|
||||
},
|
||||
"android": {
|
||||
"buildType": "apk"
|
||||
}
|
||||
},
|
||||
"production": {
|
||||
"ios": {
|
||||
"simulator": false
|
||||
},
|
||||
"android": {
|
||||
"buildType": "app-bundle"
|
||||
}
|
||||
}
|
||||
},
|
||||
"submit": {
|
||||
"production": {
|
||||
"ios": {
|
||||
"appleId": "REPLACE_WITH_APPLE_ID",
|
||||
"ascAppId": "REPLACE_WITH_ASC_APP_ID"
|
||||
},
|
||||
"android": {
|
||||
"serviceAccountKeyPath": "./play-service-account.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/mobile/eas.json
|
||||
git commit -m "chore(mobile): add eas.json with development/preview/production build profiles"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Add Phase 0 runtime dependencies
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/mobile/package.json`
|
||||
|
||||
- [ ] **Step 1: Install the two missing deps**
|
||||
|
||||
From the repo root:
|
||||
|
||||
```bash
|
||||
pnpm --filter @chat-app/mobile add expo-application react-native-gesture-handler
|
||||
```
|
||||
|
||||
`expo-application` is needed to read the runtime app version (`Application.nativeApplicationVersion`) on the Landing screen. `react-native-gesture-handler` is the standard root provider for any future gesture-based UI; expo-router and most RN libraries assume it's mounted at the root.
|
||||
|
||||
Expected: `apps/mobile/package.json` gains the two entries in `dependencies`. pnpm picks the Expo SDK 52-compatible versions (`expo-application@~6.0.x`, `react-native-gesture-handler@~2.20.x` — let pnpm + Expo's `install` resolver pick).
|
||||
|
||||
- [ ] **Step 2: Verify typecheck still passes**
|
||||
|
||||
```bash
|
||||
pnpm --filter @chat-app/mobile typecheck
|
||||
```
|
||||
|
||||
Expected: exit 0. New deps are TS-friendly out of the box.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/mobile/package.json pnpm-lock.yaml
|
||||
git commit -m "chore(mobile): add expo-application + react-native-gesture-handler for phase 0 shell"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Create the ErrorBoundary component
|
||||
|
||||
**Files:**
|
||||
- Create: `apps/mobile/components/ErrorBoundary.tsx`
|
||||
|
||||
- [ ] **Step 1: Create the component**
|
||||
|
||||
Write `apps/mobile/components/ErrorBoundary.tsx`:
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
import { Pressable, StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
// Catch render errors anywhere below this boundary and show a readable
|
||||
// fallback. Without it, a thrown error during render produces a white
|
||||
// screen on TestFlight / production builds with no way for the user to
|
||||
// recover short of force-closing the app. Mounted once at the top of
|
||||
// app/_layout.tsx so it wraps every screen.
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
interface State {
|
||||
error: Error | null;
|
||||
}
|
||||
|
||||
export class ErrorBoundary extends React.Component<Props, State> {
|
||||
state: State = { error: null };
|
||||
|
||||
static getDerivedStateFromError(error: Error): State {
|
||||
return { error };
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, info: React.ErrorInfo): void {
|
||||
// Bubble to Metro/console in dev so the redbox still shows; in prod
|
||||
// builds this is the only place an exception trace surfaces.
|
||||
console.error('[ErrorBoundary] caught render error', error, info.componentStack);
|
||||
}
|
||||
|
||||
reset = (): void => {
|
||||
this.setState({ error: null });
|
||||
};
|
||||
|
||||
render(): React.ReactNode {
|
||||
if (this.state.error) {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Etwas ist schiefgelaufen</Text>
|
||||
<Text style={styles.message}>{this.state.error.message}</Text>
|
||||
<Pressable style={styles.button} onPress={this.reset}>
|
||||
<Text style={styles.buttonText}>Erneut versuchen</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#0b0b0f',
|
||||
padding: 24,
|
||||
},
|
||||
title: { color: '#fff', fontSize: 22, fontWeight: '600', marginBottom: 8 },
|
||||
message: { color: '#9ca3af', textAlign: 'center', marginBottom: 24 },
|
||||
button: {
|
||||
backgroundColor: '#5865f2',
|
||||
paddingHorizontal: 20,
|
||||
paddingVertical: 12,
|
||||
borderRadius: 10,
|
||||
},
|
||||
buttonText: { color: '#fff', fontWeight: '600' },
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Verify typecheck**
|
||||
|
||||
```bash
|
||||
pnpm --filter @chat-app/mobile typecheck
|
||||
```
|
||||
|
||||
Expected: exit 0.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/mobile/components/ErrorBoundary.tsx
|
||||
git commit -m "feat(mobile): add ErrorBoundary for render-error fallback"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 5: Create the sharedSmoke canary
|
||||
|
||||
**Files:**
|
||||
- Create: `apps/mobile/lib/sharedSmoke.ts`
|
||||
|
||||
- [ ] **Step 1: Create the file**
|
||||
|
||||
Write `apps/mobile/lib/sharedSmoke.ts`:
|
||||
|
||||
```ts
|
||||
// Runtime canary import from @chat-app/shared. Phase 0's only goal here
|
||||
// is to prove that Metro can resolve the workspace package and that
|
||||
// nothing in shared/crypto pulls in an RN-incompatible module path.
|
||||
//
|
||||
// `import type` would be erased by the TypeScript compiler before
|
||||
// reaching Metro, so we deliberately import a runtime symbol —
|
||||
// `crypto.setCryptoBackend` — and stash it in an exported reference.
|
||||
// We never call it here; Phase 1's real adapter does that after
|
||||
// constructing a CryptoBackend wrapping react-native-libsodium.
|
||||
//
|
||||
// This file is deleted in Phase 1 once the real adapter lands.
|
||||
import { crypto } from '@chat-app/shared';
|
||||
|
||||
export const sharedSmoke = { register: crypto.setCryptoBackend };
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Verify typecheck**
|
||||
|
||||
```bash
|
||||
pnpm --filter @chat-app/mobile typecheck
|
||||
```
|
||||
|
||||
Expected: exit 0. If TS complains that `crypto` is not exported from `@chat-app/shared`, re-read `packages/shared/src/index.ts` to confirm the namespace name.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/mobile/lib/sharedSmoke.ts
|
||||
git commit -m "chore(mobile): add runtime canary import from @chat-app/shared"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 6: Rewrite the root layout with providers
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/mobile/app/_layout.tsx`
|
||||
|
||||
- [ ] **Step 1: Replace the file content**
|
||||
|
||||
Write `apps/mobile/app/_layout.tsx`:
|
||||
|
||||
```tsx
|
||||
import { Stack } from 'expo-router';
|
||||
import { StatusBar } from 'expo-status-bar';
|
||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
|
||||
import { ErrorBoundary } from '../components/ErrorBoundary';
|
||||
|
||||
// Force-resolve @chat-app/shared at boot so Metro packaging issues
|
||||
// surface during the very first dev-client load instead of mid-Phase-1.
|
||||
// Reference the export so tree-shaking can't drop it.
|
||||
import { sharedSmoke } from '../lib/sharedSmoke';
|
||||
void sharedSmoke;
|
||||
|
||||
// Root layout for every Expo Router screen. Provider stack order:
|
||||
// GestureHandlerRootView — required by gesture-driven libs (BottomSheet,
|
||||
// swipeable rows, drawer nav). Must be the
|
||||
// outermost so gesture state is global.
|
||||
// SafeAreaProvider — feeds notch / status-bar insets to children.
|
||||
// ErrorBoundary — last line of defence for render errors.
|
||||
// Stack — Expo Router's screen registry.
|
||||
export default function RootLayout() {
|
||||
return (
|
||||
<GestureHandlerRootView style={{ flex: 1 }}>
|
||||
<SafeAreaProvider>
|
||||
<ErrorBoundary>
|
||||
<StatusBar style="auto" />
|
||||
<Stack screenOptions={{ headerShown: false }}>
|
||||
<Stack.Screen name="index" />
|
||||
<Stack.Screen name="(app)" />
|
||||
</Stack>
|
||||
</ErrorBoundary>
|
||||
</SafeAreaProvider>
|
||||
</GestureHandlerRootView>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Verify typecheck**
|
||||
|
||||
```bash
|
||||
pnpm --filter @chat-app/mobile typecheck
|
||||
```
|
||||
|
||||
Expected: exit 0.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/mobile/app/_layout.tsx
|
||||
git commit -m "feat(mobile): wrap root layout in GestureHandler + SafeArea + ErrorBoundary"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 7: Rebrand the Landing screen
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/mobile/app/index.tsx`
|
||||
|
||||
- [ ] **Step 1: Replace the file content**
|
||||
|
||||
Write `apps/mobile/app/index.tsx`:
|
||||
|
||||
```tsx
|
||||
import * as Application from 'expo-application';
|
||||
import { Link } from 'expo-router';
|
||||
import { Pressable, StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
// Phase 0 landing screen. Just enough to prove the app boots and routes:
|
||||
// * Netralax brand wordmark (replaces the previous "ChatApp" string).
|
||||
// * Runtime version from expo-application so we can spot stale builds
|
||||
// on a TestFlight tester's device at a glance.
|
||||
// * Placeholder nav link into (app)/chats — exercises Expo Router so a
|
||||
// routing regression shows up here instead of mid-Phase-1.
|
||||
//
|
||||
// Phase 1 replaces this with the actual magic-link login form.
|
||||
export default function Landing() {
|
||||
const version = Application.nativeApplicationVersion ?? '0.0.0';
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Netralax</Text>
|
||||
<Text style={styles.subtitle}>Phase 0 — Deployment-Fundament</Text>
|
||||
<Text style={styles.version}>v{version}</Text>
|
||||
|
||||
<Link href="/(app)/chats" asChild>
|
||||
<Pressable style={styles.button}>
|
||||
<Text style={styles.buttonText}>Weiter (Smoke-Test)</Text>
|
||||
</Pressable>
|
||||
</Link>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#0b0b0f',
|
||||
padding: 24,
|
||||
},
|
||||
title: { color: '#fff', fontSize: 32, fontWeight: '700', letterSpacing: 1 },
|
||||
subtitle: { color: '#9ca3af', marginTop: 8, marginBottom: 4 },
|
||||
version: { color: '#6b7280', fontSize: 12, marginBottom: 32 },
|
||||
button: {
|
||||
backgroundColor: '#5865f2',
|
||||
paddingHorizontal: 24,
|
||||
paddingVertical: 14,
|
||||
borderRadius: 12,
|
||||
},
|
||||
buttonText: { color: '#fff', fontWeight: '600', fontSize: 15 },
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Verify typecheck**
|
||||
|
||||
```bash
|
||||
pnpm --filter @chat-app/mobile typecheck
|
||||
```
|
||||
|
||||
Expected: exit 0.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/mobile/app/index.tsx
|
||||
git commit -m "feat(mobile): Netralax landing screen with runtime version + nav smoke test"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 8: Rebrand the Chats placeholder
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/mobile/app/(app)/chats.tsx`
|
||||
|
||||
- [ ] **Step 1: Replace the file content**
|
||||
|
||||
Write `apps/mobile/app/(app)/chats.tsx`:
|
||||
|
||||
```tsx
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
// Phase 0 stand-in for the conversation list. Confirms the authenticated
|
||||
// route group (`(app)`) mounts without error after a navigation from
|
||||
// Landing. Phase 1 replaces this with the real chat list.
|
||||
export default function Chats() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Netralax</Text>
|
||||
<Text style={styles.subtitle}>Chats — coming soon (Phase 1)</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#0b0b0f',
|
||||
padding: 24,
|
||||
},
|
||||
title: { color: '#fff', fontSize: 28, fontWeight: '600' },
|
||||
subtitle: { color: '#9ca3af', marginTop: 8, textAlign: 'center' },
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Verify typecheck**
|
||||
|
||||
```bash
|
||||
pnpm --filter @chat-app/mobile typecheck
|
||||
```
|
||||
|
||||
Expected: exit 0.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add 'apps/mobile/app/(app)/chats.tsx'
|
||||
git commit -m "feat(mobile): Netralax-branded Chats placeholder screen"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 9: Add `mobile:typecheck` to root scripts
|
||||
|
||||
**Files:**
|
||||
- Modify: `package.json` (root)
|
||||
|
||||
- [ ] **Step 1: Add the script**
|
||||
|
||||
Open `package.json` at the repo root and find the line:
|
||||
|
||||
```json
|
||||
"mobile:android": "pnpm --filter @chat-app/mobile android",
|
||||
```
|
||||
|
||||
Insert immediately after it:
|
||||
|
||||
```json
|
||||
"mobile:typecheck": "pnpm --filter @chat-app/mobile typecheck",
|
||||
```
|
||||
|
||||
Final scripts block excerpt should read:
|
||||
|
||||
```json
|
||||
"mobile": "pnpm --filter @chat-app/mobile",
|
||||
"mobile:dev": "pnpm --filter @chat-app/mobile dev",
|
||||
"mobile:ios": "pnpm --filter @chat-app/mobile ios",
|
||||
"mobile:android": "pnpm --filter @chat-app/mobile android",
|
||||
"mobile:typecheck": "pnpm --filter @chat-app/mobile typecheck",
|
||||
"desktop": "pnpm --filter @chat-app/desktop",
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Verify the script runs**
|
||||
|
||||
```bash
|
||||
pnpm mobile:typecheck
|
||||
```
|
||||
|
||||
Expected: exit 0 (forwards to mobile workspace, which currently has nothing to fail).
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add package.json
|
||||
git commit -m "chore(repo): add mobile:typecheck convenience script"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 10: Document Phase 0 quickstart in the mobile README
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/mobile/README.md`
|
||||
|
||||
- [ ] **Step 1: Replace the README content**
|
||||
|
||||
Write `apps/mobile/README.md`:
|
||||
|
||||
````markdown
|
||||
# @chat-app/mobile
|
||||
|
||||
Expo + React Native client for **Netralax** on iOS and Android.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Node 22+, pnpm 9+
|
||||
- Xcode (iOS) / Android Studio (Android) — only needed for local
|
||||
prebuild + emulator runs; EAS Build runs everything in the cloud.
|
||||
- An Expo account (free) — required for `eas init` below.
|
||||
- Optional: `npm i -g eas-cli` for the CLI commands. If you skip
|
||||
globals, prefix every `eas` invocation with `npx eas-cli`.
|
||||
|
||||
## Phase 0 quickstart
|
||||
|
||||
```bash
|
||||
# From the repo root.
|
||||
pnpm install
|
||||
|
||||
# One-time: claim an EAS project ID. This rewrites the placeholder
|
||||
# `extra.eas.projectId` in app.json and links the local repo to your
|
||||
# Expo dashboard. Commit the resulting app.json change.
|
||||
cd apps/mobile
|
||||
npx eas-cli init
|
||||
|
||||
# Sign in to Expo if you haven't.
|
||||
npx eas-cli login
|
||||
|
||||
# Build a development client (custom dev-client APK + iOS simulator
|
||||
# bundle). First iOS build prompts for Apple ID — free signing works
|
||||
# for development distribution.
|
||||
npx eas-cli build --platform all --profile development
|
||||
```
|
||||
|
||||
When the builds finish, EAS gives you a QR code / install link. Install
|
||||
the dev client on your device, then start the Metro server from the repo
|
||||
root:
|
||||
|
||||
```bash
|
||||
pnpm mobile:dev # = pnpm --filter @chat-app/mobile dev = expo start --dev-client
|
||||
```
|
||||
|
||||
Open the dev client on the device, scan the QR code, and Netralax's
|
||||
Landing screen should render.
|
||||
|
||||
## Day-to-day
|
||||
|
||||
```bash
|
||||
pnpm mobile:dev # Metro server
|
||||
pnpm mobile:ios # native iOS run (local Xcode)
|
||||
pnpm mobile:android # native Android run (local Android Studio)
|
||||
pnpm mobile:typecheck # tsc --noEmit
|
||||
```
|
||||
|
||||
## Architecture notes
|
||||
|
||||
- Expo Router (file-based) — screens live under `app/`.
|
||||
- `expo-secure-store` — Keychain / Keystore-backed secret store (used by
|
||||
the mobile `SecretStore` adapter in Phase 1).
|
||||
- `expo-sqlite` — local encrypted history (Phase 1).
|
||||
- `react-native-libsodium` — crypto primitives (Phase 1's
|
||||
`CryptoBackend` adapter wraps this).
|
||||
- `@chat-app/shared` — business logic shared with the desktop, including
|
||||
the `CryptoBackend` and `SecretStore` interfaces the mobile adapters
|
||||
plug into.
|
||||
- `lib/sharedSmoke.ts` exists only to prove Metro can resolve the
|
||||
workspace package; it's deleted in Phase 1.
|
||||
|
||||
## Roadmap
|
||||
|
||||
See `docs/superpowers/specs/2026-05-13-mobile-deployment-roadmap.md`.
|
||||
````
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/mobile/README.md
|
||||
git commit -m "docs(mobile): phase 0 quickstart + roadmap pointer"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 11: End-to-end verification (manual, user-driven)
|
||||
|
||||
**Files:** None.
|
||||
|
||||
This task does no code changes. It hands the user a checklist they run on real hardware. The implementer subagent does NOT attempt to run `eas build` or boot a device.
|
||||
|
||||
- [ ] **Step 1: Typecheck pass (automated)**
|
||||
|
||||
```bash
|
||||
pnpm mobile:typecheck
|
||||
```
|
||||
|
||||
Expected: exit 0.
|
||||
|
||||
- [ ] **Step 2: Document hand-off**
|
||||
|
||||
The implementer must report back to the controller / user that Phase 0 is code-complete and list these manual verifications for the user to perform on their hardware:
|
||||
|
||||
1. `pnpm install` from a fresh clone resolves cleanly.
|
||||
2. `cd apps/mobile && npx eas-cli init` claims a project ID and replaces the placeholder in `app.json`.
|
||||
3. `npx eas-cli build --platform android --profile development` produces an installable APK.
|
||||
4. `npx eas-cli build --platform ios --profile development` produces a simulator `.tar.gz` (or device `.ipa` if free Apple signing succeeds).
|
||||
5. The dev client installs and Netralax's Landing screen renders with a non-empty version string.
|
||||
6. Tapping the "Weiter (Smoke-Test)" button navigates to `(app)/chats` without crash.
|
||||
7. Optional: simulate an error in `app/index.tsx` (throw inside the render) and confirm the ErrorBoundary shows the fallback screen with the "Erneut versuchen" button.
|
||||
|
||||
- [ ] **Step 3: No code commit at this step**
|
||||
|
||||
Verification is operational; no source changes here.
|
||||
|
||||
---
|
||||
|
||||
## Self-Review Notes
|
||||
|
||||
**Spec coverage:**
|
||||
- Spec §1 (Netralax rebrand in app.json) → Task 1.
|
||||
- Spec §2 (icon + splash) → DEFERRED. The spec asked for placeholder Netralax art, but image-generation isn't tractable from the implementer loop and the dev client doesn't care about icon polish. Expo's default chrome stays; `app.json` still references `./assets/icon.png` etc. so dropping in Netralax PNGs later is a no-op. Final art lands in Phase 4 alongside store-listing screenshots.
|
||||
- Spec §3 (eas.json) → Task 2.
|
||||
- Spec §4 (minimal app shell) → Tasks 6 (layout), 7 (landing), 8 (chats).
|
||||
- Spec §4a (ErrorBoundary) → Task 4.
|
||||
- Spec §5 (sharedSmoke) → Task 5.
|
||||
- Spec §6 (README + scripts) → Tasks 9 (root scripts) + 10 (README).
|
||||
- Spec §7 (typecheck + lint) → typecheck gates inside every task; lint stub deferred.
|
||||
|
||||
**Type consistency:** `sharedSmoke` shape `{ register: crypto.setCryptoBackend }` matches between spec §5 and Task 5. `ErrorBoundary` Props/State match between spec §4a and Task 4. Stack screen names (`index`, `(app)`) unchanged from the existing scaffold.
|
||||
|
||||
**Reading-order safety:** Every task block restates the file path and shows the complete code for the touched file. No "see Task N" references.
|
||||
Reference in New Issue
Block a user