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).
This commit is contained in:
EdisonJwa
2026-05-16 14:41:33 +08:00
parent f0ddb160a0
commit 6d094f3dbe
2 changed files with 247 additions and 1 deletions
+2 -1
View File
@@ -1,7 +1,7 @@
# Document Index
**Document type:** Configuration / Documentation Control
**Version:** 0.9.8
**Version:** 0.9.9
**Status:** Baseline Candidate
**Language:** English
**Product:** Chanora
@@ -90,3 +90,4 @@ SysDes -> SYS.4
| 0.9.6 | 2026-05-16 | Added DEC-031 (missed-key-up watchdog disabled on P0; redesign deferred to P1) and new controlled document `docs/verification/windows-p0-acceptance.md` carrying the 15-row human-must acceptance plan for the v1.0.0-rc.8 Windows ship. No spec items added; DEC-028 retained as historical context, superseded by DEC-031 for the v1 ship. |
| 0.9.7 | 2026-05-16 | Added controlled document `docs/verification/linux-p0-acceptance.md` carrying the 15-row human-must acceptance plan for the v1.0.0-rc.8 Linux (GNOME on Wayland) ship. Auto-test sign-off rows filled from the Arch host `100.74.219.114` verification pass: `cargo check --workspace --release` clean, `cargo test --workspace --lib` 78 / 0 / 1, `cargo test -p chanora_audio --test linux_portal_smoke -- --ignored` 1 / 0, `cargo test -p chanora_audio --test ptt_privacy` 1 / 0. No spec items added; the existing SDD-086 GNOME-Wayland portal backend is what this document signs off. |
| 0.9.8 | 2026-05-16 | Added controlled documents `docs/verification/macos-p0-acceptance.md` (15-row checklist for Apple Silicon macOS, SDD-085 CGEventTap backend now fully live with IOHIDCheckAccess + CGEventTapCreate + per-1.5s permission watcher) and `docs/verification/ios-p0-acceptance.md` (12-row checklist for physical iPhone via the free Apple Personal Team, SDD-094..097 audio lifecycle, AVAudioSession `.playAndRecord/.voiceChat` configured Swift-side in AppDelegate). The macOS PTT backend descriptor advertises L2/L3 only when Input Monitoring is granted, otherwise L0Focused per SRS-198 honest capability advertising. iOS PTT is L0Focused by design (DEC-025 explicitly does not pursue a global PTT analogue on iOS). No new spec items. |
| 0.9.9 | 2026-05-16 | Added controlled document `docs/verification/ipad-p0-acceptance.md` carrying the 15-row checklist for iPad P0 sign-off (same TS-3.3 reach, three iPad-specific rows: wide-mode landscape layout under the existing 840 dp LayoutBuilder breakpoint, Split View / Slide Over no-crash assertion since `UIApplicationSupportsMultipleScenes` stays `false` in P0, and AirPlay 2 audio routing). The iPad build artefact is the **same** `Runner.app` produced for iPhone — `TARGETED_DEVICE_FAMILY = "1,2"` in `ios/Runner.xcodeproj/project.pbxproj` is the Universal family. DEC-025 was originally iPhone-only for the mobile target; this row formally extends P0 coverage to iPad (still within the same iOS toolchain, no new Rust or Flutter code). No spec items added. |
+245
View File
@@ -0,0 +1,245 @@
# 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).