An easy-to-host PDS on the ATProtocol, iPhone and MacOS. Maintain control of your keys and data, always.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix(MM-143): address PR review feedback

- Remove security.csp=null from tauri.conf.json — restores Tauri's
restrictive default CSP (critical for an identity wallet)
- Fix package.json version 0.0.1 → 0.1.0 to match workspace/tauri.conf
- Fix vite.config.ts host fallback '0.0.0.0' → 'localhost' so standalone
pnpm dev does not expose the dev server to the LAN
- Add #[cfg(test)] block to lib.rs with 3 greet tests (normal, empty,
special chars) establishing the testing pattern for src-tauri
- Commit docs/implementation-plans/2026-03-14-MM-143/ (was untracked)

authored by

Malpercio and committed by
Tangled
7e7b5bc1 91a1178e

+1806 -7
+1 -1
apps/identity-wallet/package.json
··· 1 1 { 2 2 "name": "identity-wallet", 3 - "version": "0.0.1", 3 + "version": "0.1.0", 4 4 "private": true, 5 5 "type": "module", 6 6 "scripts": {
+23
apps/identity-wallet/src-tauri/src/lib.rs
··· 10 10 .run(tauri::generate_context!()) 11 11 .expect("error while running tauri application"); 12 12 } 13 + 14 + #[cfg(test)] 15 + mod tests { 16 + use super::*; 17 + 18 + #[test] 19 + fn greet_formats_name() { 20 + assert_eq!(greet("World".to_string()), "Hello, World!"); 21 + } 22 + 23 + #[test] 24 + fn greet_empty_name() { 25 + assert_eq!(greet(String::new()), "Hello, !"); 26 + } 27 + 28 + #[test] 29 + fn greet_special_characters() { 30 + assert_eq!( 31 + greet("<script>alert(1)</script>".to_string()), 32 + "Hello, <script>alert(1)</script>!" 33 + ); 34 + } 35 + }
+1 -4
apps/identity-wallet/src-tauri/tauri.conf.json
··· 17 17 "height": 600, 18 18 "resizable": true 19 19 } 20 - ], 21 - "security": { 22 - "csp": null 23 - } 20 + ] 24 21 }, 25 22 "bundle": { 26 23 "active": true
+3 -2
apps/identity-wallet/vite.config.ts
··· 9 9 port: 5173, 10 10 strictPort: true, 11 11 // TAURI_DEV_HOST is set by `cargo tauri ios dev` to the machine's LAN IP; 12 - // the iOS simulator connects to the dev server over LAN, not localhost 13 - host: process.env.TAURI_DEV_HOST || '0.0.0.0', 12 + // the iOS simulator connects to the dev server over LAN, not localhost. 13 + // Falls back to 'localhost' (not '0.0.0.0') so standalone `pnpm dev` doesn't expose to the LAN. 14 + host: process.env.TAURI_DEV_HOST || 'localhost', 14 15 hmr: process.env.TAURI_DEV_HOST 15 16 ? { 16 17 protocol: 'ws',
+406
docs/implementation-plans/2026-03-14-MM-143/phase_01.md
··· 1 + # MM-143: Tauri Mobile Project Scaffolding — Phase 1: Project Scaffolding + Workspace Integration 2 + 3 + **Goal:** Establish `apps/identity-wallet/` with a SvelteKit 2 + Svelte 5 static frontend and a stub Rust crate integrated into the Cargo workspace so `cargo build` and `pnpm build` both succeed. 4 + 5 + **Architecture:** SvelteKit 2 frontend configured as a fully static SPA (adapter-static with `fallback: 'index.html'`, SSR disabled) — shaped to be loaded by Tauri's WebView in Phase 2. Stub Rust crate at `apps/identity-wallet/src-tauri/` declares `crate-type = ["staticlib", "cdylib", "rlib"]` to satisfy iOS/Android/desktop build requirements from day one. The crate inherits `version`, `edition`, and `publish` from `[workspace.package]`, matching how all existing crates are configured. 6 + 7 + **Tech Stack:** SvelteKit 2, Svelte 5, TypeScript, Vite 5, pnpm, Rust (stable), Cargo workspace resolver v2 8 + 9 + **Scope:** Phase 1 of 3 — infrastructure only. No tests. Verified by running `cargo build`, `cargo clippy`, `cargo fmt --check`, and `pnpm build`. 10 + 11 + **Codebase verified:** 2026-03-14 12 + 13 + --- 14 + 15 + ## Acceptance Criteria Coverage 16 + 17 + This phase implements and verifies operationally (no unit tests — infrastructure phase): 18 + 19 + ### MM-143.AC1: Project directory structure exists 20 + - **MM-143.AC1.1 Success (partial):** `apps/identity-wallet/` contains `package.json`, `svelte.config.js`, `vite.config.ts`, `src/app.html`, `src/routes/+layout.ts`, `src/routes/+layout.svelte`, `src/routes/+page.svelte` — `src/lib/ipc.ts` is added in Phase 2 21 + - **MM-143.AC1.2 Success (partial):** `apps/identity-wallet/src-tauri/` contains `Cargo.toml`, `src/lib.rs`, `src/main.rs` — `tauri.conf.json` and `build.rs` are added in Phase 2 22 + - **MM-143.AC1.3 Success:** SvelteKit version is 2.x and Svelte version is 5.x (verified in `package.json`) 23 + 24 + ### MM-143.AC2: Cargo workspace build succeeds 25 + - **MM-143.AC2.1 Success:** `apps/identity-wallet/src-tauri` appears in root `Cargo.toml` `[workspace] members` 26 + - **MM-143.AC2.2 Success:** `cargo build` at workspace root completes without errors 27 + - **MM-143.AC2.3 Success:** `cargo clippy --workspace -- -D warnings` passes 28 + - **MM-143.AC2.4 Success:** `cargo fmt --all --check` passes 29 + - **MM-143.AC2.5 Failure:** Adding `src-tauri` to workspace members does not introduce errors in existing crates (`relay`, `repo-engine`, `crypto`, `common`) 30 + 31 + --- 32 + 33 + <!-- START_SUBCOMPONENT_A (tasks 1-2) --> 34 + 35 + <!-- START_TASK_1 --> 36 + ### Task 1: Create SvelteKit 2 + Svelte 5 frontend project 37 + 38 + **Verifies:** None (infrastructure task — operational verification only) 39 + 40 + **Files:** 41 + - Create: `apps/identity-wallet/package.json` 42 + - Create: `apps/identity-wallet/svelte.config.js` 43 + - Create: `apps/identity-wallet/vite.config.ts` 44 + - Create: `apps/identity-wallet/tsconfig.json` 45 + - Create: `apps/identity-wallet/src/app.html` 46 + - Create: `apps/identity-wallet/src/routes/+layout.ts` 47 + - Create: `apps/identity-wallet/src/routes/+layout.svelte` 48 + - Create: `apps/identity-wallet/src/routes/+page.svelte` 49 + - Generated: `apps/identity-wallet/pnpm-lock.yaml` (created automatically by `pnpm install`) 50 + 51 + **Step 1: Create directories** 52 + 53 + ```bash 54 + mkdir -p apps/identity-wallet/src/routes 55 + ``` 56 + 57 + **Step 2: Create `apps/identity-wallet/package.json`** 58 + 59 + ```json 60 + { 61 + "name": "identity-wallet", 62 + "version": "0.0.1", 63 + "private": true, 64 + "type": "module", 65 + "scripts": { 66 + "dev": "vite dev", 67 + "build": "vite build", 68 + "preview": "vite preview", 69 + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 70 + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" 71 + }, 72 + "devDependencies": { 73 + "@sveltejs/adapter-static": "^3.0.8", 74 + "@sveltejs/kit": "^2.20.4", 75 + "@sveltejs/vite-plugin-svelte": "^5.0.3", 76 + "svelte": "^5.25.8", 77 + "svelte-check": "^4.1.5", 78 + "tslib": "^2.8.1", 79 + "typescript": "^5.8.2", 80 + "vite": "^5.4.8" 81 + } 82 + } 83 + ``` 84 + 85 + **Step 3: Create `apps/identity-wallet/svelte.config.js`** 86 + 87 + Note: `out: 'dist'` aligns the build output directory with `tauri.conf.json`'s `frontendDist: "../dist"` setting (added in Phase 2). 88 + 89 + ```js 90 + import adapter from '@sveltejs/adapter-static'; 91 + import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 92 + 93 + /** @type {import('@sveltejs/kit').Config} */ 94 + const config = { 95 + preprocess: vitePreprocess(), 96 + kit: { 97 + adapter: adapter({ 98 + // fallback: 'index.html' routes unmatched paths to index for client-side navigation (SPA mode) 99 + fallback: 'index.html', 100 + // out: 'dist' matches tauri.conf.json frontendDist: "../dist" (configured in Phase 2) 101 + out: 'dist', 102 + }), 103 + }, 104 + }; 105 + 106 + export default config; 107 + ``` 108 + 109 + **Step 4: Create `apps/identity-wallet/vite.config.ts`** 110 + 111 + Phase 1 uses basic Tauri-compatible server settings. Phase 2 adds `clearScreen: false`, HMR config, and `envPrefix`. 112 + 113 + ```typescript 114 + import { sveltekit } from '@sveltejs/kit/vite'; 115 + import { defineConfig } from 'vite'; 116 + 117 + export default defineConfig({ 118 + plugins: [sveltekit()], 119 + server: { 120 + port: 5173, 121 + strictPort: true, 122 + // host: '0.0.0.0' allows the iOS simulator to reach this dev server over LAN 123 + host: '0.0.0.0', 124 + }, 125 + }); 126 + ``` 127 + 128 + **Step 5: Create `apps/identity-wallet/tsconfig.json`** 129 + 130 + `.svelte-kit/tsconfig.json` is generated automatically by SvelteKit when `pnpm build` (or `pnpm check`) runs. The `extends` field resolves after first build. 131 + 132 + ```json 133 + { 134 + "extends": "./.svelte-kit/tsconfig.json", 135 + "compilerOptions": { 136 + "strict": true 137 + } 138 + } 139 + ``` 140 + 141 + **Step 6: Create `apps/identity-wallet/src/routes/+layout.ts`** 142 + 143 + ```typescript 144 + // Disable SSR and prerendering globally — Tauri apps have no web server. 145 + // The frontend runs entirely in WKWebView (iOS) and loads files from disk. 146 + export const ssr = false; 147 + export const prerender = false; 148 + ``` 149 + 150 + **Step 7: Create `apps/identity-wallet/src/routes/+layout.svelte`** 151 + 152 + Svelte 5 replaces `<slot />` with `{@render children()}`. The `children` prop is typed as `Snippet` from Svelte's type exports. 153 + 154 + ```svelte 155 + <script lang="ts"> 156 + import type { Snippet } from 'svelte'; 157 + 158 + let { children }: { children: Snippet } = $props(); 159 + </script> 160 + 161 + {@render children()} 162 + ``` 163 + 164 + **Step 8: Create `apps/identity-wallet/src/app.html`** 165 + 166 + SvelteKit requires `src/app.html` as its HTML template — `pnpm build` will fail without it. The `%sveltekit.head%` and `%sveltekit.body%` placeholders are mandatory and injected by SvelteKit at build time. 167 + 168 + ```html 169 + <!doctype html> 170 + <html lang="en"> 171 + <head> 172 + <meta charset="utf-8" /> 173 + <meta name="viewport" content="width=device-width, initial-scale=1" /> 174 + %sveltekit.head% 175 + </head> 176 + <body> 177 + <div style="display: contents">%sveltekit.body%</div> 178 + </body> 179 + </html> 180 + ``` 181 + 182 + **Step 9: Create `apps/identity-wallet/src/routes/+page.svelte`** 183 + 184 + Static placeholder only — the Greet button and IPC demo are added in Phase 2. 185 + 186 + ```svelte 187 + <!-- Static placeholder — IPC demo (greet button) is added in Phase 2 --> 188 + <h1>Identity Wallet</h1> 189 + <p>Coming in Phase 2.</p> 190 + ``` 191 + 192 + **Step 10: Update `.gitignore` to exclude frontend build artifacts** 193 + 194 + The workspace `.gitignore` is at `/Users/jacob.zweifel/workspace/malpercio-dev/ezpds/.gitignore`. Append these lines at the end of the file: 195 + 196 + ``` 197 + # SvelteKit / frontend build artifacts 198 + apps/identity-wallet/.svelte-kit/ 199 + apps/identity-wallet/dist/ 200 + apps/identity-wallet/node_modules/ 201 + ``` 202 + 203 + **Step 11: Install dependencies and verify build** 204 + 205 + ```bash 206 + cd apps/identity-wallet 207 + pnpm install 208 + ``` 209 + 210 + Expected: Installs without errors. `pnpm-lock.yaml` is created. 211 + 212 + ```bash 213 + pnpm build 214 + ``` 215 + 216 + Expected: Builds without errors. Creates `apps/identity-wallet/dist/` containing `index.html` and static assets. 217 + 218 + **Step 12: Commit** 219 + 220 + ```bash 221 + # From workspace root 222 + git add apps/identity-wallet/package.json \ 223 + apps/identity-wallet/pnpm-lock.yaml \ 224 + apps/identity-wallet/svelte.config.js \ 225 + apps/identity-wallet/vite.config.ts \ 226 + apps/identity-wallet/tsconfig.json \ 227 + apps/identity-wallet/src/ \ 228 + .gitignore 229 + git commit -m "feat(MM-143): scaffold SvelteKit 2 + Svelte 5 frontend at apps/identity-wallet" 230 + ``` 231 + <!-- END_TASK_1 --> 232 + 233 + <!-- START_TASK_2 --> 234 + ### Task 2: Create src-tauri stub Rust crate 235 + 236 + **Verifies:** None (infrastructure task — workspace integration verification done in Task 3) 237 + 238 + **Files:** 239 + - Create: `apps/identity-wallet/src-tauri/Cargo.toml` 240 + - Create: `apps/identity-wallet/src-tauri/src/lib.rs` 241 + - Create: `apps/identity-wallet/src-tauri/src/main.rs` 242 + 243 + **Step 1: Create directories** 244 + 245 + ```bash 246 + mkdir -p apps/identity-wallet/src-tauri/src 247 + ``` 248 + 249 + **Step 2: Create `apps/identity-wallet/src-tauri/Cargo.toml`** 250 + 251 + Phase 1 uses a stub with no external dependencies — `tauri`, `tauri-build`, and `tauri.conf.json` are added in Phase 2. The three crate-types are all needed from day one: `staticlib` for iOS, `cdylib` for Android, `rlib` for standard `cargo build` and the binary target. 252 + 253 + Note: `[profile.release]` settings in workspace member crates are ignored by Cargo (only the workspace root's profile settings apply). They are included here per the design plan and serve as documentation for standalone builds. 254 + 255 + ```toml 256 + [package] 257 + name = "identity-wallet" 258 + version.workspace = true 259 + edition.workspace = true 260 + publish.workspace = true 261 + 262 + [lib] 263 + name = "identity_wallet" 264 + path = "src/lib.rs" 265 + # staticlib → iOS static binary 266 + # cdylib → Android shared library 267 + # rlib → normal `cargo build` + integration tests 268 + crate-type = ["staticlib", "cdylib", "rlib"] 269 + 270 + # Tauri-recommended release optimizations for mobile binary size. 271 + # IMPORTANT: Cargo ignores [profile.*] in workspace member crates — these settings 272 + # have NO EFFECT here because src-tauri is always built as a workspace member. 273 + # They are included per the design plan as documentation of Tauri's recommendations. 274 + # When iOS release binary size becomes a concern, move these to the root Cargo.toml's 275 + # [profile.release] section (note: that will affect ALL workspace crates' release builds). 276 + [profile.release] 277 + strip = true 278 + lto = true 279 + opt-level = "z" 280 + ``` 281 + 282 + **Step 3: Create `apps/identity-wallet/src-tauri/src/lib.rs`** 283 + 284 + Stub — `#[tauri::command]` greet and Tauri Builder are added in Phase 2. 285 + 286 + ```rust 287 + // Stub — greet command and Tauri builder are wired up in Phase 2. 288 + pub fn run() {} 289 + ``` 290 + 291 + **Step 4: Create `apps/identity-wallet/src-tauri/src/main.rs`** 292 + 293 + The `windows_subsystem` attribute prevents a console window on Windows release builds. Harmless on macOS/iOS (guarded by `cfg_attr`). 294 + 295 + ```rust 296 + // Prevents a console window from appearing on Windows in release builds. 297 + #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] 298 + 299 + fn main() { 300 + identity_wallet::run(); 301 + } 302 + ``` 303 + 304 + **⚠️ Do NOT run `cargo build` yet.** The crate is not in the workspace members list. Running `cargo build` now will not include `identity-wallet` and will not fail — but it also won't verify anything. Task 3 adds the crate to the workspace and runs the actual verification. 305 + <!-- END_TASK_2 --> 306 + 307 + <!-- END_SUBCOMPONENT_A --> 308 + 309 + <!-- START_SUBCOMPONENT_B (tasks 3-3) --> 310 + 311 + <!-- START_TASK_3 --> 312 + ### Task 3: Integrate src-tauri into Cargo workspace and add iOS targets 313 + 314 + **Verifies:** MM-143.AC2.1, MM-143.AC2.2, MM-143.AC2.3, MM-143.AC2.4, MM-143.AC2.5 315 + 316 + **Files:** 317 + - Modify: `/Users/jacob.zweifel/workspace/malpercio-dev/ezpds/Cargo.toml` (add workspace member) 318 + - Modify: `/Users/jacob.zweifel/workspace/malpercio-dev/ezpds/rust-toolchain.toml` (add iOS targets) 319 + 320 + **Step 1: Add `apps/identity-wallet/src-tauri` to workspace members** 321 + 322 + In `/Users/jacob.zweifel/workspace/malpercio-dev/ezpds/Cargo.toml`, the current `members` block is: 323 + 324 + ```toml 325 + members = [ 326 + "crates/relay", 327 + "crates/repo-engine", 328 + "crates/crypto", 329 + "crates/common", 330 + ] 331 + ``` 332 + 333 + Change it to: 334 + 335 + ```toml 336 + members = [ 337 + "crates/relay", 338 + "crates/repo-engine", 339 + "crates/crypto", 340 + "crates/common", 341 + "apps/identity-wallet/src-tauri", 342 + ] 343 + ``` 344 + 345 + **Step 2: Add iOS targets to `rust-toolchain.toml`** 346 + 347 + In `/Users/jacob.zweifel/workspace/malpercio-dev/ezpds/rust-toolchain.toml`, the current `targets` line is: 348 + 349 + ```toml 350 + targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"] 351 + ``` 352 + 353 + Replace it with a multi-line form that includes the two iOS targets: 354 + 355 + ```toml 356 + targets = [ 357 + "aarch64-apple-darwin", 358 + "x86_64-apple-darwin", 359 + "x86_64-unknown-linux-gnu", 360 + "aarch64-unknown-linux-gnu", 361 + "aarch64-apple-ios", 362 + "aarch64-apple-ios-sim", 363 + ] 364 + ``` 365 + 366 + **Step 3: Verify `cargo build` succeeds (verifies MM-143.AC2.2 and MM-143.AC2.5)** 367 + 368 + Run from the workspace root: 369 + 370 + ```bash 371 + cargo build 372 + ``` 373 + 374 + Expected: Builds all 5 workspace members (relay, repo-engine, crypto, common, identity-wallet) without errors. The `identity-wallet` crate compiles as staticlib + cdylib + rlib + binary. 375 + 376 + If it fails, check: 377 + - Path `apps/identity-wallet/src-tauri` matches the actual directory 378 + - `lib.rs` and `main.rs` exist at the correct paths 379 + 380 + **Step 4: Verify clippy passes (verifies MM-143.AC2.3)** 381 + 382 + ```bash 383 + cargo clippy --workspace -- -D warnings 384 + ``` 385 + 386 + Expected: Zero warnings or errors across all workspace members. 387 + 388 + **Step 5: Verify formatting (verifies MM-143.AC2.4)** 389 + 390 + ```bash 391 + cargo fmt --all --check 392 + ``` 393 + 394 + Expected: Exits with code 0. 395 + 396 + If it fails, run `cargo fmt --all` to fix formatting, then re-run `--check` to confirm. 397 + 398 + **Step 6: Commit** 399 + 400 + ```bash 401 + git add Cargo.toml rust-toolchain.toml apps/identity-wallet/src-tauri/ 402 + git commit -m "feat(MM-143): add src-tauri stub crate to workspace, add iOS targets to rust-toolchain" 403 + ``` 404 + <!-- END_TASK_3 --> 405 + 406 + <!-- END_SUBCOMPONENT_B -->
+546
docs/implementation-plans/2026-03-14-MM-143/phase_02.md
··· 1 + # MM-143: Tauri Mobile Project Scaffolding — Phase 2: Tauri Configuration + IPC Bridge 2 + 3 + **Goal:** Wire up Tauri fully — `tauri.conf.json`, the `greet` Rust command, and the SvelteKit frontend page that calls it — so `cargo tauri ios dev` works end-to-end in an iOS simulator. 4 + 5 + **Architecture:** The Rust crate at `apps/identity-wallet/src-tauri/` gains the Tauri builder (`run()`), one IPC command (`greet`), and the build script required to compile. The SvelteKit frontend gains a typed `invoke()` wrapper in `src/lib/ipc.ts` and a Greet button in `+page.svelte` using Svelte 5 runes. The Vite server is configured to expose the dev server to the iOS simulator via `TAURI_DEV_HOST` (Tauri v2's recommended approach — no `internal-ip` package needed). 6 + 7 + **Tech Stack:** Tauri v2 (Rust), `@tauri-apps/api` v2 (TypeScript), Svelte 5 runes, Vite 5 8 + 9 + **Scope:** Phase 2 of 3 — infrastructure and manual simulator verification. No automated unit tests. Verified by `cargo build --workspace` + `pnpm build` + manual `cargo tauri ios dev` in simulator. 10 + 11 + **Codebase verified:** 2026-03-14 12 + 13 + **Design discrepancy noted:** The design plan mentions `internal-ip` npm package for HMR. Tauri v2's current recommendation is to use the `TAURI_DEV_HOST` environment variable instead — Tauri sets this automatically to the machine's LAN IP when running `cargo tauri ios dev`. This eliminates the `internal-ip` dependency. The behavior is identical. 14 + 15 + --- 16 + 17 + ## Acceptance Criteria Coverage 18 + 19 + This phase implements and verifies operationally: 20 + 21 + ### MM-143.AC1: Project directory structure exists 22 + - **MM-143.AC1.1 Success (completes):** `apps/identity-wallet/src/lib/ipc.ts` created (completing the full file list from AC1.1) 23 + - **MM-143.AC1.2 Success (completes):** `apps/identity-wallet/src-tauri/` now contains `tauri.conf.json` and `build.rs` (completing AC1.2) 24 + - **MM-143.AC1.4 Success:** Tauri version is 2.x (verified in `src-tauri/Cargo.toml` `[dependencies]`) 25 + 26 + ### MM-143.AC3: App launches in iOS simulator (manual verification) 27 + - **MM-143.AC3.1 Success:** `cargo tauri ios dev` completes and the app appears in the iOS simulator 28 + - **MM-143.AC3.2 Success:** App displays a visible placeholder screen (not a blank white screen or error) 29 + - **MM-143.AC3.3 Failure:** App does not crash on launch (no crash dialog in simulator) 30 + 31 + ### MM-143.AC4: IPC bridge functions correctly (manual verification) 32 + - **MM-143.AC4.1 Success:** Pressing the Greet button triggers the Rust `greet` command via `invoke()` 33 + - **MM-143.AC4.2 Success:** The Rust response (e.g., "Hello, World!") is displayed in the UI 34 + - **MM-143.AC4.3 Failure:** No JavaScript console errors appear in the WebView inspector during IPC invocation 35 + - **MM-143.AC4.4 Edge:** Greet button and response text are visible without scrolling on a standard iPhone 15 screen 36 + 37 + --- 38 + 39 + <!-- START_SUBCOMPONENT_A (tasks 1-2) --> 40 + 41 + <!-- START_TASK_1 --> 42 + ### Task 1: Add Tauri Rust dependencies, build.rs, and tauri.conf.json 43 + 44 + **Verifies:** MM-143.AC1.2 (tauri.conf.json and build.rs exist), MM-143.AC1.4 (Tauri version 2.x in Cargo.toml) 45 + 46 + **Files:** 47 + - Modify: `apps/identity-wallet/src-tauri/Cargo.toml` (add tauri and tauri-build deps) 48 + - Create: `apps/identity-wallet/src-tauri/build.rs` 49 + - Create: `apps/identity-wallet/src-tauri/tauri.conf.json` 50 + - Modify: `/Users/jacob.zweifel/workspace/malpercio-dev/ezpds/flake.nix` (scope `buildDepsOnly` to relay-only packages) 51 + 52 + **IMPORTANT — ordering:** `tauri::generate_context!()` (added in Task 2) reads `tauri.conf.json` at compile time. Both `tauri.conf.json` and the `tauri-build` build script must exist BEFORE updating `lib.rs` in Task 2. Create all three files in this task, then verify `cargo build` succeeds with the Phase 1 stub `lib.rs` before proceeding to Task 2. 53 + 54 + **Step 1: Update `apps/identity-wallet/src-tauri/Cargo.toml`** 55 + 56 + Add `[dependencies]`, `[build-dependencies]`, and complete the package section. `serde` and `serde_json` are declared in `[workspace.dependencies]` (see root `Cargo.toml` lines 30-32) so they use `{ workspace = true }`. `tauri` and `tauri-build` are Tauri-specific and stay local per the design plan. 57 + 58 + Replace the entire `Cargo.toml` with: 59 + 60 + ```toml 61 + [package] 62 + name = "identity-wallet" 63 + version.workspace = true 64 + edition.workspace = true 65 + publish.workspace = true 66 + 67 + [lib] 68 + name = "identity_wallet" 69 + path = "src/lib.rs" 70 + # staticlib → iOS static binary 71 + # cdylib → Android shared library 72 + # rlib → normal `cargo build` + integration tests 73 + crate-type = ["staticlib", "cdylib", "rlib"] 74 + 75 + [dependencies] 76 + # Tauri-specific — declared locally (not in workspace.dependencies per design plan) 77 + tauri = "2" 78 + # serde/serde_json are in workspace.dependencies (root Cargo.toml lines 30-32) 79 + serde = { workspace = true } 80 + serde_json = { workspace = true } 81 + 82 + [build-dependencies] 83 + # Tauri-specific — declared locally 84 + tauri-build = "2" 85 + 86 + # Tauri-recommended release optimizations for mobile binary size. 87 + # IMPORTANT: Cargo ignores [profile.*] in workspace member crates — these settings 88 + # have NO EFFECT here because src-tauri is always built as a workspace member. 89 + # They are included per the design plan as documentation of Tauri's recommendations. 90 + # When iOS release binary size becomes a concern, move these to the root Cargo.toml's 91 + # [profile.release] section (note: that will affect ALL workspace crates' release builds). 92 + [profile.release] 93 + strip = true 94 + lto = true 95 + opt-level = "z" 96 + ``` 97 + 98 + **Step 2: Create `apps/identity-wallet/src-tauri/build.rs`** 99 + 100 + ```rust 101 + fn main() { 102 + tauri_build::build() 103 + } 104 + ``` 105 + 106 + **Step 3: Create `apps/identity-wallet/src-tauri/tauri.conf.json`** 107 + 108 + `beforeDevCommand` and `beforeBuildCommand` tell Tauri to start the Vite dev server automatically when running `cargo tauri ios dev`. 109 + 110 + ```json 111 + { 112 + "$schema": "https://schema.tauri.app/config/2", 113 + "productName": "Identity Wallet", 114 + "version": "0.1.0", 115 + "identifier": "dev.malpercio.identitywallet", 116 + "build": { 117 + "devUrl": "http://localhost:5173", 118 + "frontendDist": "../dist", 119 + "beforeDevCommand": "pnpm dev", 120 + "beforeBuildCommand": "pnpm build" 121 + }, 122 + "app": { 123 + "windows": [ 124 + { 125 + "title": "Identity Wallet", 126 + "width": 400, 127 + "height": 600, 128 + "resizable": true 129 + } 130 + ], 131 + "security": { 132 + "csp": null 133 + } 134 + }, 135 + "bundle": { 136 + "active": true 137 + } 138 + } 139 + ``` 140 + 141 + **Step 4: Update `flake.nix` to scope `buildDepsOnly` to relay-related packages** 142 + 143 + Adding `tauri = "2"` to the workspace means `buildDepsOnly` (which builds deps for ALL workspace members) would now attempt to compile Tauri's native dependencies — `webkit2gtk` on Linux, Apple frameworks on macOS — which are not in `commonArgs.buildInputs`. This breaks `nix build .#relay` and `just nix-check`. 144 + 145 + Fix: scope `buildDepsOnly` to only the 4 relay-related packages. 146 + 147 + In `/Users/jacob.zweifel/workspace/malpercio-dev/ezpds/flake.nix`, the current `cargoArtifacts` line (line 42) is: 148 + 149 + ```nix 150 + cargoArtifacts = craneLib.buildDepsOnly commonArgs; 151 + ``` 152 + 153 + Change it to: 154 + 155 + ```nix 156 + # Scope buildDepsOnly to relay-related crates only. 157 + # apps/identity-wallet/src-tauri uses Tauri (webkit2gtk on Linux, Apple frameworks 158 + # on macOS) which are not in commonArgs.buildInputs. Without this scope, 159 + # buildDepsOnly would attempt to compile Tauri's native deps and fail in Nix. 160 + cargoArtifacts = craneLib.buildDepsOnly (commonArgs // { 161 + cargoExtraArgs = "--package relay --package repo-engine --package crypto --package common"; 162 + }); 163 + ``` 164 + 165 + After editing, verify the Nix syntax is correct: 166 + 167 + ```bash 168 + just nix-check 169 + ``` 170 + 171 + Expected: Flake check passes without errors. 172 + 173 + **Step 5: Verify `cargo build` still succeeds with the Phase 1 stub** 174 + 175 + The Phase 1 `lib.rs` has `pub fn run() {}` (no `generate_context!()` yet). The build script runs and processes `tauri.conf.json`. The stub lib compiles. This verifies the new deps resolve correctly. 176 + 177 + ```bash 178 + cargo build 179 + ``` 180 + 181 + Expected: Builds without errors. This may take several minutes on first run as Tauri's dependency tree is large. 182 + 183 + **Step 6: Commit** 184 + 185 + ```bash 186 + git add apps/identity-wallet/src-tauri/Cargo.toml \ 187 + apps/identity-wallet/src-tauri/build.rs \ 188 + apps/identity-wallet/src-tauri/tauri.conf.json \ 189 + flake.nix \ 190 + Cargo.lock 191 + git commit -m "feat(MM-143): add tauri deps and tauri.conf.json to src-tauri; scope flake buildDepsOnly" 192 + ``` 193 + <!-- END_TASK_1 --> 194 + 195 + <!-- START_TASK_2 --> 196 + ### Task 2: Implement the greet command in lib.rs 197 + 198 + **Verifies:** MM-143.AC2.2 (cargo build), MM-143.AC2.3 (clippy), MM-143.AC2.4 (fmt) 199 + 200 + **Files:** 201 + - Modify: `apps/identity-wallet/src-tauri/src/lib.rs` 202 + - No changes to `apps/identity-wallet/src-tauri/src/main.rs` (Phase 1 version already calls `identity_wallet::run()` correctly) 203 + 204 + **Step 1: Replace `apps/identity-wallet/src-tauri/src/lib.rs`** 205 + 206 + `#[cfg_attr(mobile, tauri::mobile_entry_point)]` marks `run()` as the iOS/Android entry point. On mobile, the OS calls `run()` directly instead of `main()`. On desktop, `main()` (from `main.rs`) still calls `run()` as usual. 207 + 208 + `generate_context!()` reads `tauri.conf.json` at compile time (the file must already exist — created in Task 1). 209 + 210 + ```rust 211 + #[tauri::command] 212 + fn greet(name: String) -> String { 213 + format!("Hello, {}!", name) 214 + } 215 + 216 + #[cfg_attr(mobile, tauri::mobile_entry_point)] 217 + pub fn run() { 218 + tauri::Builder::default() 219 + .invoke_handler(tauri::generate_handler![greet]) 220 + .run(tauri::generate_context!()) 221 + .expect("error while running tauri application"); 222 + } 223 + ``` 224 + 225 + **Step 2: Verify `cargo build` succeeds (verifies MM-143.AC2.2)** 226 + 227 + ```bash 228 + cargo build 229 + ``` 230 + 231 + Expected: All workspace members build without errors. 232 + 233 + **Step 3: Verify clippy passes (verifies MM-143.AC2.3)** 234 + 235 + ```bash 236 + cargo clippy --workspace -- -D warnings 237 + ``` 238 + 239 + Expected: Zero warnings or errors. 240 + 241 + **Step 4: Verify formatting (verifies MM-143.AC2.4)** 242 + 243 + ```bash 244 + cargo fmt --all --check 245 + ``` 246 + 247 + Expected: Exits with code 0. If it fails, run `cargo fmt --all` then re-check. 248 + 249 + **Step 5: Commit** 250 + 251 + ```bash 252 + git add apps/identity-wallet/src-tauri/src/lib.rs 253 + git commit -m "feat(MM-143): implement greet IPC command in src-tauri" 254 + ``` 255 + <!-- END_TASK_2 --> 256 + 257 + <!-- END_SUBCOMPONENT_A --> 258 + 259 + <!-- START_SUBCOMPONENT_B (tasks 3-4) --> 260 + 261 + <!-- START_TASK_3 --> 262 + ### Task 3: Add @tauri-apps/api, create ipc.ts, update vite.config.ts 263 + 264 + **Verifies:** MM-143.AC1.1 (completes — ipc.ts now exists) 265 + 266 + **Files:** 267 + - Modify: `apps/identity-wallet/package.json` (add `@tauri-apps/api` to dependencies) 268 + - Modify: `apps/identity-wallet/vite.config.ts` (add clearScreen, HMR config, envPrefix) 269 + - Create: `apps/identity-wallet/src/lib/ipc.ts` 270 + 271 + **Step 1: Add `@tauri-apps/api` to `apps/identity-wallet/package.json`** 272 + 273 + `@tauri-apps/api` is a runtime dependency (not devDependency) because it runs in the WebView at runtime. Add a `"dependencies"` block to the existing `package.json`: 274 + 275 + ```json 276 + { 277 + "name": "identity-wallet", 278 + "version": "0.0.1", 279 + "private": true, 280 + "type": "module", 281 + "scripts": { 282 + "dev": "vite dev", 283 + "build": "vite build", 284 + "preview": "vite preview", 285 + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 286 + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" 287 + }, 288 + "dependencies": { 289 + "@tauri-apps/api": "^2" 290 + }, 291 + "devDependencies": { 292 + "@sveltejs/adapter-static": "^3.0.8", 293 + "@sveltejs/kit": "^2.20.4", 294 + "@sveltejs/vite-plugin-svelte": "^5.0.3", 295 + "svelte": "^5.25.8", 296 + "svelte-check": "^4.1.5", 297 + "tslib": "^2.8.1", 298 + "typescript": "^5.8.2", 299 + "vite": "^5.4.8" 300 + } 301 + } 302 + ``` 303 + 304 + **Step 2: Replace `apps/identity-wallet/vite.config.ts`** 305 + 306 + `TAURI_DEV_HOST` is set automatically by `cargo tauri ios dev` to the machine's LAN IP so the iOS simulator can connect to the Vite dev server. When this env var is unset (desktop dev or CI), the server falls back to `'0.0.0.0'` with no custom HMR config. 307 + 308 + **Design discrepancies noted in this task:** 309 + - The design plan mentions `internal-ip` npm package for HMR host detection. Tauri v2 provides `TAURI_DEV_HOST` for this purpose, which is simpler and has no extra dependency. 310 + - The design plan specifies `envPrefix: ['VITE_', 'TAURI_']`. Tauri v2 uses `TAURI_ENV_*` as the prefix for its environment variables (e.g., `TAURI_ENV_PLATFORM`, `TAURI_ENV_FAMILY`), not bare `TAURI_*`. Using `'TAURI_ENV_'` is correct for Tauri v2. 311 + 312 + ```typescript 313 + import { sveltekit } from '@sveltejs/kit/vite'; 314 + import { defineConfig } from 'vite'; 315 + 316 + export default defineConfig({ 317 + plugins: [sveltekit()], 318 + // clearScreen: false surfaces Rust compiler errors in the terminal instead of clearing them 319 + clearScreen: false, 320 + server: { 321 + port: 5173, 322 + strictPort: true, 323 + // TAURI_DEV_HOST is set by `cargo tauri ios dev` to the machine's LAN IP; 324 + // the iOS simulator connects to the dev server over LAN, not localhost 325 + host: process.env.TAURI_DEV_HOST || '0.0.0.0', 326 + hmr: process.env.TAURI_DEV_HOST 327 + ? { 328 + protocol: 'ws', 329 + host: process.env.TAURI_DEV_HOST, 330 + port: 5173, 331 + } 332 + : undefined, 333 + }, 334 + // Expose VITE_* and TAURI_ENV_* environment variables to the frontend 335 + envPrefix: ['VITE_', 'TAURI_ENV_'], 336 + }); 337 + ``` 338 + 339 + **Step 3: Create `apps/identity-wallet/src/lib/` directory** 340 + 341 + ```bash 342 + mkdir -p apps/identity-wallet/src/lib 343 + ``` 344 + 345 + **Step 4: Create `apps/identity-wallet/src/lib/ipc.ts`** 346 + 347 + Typed wrapper around Tauri's `invoke()`. The command name `'greet'` matches the Rust function name exactly (snake_case maps to snake_case). The argument key `name` matches the Rust function parameter name. 348 + 349 + ```typescript 350 + import { invoke } from '@tauri-apps/api/core'; 351 + 352 + export const greet = (name: string): Promise<string> => 353 + invoke('greet', { name }); 354 + ``` 355 + 356 + **Step 5: Install updated dependencies and verify build** 357 + 358 + ```bash 359 + cd apps/identity-wallet 360 + pnpm install 361 + pnpm build 362 + ``` 363 + 364 + Expected: `pnpm install` updates `pnpm-lock.yaml` with `@tauri-apps/api`. `pnpm build` succeeds (the `ipc.ts` module compiles even though it's not yet imported by any page). 365 + 366 + **Step 6: Commit** 367 + 368 + ```bash 369 + # From workspace root 370 + git add apps/identity-wallet/package.json \ 371 + apps/identity-wallet/pnpm-lock.yaml \ 372 + apps/identity-wallet/vite.config.ts \ 373 + apps/identity-wallet/src/lib/ipc.ts 374 + git commit -m "feat(MM-143): add @tauri-apps/api, ipc.ts, update vite config for iOS HMR" 375 + ``` 376 + <!-- END_TASK_3 --> 377 + 378 + <!-- START_TASK_4 --> 379 + ### Task 4: Update +page.svelte with Greet button and verify full build 380 + 381 + **Verifies:** MM-143.AC4.4 (button and response text visible without scrolling on iPhone 15) 382 + 383 + **Files:** 384 + - Modify: `apps/identity-wallet/src/routes/+page.svelte` 385 + 386 + **Step 1: Replace `apps/identity-wallet/src/routes/+page.svelte`** 387 + 388 + Uses Svelte 5 runes: `$state()` for reactive variables. `onclick` (not `on:click`) is the Svelte 5 DOM event syntax. The centered layout ensures AC4.4 is met — all interactive elements fit within iPhone 15's 852pt height without scrolling. 389 + 390 + ```svelte 391 + <script lang="ts"> 392 + import { greet } from '$lib/ipc'; 393 + 394 + let name = $state('World'); 395 + let greetMsg = $state(''); 396 + 397 + async function handleGreet() { 398 + greetMsg = await greet(name); 399 + } 400 + </script> 401 + 402 + <main> 403 + <h1>Identity Wallet</h1> 404 + <div class="greet-form"> 405 + <input 406 + type="text" 407 + bind:value={name} 408 + placeholder="Enter a name" 409 + /> 410 + <button onclick={handleGreet}>Greet</button> 411 + </div> 412 + {#if greetMsg} 413 + <p class="greeting">{greetMsg}</p> 414 + {/if} 415 + </main> 416 + 417 + <style> 418 + main { 419 + display: flex; 420 + flex-direction: column; 421 + align-items: center; 422 + justify-content: center; 423 + min-height: 100vh; 424 + padding: 1rem; 425 + font-family: system-ui, sans-serif; 426 + box-sizing: border-box; 427 + } 428 + 429 + .greet-form { 430 + display: flex; 431 + flex-direction: column; 432 + gap: 0.5rem; 433 + width: 100%; 434 + max-width: 280px; 435 + margin-top: 1rem; 436 + } 437 + 438 + input, 439 + button { 440 + padding: 0.5rem; 441 + font-size: 1rem; 442 + border-radius: 4px; 443 + border: 1px solid #ccc; 444 + box-sizing: border-box; 445 + width: 100%; 446 + } 447 + 448 + button { 449 + cursor: pointer; 450 + background: #007aff; 451 + color: white; 452 + border-color: #007aff; 453 + } 454 + 455 + .greeting { 456 + margin-top: 1rem; 457 + font-size: 1.25rem; 458 + } 459 + </style> 460 + ``` 461 + 462 + **Step 2: Verify pnpm build succeeds** 463 + 464 + ```bash 465 + cd apps/identity-wallet 466 + pnpm build 467 + ``` 468 + 469 + Expected: Builds without errors. The page compiles and the `$lib/ipc` import resolves correctly. 470 + 471 + **Step 3: Commit** 472 + 473 + ```bash 474 + # From workspace root 475 + git add apps/identity-wallet/src/routes/+page.svelte 476 + git commit -m "feat(MM-143): add Greet IPC demo to +page.svelte using Svelte 5 runes" 477 + ``` 478 + <!-- END_TASK_4 --> 479 + 480 + <!-- END_SUBCOMPONENT_B --> 481 + 482 + <!-- START_SUBCOMPONENT_C (tasks 5-5) --> 483 + 484 + <!-- START_TASK_5 --> 485 + ### Task 5: Manual iOS simulator verification 486 + 487 + **Verifies:** MM-143.AC3.1, MM-143.AC3.2, MM-143.AC3.3, MM-143.AC4.1, MM-143.AC4.2, MM-143.AC4.3 488 + 489 + **This task cannot be automated** — it requires a macOS machine with Xcode and iOS Simulator. 490 + 491 + **Prerequisites (must be installed before proceeding):** 492 + 1. Xcode (latest, from App Store) with iOS 17+ Simulator platform installed (Xcode → Settings → Platforms) 493 + 2. Cocoapods: `sudo gem install cocoapods` 494 + 3. `cargo-tauri` must be in PATH. **Choose one:** 495 + - **Recommended:** Run Phase 3 Task 1 first (adds `pkgs.cargo-tauri` to devenv.nix, then enter `nix develop --impure --accept-flake-config`) 496 + - **Alternative:** Install directly: `cargo install tauri-cli` (does not require Nix, but takes longer and is not pinned to the workspace version) 497 + 498 + Note: Phase 2 and Phase 3 are independent (both depend only on Phase 1). Phase 3 Task 1 can be completed before Phase 2 Task 5 without breaking anything. 499 + 500 + **Step 1: First-time iOS project init (run once per developer machine)** 501 + 502 + This generates `src-tauri/gen/apple/` (gitignored — machine-specific Xcode project). 503 + 504 + ```bash 505 + cd apps/identity-wallet 506 + cargo tauri ios init 507 + ``` 508 + 509 + Expected: Creates `src-tauri/gen/apple/` directory with Xcode project files. 510 + 511 + **Step 2: Launch Xcode first (required for license acceptance)** 512 + 513 + Open Xcode.app manually before running `cargo tauri ios dev`. If Xcode has a pending license agreement, `cargo tauri ios dev` will fail silently. 514 + 515 + **Step 3: Run the app in iOS simulator** 516 + 517 + ```bash 518 + cd apps/identity-wallet 519 + cargo tauri ios dev 520 + ``` 521 + 522 + Expected: Tauri starts the Vite dev server (`pnpm dev` runs automatically via `beforeDevCommand`), compiles the Rust crate for `aarch64-apple-ios-sim`, and launches the app in the iOS simulator. 523 + 524 + **Verification — AC3.1, AC3.2, AC3.3:** 525 + - [ ] The app appears in the iOS Simulator (AC3.1) 526 + - [ ] The screen shows "Identity Wallet" heading and a Greet button (AC3.2 — not blank) 527 + - [ ] No crash dialog appears (AC3.3) 528 + 529 + **Step 4: Test the IPC bridge** 530 + 531 + In the simulator: 532 + 1. The input field shows "World" by default 533 + 2. Tap the **Greet** button 534 + 535 + **Verification — AC4.1, AC4.2, AC4.3, AC4.4:** 536 + - [ ] Tapping Greet triggers the Rust `greet` command (AC4.1) 537 + - [ ] "Hello, World!" appears below the button (AC4.2) 538 + - [ ] Open Safari → Develop → [Simulator] → inspect the WebView; zero console errors during button tap (AC4.3) 539 + - [ ] All interactive elements (input, button, response) are visible without scrolling (AC4.4) 540 + 541 + **If the app shows a blank white screen:** Check that `pnpm build` ran successfully in `apps/identity-wallet/` — the WebView loads from `dist/index.html`. 542 + 543 + **If the Greet button does nothing:** Check that `tauri.conf.json`'s `identifier` matches the iOS bundle ID. Also verify the Rust command name `'greet'` in `ipc.ts` matches the `#[tauri::command]` function name in `lib.rs`. 544 + <!-- END_TASK_5 --> 545 + 546 + <!-- END_SUBCOMPONENT_C -->
+374
docs/implementation-plans/2026-03-14-MM-143/phase_03.md
··· 1 + # MM-143: Tauri Mobile Project Scaffolding — Phase 3: Nix Dev Environment + Gitignore + Documentation 2 + 3 + **Goal:** Add `cargo-tauri`, `nodejs_22`, and `pnpm` to the Nix dev shell; exclude the Tauri-generated Xcode project from git; and document the iOS developer setup. 4 + 5 + **Architecture:** Three infrastructure changes — devenv.nix package additions following the existing `pkgs.*` list pattern, a `.gitignore` entry for `apps/identity-wallet/src-tauri/gen/`, and two CLAUDE.md documents (new `apps/identity-wallet/CLAUDE.md` + pointer section in root `CLAUDE.md`) following the `nix/CLAUDE.md` domain-level documentation pattern. 6 + 7 + **Tech Stack:** Nix (devenv.nix, nixpkgs rolling via `cachix/devenv-nixpkgs/rolling`) 8 + 9 + **Scope:** Phase 3 of 3 — pure infrastructure and documentation. No code compilation. Verified by checking `devenv.nix` contents and CLAUDE.md existence. 10 + 11 + **Codebase verified:** 2026-03-14 12 + 13 + **AC6 note:** MM-143.AC6 (CI pipeline documented) is already satisfied — the design plan at `docs/design-plans/2026-03-14-MM-143.md` specifies the `rust-check` and `frontend-check` jobs in the "Suggested CI Pipeline" section. No code changes are required for AC6. 14 + 15 + --- 16 + 17 + ## Acceptance Criteria Coverage 18 + 19 + This phase implements and verifies operationally: 20 + 21 + ### MM-143.AC5: Dev environment and documentation 22 + - **MM-143.AC5.1 Success:** `cargo-tauri` is available in PATH after `nix develop --impure --accept-flake-config` 23 + - **MM-143.AC5.2 Success:** `node` (22.x) is available in PATH after `nix develop` 24 + - **MM-143.AC5.3 Success:** `pnpm` is available in PATH after `nix develop` 25 + - **MM-143.AC5.4 Success:** `apps/identity-wallet/CLAUDE.md` exists and covers: macOS/Xcode prerequisites, Cocoapods installation, `pnpm install` first-time setup, `cargo tauri ios init` first-time setup, and `cargo tauri ios dev` development workflow 26 + - **MM-143.AC5.5 Success:** Root `CLAUDE.md` contains a pointer to `apps/identity-wallet/CLAUDE.md` 27 + - **MM-143.AC5.6 Success:** `apps/identity-wallet/src-tauri/gen/` is listed in `.gitignore` 28 + 29 + ### MM-143.AC6: CI pipeline documented (already satisfied) 30 + - **MM-143.AC6.1, AC6.2, AC6.3:** Verified by the design plan at `docs/design-plans/2026-03-14-MM-143.md` (Suggested CI Pipeline section). No implementation tasks required. 31 + 32 + --- 33 + 34 + <!-- START_SUBCOMPONENT_A (tasks 1-4) --> 35 + 36 + <!-- START_TASK_1 --> 37 + ### Task 1: Add cargo-tauri, nodejs_22, and pnpm to devenv.nix 38 + 39 + **Verifies:** MM-143.AC5.1 (cargo-tauri in PATH), MM-143.AC5.2 (node 22.x in PATH), MM-143.AC5.3 (pnpm in PATH) 40 + 41 + **Files:** 42 + - Modify: `/Users/jacob.zweifel/workspace/malpercio-dev/ezpds/devenv.nix` 43 + 44 + **Step 1: Update the `packages` list in `devenv.nix`** 45 + 46 + The current `packages` list (lines 8-13) is: 47 + 48 + ```nix 49 + packages = [ 50 + pkgs.just 51 + pkgs.cargo-audit 52 + pkgs.sqlite 53 + pkgs.pkg-config 54 + ]; 55 + ``` 56 + 57 + Change it to (three new entries appended): 58 + 59 + ```nix 60 + packages = [ 61 + pkgs.just 62 + pkgs.cargo-audit 63 + pkgs.sqlite 64 + pkgs.pkg-config 65 + pkgs.cargo-tauri 66 + pkgs.nodejs_22 67 + pkgs.pnpm 68 + ]; 69 + ``` 70 + 71 + Verified nixpkgs attribute names (from `cachix/devenv-nixpkgs/rolling`): 72 + - `pkgs.cargo-tauri` — Tauri CLI (version 2.9.6+) 73 + - `pkgs.nodejs_22` — Node.js 22 LTS (underscore, not hyphen; no `_x` suffix) 74 + - `pkgs.pnpm` — pnpm top-level package (moved from `nodePackages.pnpm` in nixpkgs 23.05+) 75 + 76 + **Step 2: Validate Nix flake syntax** 77 + 78 + ```bash 79 + just nix-check 80 + ``` 81 + 82 + Expected: Flake check passes without errors. This validates the Nix expression is syntactically correct. 83 + 84 + If `just` is not available outside the dev shell, run directly: 85 + 86 + ```bash 87 + nix flake check --impure --accept-flake-config 88 + ``` 89 + 90 + **Step 3: Commit** 91 + 92 + ```bash 93 + git add devenv.nix 94 + git commit -m "feat(MM-143): add cargo-tauri, nodejs_22, pnpm to devenv.nix" 95 + ``` 96 + 97 + **Step 4: Manual verification (requires re-entering dev shell)** 98 + 99 + After the commit, re-enter the dev shell to verify all three tools are available: 100 + 101 + ```bash 102 + nix develop --impure --accept-flake-config 103 + cargo-tauri --version # expected: cargo-tauri 2.x.x (verifies AC5.1) 104 + node --version # expected: v22.x.x (verifies AC5.2) 105 + pnpm --version # expected: x.x.x (verifies AC5.3) 106 + ``` 107 + 108 + Note: Re-entering the dev shell may take several minutes on first run as Nix downloads and caches the new packages. 109 + <!-- END_TASK_1 --> 110 + 111 + <!-- START_TASK_2 --> 112 + ### Task 2: Add src-tauri/gen/ to .gitignore 113 + 114 + **Verifies:** MM-143.AC5.6 (`apps/identity-wallet/src-tauri/gen/` in .gitignore) 115 + 116 + **Files:** 117 + - Modify: `/Users/jacob.zweifel/workspace/malpercio-dev/ezpds/.gitignore` 118 + 119 + **Step 1: Append Tauri gen pattern to `.gitignore`** 120 + 121 + The current `.gitignore` has 30 lines (Phase 1 may have already added the SvelteKit/frontend entries). Append at the end of the file: 122 + 123 + ``` 124 + # Tauri-generated Xcode project (machine-specific; regenerated per developer via `cargo tauri ios init`) 125 + apps/identity-wallet/src-tauri/gen/ 126 + ``` 127 + 128 + If Phase 1's frontend build artifact entries (`apps/identity-wallet/.svelte-kit/`, `apps/identity-wallet/dist/`, `apps/identity-wallet/node_modules/`) were not already added during Phase 1 execution, add them as well: 129 + 130 + ``` 131 + # SvelteKit / frontend build artifacts 132 + apps/identity-wallet/.svelte-kit/ 133 + apps/identity-wallet/dist/ 134 + apps/identity-wallet/node_modules/ 135 + 136 + # Tauri-generated Xcode project (machine-specific; regenerated per developer via `cargo tauri ios init`) 137 + apps/identity-wallet/src-tauri/gen/ 138 + ``` 139 + 140 + **Step 2: Verify the entry is present** 141 + 142 + ```bash 143 + grep "src-tauri/gen" .gitignore 144 + ``` 145 + 146 + Expected: Prints the line containing `apps/identity-wallet/src-tauri/gen/`. 147 + 148 + **Step 3: Commit** 149 + 150 + ```bash 151 + git add .gitignore 152 + git commit -m "chore(MM-143): gitignore Tauri-generated Xcode project at src-tauri/gen/" 153 + ``` 154 + <!-- END_TASK_2 --> 155 + 156 + <!-- START_TASK_3 --> 157 + ### Task 3: Create apps/identity-wallet/CLAUDE.md 158 + 159 + **Verifies:** MM-143.AC5.4 (CLAUDE.md exists with all required sections) 160 + 161 + **Files:** 162 + - Create: `apps/identity-wallet/CLAUDE.md` 163 + 164 + **Step 1: Create `apps/identity-wallet/CLAUDE.md`** 165 + 166 + Modeled on `nix/CLAUDE.md` (Purpose → Contracts → Dependencies → Key Decisions → Invariants → Key Files). Covers all AC5.4 requirements: macOS/Xcode prerequisites, Cocoapods, `pnpm install`, `cargo tauri ios init`, and `cargo tauri ios dev`. 167 + 168 + ```markdown 169 + # Identity Wallet Mobile App 170 + 171 + Last verified: 2026-03-14 172 + 173 + ## Purpose 174 + 175 + Tauri v2 iOS application — SvelteKit 2 + Svelte 5 frontend running in a native WKWebView, communicating with a Rust backend exclusively through Tauri's IPC bridge. First frontend code in the repository. 176 + 177 + ## Contracts 178 + 179 + ### Frontend (SvelteKit 2 + Svelte 5) 180 + 181 + **Exposes:** 182 + - `src/lib/ipc.ts` — typed wrappers for all Tauri IPC commands; import these instead of calling `invoke()` directly 183 + - `src/routes/+page.svelte` — root page (Greet IPC demo) 184 + 185 + **Guarantees:** 186 + - SSR is disabled globally (`ssr = false` in `src/routes/+layout.ts`); the frontend is a fully static SPA loaded from disk by WKWebView 187 + - Build output lands in `dist/` (configured via `out: 'dist'` in `svelte.config.js`) 188 + - Frontend calls Tauri commands only through `src/lib/ipc.ts` — no raw `invoke()` calls in page components 189 + 190 + **Expects:** 191 + - `pnpm install` has been run in `apps/identity-wallet/` 192 + - Node.js 22.x is in PATH (provided by the Nix dev shell) 193 + 194 + ### Rust Backend (src-tauri/) 195 + 196 + **Exposes:** 197 + - `src/lib.rs::greet(name: String) -> String` — registered Tauri IPC command callable from the frontend via `invoke('greet', { name })` 198 + 199 + **Guarantees:** 200 + - `crate-type = ["staticlib", "cdylib", "rlib"]` supports iOS (staticlib), Android (cdylib), and normal cargo builds (rlib) 201 + - `src/main.rs` is the desktop entry point; `src/lib.rs::run()` is the iOS/Android entry point (via `#[cfg_attr(mobile, tauri::mobile_entry_point)]`) 202 + - `tauri.conf.json` configures the bundle identifier, dev URL (`http://localhost:5173`), and frontend dist path (`../dist`) 203 + 204 + **Expects:** 205 + - `tauri.conf.json` exists in `src-tauri/` before `cargo build` runs — the config is read at compile time by `generate_context!()` 206 + - `cargo-tauri` is in PATH (provided by the Nix dev shell) 207 + - Xcode and iOS Simulator are installed on the developer's macOS machine 208 + 209 + ## Dependencies 210 + 211 + - Frontend → Rust backend (via Tauri IPC — `@tauri-apps/api/core` `invoke()`) 212 + - Rust backend → Cargo workspace (inherits `version`, `edition`, `publish` from root `Cargo.toml`) 213 + - `src-tauri/gen/` → NOT tracked in git; generated per-developer by `cargo tauri ios init` (gitignored) 214 + 215 + ## Prerequisites (macOS/iOS Development) 216 + 217 + 1. **macOS Ventura (13) or later** 218 + 219 + 2. **Xcode** (latest stable, from App Store) 220 + - After installing, open Xcode.app once to accept the license agreement — failing to do this causes `cargo tauri ios dev` to fail silently 221 + - Install the iOS Simulator platform: Xcode → Settings → Platforms → iOS 222 + 223 + 3. **Cocoapods** — Tauri's iOS build uses it to link native Apple frameworks: 224 + ```bash 225 + sudo gem install cocoapods 226 + ``` 227 + 228 + 4. **Apple Developer account** — optional for Simulator; required for physical device (TestFlight/App Store) builds 229 + 230 + ## First-Time Setup 231 + 232 + After cloning the repo, perform these steps once per developer machine: 233 + 234 + ```bash 235 + # 1. Enter the Nix dev shell (provides cargo-tauri, node 22, pnpm) 236 + nix develop --impure --accept-flake-config 237 + 238 + # 2. Install frontend dependencies 239 + cd apps/identity-wallet 240 + pnpm install 241 + 242 + # 3. Generate the Xcode project (output is in src-tauri/gen/apple/ — gitignored) 243 + cargo tauri ios init 244 + ``` 245 + 246 + Note: `src-tauri/gen/` contains a machine-specific Xcode project. It is gitignored and must be re-generated on each developer machine. Do not commit it. 247 + 248 + ## Development Workflow 249 + 250 + ```bash 251 + # Enter the dev shell if not already active 252 + nix develop --impure --accept-flake-config 253 + 254 + # Launch the app in the iOS Simulator 255 + # This starts pnpm dev + compiles the Rust crate for aarch64-apple-ios-sim + opens the Simulator 256 + cd apps/identity-wallet 257 + cargo tauri ios dev 258 + ``` 259 + 260 + For a non-iOS build (CI or any machine without Xcode): 261 + 262 + ```bash 263 + # From workspace root — builds all workspace crates including src-tauri for the host platform 264 + cargo build 265 + ``` 266 + 267 + ## Key Decisions 268 + 269 + - **`adapter-static` + `ssr = false`**: Tauri WebViews load files from disk — there is no web server. SSR is meaningless and globally disabled. 270 + - **`out: 'dist'` in svelte.config.js**: Matches `tauri.conf.json`'s `frontendDist: "../dist"`. 271 + - **`TAURI_DEV_HOST` for HMR**: Tauri v2 automatically sets this env var to the machine's LAN IP when running `cargo tauri ios dev`. The iOS simulator connects to the Vite dev server over LAN, not localhost. 272 + - **`generate_context!()` is compile-time**: `tauri.conf.json` must exist when `src-tauri/` is compiled — the macro embeds the config at compile time and will fail to compile if the file is missing. 273 + - **`src-tauri/gen/` is gitignored**: The Xcode project generated by `cargo tauri ios init` is machine-specific. Committing it causes merge conflicts and bloats the repo. 274 + - **`tauri` and `tauri-build` declared locally**: These crates are not in `[workspace.dependencies]` because no other workspace crate uses them. `serde` and `serde_json` use `{ workspace = true }` per the standard workspace pattern. 275 + 276 + ## Invariants 277 + 278 + - `src/lib/ipc.ts` is the only file that calls `invoke()` directly; page components import from `ipc.ts` 279 + - `tauri.conf.json` bundle identifier `dev.malpercio.identitywallet` must match the iOS provisioning profile for physical device builds 280 + - `src-tauri/gen/` is never committed — regenerate with `cargo tauri ios init` 281 + - `pnpm-lock.yaml` is committed and kept in sync with `package.json` 282 + 283 + ## Key Files 284 + 285 + - `src-tauri/tauri.conf.json` — Tauri config: bundle ID, devUrl, frontendDist, window settings 286 + - `src-tauri/src/lib.rs` — Tauri IPC commands and `run()` (mobile entry point) 287 + - `src-tauri/src/main.rs` — Desktop entry point (calls `lib::run()`) 288 + - `src/lib/ipc.ts` — Typed TypeScript wrappers for all Tauri IPC commands 289 + - `src/routes/+layout.ts` — `ssr = false; prerender = false` (global SPA config) 290 + - `svelte.config.js` — adapter-static with `out: 'dist'` (SPA mode, matches tauri.conf.json) 291 + - `vite.config.ts` — Tauri-compatible Vite server (clearScreen, HMR via TAURI_DEV_HOST, envPrefix) 292 + ``` 293 + 294 + **Step 2: Commit** 295 + 296 + ```bash 297 + git add apps/identity-wallet/CLAUDE.md 298 + git commit -m "docs(MM-143): add apps/identity-wallet/CLAUDE.md with iOS developer setup" 299 + ``` 300 + <!-- END_TASK_3 --> 301 + 302 + <!-- START_TASK_4 --> 303 + ### Task 4: Add Mobile section to root CLAUDE.md 304 + 305 + **Verifies:** MM-143.AC5.5 (root CLAUDE.md contains pointer to apps/identity-wallet/CLAUDE.md) 306 + 307 + **Files:** 308 + - Modify: `/Users/jacob.zweifel/workspace/malpercio-dev/ezpds/CLAUDE.md` 309 + 310 + **Step 1: Read the current root `CLAUDE.md`** 311 + 312 + The current `CLAUDE.md` has these h2 sections in order: 313 + 1. `## Tech Stack` 314 + 2. `## Commands` 315 + 3. `## Dev Environment` 316 + 4. `## Project Structure` 317 + 5. `## Flake Outputs` 318 + 6. `## Bruno API Collection` 319 + 7. `## Conventions` 320 + 8. `## Boundaries` 321 + 322 + **Step 2: Insert `## Mobile` between `## Project Structure` and `## Flake Outputs`** 323 + 324 + Add the following section between the `## Project Structure` block and the `## Flake Outputs` heading: 325 + 326 + ```markdown 327 + ## Mobile 328 + 329 + - `apps/identity-wallet/` — Tauri v2 iOS app (SvelteKit 2 + Svelte 5 frontend, Rust backend) 330 + - Developer setup and iOS workstation guide: see [`apps/identity-wallet/CLAUDE.md`](apps/identity-wallet/CLAUDE.md) 331 + ``` 332 + 333 + Also update the `Last verified` line at the top of CLAUDE.md to `Last verified: 2026-03-14` (it already shows this date, so no change needed). 334 + 335 + **Step 3: Update `## Project Structure` to include `apps/`** 336 + 337 + If the current `## Project Structure` section only lists `crates/`, `nix/`, and `docs/`, prepend the `apps/` entry: 338 + 339 + ```markdown 340 + ## Project Structure 341 + - `apps/identity-wallet/` - Tauri v2 mobile app (iOS) 342 + - `crates/relay/` - Web relay (axum-based) 343 + - `crates/repo-engine/` - ATProto repo engine 344 + - `crates/crypto/` - Cryptographic operations ... 345 + - `crates/common/` - Shared types and utilities 346 + - `nix/` - Nix packaging and deployment ... 347 + - `docs/` - Specs, design plans, implementation plans 348 + ``` 349 + 350 + **Step 4: Update `## Dev Environment` shell tools list** 351 + 352 + The current `## Dev Environment` section lists the tools the Nix shell provides (line starting with `- Shell provides:`). After Phase 3 Task 1 adds `cargo-tauri`, `nodejs_22`, and `pnpm` to devenv.nix, update this line to include them. 353 + 354 + Find the line that reads: 355 + ``` 356 + - Shell provides: just, cargo-audit, sqlite (runtime binary + dev headers/library for sqlx's libsqlite3-sys), pkg-config 357 + ``` 358 + 359 + Change it to: 360 + ``` 361 + - Shell provides: just, cargo-audit, sqlite (runtime binary + dev headers/library for sqlx's libsqlite3-sys), pkg-config, cargo-tauri, node (22.x), pnpm 362 + ``` 363 + 364 + Also update the `Last verified` date if it needs updating. 365 + 366 + **Step 5: Commit** 367 + 368 + ```bash 369 + git add CLAUDE.md 370 + git commit -m "docs(MM-143): add Mobile section to root CLAUDE.md, update Dev Environment and Project Structure" 371 + ``` 372 + <!-- END_TASK_4 --> 373 + 374 + <!-- END_SUBCOMPONENT_A -->
+452
docs/implementation-plans/2026-03-14-MM-143/test-requirements.md
··· 1 + # MM-143: Test Requirements 2 + 3 + This document maps every acceptance criterion from MM-143 to a verification method: automated command, manual human verification, or design-doc verification. A developer can verify all ACs from this document alone. 4 + 5 + Prerequisite for all checks: enter the Nix dev shell with `nix develop --impure --accept-flake-config` from the workspace root. 6 + 7 + --- 8 + 9 + ## Summary 10 + 11 + | AC | Description | Verification Type | 12 + |----------|------------------------------------------|-------------------| 13 + | AC1.1 | Frontend files exist | Automated | 14 + | AC1.2 | src-tauri files exist | Automated | 15 + | AC1.3 | SvelteKit 2.x / Svelte 5.x versions | Automated | 16 + | AC1.4 | Tauri 2.x version | Automated | 17 + | AC2.1 | src-tauri in workspace members | Automated | 18 + | AC2.2 | `cargo build` succeeds | Automated | 19 + | AC2.3 | `cargo clippy` passes | Automated | 20 + | AC2.4 | `cargo fmt --check` passes | Automated | 21 + | AC2.5 | Existing crates unaffected | Automated | 22 + | AC3.1 | App appears in iOS simulator | Manual | 23 + | AC3.2 | Placeholder screen visible | Manual | 24 + | AC3.3 | No crash on launch | Manual | 25 + | AC4.1 | Greet button triggers Rust command | Manual | 26 + | AC4.2 | Rust response displayed in UI | Manual | 27 + | AC4.3 | No JS console errors during IPC | Manual | 28 + | AC4.4 | UI visible without scrolling (iPhone 15) | Manual | 29 + | AC5.1 | cargo-tauri in PATH after nix develop | Automated | 30 + | AC5.2 | node 22.x in PATH after nix develop | Automated | 31 + | AC5.3 | pnpm in PATH after nix develop | Automated | 32 + | AC5.4 | CLAUDE.md exists with required content | Automated | 33 + | AC5.5 | Root CLAUDE.md points to wallet CLAUDE.md| Automated | 34 + | AC5.6 | src-tauri/gen/ in .gitignore | Automated | 35 + | AC6.1 | rust-check job documented | Design-doc | 36 + | AC6.2 | frontend-check job documented | Design-doc | 37 + | AC6.3 | Simulator testing excluded from CI noted | Design-doc | 38 + 39 + --- 40 + 41 + ## AC1: Project directory structure exists 42 + 43 + ### AC1.1 — Frontend files exist (Automated) 44 + 45 + Verify that all required frontend files are present at `apps/identity-wallet/`. 46 + 47 + ```bash 48 + # Run from workspace root. All 7 files must exist (exit code 0 for each). 49 + test -f apps/identity-wallet/package.json && \ 50 + test -f apps/identity-wallet/svelte.config.js && \ 51 + test -f apps/identity-wallet/vite.config.ts && \ 52 + test -f apps/identity-wallet/src/routes/+layout.ts && \ 53 + test -f apps/identity-wallet/src/routes/+layout.svelte && \ 54 + test -f apps/identity-wallet/src/routes/+page.svelte && \ 55 + test -f apps/identity-wallet/src/lib/ipc.ts && \ 56 + echo "AC1.1 PASS" || echo "AC1.1 FAIL" 57 + ``` 58 + 59 + ### AC1.2 — src-tauri files exist (Automated) 60 + 61 + Verify that all required Rust backend files are present at `apps/identity-wallet/src-tauri/`. 62 + 63 + ```bash 64 + test -f apps/identity-wallet/src-tauri/Cargo.toml && \ 65 + test -f apps/identity-wallet/src-tauri/tauri.conf.json && \ 66 + test -f apps/identity-wallet/src-tauri/build.rs && \ 67 + test -f apps/identity-wallet/src-tauri/src/lib.rs && \ 68 + test -f apps/identity-wallet/src-tauri/src/main.rs && \ 69 + echo "AC1.2 PASS" || echo "AC1.2 FAIL" 70 + ``` 71 + 72 + ### AC1.3 — SvelteKit 2.x and Svelte 5.x (Automated) 73 + 74 + Verify version ranges declared in `package.json`. 75 + 76 + ```bash 77 + # Check that @sveltejs/kit version starts with ^2 78 + grep -q '"@sveltejs/kit": "\^2' apps/identity-wallet/package.json && \ 79 + # Check that svelte version starts with ^5 80 + grep -q '"svelte": "\^5' apps/identity-wallet/package.json && \ 81 + echo "AC1.3 PASS" || echo "AC1.3 FAIL" 82 + ``` 83 + 84 + ### AC1.4 — Tauri 2.x (Automated) 85 + 86 + Verify Tauri dependency version in `src-tauri/Cargo.toml`. 87 + 88 + ```bash 89 + # Check that tauri dependency is version 2 (e.g., tauri = "2" or tauri = "2.x.y") 90 + grep -qE '^tauri = "2' apps/identity-wallet/src-tauri/Cargo.toml && \ 91 + echo "AC1.4 PASS" || echo "AC1.4 FAIL" 92 + ``` 93 + 94 + --- 95 + 96 + ## AC2: Cargo workspace build succeeds 97 + 98 + ### AC2.1 — src-tauri in workspace members (Automated) 99 + 100 + ```bash 101 + grep -q 'apps/identity-wallet/src-tauri' Cargo.toml && \ 102 + echo "AC2.1 PASS" || echo "AC2.1 FAIL" 103 + ``` 104 + 105 + ### AC2.2 — cargo build succeeds (Automated) 106 + 107 + ```bash 108 + cargo build 2>&1 && echo "AC2.2 PASS" || echo "AC2.2 FAIL" 109 + ``` 110 + 111 + ### AC2.3 — cargo clippy passes (Automated) 112 + 113 + ```bash 114 + cargo clippy --workspace -- -D warnings 2>&1 && echo "AC2.3 PASS" || echo "AC2.3 FAIL" 115 + ``` 116 + 117 + ### AC2.4 — cargo fmt passes (Automated) 118 + 119 + ```bash 120 + cargo fmt --all --check 2>&1 && echo "AC2.4 PASS" || echo "AC2.4 FAIL" 121 + ``` 122 + 123 + ### AC2.5 — Existing crates unaffected (Automated) 124 + 125 + Verify that the four original crates still build individually, confirming the new workspace member did not introduce errors. 126 + 127 + ```bash 128 + cargo build --package relay && \ 129 + cargo build --package repo-engine && \ 130 + cargo build --package crypto && \ 131 + cargo build --package common && \ 132 + echo "AC2.5 PASS" || echo "AC2.5 FAIL" 133 + ``` 134 + 135 + --- 136 + 137 + ## AC3: App launches in iOS simulator (Manual) 138 + 139 + **Justification:** iOS simulator testing requires a physical macOS machine with Xcode, iOS Simulator platform, and Cocoapods installed. These cannot run in CI or be meaningfully automated. 140 + 141 + **Prerequisites:** 142 + 1. macOS Ventura (13) or later 143 + 2. Xcode installed (latest stable from App Store) 144 + 3. iOS Simulator platform installed (Xcode -> Settings -> Platforms -> iOS) 145 + 4. Cocoapods installed: `sudo gem install cocoapods` 146 + 5. Inside the Nix dev shell (`nix develop --impure --accept-flake-config`) 147 + 148 + **One-time setup (if not already done):** 149 + 150 + ```bash 151 + cd apps/identity-wallet 152 + pnpm install 153 + cargo tauri ios init 154 + ``` 155 + 156 + **Steps:** 157 + 158 + 1. Open Xcode.app once to accept any pending license agreement. 159 + 2. Run from `apps/identity-wallet/`: 160 + ```bash 161 + cargo tauri ios dev 162 + ``` 163 + 3. Wait for the Vite dev server to start and the Rust crate to compile for `aarch64-apple-ios-sim`. The iOS Simulator will open automatically. 164 + 165 + **Verification checklist:** 166 + 167 + ### AC3.1 — App appears in iOS simulator 168 + - [ ] After `cargo tauri ios dev` completes, an app window appears in the iOS Simulator. 169 + - **PASS** if the app window is visible. **FAIL** if the simulator opens but no app launches, or if the command errors out before the simulator opens. 170 + 171 + ### AC3.2 — Placeholder screen visible (not blank) 172 + - [ ] The app displays the "Identity Wallet" heading and a Greet button with an input field. 173 + - **PASS** if content is visible. **FAIL** if the screen is blank white, shows "about:blank", or displays an error page. 174 + 175 + ### AC3.3 — No crash on launch 176 + - [ ] No crash dialog appears in the simulator after the app loads. 177 + - [ ] The app remains responsive (does not freeze or hang). 178 + - **PASS** if the app stays running for at least 10 seconds after launch. **FAIL** if a crash dialog appears or the app force-closes. 179 + 180 + --- 181 + 182 + ## AC4: IPC bridge functions correctly (Manual) 183 + 184 + **Justification:** IPC verification requires tapping the Greet button in the iOS simulator and observing the WebView response. The WebView inspector (Safari Developer Tools) is needed to check for console errors. None of this can be automated without a GUI testing framework connected to the simulator. 185 + 186 + **Prerequisites:** Same as AC3 (app must be running in the iOS simulator via `cargo tauri ios dev`). 187 + 188 + **Steps:** 189 + 190 + 1. In the iOS Simulator, observe the input field. It should contain "World" by default. 191 + 2. Tap the **Greet** button. 192 + 3. Open Safari on macOS. Go to **Develop** menu -> **[Simulator name]** -> select the WebView to open the Web Inspector. 193 + 194 + **Verification checklist:** 195 + 196 + ### AC4.1 — Greet button triggers the Rust greet command 197 + - [ ] After tapping the Greet button, the UI updates (a response message appears below the button). 198 + - **PASS** if a response appears. **FAIL** if tapping the button does nothing or throws a visible error. 199 + 200 + ### AC4.2 — Rust response displayed in the UI 201 + - [ ] The text "Hello, World!" appears on screen after tapping Greet with the default "World" input. 202 + - [ ] Change the input to a different name (e.g., "Alice"), tap Greet again, and verify "Hello, Alice!" appears. 203 + - **PASS** if the correct greeting is displayed for any name. **FAIL** if the displayed text is wrong, empty, or an error message. 204 + 205 + ### AC4.3 — No JavaScript console errors during IPC 206 + - [ ] In Safari Web Inspector's Console tab, there are zero error-level messages during and after tapping the Greet button. 207 + - [ ] Warnings are acceptable; errors are not. 208 + - **PASS** if the Console shows no red error entries during the IPC invocation. **FAIL** if any `Error` or `TypeError` appears. 209 + 210 + ### AC4.4 — UI visible without scrolling on iPhone 15 211 + - [ ] In the iOS Simulator, select **Device** -> **iPhone 15** (or the default simulator if already iPhone 15). 212 + - [ ] The heading ("Identity Wallet"), input field, Greet button, and response text are all visible on screen without scrolling. 213 + - **PASS** if all four elements are visible simultaneously. **FAIL** if any element requires scrolling to reach. 214 + 215 + --- 216 + 217 + ## AC5: Dev environment and documentation 218 + 219 + ### AC5.1 — cargo-tauri in PATH (Automated) 220 + 221 + This check must be run inside a fresh Nix dev shell. 222 + 223 + ```bash 224 + nix develop --impure --accept-flake-config --command bash -c \ 225 + 'command -v cargo-tauri && cargo-tauri --version | grep -qE "^cargo-tauri-cli 2\." && echo "AC5.1 PASS" || echo "AC5.1 FAIL"' 226 + ``` 227 + 228 + **Note:** If the above command takes too long due to Nix evaluation, you can also verify manually: 229 + 1. Enter the dev shell: `nix develop --impure --accept-flake-config` 230 + 2. Run: `cargo-tauri --version` 231 + 3. Verify the output shows version 2.x.x. 232 + 233 + ### AC5.2 — Node.js 22.x in PATH (Automated) 234 + 235 + ```bash 236 + nix develop --impure --accept-flake-config --command bash -c \ 237 + 'node --version | grep -qE "^v22\." && echo "AC5.2 PASS" || echo "AC5.2 FAIL"' 238 + ``` 239 + 240 + ### AC5.3 — pnpm in PATH (Automated) 241 + 242 + ```bash 243 + nix develop --impure --accept-flake-config --command bash -c \ 244 + 'command -v pnpm > /dev/null && echo "AC5.3 PASS" || echo "AC5.3 FAIL"' 245 + ``` 246 + 247 + ### AC5.4 — CLAUDE.md exists with required content (Automated) 248 + 249 + Verify the file exists and contains all five required topics: macOS/Xcode prerequisites, Cocoapods, `pnpm install`, `cargo tauri ios init`, `cargo tauri ios dev`. 250 + 251 + ```bash 252 + test -f apps/identity-wallet/CLAUDE.md && \ 253 + grep -q "Xcode" apps/identity-wallet/CLAUDE.md && \ 254 + grep -q "Cocoapods" apps/identity-wallet/CLAUDE.md && \ 255 + grep -q "cocoapods" apps/identity-wallet/CLAUDE.md && \ 256 + grep -q "pnpm install" apps/identity-wallet/CLAUDE.md && \ 257 + grep -q "cargo tauri ios init" apps/identity-wallet/CLAUDE.md && \ 258 + grep -q "cargo tauri ios dev" apps/identity-wallet/CLAUDE.md && \ 259 + echo "AC5.4 PASS" || echo "AC5.4 FAIL" 260 + ``` 261 + 262 + ### AC5.5 — Root CLAUDE.md points to wallet CLAUDE.md (Automated) 263 + 264 + ```bash 265 + grep -q "apps/identity-wallet/CLAUDE.md" CLAUDE.md && \ 266 + echo "AC5.5 PASS" || echo "AC5.5 FAIL" 267 + ``` 268 + 269 + ### AC5.6 — src-tauri/gen/ in .gitignore (Automated) 270 + 271 + ```bash 272 + grep -q "src-tauri/gen" .gitignore && \ 273 + echo "AC5.6 PASS" || echo "AC5.6 FAIL" 274 + ``` 275 + 276 + --- 277 + 278 + ## AC6: CI pipeline documented (Design-doc verification) 279 + 280 + **Justification:** AC6 is satisfied by the existence and content of the design plan itself. No code changes or runtime verification are needed. The CI pipeline is documented but not implemented -- the developer wires it up in tangled.org CI separately. 281 + 282 + **Design plan location:** `docs/design-plans/2026-03-14-MM-143.md`, section "Suggested CI Pipeline" 283 + 284 + ### AC6.1 — rust-check job documented 285 + 286 + - [ ] Open `docs/design-plans/2026-03-14-MM-143.md` and locate the "Suggested CI Pipeline" section. 287 + - [ ] Verify it specifies a `rust-check` job that includes: 288 + - `cargo fmt --all --check` 289 + - `cargo clippy --workspace -- -D warnings` (or equivalent `cargo clippy --workspace`) 290 + - `cargo build --workspace` 291 + - **PASS** if all three commands are listed under `rust-check`. 292 + 293 + **Automated shortcut:** 294 + 295 + ```bash 296 + grep -q "rust-check" docs/design-plans/2026-03-14-MM-143.md && \ 297 + grep -q "cargo fmt" docs/design-plans/2026-03-14-MM-143.md && \ 298 + grep -q "cargo clippy" docs/design-plans/2026-03-14-MM-143.md && \ 299 + grep -q "cargo build" docs/design-plans/2026-03-14-MM-143.md && \ 300 + echo "AC6.1 PASS" || echo "AC6.1 FAIL" 301 + ``` 302 + 303 + ### AC6.2 — frontend-check job documented 304 + 305 + - [ ] Verify the "Suggested CI Pipeline" section specifies a `frontend-check` job that includes: 306 + - `pnpm install` in `apps/identity-wallet/` 307 + - `pnpm build` 308 + - **PASS** if both commands are listed under `frontend-check`. 309 + 310 + **Automated shortcut:** 311 + 312 + ```bash 313 + grep -q "frontend-check" docs/design-plans/2026-03-14-MM-143.md && \ 314 + grep -q "pnpm install" docs/design-plans/2026-03-14-MM-143.md && \ 315 + grep -q "pnpm build" docs/design-plans/2026-03-14-MM-143.md && \ 316 + echo "AC6.2 PASS" || echo "AC6.2 FAIL" 317 + ``` 318 + 319 + ### AC6.3 — Simulator testing excluded from CI 320 + 321 + - [ ] Verify the design document explicitly states that mobile simulator testing is excluded from automated CI. 322 + - **PASS** if the document contains language indicating simulator testing is manual/excluded. 323 + 324 + **Automated shortcut:** 325 + 326 + ```bash 327 + grep -qi "excluded from automated CI" docs/design-plans/2026-03-14-MM-143.md && \ 328 + echo "AC6.3 PASS" || echo "AC6.3 FAIL" 329 + ``` 330 + 331 + --- 332 + 333 + ## Full Automated Verification Script 334 + 335 + Run all automated checks in sequence from the workspace root (inside the Nix dev shell): 336 + 337 + ```bash 338 + #!/usr/bin/env bash 339 + set -euo pipefail 340 + 341 + PASS=0 342 + FAIL=0 343 + 344 + check() { 345 + local ac="$1" 346 + shift 347 + if "$@" > /dev/null 2>&1; then 348 + echo "PASS: $ac" 349 + ((PASS++)) 350 + else 351 + echo "FAIL: $ac" 352 + ((FAIL++)) 353 + fi 354 + } 355 + 356 + echo "=== AC1: Project directory structure ===" 357 + check "AC1.1" bash -c ' 358 + test -f apps/identity-wallet/package.json && 359 + test -f apps/identity-wallet/svelte.config.js && 360 + test -f apps/identity-wallet/vite.config.ts && 361 + test -f apps/identity-wallet/src/routes/+layout.ts && 362 + test -f apps/identity-wallet/src/routes/+layout.svelte && 363 + test -f apps/identity-wallet/src/routes/+page.svelte && 364 + test -f apps/identity-wallet/src/lib/ipc.ts 365 + ' 366 + check "AC1.2" bash -c ' 367 + test -f apps/identity-wallet/src-tauri/Cargo.toml && 368 + test -f apps/identity-wallet/src-tauri/tauri.conf.json && 369 + test -f apps/identity-wallet/src-tauri/build.rs && 370 + test -f apps/identity-wallet/src-tauri/src/lib.rs && 371 + test -f apps/identity-wallet/src-tauri/src/main.rs 372 + ' 373 + check "AC1.3" bash -c ' 374 + grep -q "\"@sveltejs/kit\": \"\\^2" apps/identity-wallet/package.json && 375 + grep -q "\"svelte\": \"\\^5" apps/identity-wallet/package.json 376 + ' 377 + check "AC1.4" bash -c 'grep -qE "^tauri = \"2" apps/identity-wallet/src-tauri/Cargo.toml' 378 + 379 + echo "" 380 + echo "=== AC2: Cargo workspace build ===" 381 + check "AC2.1" bash -c 'grep -q "apps/identity-wallet/src-tauri" Cargo.toml' 382 + check "AC2.2" cargo build 383 + check "AC2.3" cargo clippy --workspace -- -D warnings 384 + check "AC2.4" cargo fmt --all --check 385 + check "AC2.5" bash -c ' 386 + cargo build --package relay && 387 + cargo build --package repo-engine && 388 + cargo build --package crypto && 389 + cargo build --package common 390 + ' 391 + 392 + echo "" 393 + echo "=== AC5: Dev environment and documentation ===" 394 + check "AC5.4" bash -c ' 395 + test -f apps/identity-wallet/CLAUDE.md && 396 + grep -q "Xcode" apps/identity-wallet/CLAUDE.md && 397 + grep -q "Cocoapods" apps/identity-wallet/CLAUDE.md && 398 + grep -q "pnpm install" apps/identity-wallet/CLAUDE.md && 399 + grep -q "cargo tauri ios init" apps/identity-wallet/CLAUDE.md && 400 + grep -q "cargo tauri ios dev" apps/identity-wallet/CLAUDE.md 401 + ' 402 + check "AC5.5" bash -c 'grep -q "apps/identity-wallet/CLAUDE.md" CLAUDE.md' 403 + check "AC5.6" bash -c 'grep -q "src-tauri/gen" .gitignore' 404 + 405 + echo "" 406 + echo "=== AC6: CI pipeline documented ===" 407 + check "AC6.1" bash -c ' 408 + grep -q "rust-check" docs/design-plans/2026-03-14-MM-143.md && 409 + grep -q "cargo fmt" docs/design-plans/2026-03-14-MM-143.md && 410 + grep -q "cargo clippy" docs/design-plans/2026-03-14-MM-143.md && 411 + grep -q "cargo build" docs/design-plans/2026-03-14-MM-143.md 412 + ' 413 + check "AC6.2" bash -c ' 414 + grep -q "frontend-check" docs/design-plans/2026-03-14-MM-143.md && 415 + grep -q "pnpm install" docs/design-plans/2026-03-14-MM-143.md && 416 + grep -q "pnpm build" docs/design-plans/2026-03-14-MM-143.md 417 + ' 418 + check "AC6.3" bash -c 'grep -qi "excluded from automated CI" docs/design-plans/2026-03-14-MM-143.md' 419 + 420 + echo "" 421 + echo "=== Manual verification required ===" 422 + echo "SKIP: AC3.1 — App appears in iOS simulator (requires Xcode + iOS Simulator)" 423 + echo "SKIP: AC3.2 — Placeholder screen visible (requires Xcode + iOS Simulator)" 424 + echo "SKIP: AC3.3 — No crash on launch (requires Xcode + iOS Simulator)" 425 + echo "SKIP: AC4.1 — Greet button triggers Rust command (requires Xcode + iOS Simulator)" 426 + echo "SKIP: AC4.2 — Rust response displayed in UI (requires Xcode + iOS Simulator)" 427 + echo "SKIP: AC4.3 — No JS console errors (requires Xcode + iOS Simulator + Safari Web Inspector)" 428 + echo "SKIP: AC4.4 — UI visible without scrolling on iPhone 15 (requires Xcode + iOS Simulator)" 429 + 430 + echo "" 431 + echo "=== Dev shell tools (run inside nix develop) ===" 432 + echo "NOTE: AC5.1, AC5.2, AC5.3 require a fresh Nix dev shell to verify." 433 + echo " Run: nix develop --impure --accept-flake-config" 434 + echo " Then: cargo-tauri --version (expect 2.x.x)" 435 + echo " Then: node --version (expect v22.x.x)" 436 + echo " Then: pnpm --version (expect any version)" 437 + 438 + echo "" 439 + echo "=== Results ===" 440 + echo "Automated: $PASS passed, $FAIL failed" 441 + echo "Manual: 7 checks require iOS simulator verification (AC3.1-AC3.3, AC4.1-AC4.4)" 442 + echo "Total: $((PASS + FAIL)) automated + 7 manual = $((PASS + FAIL + 7)) checks" 443 + ``` 444 + 445 + **Note on AC5.1-AC5.3:** These are marked "Automated" in the summary table because they can be verified with a single command, but they require entering a Nix dev shell first. The full verification script above prints a reminder to run them separately inside the dev shell. If you are already inside the dev shell, you can add these inline checks: 446 + 447 + ```bash 448 + # Only works inside `nix develop --impure --accept-flake-config` 449 + cargo-tauri --version | grep -qE "^cargo-tauri-cli 2\." && echo "AC5.1 PASS" || echo "AC5.1 FAIL" 450 + node --version | grep -qE "^v22\." && echo "AC5.2 PASS" || echo "AC5.2 FAIL" 451 + command -v pnpm > /dev/null && echo "AC5.3 PASS" || echo "AC5.3 FAIL" 452 + ```