Files
chanora/docs/verification/ipad-p0-acceptance.md
T
EdisonJwa 6d094f3dbe docs(verification,index): iPad p0 acceptance checklist + 0.9.9 index row
New controlled document docs/verification/ipad-p0-acceptance.md
extends iOS P0 coverage to iPad. The build artefact is identical
to iPhone — `TARGETED_DEVICE_FAMILY = "1,2"` in
ios/Runner.xcodeproj/project.pbxproj is the Universal family, so
the same Runner.app installs on iPad with the same personal-team
provisioning profile.

15-row checklist mirrors ios-p0-acceptance.md TC-1..TC-12 and adds
three iPad-specific rows:

  TC-13 wide-mode landscape layout — iPad in landscape is well
        above the 840 dp LayoutBuilder breakpoint shipped in
        apps/chanora_flutter/lib/main.dart, so the connected view
        splits into a 320 dp left column (banner + Voice Bar) plus
        an expanding channel tree. Portrait rotation collapses
        back to the stacked iPhone layout. Long channel-name pills
        still ellipsize per the earlier voice_bar.dart fix.

  TC-14 Split View / Slide Over no-crash — Apple iPad multitasking
        is intentionally unsupported in P0. UIApplicationSupports\
        MultipleScenes stays false. This row asserts the app does
        not crash when iPadOS tries to host it in Split View; the
        actual multi-scene wiring is P1.

  TC-15 AirPlay 2 audio route — verifies AVAudioSession routing
        honours an AirPlay 2 destination picked via Control
        Center, and routes back cleanly when iPad is reselected.

docs/governance/document-index.md
  Bumped to 0.9.9 with the change-history row noting the iPad
  acceptance doc. No spec items added; DEC-025 was originally
  iPhone-only for the mobile target and this row formally
  extends P0 coverage to iPad within the same iOS toolchain.

python3 tools/validate_docs.py: clean (pre-existing 35-filename
[FAIL] retained, unchanged).
2026-05-16 14:41:33 +08:00

12 KiB

iPad P0 acceptance — human-must test cases (v1.0.0-rc.8)

This document captures the human-side verification pass for the iPad P0 ship of Chanora. The build artefact is the same Runner.app produced for iPhone (TARGETED_DEVICE_FAMILY = "1,2" in Runner.xcodeproj — both iPhone (1) and iPad (2)). Apple's free Personal Team provisioning profile includes iPad in the universal family, so the same sideload that installs on an iPhone installs on an iPad.

The officially-tested environment is iPadOS 17 or later on a physical iPad, signed with the developer's personal team (DEC-025 covers iPhone; this document extends P0 coverage to iPad). Simulator support is best-effort — microphone capture and AirPlay 2 routing cannot be assessed in the simulator.

Automated tests (cargo check, cargo test --workspace --lib) sign off the mechanical side. This checklist signs off the user- experience side. A failure on any row blocks promotion of the iPad build from rc.8.

Source: this checklist extends ios-p0-acceptance.md with the three iPad-specific rows (split-view multitasking, on-screen keyboard occlusion, AirPlay 2 audio route) and removes the rows that don't apply (background-audio behaviour is identical because both share the same UIBackgroundModes = ['audio'] declaration in Info.plist). SDD references: SDD-094..097 (v1 audio + PTT lifecycle), DEC-025 (target environment), DEC-027 (diagnostic privacy invariant), SRS-197 (iOS/iPadOS audio routing contract).

iPad does not have a global PTT capability — Apple's iOS / iPadOS sandbox model has no session-level event-tap analogue. The desktop PTT backend ladder (SDD-081..088) collapses to FocusedPttBackend and the capability badge advertises L0Focused. PTT works only while Chanora is the foreground app (or the audio-background mode keeps streaming when Chanora is backgrounded; see TC-8 / TC-13).

0. Pre-flight

# Step Expected
0.1 Same Xcode signing setup as iOS: in Runner.xcworkspace, Runner target → Signing & Capabilities → personal team selected, unique bundle id. (If you already did this for iPhone the iPad uses the same provisioning profile.) No code-signing errors in Xcode.
0.2 Connect iPad via USB and trust the development Mac. On iPad: Settings → General → VPN & Device Management → developer-cert → Trust. iPad appears in Xcode's device picker.
0.3 Build for iPad: in Xcode pick the iPad as the run destination + Runner scheme → Cmd+R. (Or via CLI: flutter build ios --release then sideload with xcrun devicectl device install app.) App installs on iPad and launches.
0.4 First launch: iPad prompts for Microphone access — Allow. The prompt string matches NSMicrophoneUsageDescription from ios/Runner/Info.plist. Mic permission granted.
0.5 About / version label inside Chanora Shows v1.0.0-rc.8.

