···11+# Human Test Plan: Claim Flow Frontend (Import Screens + Multi-Identity)
22+33+**Implementation plan:** `docs/implementation-plans/2026-03-28-plc-key-management/` (phases 1-5)
44+**Generated:** 2026-03-29
55+66+## Prerequisites
77+88+- macOS with Xcode installed (latest stable)
99+- iOS Simulator available (iPhone target)
1010+- Nix dev shell active from workspace root: `nix develop --impure --accept-flake-config`
1111+- Frontend dependencies installed: `cd apps/identity-wallet && pnpm install`
1212+- Xcode project generated: `cargo tauri ios init` (with PATH patch and sandbox disabling per `apps/identity-wallet/CLAUDE.md`)
1313+- TypeScript types compiling: `cd apps/identity-wallet && pnpm check`
1414+- Rust compiling: `cargo build -p identity-wallet --lib`
1515+- An existing AT Protocol identity on a reachable PDS (e.g., a Bluesky account) for the import flow
1616+- Access to the email address associated with that identity (for the verification code)
1717+- App launched via `cd apps/identity-wallet && cargo tauri ios dev`
1818+1919+---
2020+2121+## Phase 1: Mode Selector and Identity-Aware Routing
2222+2323+| Step | Action | Expected |
2424+|------|--------|----------|
2525+| 1 | Reset the iOS Simulator: Device > Erase All Content and Settings | Simulator is clean; no Keychain entries |
2626+| 2 | Launch the app via `cd apps/identity-wallet && cargo tauri ios dev` | App compiles and opens in the Simulator |
2727+| 3 | Observe the first screen | The mode selector screen is displayed (NOT the relay config screen). Heading reads "Identity Wallet" with tagline "Your self-sovereign identity, in your pocket." |
2828+| 4 | Verify two buttons are visible | "Create new identity" button (primary/prominent styling) and "I have an identity" button (secondary styling) are both visible |
2929+| 5 | Tap each button area to confirm interactivity | Both buttons respond to taps (visual feedback on press); neither is disabled or grayed out |
3030+3131+**Covers:** plc-key-management.AC5.1
3232+3333+---
3434+3535+## Phase 2: Identity Input and Resolution
3636+3737+| Step | Action | Expected |
3838+|------|--------|----------|
3939+| 1 | From the mode selector, tap "I have an identity" | Identity input screen appears with: a text input field, a "Resolve" button, and a "Back" button |
4040+| 2 | Enter a handle that does not exist: `this-handle-definitely-does-not-exist-12345.test` | Text appears in the input field |
4141+| 3 | Tap "Resolve" | A loading indicator appears briefly |
4242+| 4 | Observe the screen after loading | Inline error message: "Handle not found. Check the spelling and try again." The user remains on the identity input screen. No "Continue" button is visible |
4343+| 5 | Clear the text input completely | Input field is empty |
4444+| 6 | Enter your valid AT Protocol handle (e.g., `yourname.bsky.social`) | Handle text appears in the input field |
4545+| 7 | Tap "Resolve" | A loading indicator appears while resolution is in progress |
4646+| 8 | Observe the resolved identity card | Card displays: Handle (e.g., `@yourname.bsky.social`), DID (truncated `did:plc:...`), PDS URL (e.g., `https://morel.us-east.host.bsky.network`), Rotation key status (either "Your device is the root key" in green or "Device key is not the root key" in neutral styling) |
4747+| 9 | Verify "Continue" button appeared | A "Continue" button is visible below the identity card |
4848+| 10 | Verify error message cleared | The previous "Handle not found" error is no longer displayed |
4949+| 11 | Tap "Back" | Navigation returns to the mode selector screen with both buttons visible |
5050+5151+**Covers:** plc-key-management.AC5.3, plc-key-management.AC5.4
5252+5353+---
5454+5555+## Phase 3: PDS Authentication and Email Verification
5656+5757+| Step | Action | Expected |
5858+|------|--------|----------|
5959+| 1 | From mode selector, tap "I have an identity", enter your valid handle, tap "Resolve", then tap "Continue" | PDS auth screen appears showing the PDS URL and an "Authenticate with PDS" button. A "Back" button is visible |
6060+| 2 | Tap "Authenticate with PDS" | A spinner appears with text "Opening browser for PDS authentication..." then Safari opens with the PDS OAuth authorization page |
6161+| 3 | Complete the OAuth authorization in Safari (approve the request) | Safari redirects back to the app via deep link. The app advances to the email verification screen |
6262+| 4 | Observe the email verification screen on mount | A spinner appears with "Sending verification email..." |
6363+| 5 | Wait for the email sending to complete | The screen shows: instruction text ("A verification code has been sent to your email..."), a text input for the token, and a "Verify" button |
6464+| 6 | Enter an invalid token: `000000` | Text appears in the token input |
6565+| 7 | Tap "Verify" | An inline error message appears: "Invalid or expired verification code. Check your email and try again." The user remains on the email verification screen. The token input is still editable |
6666+| 8 | Clear the input and enter the correct verification code from your email | Correct code is in the input field |
6767+| 9 | Tap "Verify" | The error clears. A loading state appears. On success, the app navigates to the review operation screen |
6868+6969+**Covers:** plc-key-management.AC5.5, plc-key-management.AC5.6, plc-key-management.AC5.7
7070+7171+---
7272+7373+## Phase 4: Review Operation and Claim Submission
7474+7575+| Step | Action | Expected |
7676+|------|--------|----------|
7777+| 1 | Observe the review operation screen | The screen displays: **Keys section** showing keys being added (green, `+` prefix) and/or keys being removed (red, `-` prefix), or "No key changes". **Services section** showing service changes or "No service changes" |
7878+| 2 | Verify key display formatting | Key values are in monospace font, truncated for mobile (first 20 characters + "...") |
7979+| 3 | Verify color coding | Added items use green (`#22c55e`), removed items use red (`#ef4444`), modified items use amber (`#f59e0b`) |
8080+| 4 | Verify buttons at bottom | "Confirm & Submit" (primary) and "Cancel" (secondary) buttons are visible |
8181+| 5a | **If warnings are present:** observe warning display | Warning messages appear in amber/yellow highlighted boxes. "Confirm & Submit" button is disabled (grayed out). A checkbox appears: "I understand these warnings and want to proceed" |
8282+| 5b | **If warnings present:** tap the acknowledgment checkbox | "Confirm & Submit" button becomes enabled/tappable |
8383+| 5c | **If warnings present:** untap the checkbox | Button becomes disabled again |
8484+| 5d | **If warnings present:** re-tap checkbox to enable, then tap "Confirm & Submit" | Submission proceeds (see step 6) |
8585+| 5e | **If no warnings:** verify no warning section or checkbox is shown | "Confirm & Submit" button is enabled by default |
8686+| 6 | Tap "Confirm & Submit" (with checkbox acknowledged if warnings present) | A loading state appears |
8787+| 7 | Observe the claim success screen | Screen shows: green checkmark icon/circle, heading "Identity Claimed Successfully", description text about rotation key control, DID document summary card showing DID, handle, and PDS endpoint |
8888+| 8 | Verify "Done" button | A "Done" button is visible |
8989+| 9 | Tap "Done" | App navigates to the home screen (IdentityListHome). The claimed identity appears as a card on the home screen |
9090+9191+**Covers:** plc-key-management.AC5.8, plc-key-management.AC5.9, plc-key-management.AC5.10
9292+9393+---
9494+9595+## Phase 5: Multi-Identity Home and Add Identity
9696+9797+| Step | Action | Expected |
9898+|------|--------|----------|
9999+| 1 | Observe the home screen after completing the import flow | IdentityListHome displays one identity card |
100100+| 2 | Verify identity card content | Card shows: DID avatar (colored circle), handle (e.g., `@yourname.bsky.social`) or "Unknown handle", truncated DID, PDS endpoint |
101101+| 3 | Verify rotation key status badge on the card | Green "Root Key" badge if device key is `rotationKeys[0]`, amber "Not Root" if not primary, or gray "Unknown" if undetermined |
102102+| 4 | Tap the identity card | Navigates to identity detail view (DIDDocumentScreen) showing the full DID document |
103103+| 5 | Tap "Back" on detail view | Returns to the home screen |
104104+| 6 | Verify "Add Identity" button | An "Add Identity" button is visible at the bottom of the identity list |
105105+| 7 | Tap "Add Identity" | The mode selector screen appears with both "Create new identity" and "I have an identity" options |
106106+107107+**Covers:** plc-key-management.AC5.11, plc-key-management.AC5.12
108108+109109+---
110110+111111+## Phase 6: Returning User and Onboarding Regression
112112+113113+### AC5.2 -- Returning user skips mode selector
114114+115115+| Step | Action | Expected |
116116+|------|--------|----------|
117117+| 1 | From the home screen with at least one identity claimed (from Phase 4) | Home screen is visible |
118118+| 2 | Force-quit the app (swipe up from app switcher, or stop the dev server) | App is terminated |
119119+| 3 | Relaunch the app via `cargo tauri ios dev` | App opens. The mode selector does NOT appear. The home screen (IdentityListHome) is displayed directly with the previously claimed identity card(s) |
120120+121121+**Covers:** plc-key-management.AC5.2
122122+123123+### AC5.13 -- Onboarding regression
124124+125125+| Step | Action | Expected |
126126+|------|--------|----------|
127127+| 1 | Reset the iOS Simulator (Erase All Content and Settings) | Clean state |
128128+| 2 | Launch the app via `cargo tauri ios dev` | Mode selector screen appears |
129129+| 3 | Tap "Create new identity" | Relay config screen appears (or is skipped if a relay URL is saved) |
130130+| 4 | Enter a valid relay URL (e.g., `https://relay.ezpds.com` or `http://localhost:2583`) and tap Connect | Welcome screen appears with "Get Started" button |
131131+| 5 | Proceed through the full onboarding flow: Welcome > Claim Code > Email > Handle > Password > Loading > DID Ceremony > DID Success > Shamir Backup > Handle Registration > Complete > Authenticating | Each screen renders correctly with proper inputs, buttons, and transitions |
132132+| 6 | Observe the final screen | Onboarding completes; app arrives at IdentityListHome |
133133+| 7 | Verify the identity card | The newly created identity appears as a card with correct handle and DID |
134134+| 8 | Force-quit and relaunch the app | App opens directly to the home screen (mode selector skipped) |
135135+136136+**Covers:** plc-key-management.AC5.13
137137+138138+---
139139+140140+## End-to-End Scenarios
141141+142142+### E2E-1: First Launch Import Existing Identity
143143+144144+Validates the complete import flow from fresh install through claim submission to home screen.
145145+146146+| Step | Action | Expected |
147147+|------|--------|----------|
148148+| 1 | Reset the iOS Simulator | Simulator is clean |
149149+| 2 | Launch the app | Mode selector appears with two options |
150150+| 3 | Tap "I have an identity" | Identity input screen appears |
151151+| 4 | Enter a valid handle and tap "Resolve" | Identity info card displays DID, handle, PDS, rotation key status |
152152+| 5 | Tap "Continue" | PDS auth screen appears with PDS URL |
153153+| 6 | Tap "Authenticate with PDS" | Safari opens for OAuth |
154154+| 7 | Complete OAuth in Safari | App returns to email verification screen |
155155+| 8 | Wait for "Sending verification email..." to complete | Token input form appears |
156156+| 9 | Enter verification code from email, tap "Verify" | Review operation screen appears with diff |
157157+| 10 | Tap "Confirm & Submit" (acknowledge warnings if present) | Claim success screen appears with DID doc summary |
158158+| 11 | Tap "Done" | Home screen shows the claimed identity card with status badge |
159159+160160+### E2E-2: Multiple Identities (Create Then Import)
161161+162162+Validates that both identity creation paths coexist and multi-identity home correctly displays cards from different flows.
163163+164164+| Step | Action | Expected |
165165+|------|--------|----------|
166166+| 1 | Reset the iOS Simulator | Simulator is clean |
167167+| 2 | Launch the app, tap "Create new identity" | Relay config screen appears |
168168+| 3 | Complete full onboarding (relay config through auth) | Home screen shows one identity card |
169169+| 4 | Tap "Add Identity" button | Mode selector appears |
170170+| 5 | Tap "I have an identity" | Identity input screen appears |
171171+| 6 | Complete import flow (resolve handle, PDS auth, email verification, review, submit) | Claim success screen appears |
172172+| 7 | Tap "Done" | Home screen shows two identity cards with status badges |
173173+| 8 | Force-quit and relaunch | Home screen appears directly with both cards |
174174+175175+### E2E-3: Import Flow Error Recovery
176176+177177+Validates that all error states in the import flow are recoverable.
178178+179179+| Step | Action | Expected |
180180+|------|--------|----------|
181181+| 1 | From mode selector, tap "I have an identity" | Identity input screen appears |
182182+| 2 | Enter an invalid handle (`this-handle-definitely-does-not-exist-12345.test`), tap "Resolve" | Inline error: "Handle not found. Check the spelling and try again." |
183183+| 3 | Clear input, enter a valid handle, tap "Resolve" | Identity card appears; error clears |
184184+| 4 | Tap "Continue", then tap "Authenticate with PDS" | Safari opens |
185185+| 5 | Cancel or deny OAuth in Safari | Error message on PDS auth screen |
186186+| 6 | Tap "Authenticate with PDS" again | Safari reopens for retry |
187187+| 7 | Complete OAuth successfully | Email verification screen appears |
188188+| 8 | Enter wrong verification code (`000000`), tap "Verify" | Inline error: "Invalid or expired verification code. Check your email and try again." |
189189+| 9 | Enter correct code, tap "Verify" | Review operation screen appears; error clears |
190190+| 10 | Tap "Cancel" on review screen | Returns to identity input screen |
191191+192192+### E2E-4: Returning User Skips Mode Selector
193193+194194+Validates that Keychain-persisted identity state survives app restart.
195195+196196+| Step | Action | Expected |
197197+|------|--------|----------|
198198+| 1 | Complete E2E-1 (at least one identity claimed) | Home screen visible |
199199+| 2 | Force-quit the app completely | App terminated |
200200+| 3 | Relaunch the app | Home screen appears directly (mode selector skipped) |
201201+| 4 | Verify identity cards are displayed | All previously claimed identities shown with correct data |
202202+203203+---
204204+205205+## Traceability
206206+207207+| Acceptance Criterion | Automated Test | Manual Step |
208208+|----------------------|----------------|-------------|
209209+| plc-key-management.AC5.1 | -- | Phase 1: Mode selector with two buttons on fresh launch |
210210+| plc-key-management.AC5.2 | `IdentityStore::list_identities()` tests | Phase 6: Relaunch with existing identities skips to home |
211211+| plc-key-management.AC5.3 | `resolve_identity()` tests | Phase 2: Enter handle, tap Resolve, verify identity card |
212212+| plc-key-management.AC5.4 | `resolve_identity()` error path tests | Phase 2: Enter bad handle, verify inline error |
213213+| plc-key-management.AC5.5 | `start_pds_auth()` tests | Phase 3: Tap Authenticate, verify Safari opens, complete OAuth |
214214+| plc-key-management.AC5.6 | `sign_and_verify_claim()` tests | Phase 3: Enter correct token, verify flow advances |
215215+| plc-key-management.AC5.7 | `sign_and_verify_claim()` INVALID_TOKEN test | Phase 3: Enter wrong token, verify inline error |
216216+| plc-key-management.AC5.8 | -- | Phase 4: Inspect diff display with color coding |
217217+| plc-key-management.AC5.9 | -- | Phase 4: Verify button disabled with warnings, checkbox enables it |
218218+| plc-key-management.AC5.10 | `submit_claim()` tests | Phase 4: Verify success screen content, tap Done |
219219+| plc-key-management.AC5.11 | `get_did_doc()` + `get_or_create_device_key()` tests | Phase 5: Verify multi-identity cards with status badges |
220220+| plc-key-management.AC5.12 | -- | Phase 5: Tap "Add Identity", verify mode selector appears |
221221+| plc-key-management.AC5.13 | -- | Phase 6: Full onboarding regression |