TC-1: Connect + identity persistence (DEC-006, SRS-201)

# Step Expected
1.1 Enter a reachable TS3 server address, a nickname, blank password Connect button enabled.
1.2 Tap Connect Server snapshot appears.
1.3 Quit (background) + relaunch with the same nickname Server-visible UID is the same (identity persisted in Documents/identity.tskey).

TC-2: Voice channel join (SRS-204, SDD-094)

# Step Expected
2.1 Tap a voice channel Join works; voice bar shows the channel name. Mic capture starts (iOS status bar shows the orange mic indicator on iPadOS 17+).
2.2 Tap a different voice channel Smooth move; no audio dropout.

TC-3: PTT press / release (Focused only — iPadOS limitation)

# Step Expected
3.1 Open Voice settings (gear icon), select Push to talk Capability badge clearly states L0 Focused with explanatory text that iPadOS does not permit global hotkeys.
3.2 The "Bound key" UI is replaced by an on-screen Push-to-Talk button. Touch and hold the button → mic indicator turns "Mic on". Release → indicator returns "Mic off" after the configured release-tail.

TC-4: Release tail (SDD-097)

# Step Expected
4.1 Set release-tail to 0 ms; tap-and-release the PTT button Sharp on/off transition.
4.2 Set release-tail to 500 ms; tap-and-release Mic stays on for ~500 ms after release.

TC-5: Continuous mode

# Step Expected
5.1 Switch to Continuous mode PTT button disappears; mic stays open while in the voice channel.

TC-6: Mic mute

# Step Expected
6.1 Mute mic via the Voice Bar Mic icon shows muted state. Touching the PTT button does NOT unmute.

TC-7: Speaker mute

# Step Expected
7.1 Speaker mute Other clients silenced locally.

TC-8: Background audio (UIBackgroundModes = audio)

# Step Expected
8.1 While connected + in a voice channel + in Continuous mode, swipe Chanora to the App Switcher (or press the home button if your iPad has one). The iPadOS orange mic indicator stays on; voice continues to be received and sent.
8.2 Return to Chanora Session intact; no reconnect cycle.
8.3 Lock the iPad for 30 s, then unlock Session still intact (provided you stayed in the channel and on Wi-Fi/cellular).

TC-9: AVAudioSession routing (SRS-197)

# Step Expected
9.1 Connect AirPods / Bluetooth headset; talk on Chanora Audio routes to the headset (.allowBluetooth + .allowBluetoothA2DP are set in AppDelegate.swift::application(_:didFinishLaunchingWithOptions:)).
9.2 Disconnect the headset mid-call Audio routes back to the built-in speakers (.defaultToSpeaker).
9.3 (If iPad has FaceTime configured) place a FaceTime call to yourself or have someone call. Chanora's audio session yields to the FaceTime call.
9.4 End the FaceTime call Chanora resumes audio without manual reconnect.

TC-10: Reconnect

# Step Expected
10.1 Toggle Airplane Mode on for 10 s, then off Connection-lost banner appears, then recovers.

TC-11: Diagnostic export + privacy (DEC-027)

# Step Expected
11.1 Export a diagnostic bundle Allow-listed fields only; no raw key labels, no audio bytes.

TC-12: Capability badge label

# Step Expected
12.1 Capability badge Shows L0Focused with explanation that iPadOS does not expose global hotkeys.

TC-13: iPad-specific — wide-mode landscape layout

The Flutter UI uses a LayoutBuilder 840 dp breakpoint (apps/chanora_flutter/lib/main.dart). On iPhone in portrait the body is below 840 dp, so the layout is stacked (Voice Bar on top, channel tree below). On iPad in landscape the body is well above 840 dp, so the layout splits into a 320 dp left column (banner + Voice Bar) and the rest filled by the channel tree.

# Step Expected
13.1 Rotate iPad to landscape, connect to a server, double-click into a voice channel. Voice Bar pinned to a 320 dp left column. Channel tree fills the rest. The "not production ready" banner sits inside the left column above the Voice Bar (per the earlier left-column-banner change).
13.2 Rotate iPad to portrait. Layout flips back to the stacked iPhone-style arrangement. Voice Bar on top full-width, channel tree below. Banner pins full-width at the top above the connection status.
13.3 In landscape, join a channel with a very long name. The channel-name pill in the Voice Bar truncates with an ellipsis instead of overflowing under the channel tree. Mute icons stay anchored to the right edge of the left column.

TC-14: iPad-specific — Split View / Slide Over

Multitasking with other iPad apps. Our scaffold currently sets UIApplicationSupportsMultipleScenes = false in Info.plist, so Split View is not supported in P0. This row exists to document that and ensure the app doesn't crash when iPadOS tries.

# Step Expected
14.1 In landscape, swipe up from the iPad home indicator → drag Chanora to the right edge to enter Split View, or pick "Slide Over" from the iPadOS multitasking menu. iPadOS shows the Split View / Slide Over UI but Chanora remains full-screen and does not crash. The non-support is by design for P0; a P1 follow-up will switch to multi-scene.

TC-15: iPad-specific — AirPlay 2 audio route

# Step Expected
15.1 While in a voice channel, open Control Center → AirPlay → pick an AirPlay 2 speaker (HomePod, Apple TV, AirPlay-compatible receiver). Audio routes to the AirPlay destination. The PTT-active indicator continues to work; the mic stays routed to the iPad built-in mic (AirPlay 2 receivers don't typically have a return mic channel).
15.2 Reselect "iPad" as the audio route Audio returns to local routing. No restart needed.

Auto-test sign-off

These rows do not require human interaction. The values are the same as the iOS document — iPad uses the identical Rust + Flutter build artefacts.

Layer Result
cargo check --workspace on macOS host clean
cargo test --workspace --lib on macOS host unchanged (iPad-specific Rust code does not exist; iOS-targeted code is #[cfg(target_os = "ios")]-gated and applies identically to iPadOS)
cargo build --release --target aarch64-apple-ios -p chanora_bridge clean — produces libchanora_bridge.a
flutter build ios --release clean — produces Runner.app ≈ 17 MB (same artefact as iPhone)
Runner.xcodeproj TARGETED_DEVICE_FAMILY = "1,2" Universal family confirmed via grep TARGETED_DEVICE_FAMILY ios/Runner.xcodeproj/project.pbxproj.

Sign-off form

chanora v1.0.0-rc.8 — iPad P0 acceptance
Device:     ______________________ (iPad model + iPadOS version)
Tester:     ______________________
Date:       ______________________

TC-1  Connect + identity persistence       [ pass / fail ]  notes:
TC-2  Voice channel join                   [ pass / fail ]  notes:
TC-3  PTT press / release (Focused)        [ pass / fail ]  notes:
TC-4  Release tail                          [ pass / fail ]  notes:
TC-5  Continuous mode                       [ pass / fail ]  notes:
TC-6  Mic mute                              [ pass / fail ]  notes:
TC-7  Speaker mute                          [ pass / fail ]  notes:
TC-8  Background audio                      [ pass / fail ]  notes:
TC-9  AVAudioSession routing                [ pass / fail ]  notes:
TC-10 Reconnect                             [ pass / fail ]  notes:
TC-11 Diagnostic export + privacy           [ pass / fail ]  notes:
TC-12 Capability badge label                [ pass / fail ]  notes:
TC-13 Wide-mode landscape layout            [ pass / fail ]  notes:
TC-14 Split View / Slide Over no-crash      [ pass / fail ]  notes:
TC-15 AirPlay 2 audio route                 [ pass / fail ]  notes:

Overall result:   [ iPad rc.8 PASS / iPad rc.8 FAIL ]
Sign:             ______________________

Known gaps (not blockers)

  • Split View / Slide Over multitasking is intentionally unsupported in P0. UIApplicationSupportsMultipleScenes stays false until we wire iPadOS multi-scene handling (P1).
  • Apple Pencil + external keyboard shortcuts: not wired. A paid Developer Program account would let us declare GCKeyboard hardware-keyboard support for things like Cmd+M to mute — P1 follow-up.
  • Stage Manager (iPadOS 16+): app runs but the layout is not optimised for resizable Stage Manager windows. P1.
  • TestFlight + App Store distribution: requires the paid $99/yr Apple Developer Program. P1 follow-up.
  • Simulator audio: cpal-via-CoreAudio inside the iPad simulator routes to the Mac's audio devices and is fine for smoke-testing the UI, but real microphone capture / AEC behaviour and AirPlay 2 routing cannot be assessed there. Test on a physical iPad.
  • VAD (TransmitMode::VoiceActivity) is intentionally disabled in P0 (DEC-030).