docs(p0): compact MVP spec for Android Oboe focus

This commit is contained in:
Edison Jwa
2026-05-20 14:52:33 +09:00
parent 8c253f1d4d
commit 7d6d56e330
63 changed files with 8000 additions and 46507 deletions
-319
View File
@@ -1,319 +0,0 @@
# Android P0 acceptance — human-must test cases (v1.0.0-rc.8)
This document captures the human-side verification pass for the
Android P0 ship of Chanora. The officially-tested environment is
**Android 9 (API 28) or later on a physical Android device, installed
via either Play Internal Track or `bundletool install-apks`** (DEC-004).
Emulator support is best-effort — microphone capture and audio routing
in the AVD do not represent real-device behaviour.
Automated tests (`cargo check`, `cargo test --workspace --lib`,
`flutter test`, `flutter build appbundle --release`) sign off the
mechanical side. This checklist signs off the user-experience side.
A failure on any row blocks promotion of the Android build from rc.8.
Source: this checklist mirrors the iOS, iPadOS, macOS, Linux, and
Windows acceptance documents. SRS references: SRS-013 (audio),
SRS-111 (foreground service P0), SRS-119 (AAB packaging),
SRS-163 (Android back navigation), SRS-187 (min API 28),
SRS-188 (Play target API), SRS-208 (in-call audio mode),
SRS-209 (RECORD_AUDIO + listen-only fallback). SAD references:
SAD-018, SAD-037, SAD-063, SAD-077, SAD-081, SAD-084, SAD-085,
SAD-086. SDD references: SDD-028, SDD-073, SDD-105, SDD-106,
SDD-107, SDD-108, SDD-109, SDD-110. Decision: DEC-004
(Android min API 28). System requirements: SysRS-288, SysRS-305,
SysRS-306.
Android does NOT have a global PTT capability comparable to Windows /
macOS / Linux desktops. Per SDD-110 the desktop PTT backend ladder
collapses to `FocusedPttBackend` on Android and the capability badge
always advertises `L0Focused`. PTT works only while Chanora itself is
the foreground app (and while the voice foreground service per
SDD-107 keeps the mic stream alive when the screen is off).
## 0. Pre-flight
| # | Step | Expected |
|---|---|---|
| 0.1 | On a Linux/macOS host install Android SDK + NDK + cmdline-tools. Accept SDK licences. | `adb`, `bundletool`, and Gradle wrapper are runnable. |
| 0.2 | Connect a physical Android device (API 28+) via USB. Enable Developer Options → USB debugging. | `adb devices` lists the device as `device` (not `unauthorized`). |
| 0.3 | Build the bridge for Android: `rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android i686-linux-android && cd ~/chanora && cargo ndk -t arm64-v8a -t armeabi-v7a -t x86_64 -t x86 -p 28 -- build --release -p chanora_bridge`. | `target/<abi>/release/libchanora_bridge.so` exists for each ABI. (SDD-073 NDK ABI set, SDD-105 JNI load.) |
| 0.4 | Build the AAB: `cd apps/chanora_flutter && flutter build appbundle --release`. | `build/app/outputs/bundle/release/app-release.aab` exists. (SRS-119, SDD-109) |
| 0.5 | Validate the AAB with bundletool: `bundletool build-apks --bundle=app-release.aab --output=chanora.apks --connected-device`. | bundletool reports `Universal APK`/`split APKs` per ABI and no validation errors. (SDD-109) |
| 0.6 | Install on the connected device: `bundletool install-apks --apks=chanora.apks`. (Or Play Console → Internal Testing → upload + opt-in.) | App installs without signature errors. (SDD-073 release-signing CI assertion.) |
| 0.7 | Launch the app. First-launch microphone prompt appears. | Prompt rationale is localized. (SRS-209, SDD-106) |
| 0.8 | About / version label inside Chanora | Shows **`v1.0.0-rc.8`**. |
## Device matrix expectations
Per SRS-187 / SRS-188, DEC-004, AND SDD-116 (Android voice audio backend
device matrix) the human pass shall be executed on the following devices:
| Tier | Android version | Reason | Acceptance subset |
|---|---|---|---|
| Floor | Android 9 (API 28) | Minimum supported per DEC-004 / SRS-187 / SysRS-288; SDD-116 row A | TC-1..TC-11 (all), TC-14 |
| Mid-tier | Android 12 (API 31) | Covers FGS foreground-type tightening (API 31); SDD-116 row B | TC-1..TC-11 (all), TC-12 predictive-back (if API ≥ 33), TC-15 |
| Target | Android 14 (API 34) per SRS-188 | Play upload + runtime target; SDD-116 row C; FGS-type=microphone strict on API 34 | TC-1..TC-11 (all), TC-13 Play Internal install path, TC-16 |
| OEM: Pixel | Any API ≥ 28 | SDD-116 OEM coverage — Pixel | TC-17 latency rig |
| OEM: Samsung | Any API ≥ 28 | SDD-116 OEM coverage — Samsung | TC-18 latency rig |
The Pixel and Samsung OEM rows MAY overlap with the API tier rows (e.g.
the API 34 device may be a Pixel). The minimum aggregate is four
physical devices when overlap is used, five otherwise.
## TC-1: Install + 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 | Background + relaunch with the same nickname | Server-visible UID is the same (identity persisted in app-private files dir) |
Traces: SRS-013, SRS-209. SWE.6: SWE6-SV-018. SWE.5: SWE5-IV-016.
## TC-2: Microphone permission state machine (SRS-209, SDD-106)
| # | Step | Expected |
|---|---|---|
| 2.1 | First run: tap PTT → system prompt appears → Allow. | Mic transmits. Capability badge says `L0Focused`. |
| 2.2 | Revoke RECORD_AUDIO via Settings → Apps → Chanora → Permissions while app is running. Return to Chanora and attempt to transmit. | App degrades to listen-only stream (per SRS-209 fallback). No crash. User-visible banner explains state. |
| 2.3 | Re-grant RECORD_AUDIO. | Transmit re-enables without restart (or via clearly-labelled "Retry" button). |
| 2.4 | Fresh install: deny the prompt twice (Android treats this as "permanently denied"). Attempt PTT. | App stays in listen-only; in-app banner deep-links to system settings. |
Traces: SRS-209, SysRS-306, SAD-085, SDD-106. SWE.4: SWE4-UV-041. SWE.5: SWE5-IV-017. SWE.6: SWE6-SV-019.
## TC-3: Voice channel join + foreground service longevity (SRS-111, SDD-107)
| # | Step | Expected |
|---|---|---|
| 3.1 | Join a voice channel. | Foreground service notification appears with `foregroundServiceType="microphone"` (API 30+). Per SDD-107 the notification is non-dismissible while connected. |
| 3.2 | Lock the screen. Leave the device idle for **≥ 10 minutes**. | Voice continues to be sent/received. No `ANR`, no service kill, no `RemoteServiceException`. |
| 3.3 | Open Settings → Battery while still connected. | Chanora is not listed as "restricted". (P0: warn user if it is.) |
| 3.4 | Disconnect voice channel. | Foreground service stops, notification disappears, AudioManager mode is restored (TC-6 verifies). |
Traces: SRS-111, SAD-081, SDD-107. SWE.4: SWE4-UV-043. SWE.5: SWE5-IV-018, SWE5-IV-019. SWE.6: SWE6-SV-018.
## TC-4: PTT press / release (Focused only — Android limitation, SDD-110)
| # | Step | Expected |
|---|---|---|
| 4.1 | Open Voice settings → Push to talk | Capability badge clearly states **L0 Focused** with explanatory text noting Android does not expose global hotkeys to background apps. |
| 4.2 | Touch and hold the on-screen PTT button | Mic indicator goes active while held; releases on touch-up. |
| 4.3 | Background the app while holding PTT (swipe home). | Per SDD-110 PTT does NOT continue while backgrounded; transition is observable in the UI on return. |
Traces: SRS-195, SRS-196, SAD-086, SDD-110. SWE.4: SWE4-UV-044. SWE.6: SWE6-SV-024.
## TC-5: Release tail
| # | Step | Expected |
|---|---|---|
| 5.1 | Set release-tail to 0 ms; tap-release | Sharp on/off. |
| 5.2 | Set release-tail to 500 ms; tap-release | Mic stays on ~500 ms after release. |
## TC-6: AudioManager in-call mode round-trip (SRS-208, SDD-108)
| # | Step | Expected |
|---|---|---|
| 6.1 | Before connecting, capture `AudioManager.getMode()` via diagnostic export (or `adb shell dumpsys audio`). Note the value (typically `MODE_NORMAL`). | Baseline recorded. |
| 6.2 | Connect + join voice channel. | Per SRS-208 / SDD-108 the controller enters in-call mode (`MODE_IN_COMMUNICATION` or platform equivalent). |
| 6.3 | Disconnect. | Per SDD-108 prior-mode snapshot is restored. Re-read AudioManager mode == baseline from 6.1. |
| 6.4 | Bluetooth SCO route check (P1 awareness, P0 minimum: no crash): pair a Bluetooth headset, connect Chanora voice. | P0: no crash, no permanent route lock. P1 follow-up: SCO routes correctly. |
Traces: SRS-208, SysRS-305, SAD-084, SDD-108. SWE.4: SWE4-UV-045. SWE.5: SWE5-IV-019. SWE.6: SWE6-SV-020. SYS.4: SYS4-SIV-017.
## TC-7: Mic / speaker mute
| # | Step | Expected |
|---|---|---|
| 7.1 | Mute mic via VoiceBar | Mic icon shows muted state; PTT does not unmute. |
| 7.2 | Speaker mute | Other clients silenced locally. |
## TC-8: Reconnect
| # | Step | Expected |
|---|---|---|
| 8.1 | Toggle airplane mode for 10 s then off | Connection-lost banner, then recovers. Foreground service does not crash during the transition. |
## TC-9: Diagnostic export + privacy (DEC-027)
| # | Step | Expected |
|---|---|---|
| 9.1 | Export a diagnostic bundle (Settings → Diagnostics → Export). | Bundle is saved via the system Storage Access Framework picker. Allow-listed fields only; no raw key labels, no audio bytes, no `RECORD_AUDIO` permission token. |
Traces: SRS-117/SRS-118 family. SWE.6: SWE6-SV-006.
## TC-10: Capability badge labels (SDD-110)
| # | Step | Expected |
|---|---|---|
| 10.1 | Capability badge | Shows `L0Focused` with text explaining Android does not expose session-level global hotkeys. |
## TC-11: Android back navigation (SRS-163, SDD-028)
| # | Step | Expected |
|---|---|---|
| 11.1 | Open a modal sheet (e.g. Settings → Push-to-Talk binding sheet) and press the system back gesture / button. | Modal closes; route stack unchanged. (SDD-028 modal-first rule.) |
| 11.2 | From a leaf screen press back. | Pops one route. Does not exit the app unless on the home route. |
| 11.3 | While PTT is actively held, press back. | Back is **ignored** until PTT is released. (SDD-028 PTT-active-ignore rule.) |
| 11.4 | From the home route press back. | App backgrounds via standard activity finish (does not crash). |
Traces: SRS-163, SAD-018, SDD-028. SWE.4: SWE4-UV-040, SWE4-UV-042. SWE.5: SWE5-IV-020. SWE.6: SWE6-SV-021.
## TC-12: Predictive back (Android 13+ API 33 branch, SDD-028)
| # | Step | Expected |
|---|---|---|
| 12.1 | On API 33+ enable "Predictive back animations" in Developer Options. Trigger a back gesture from a non-home route. | Predictive back preview shows; release commits the pop. SDD-028 API 33+ branch (`OnBackInvokedCallback`) is exercised. |
| 12.2 | On API ≤ 32 the same scenario uses the legacy `OnBackPressedDispatcher` branch (SDD-028 pre-33 branch). | Same end behaviour; no predictive preview. |
Traces: SRS-163, SDD-028. SWE.4: SWE4-UV-042. SWE.6: SWE6-SV-021.
## TC-13: Play Internal Track / bundletool install path (SRS-119, SRS-188, SDD-109)
| # | Step | Expected |
|---|---|---|
| 13.1 | Upload `app-release.aab` to Play Console → Internal Testing track (or via `bundletool` for the same effect). | Play accepts the bundle: targetSdk matches SRS-188, minSdk == 28 per DEC-004, signing key is the release CI key per SDD-073. |
| 13.2 | Opt-in tester installs via Play store internal link. | App installs and runs identically to the bundletool path (TC-0.6). |
| 13.3 | Verify the AAB contains per-ABI split APKs (`armeabi-v7a`, `arm64-v8a`, `x86_64`). | bundletool report lists all expected ABIs (SDD-109 ABI splits). |
Traces: SRS-119, SRS-188, SAD-063, SDD-073, SDD-109. SWE.5: SWE5-IV-020. SWE.6: SWE6-SV-022, SWE6-SV-023. SYS.4: SYS4-SIV-018.
## TC-14: Per-device evidence collection on API 28 device (SDD-116 matrix row A)
| # | Step | Expected |
|---|---|---|
| 14.1 | On the API 28 device, complete pre-flight then `voice_join` a test channel. | Channel join succeeds; FGS active per TC-3. |
| 14.2 | Export the backend diagnostics bundle (Settings → Diagnostics → Export, or the equivalent in-app action). | Export contains `achieved_performance_mode`, `achieved_sharing_mode`, `achieved_input_preset`, `achieved_output_usage`, `achieved_output_content_type`, and per-effect `{aec, ns, agc}` engagement (`hw` / `sw` / `unavailable`). |
| 14.3 | Record all six achieved values + per-effect engagement into the SDD-116 matrix table for the API 28 row. | All fields populated; no `unknown`. |
| 14.4 | `voice_leave`. | Backend tears down cleanly per SWE5-IV-023 reverse order. |
Traces: SRS-210, SRS-211, SRS-212, SRS-213, SRS-214, SRS-215, SDD-111, SDD-112, SDD-113, SDD-116. SWE.6: SWE6-SV-026, SWE6-SV-027, SWE6-SV-028, SWE6-SV-029, SWE6-SV-030. SWE.5: SWE5-IV-021.
## TC-15: Per-device evidence collection on API 31 device (SDD-116 matrix row B)
| # | Step | Expected |
|---|---|---|
| 15.1 | On the API 31 device, complete pre-flight then `voice_join`. | Channel join succeeds. |
| 15.2 | Export diagnostics bundle. | Same fields as TC-14.2 populated. |
| 15.3 | Record values into SDD-116 matrix table for the API 31 row. | All fields populated. |
| 15.4 | Verify FGS-foreground-type tightening branch is exercised: `dumpsys activity services <pkg>` shows the voice service running. | Service entry present; type recorded. |
| 15.5 | `voice_leave`. | Clean teardown. |
Traces: SRS-210..SRS-215, SDD-111..SDD-116. SWE.6: SWE6-SV-026..030. SWE.5: SWE5-IV-021, SWE5-IV-023.
## TC-16: Per-device evidence collection on API 34 device (SDD-116 matrix row C)
| # | Step | Expected |
|---|---|---|
| 16.1 | On the API 34 device, complete pre-flight then `voice_join`. | Channel join succeeds. |
| 16.2 | Export diagnostics bundle. | Same fields as TC-14.2 populated. |
| 16.3 | Record values into SDD-116 matrix table for the API 34 row. | All fields populated. |
| 16.4 | Verify `dumpsys activity services <pkg>` shows `foregroundServiceType=microphone` for the voice service (API 34 type tightening). | `microphone` type present. |
| 16.5 | Screen-off / locked for ≥ 10 minutes during a live voice session. | Mic frames continue (per-minute frame-count probe in diagnostics is non-zero each minute). No `MissingForegroundServiceTypeException` in logcat. |
| 16.6 | `voice_leave`. | Clean teardown. |
Traces: SRS-210..SRS-215, SDD-107, SDD-111..SDD-116. SWE.6: SWE6-SV-026..030. SWE.5: SWE5-IV-021, SWE5-IV-023, SWE5-IV-026.
## TC-17: Glass-to-glass latency measurement on Pixel device (SDD-116 OEM coverage)
| # | Step | Expected |
|---|---|---|
| 17.1 | Set up the latency rig per `swe6-software-verification-plan.md` "Glass-to-glass latency measurement procedure" (DUT = Pixel device, reference listener on LAN, stereo capture on audio interface). | Rig produces clean impulse / listener channels. |
| 17.2 | Record `achieved_performance_mode` for the Pixel DUT. | Recorded (typically `LowLatency` on Pixel). |
| 17.3 | Run 20 impulses; compute median and p95 latency. | Both recorded. |
| 17.4 | Compare against SRS-210 tier per SWE6-SV-025 pass criteria. | If `LowLatency`: median ≤ 150 ms, p95 ≤ 180 ms. Otherwise: median ≤ 250 ms, p95 ≤ 300 ms. |
| 17.5 | Archive raw stereo WAVs + measurement script + per-impulse CSV. | Artifacts attached to test report. |
Traces: SRS-210, SDD-111, SDD-112, SDD-116. SWE.6: SWE6-SV-025. SWE.4: SWE4-UV-051 (tier classifier).
## TC-18: Glass-to-glass latency measurement on Samsung device (SDD-116 OEM coverage)
| # | Step | Expected |
|---|---|---|
| 18.1 | Re-run the TC-17 rig with the DUT swapped to a Samsung device. | Rig setup unchanged. |
| 18.2 | Record `achieved_performance_mode` for the Samsung DUT. | Recorded. |
| 18.3 | Run 20 impulses; compute median and p95 latency. | Both recorded. |
| 18.4 | Compare against SRS-210 tier per SWE6-SV-025 pass criteria. | Same thresholds as TC-17.4 against the Samsung-achieved tier. |
| 18.5 | Archive evidence as TC-17.5. | Artifacts attached. |
Traces: SRS-210, SDD-111, SDD-112, SDD-116. SWE.6: SWE6-SV-025. SWE.4: SWE4-UV-051.
## Auto-test sign-off
These rows do not require human interaction.
| Layer | Result |
|---|---|
| `cargo check --workspace` | clean |
| `cargo test --workspace --lib` | unchanged (Android-specific Rust code is `#[cfg(target_os = "android")]`-gated) |
| `cargo ndk -t arm64-v8a -t armeabi-v7a -t x86_64 -p 28 -- build --release -p chanora_bridge` | clean per ABI |
| `flutter test` in `apps/chanora_flutter` | green; widget + e2e suites carry SRS trace headers |
| `flutter build appbundle --release` | clean — produces `app-release.aab` |
| `bundletool validate --bundle=app-release.aab` | no errors |
| Gradle `assertMinSdk28` task (per SDD-073) | passes; minSdk in merged manifest == 28 |
| R8 / ProGuard release pass (per SDD-073) | clean; mapping file archived |
| CI release-signing key custody check (per SDD-073) | signing key reference matches expected fingerprint |
## Sign-off form
```
chanora v1.0.0-rc.8 — Android P0 acceptance
Device 1 (API 28): ______________________ (model + Android version)
Device 2 (mid): ______________________ (model + Android version)
Device 3 (target): ______________________ (model + Android version)
Tester: ______________________
Date: ______________________
TC-1 Install + identity persistence [ pass / fail ] notes:
TC-2 RECORD_AUDIO state machine [ pass / fail ] notes:
TC-3 Voice channel + FGS longevity [ pass / fail ] notes:
TC-4 PTT press / release (Focused) [ pass / fail ] notes:
TC-5 Release tail [ pass / fail ] notes:
TC-6 AudioManager mode round-trip [ pass / fail ] notes:
TC-7 Mic / speaker mute [ pass / fail ] notes:
TC-8 Reconnect [ pass / fail ] notes:
TC-9 Diagnostic export + privacy [ pass / fail ] notes:
TC-10 Capability badge label [ pass / fail ] notes:
TC-11 Back navigation [ pass / fail ] notes:
TC-12 Predictive back (API 33+) [ pass / fail ] notes:
TC-13 Play Internal / bundletool install [ pass / fail ] notes:
TC-14 Per-device evidence API 28 (SDD-116 A)[ pass / fail ] notes:
TC-15 Per-device evidence API 31 (SDD-116 B)[ pass / fail ] notes:
TC-16 Per-device evidence API 34 (SDD-116 C)[ pass / fail ] notes:
TC-17 Latency rig — Pixel [ pass / fail ] notes:
TC-18 Latency rig — Samsung [ pass / fail ] notes:
Overall result: [ Android rc.8 PASS / Android rc.8 FAIL ]
Sign: ______________________
```
## Known gaps (not blockers)
- **Bluetooth SCO routing**: P0 only requires no-crash behaviour. Full SCO route correctness is P1.
- **Background PTT**: not possible on Android per platform model; `FocusedPttBackend` only (SDD-110).
- **VAD** (`TransmitMode::VoiceActivity`) intentionally disabled in P0 (DEC-030).
- **Tablet / foldable layouts**: this checklist targets phone form factor. The wide-mode (≥ 840 dp) UI works but is not signed off here.
- **Play Console upload key custody**: depends on whoever holds the release keystore; CI signing assertion (SDD-073) covers the mechanical side only.
-183
View File
@@ -1,183 +0,0 @@
# iOS P0 acceptance — human-must test cases (v1.0.0-rc.8)
This document captures the human-side verification pass for the
iOS P0 ship of Chanora. The officially-tested environment is
**iOS 17 or later on a physical iPhone signed with the developer's
personal team** (DEC-025). Simulator support is best-effort —
microphone capture is simulated by the host's mic but global PTT
behaviour cannot be exercised 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 iOS
build from rc.8.
Source: this checklist mirrors the macOS, Linux, and Windows
acceptance documents. Auto-test sign-off lives in this document's
"Auto-test sign-off" section. SDD references: SDD-094..097, SDD-098..104 (v1
audio + PTT lifecycle), DEC-025 (target environment), DEC-027
(diagnostic privacy invariant), SRS-197 (iOS audio routing
contract), SRS-198 (honest capability advertising under runtime
permission state).
iOS does NOT have a global PTT capability comparable to Windows /
macOS / Linux desktops. The desktop PTT backend ladder
(`SDD-081..088`) collapses to `FocusedPttBackend` on iOS and the
capability badge always advertises `L0Focused`. PTT works only
while Chanora itself is the foreground app. This is intentional
per DEC-025 — Apple's iOS sandbox model has no path to a
session-level event tap analogue.
## 0. Pre-flight
| # | Step | Expected |
|---|---|---|
| 0.1 | Open `apps/chanora_flutter/ios/Runner.xcworkspace` in Xcode once. In Runner target → Signing & Capabilities → set your **personal team** (free Apple ID team is sufficient for the P0 device pass). Set a unique bundle identifier (e.g. `your.name.chanoraFlutter`) — the default `com.example.chanoraFlutter` may already be claimed in the App Store registry. | Xcode shows "Provisioning Profile: Xcode Managed Profile" with no errors. |
| 0.2 | Connect a physical iPhone via USB and trust the development Mac. On the iPhone: Settings → General → VPN & Device Management → developer-cert → Trust. | iPhone appears in Xcode's device picker. |
| 0.3 | Build the bridge for the device: `cd ~/chanora && rustup target add aarch64-apple-ios && cargo build --release --target aarch64-apple-ios -p chanora_bridge`. | `target/aarch64-apple-ios/release/libchanora_bridge.a` is produced. |
| 0.4 | Build the Flutter app: `cd apps/chanora_flutter && flutter build ios --release`. | `build/ios/iphoneos/Runner.app` exists. |
| 0.5 | Install + launch on the iPhone: in Xcode pick the device + Runner scheme → Run. | App launches on phone. |
| 0.6 | On first launch the iPhone prompts for **Microphone** access — Allow. | Prompt text matches `NSMicrophoneUsageDescription` from `ios/Runner/Info.plist`. |
| 0.7 | 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 the app-private support directory as `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 (the iOS status bar may show a red microphone indicator). |
| 2.2 | Tap a different voice channel | Smooth move; no audio dropout. |
## TC-3: PTT press / release (Focused only — iOS limitation, SDD-103)
| # | Step | Expected |
|---|---|---|
| 3.1 | Open Voice settings (gear icon), select Push to talk | The capability badge clearly states **L0 Focused** with explanatory text noting iOS does not permit global hotkeys. |
| 3.2 | Bind a key — the only "bound" key on iOS is the on-screen PTT button in the Voice Bar | "Bound key" UI is replaced by an on-screen Push-to-Talk button. |
| 3.3 | Touch and hold the PTT button | Mic indicator turns "Mic on" while held; releases on touch-up. |
## TC-4: Release tail (SDD-096)
| # | 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 it. |
## TC-7: Speaker mute
| # | Step | Expected |
|---|---|---|
| 7.1 | Speaker mute | Other clients silenced locally. |
## TC-8: Background audio (UIBackgroundModes = audio, SDD-102)
| # | Step | Expected |
|---|---|---|
| 8.1 | While connected + in a voice channel + in Continuous mode, swipe Chanora to the background. | The iOS red microphone indicator stays on; voice continues to be received and sent. |
| 8.2 | Return to Chanora | Session intact; no reconnect cycle. |
| 8.3 | Lock the iPhone for 30 s, then unlock | Session still intact (provided you stayed in the channel and on cellular/wifi network). |
## TC-9: AVAudioSession routing (SDD-098, SDD-100, SDD-101)
| # | Step | Expected |
|---|---|---|
| 9.1 | With voice connected, place an iPhone phone call to yourself (or have someone call). | Chanora's audio session yields to the phone call (iOS owns the audio focus). |
| 9.2 | End the phone call | Chanora resumes audio without manual reconnect. |
| 9.3 | Connect AirPods / Bluetooth headset; talk on Chanora | Audio routes to the headset (`.allowBluetoothHFP` + `.allowBluetoothA2DP` are set in `AppDelegate.swift`). |
## 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 labels (SDD-103)
| # | Step | Expected |
|---|---|---|
| 12.1 | Capability badge | Shows `L0Focused` with explanation that iOS does not expose global hotkeys. |
## Auto-test sign-off
These rows do not require human interaction.
| Layer | Result |
|---|---|
| `cargo check --workspace` on macOS host | clean |
| `cargo test --workspace --lib` on macOS host | unchanged (iOS-specific Rust code is `#[cfg(target_os = "ios")]`-gated and not exercised in host tests) |
| `cargo build --release --target aarch64-apple-ios -p chanora_bridge` | clean — produces `libchanora_bridge.a` |
| `flutter build ios --release` | clean — produces `Runner.app` |
| `AppDelegate.swift` registers `AVAudioSession.routeChangeNotification` and `AVAudioSession.interruptionNotification` observers (SDD-100, SDD-101) | Log lines `chanora_flutter: route change reason=…` and `chanora_flutter: audio interruption began/ended` in device console. |
## Sign-off form
```
chanora v1.0.0-rc.8 — iOS P0 acceptance
Device: ______________________ (model + iOS 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:
Overall result: [ iOS rc.8 PASS / iOS rc.8 FAIL ]
Sign: ______________________
```
## Known gaps (not blockers)
- **TestFlight + App Store distribution**: requires the paid $99/yr Apple Developer Program (the personal team is sufficient for sideloaded device testing but not for TestFlight). P1 follow-up.
- **Simulator audio**: cpal-via-CoreAudio inside the iOS simulator routes to the Mac's audio devices and is fine for smoke-testing the UI, but real microphone capture / AEC behaviour cannot be assessed there. Test on a physical iPhone.
- **VAD** (`TransmitMode::VoiceActivity`) is intentionally disabled in P0 (DEC-030).
- **iPad layout**: this checklist targets iPhone. The wide-mode (≥ 840 dp) UI works on iPad but is not signed off here.
-245
View File
@@ -1,245 +0,0 @@
# 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).
-228
View File
@@ -1,228 +0,0 @@
# Linux P0 acceptance — human-must test cases (v1.0.0-rc.8)
This document captures the human-side verification pass for the
Linux P0 ship of Chanora. The officially-tested environment is
**GNOME on Wayland** (DEC-025); on every other Linux environment
(X11, sway, KDE, etc.) the desktop PTT layer cleanly downgrades to
`FocusedPttBackend` and the rest of the app continues to function.
Automated tests (`cargo check`, `cargo test --workspace --lib`,
the `#[ignore]`-gated `linux_portal_smoke.rs`, and the cross-
platform `ptt_privacy` integration test) sign off the mechanical
side. This checklist signs off the user-experience side. A failure
on any row blocks promotion of the Linux build from rc.8.
Source: this checklist mirrors `windows-p0-acceptance.md`. The
auto-test sign-off lives in this document's "Auto-test sign-off"
section. SDD references: SDD-081 (trait), SDD-086 (Linux GNOME
Wayland portal backend), SDD-094..097 (v1 audio + PTT lifecycle),
DEC-025 (target environment), DEC-027 (diagnostic privacy
invariant).
## 0. Pre-flight
| # | Step | Expected |
|---|---|---|
| 0.1 | Log in to the GNOME on Wayland session at the physical console of `100.74.219.114` (user `edison`). Verify `echo $XDG_SESSION_TYPE` prints `wayland` and `echo $XDG_CURRENT_DESKTOP` contains `GNOME`. | Wayland GNOME session active. SSH/`tty` sessions are **not** sufficient; on those `try_select()` correctly returns `None` and the rest of this checklist is skipped from row TC-3 onward. |
| 0.2 | Confirm `xdg-desktop-portal` and `xdg-desktop-portal-gnome` are installed: `pacman -Q xdg-desktop-portal xdg-desktop-portal-gnome` | Both packages present. (On the Arch verification host these are `1.20.4-1` and `50.0-1` respectively.) |
| 0.3 | Wipe stale install: `rm -rf ~/.local/share/app.chanora/ ~/.config/app.chanora/` | First-launch from clean state. |
| 0.4 | Build: `cd ~/chanora && cargo build --release -p chanora_bridge`. Then build the Flutter Linux bundle: `cd apps/chanora_flutter && flutter build linux --release` and copy `target/release/libchanora_bridge.so` next to the runner if the build glue did not auto-bundle it. | Build succeeds, runner binary at `apps/chanora_flutter/build/linux/x64/release/bundle/chanora_flutter` exists, bridge `.so` is in `bundle/lib/` or alongside the runner. |
| 0.5 | Launch the runner from a GNOME-Wayland terminal (so the process inherits the right `DBUS_SESSION_BUS_ADDRESS` + `WAYLAND_DISPLAY`). | Window opens, no crash. |
| 0.6 | Open About / version label | Shows **`v1.0.0-rc.8`**. |
| 0.7 | Open `~/.local/share/app.chanora/chanora_flutter/logs/chanora.log` | Contains `bridge initialised`, `log file path …`, `identity store initialised`, `bookmark store initialised`. File <1 MB. The descriptor line for the PTT backend should mention `backend_id = "gnome-wayland-portal"` (or `"focused"` if not on GNOME Wayland). |
## 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 | Click Connect | Server snapshot appears: channel tree on left, client list visible |
| 1.3 | Note the connected server name + the channel tree | Tree is sorted (parents above children); depth indent visible up to 6 levels |
| 1.4 | Disconnect, reconnect with the same nickname | Server-visible UID is the same as previous session (identity persisted via `~/.local/share/app.chanora/identity.dek`) |
| 1.5 | Disconnect | Returns to connect form, no error |
## TC-2: Voice channel join (SRS-204, SDD-094)
| # | Step | Expected |
|---|---|---|
| 2.1 | After connecting, observe the UI | **No "Start audio" button anywhere.** Voice Bar is visible at the bottom. |
| 2.2 | Double-click a voice channel | Client moves into that channel; voice bar shows the channel name; `voice_join` log line appears with no error |
| 2.3 | Double-click a different voice channel | Client moves; no audio glitch; `voice_join` repeats cleanly |
| 2.4 | Double-click the same channel again quickly (concurrency) | Either: no-op move, or single clean move; no duplicate audio engine spawn (engine is single-instance) |
## TC-3: PTT press / release (SDD-083, SDD-086, SDD-096) — GNOME-Wayland only
| # | Step | Expected |
|---|---|---|
| 3.1 | Open Voice settings (gear icon), select **Push to talk** | Bind-key row + Release-tail slider become visible |
| 3.2 | Click "Bind key" | GNOME's **system** portal dialog appears asking you to pick a shortcut. (Chanora itself never reads raw keys — this is the portal's own UI.) |
| 3.3 | Pick a key (e.g. Right Ctrl), accept | Dialog closes. VoiceBar's capability badge transitions from `L0` to `L2 GlobalHoldToTalk` (or `L3` for a mouse side-button). Bound-key label shows the portal's translated description. |
| 3.4 | With another window focused, hold the bound key | Mic indicator turns "Mic on"; `transmit_active` true in stats |
| 3.5 | Release the key | After the configured release-tail, indicator returns to "Mic off" |
| 3.6 | Switch focus several times while pressing the key | PTT still fires from another window — this is the portal's "global" guarantee |
## TC-4: Release tail (SDD-097)
| # | Step | Expected |
|---|---|---|
| 4.1 | Set release-tail to 0 ms in Voice settings | Slider updates immediately |
| 4.2 | Tap PTT key briefly | Mic on/off transition is sharp (no tail) |
| 4.3 | Set release-tail to 500 ms | Slider updates immediately |
| 4.4 | Tap PTT key briefly | Mic stays "on" for ~500 ms after release before flipping to off |
## TC-5: Continuous mode (SDD-094)
| # | Step | Expected |
|---|---|---|
| 5.1 | Switch transmit mode to **Continuous** | Bind-key + release-tail rows disappear; capability badge greys out. |
| 5.2 | Talk into mic | "Mic on" stays solid; no self-disable bug. |
| 5.3 | Switch back to PTT | Previously bound key still works without re-binding (binding persisted in `audio_meta.json`). |
## TC-6: Mic mute (SDD-097)
| # | Step | Expected |
|---|---|---|
| 6.1 | Press mic-mute toggle in Voice Bar | Mic icon shows muted state; selector hard-mute engaged. |
| 6.2 | Press PTT (still muted) | No "Sending audio while muted" spam in log. Mic indicator does not flip on. |
| 6.3 | Un-mute | PTT resumes working normally. |
## TC-7: Speaker mute
| # | Step | Expected |
|---|---|---|
| 7.1 | Press speaker-mute toggle | Other clients in channel are silenced locally; server still sees you in channel. |
## TC-8: Binding persistence across restart
| # | Step | Expected |
|---|---|---|
| 8.1 | With a working PTT binding, quit Chanora | `audio_meta.json` next to `identity.dek` shows the new `ptt_*` fields. |
| 8.2 | Relaunch | Binding restored without re-running the portal dialog. Hold key — mic flips on. |
## TC-9: Binding before voice-join
| # | Step | Expected |
|---|---|---|
| 9.1 | Connect to server, do **not** join a voice channel yet. Open Voice settings, bind a PTT key. | Portal dialog appears; binding succeeds. |
| 9.2 | Now double-click a voice channel | Voice engine starts with the existing binding active immediately. |
## TC-10: No-permission channel rejection (typed `ServerRejected` propagation)
| # | Step | Expected |
|---|---|---|
| 10.1 | Try to join a channel you lack permissions on | Red SnackBar with a human message derived from the TS3 error code (e.g. `permissions_client_insufficient` → "You do not have permission to join this channel."). Move does not silently fail. |
## TC-11: Reconnect
| # | Step | Expected |
|---|---|---|
| 11.1 | Yank the wifi or run `nmcli con down …` for 10 s, then reconnect | UI shows red "Connection lost" banner, then recovers. Transient `os error 111` blips are logged but **not** shown as a banner. |
## TC-12: Diagnostic export + privacy (DEC-027, SAD-077, SDD-090)
| # | Step | Expected |
|---|---|---|
| 12.1 | Open Diagnostics, export a bundle | Bundle contains tracing records with allow-listed fields only: `backend_id`, `bound_input_class`, `capability_level`, `host_id`, `timeout_secs`, `error`, `event`, `message`, `target`, `level`. **No `platform_key`, no `trigger_description`, no raw key codes.** |
| 12.2 | Grep the bundle for the human-readable PTT key label | No hit. |
## TC-13: PTT bind dialog labels
| # | Step | Expected |
|---|---|---|
| 13.1 | Open Voice settings; the Bind-key row's display reads the portal's translated description (e.g. "Ctrl+Right Shift") | The translated string never appears in `chanora.log`. |
## TC-14: Capability badge gating
| # | Step | Expected |
|---|---|---|
| 14.1 | On a non-GNOME-Wayland session (e.g. SSH-only `tty`, X11, sway), the badge displays `L0Focused` and explains it is information-only. The Bind-key UI is hidden in PTT mode if the backend is `focused`. | Verified manually by logging into the matching environment. |
## TC-15: Indicator label
| # | Step | Expected |
|---|---|---|
| 15.1 | In any mode, look at the stats line | Reads "Mic on" / "Mic off" (mode-neutral); **not** "PTT on" / "PTT off" |
## Auto-test sign-off
These rows do not require human interaction.
| Layer | Result on Arch host `100.74.219.114` |
|---|---|
| `cargo check --workspace --release` | clean (`Finished `release` profile [optimized] target(s) in 30.99s`) |
| `cargo test --workspace --lib` | **78 passed / 0 failed / 1 ignored** (sums across all 7 workspace crates: `chanora_audio` 32, `chanora_bridge` 0, `chanora_core` 13, `chanora_diagnostics` 12, `chanora_protocol` 8 + 1 ignored, `chanora_state` 1, `chanora_storage` 12) |
| `cargo test -p chanora_audio --test linux_portal_smoke -- --ignored` | **1 passed / 0 failed**; log line `GlobalShortcuts portal reachable, version = 1`. Run with `DBUS_SESSION_BUS_ADDRESS=unix:path=$XDG_RUNTIME_DIR/bus` + `XDG_SESSION_TYPE=wayland` + `XDG_CURRENT_DESKTOP=GNOME` exported. |
| `cargo test -p chanora_audio --test ptt_privacy` | **1 passed / 0 failed** — DEC-027 invariant holds. |
The Linux host runs fewer tests than the Korean Win 11 host (which
reports 126 / 0 / 1) because the `#[cfg(target_os = "windows")]`-
gated PTT-backend dispatcher and full-chain tests in
`crates/chanora_audio/src/ptt_backends/windows.rs` and
`core/chanora_core/src/ptt.rs::windows_full_chain_tests` are
Windows-only by construction. The Linux equivalent — the GNOME
portal flow — is exercised by the `linux_portal_smoke` ignored
integration test and the inline unit tests in
`crates/chanora_audio/src/ptt_backends/linux.rs` (counted within
the `chanora_audio` 32 above).
## Sign-off form
```
chanora v1.0.0-rc.8 — Linux P0 acceptance
Host: Arch Linux GNOME Wayland (100.74.219.114)
Tester: ______________________
Date: ______________________
Build: apps/chanora_flutter/build/linux/x64/release/bundle/chanora_flutter @ ______________________
libchanora_bridge.so @ ______________________
TC-1 Connect + identity persistence [ pass / fail ] notes:
TC-2 Voice channel join [ pass / fail ] notes:
TC-3 PTT press / release (GNOME-Wayland) [ 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 Binding persistence across restart [ pass / fail ] notes:
TC-9 Binding before voice-join [ pass / fail ] notes:
TC-10 No-permission channel rejection [ pass / fail ] notes:
TC-11 Reconnect [ pass / fail ] notes:
TC-12 Diagnostic export + privacy [ pass / fail ] notes:
TC-13 PTT bind dialog labels [ pass / fail ] notes:
TC-14 Capability badge gating [ pass / fail ] notes:
TC-15 Indicator label [ pass / fail ] notes:
Overall result: [ Linux rc.8 PASS / Linux rc.8 FAIL ]
Sign: ______________________
```
## Known gaps (not blockers)
- **X11, sway, KDE, other Wayland compositors**: officially out of
scope for v1.0.0-rc.8 per DEC-025. The desktop PTT layer falls
back to `FocusedPttBackend` (L0Focused capability) on these.
Voice + identity + connect paths still work.
- **Flutter build glue**: the Linux bundle currently requires
manually copying `libchanora_bridge.so` next to the runner or
into `bundle/lib/`. Auto-bundling is a P1 carryover.
- **VAD** (`TransmitMode::VoiceActivity`) is intentionally disabled
in P0 (DEC-030) on Linux as on Windows.
- **macOS / iOS / Android** P0 acceptance is NOT covered here.
-222
View File
@@ -1,222 +0,0 @@
# macOS P0 acceptance — human-must test cases (v1.0.0-rc.8)
This document captures the human-side verification pass for the
macOS P0 ship of Chanora. The officially-tested environment is
**Apple Silicon macOS 14+** (DEC-025). Intel macOS is best-effort —
the same build runs but is not signed-off for this release.
Automated tests (`cargo check`, `cargo test --workspace --lib`,
the live `IOHIDCheckAccess` permission query path) sign off the
mechanical side. This checklist signs off the user-experience
side. A failure on any row blocks promotion of the macOS build
from rc.8.
Source: this checklist mirrors `linux-p0-acceptance.md` and
`windows-p0-acceptance.md`. The auto-test sign-off lives in this
document's "Auto-test sign-off" section. SDD references: SDD-081
(trait), SDD-085 (macOS CGEventTap backend), SDD-094..097 (v1
audio + PTT lifecycle), DEC-025 (target environment), DEC-027
(diagnostic privacy invariant), SRS-197 (iOS/macOS audio routing
contract), SRS-198 (honest capability advertising under runtime
permission state).
## 0. Pre-flight
| # | Step | Expected |
|---|---|---|
| 0.1 | Build host: M1 / M2 / M3 Mac running macOS 14 or later. From a graphical login (NOT SSH alone) open Terminal and confirm: `sw_vers -productVersion` is `14.x` or higher; `uname -m` is `arm64`. | Apple Silicon macOS confirmed. |
| 0.2 | Wipe stale install: `rm -rf "~/Library/Application Support/app.chanora.chanoraFlutter" ~/Library/Logs/app.chanora.chanora_flutter ~/Library/Containers/com.example.chanoraFlutter`. (App-Sandbox-aware container path; macOS quietly relocates `Application Support` under `Containers/<bundle-id>/Data/` when the app is sandboxed.) | First-launch from clean state. |
| 0.3 | Build the bridge: `cd ~/chanora && cargo build --release -p chanora_bridge`. | `target/release/libchanora_bridge.dylib` ≈ 15 MB. |
| 0.4 | Build the Flutter app: `cd apps/chanora_flutter && flutter build macos --release`. | `build/macos/Build/Products/Release/chanora_flutter.app` exists, ≈ 60 MB. |
| 0.5 | Wrap + sign: `cd ~/chanora && tools/macos-postbuild.sh Release`. | Last line reads `[postbuild] OK: …chanora_flutter.app is launchable`. The `chanora_bridge.framework` is now inside `Contents/Frameworks/`. |
| 0.6 | Launch from a graphical session: `open ~/chanora/apps/chanora_flutter/build/macos/Build/Products/Release/chanora_flutter.app`. The first launch will pop a Gatekeeper warning because the app is ad-hoc signed. Click "Open Anyway" in System Settings → Privacy & Security if prompted. | Window opens; no crash. |
| 0.7 | About / version label | Shows **`v1.0.0-rc.8`**. |
| 0.8 | Open `~/Library/Logs/app.chanora.chanora_flutter/chanora.log` | Contains `bridge initialised`, `log file path …`, `identity store initialised`, `bookmark store initialised`. File < 1 MB. The PTT controller line should read `backend_id="event-tap"` and capability `L0Focused` (because Input Monitoring has not been granted yet). |
## 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 | Click Connect | Server snapshot appears: channel tree on left, client list visible |
| 1.3 | Note the connected server name + channel tree | Tree is sorted (parents above children); depth indent visible up to 6 levels |
| 1.4 | Disconnect, reconnect with the same nickname | Server-visible UID is the same as previous session (identity persisted via `Application Support/.../identity.tskey`) |
| 1.5 | Disconnect | Returns to connect form, no error |
## TC-2: Voice channel join (SRS-204, SDD-094)
| # | Step | Expected |
|---|---|---|
| 2.1 | After connecting, observe the UI | **No "Start audio" button anywhere.** Voice Bar visible at the bottom. |
| 2.2 | Double-click a voice channel | First time only: macOS shows a system prompt requesting microphone access; click **Allow**. Subsequent joins do not re-prompt. Client moves into the channel; voice bar shows the channel name. |
| 2.3 | Double-click a different voice channel | Client moves; no audio glitch; the second `voice_join` is logged cleanly with no panic. |
| 2.4 | Double-click the same channel again quickly (concurrency) | Either: no-op move, or single clean move; no duplicate audio engine spawn. |
## TC-3: PTT press / release (SDD-083, SDD-085, SDD-096) — Input Monitoring granted
| # | Step | Expected |
|---|---|---|
| 3.1 | Open Voice settings (gear icon), select **Push to talk** | Bind-key row + Release-tail slider become visible. |
| 3.2 | Click "Bind key" → press a key (e.g. Right Option) | Flutter captures the key label. macOS may show a system prompt requesting **Input Monitoring** access. Open System Settings → Privacy & Security → Input Monitoring, toggle Chanora ON. |
| 3.3 | Return to Chanora; within ~1.5 s the capability badge transitions from `L0Focused` to `L2 GlobalHoldToTalk` (or `L3` for a mouse side-button). | The transition fires WITHOUT restarting Chanora — the macOS `permission re-query worker` polls every 1.5 s and re-publishes the descriptor. |
| 3.4 | With another window focused, hold the bound key | Mic indicator turns "Mic on"; `transmit_active` true in stats. |
| 3.5 | Release the key | After the configured release-tail, indicator returns to "Mic off". |
| 3.6 | Switch focus several times while pressing the key | PTT still fires from another window — confirms the CGEventTap is reading session-global events. |
## TC-4: Release tail (SDD-097)
| # | Step | Expected |
|---|---|---|
| 4.1 | Set release-tail to 0 ms in Voice settings | Slider updates immediately. |
| 4.2 | Tap PTT key briefly | Mic on/off transition is sharp (no tail). |
| 4.3 | Set release-tail to 500 ms | Slider updates immediately. |
| 4.4 | Tap PTT key briefly | Mic stays "on" for ~500 ms after release before flipping to off. |
## TC-5: Continuous mode (SDD-094)
| # | Step | Expected |
|---|---|---|
| 5.1 | Switch transmit mode to **Continuous** | Bind-key + release-tail rows disappear; capability badge greys out. |
| 5.2 | Talk into mic | "Mic on" stays solid; no self-disable bug. |
| 5.3 | Switch back to PTT | Previously bound key still works without re-binding (binding persisted in `audio_meta.json`). |
## TC-6: Mic mute (SDD-097)
| # | Step | Expected |
|---|---|---|
| 6.1 | Press mic-mute toggle in Voice Bar | Mic icon shows muted state; selector hard-mute engaged. |
| 6.2 | Press PTT (still muted) | No "Sending audio while muted" spam in log. Mic indicator does not flip on. |
| 6.3 | Un-mute | PTT resumes working normally. |
## TC-7: Speaker mute
| # | Step | Expected |
|---|---|---|
| 7.1 | Press speaker-mute toggle | Other clients in channel are silenced locally; server still sees you in channel. |
## TC-8: Binding persistence across restart
| # | Step | Expected |
|---|---|---|
| 8.1 | With a working PTT binding, quit Chanora | `audio_meta.json` next to `identity.tskey` shows the `ptt_*` fields populated. |
| 8.2 | Relaunch | Binding restored without re-running the bind flow. Hold key — mic flips on. |
## TC-9: Binding before voice-join
| # | Step | Expected |
|---|---|---|
| 9.1 | Connect to server, do **not** join a voice channel yet. Open Voice settings, bind a PTT key. | Binding succeeds. |
| 9.2 | Now double-click a voice channel | Voice engine starts with the existing binding active immediately. |
## TC-10: No-permission channel rejection (typed `ServerRejected` propagation)
| # | Step | Expected |
|---|---|---|
| 10.1 | Try to join a channel you lack permissions on | Red SnackBar with a human message derived from the TS3 error code (e.g. `permissions_client_insufficient` → "You do not have permission to join this channel."). Move does not silently fail. |
## TC-11: Reconnect
| # | Step | Expected |
|---|---|---|
| 11.1 | Toggle Wi-Fi off for 10 s, then reconnect | UI shows red "Connection lost" banner, then recovers. Transient `Operation timed out` blips are logged but **not** shown as a banner. |
## TC-12: Diagnostic export + privacy (DEC-027, SAD-077, SDD-090)
| # | Step | Expected |
|---|---|---|
| 12.1 | Open Diagnostics, export a bundle | Bundle contains tracing records with allow-listed fields only: `backend_id`, `bound_input_class`, `capability_level`, `host_id`, `timeout_secs`, `error`, `event`, `message`, `target`, `level`. **No raw key codes, no bound key labels, no mouse-button numbers, no IOHIDAccessType raw values.** |
| 12.2 | Grep the bundle for the human-readable PTT key label | No hit. |
## TC-13: PTT bind dialog labels
| # | Step | Expected |
|---|---|---|
| 13.1 | Open Voice settings; the Bind-key row displays the Flutter-supplied label (e.g. "Space", "F10"). | The translated string never appears in `chanora.log`. |
## TC-14: Capability badge gating
| # | Step | Expected |
|---|---|---|
| 14.1 | Revoke Input Monitoring in System Settings while Chanora is running. Within ~1.5 s the badge downgrades from `L2 GlobalHoldToTalk` to `L0Focused`. The Bind-key UI hides if it would be unusable; PTT then only works when Chanora is the foreground window. | The transition is logged: `macos ptt: permission state transition from=Granted to=Denied`. |
| 14.2 | Re-grant; within ~1.5 s the badge upgrades back to L2. | Same transition log line reversed. |
## TC-15: Indicator label
| # | Step | Expected |
|---|---|---|
| 15.1 | In any mode, look at the stats line | Reads "Mic on" / "Mic off" (mode-neutral); **not** "PTT on" / "PTT off". |
## Auto-test sign-off
These rows do not require human interaction.
| Layer | Result on Apple Silicon macOS (M1 host) |
|---|---|
| `cargo check --workspace` | clean |
| `cargo test --workspace --lib` | 74 baseline + 6 new macOS keymap/state tests = **80 / 0 / 1** target; currently `chanora_audio` reports 34 / 0 / 0 on macOS (Linux had 32) |
| `cargo build --release -p chanora_bridge` | clean — produces `libchanora_bridge.dylib` ≈ 15 MB |
| `flutter build macos --release` | clean — produces `chanora_flutter.app` ≈ 60 MB |
| `tools/macos-postbuild.sh Release` | exit 0; framework-wrap + install_name_tool + ad-hoc codesign succeed |
| `chanora_flutter.app` headless launch | bridge / identity / bookmark store all initialise; `~/Library/Logs/app.chanora.chanora_flutter/chanora.log` captures clean boot |
| Live `IOHIDCheckAccess(kIOHIDRequestTypeListenEvent)` | returns `Unknown` (2) on a fresh-install box — expected; transitions to Granted/Denied after the user responds to the system prompt |
The macOS host runs 6 more `chanora_audio` tests than Linux because of the macOS-only PTT keymap + descriptor builder + RunLoopHandle Send-bound tests. Linux runs 4 portal-specific tests that macOS skips. Net cross-platform delta: `+2` macOS.
## Sign-off form
```
chanora v1.0.0-rc.8 — macOS P0 acceptance
Host: Apple Silicon macOS (M1/M2/M3)
Tester: ______________________
Date: ______________________
Build: apps/chanora_flutter/build/macos/Build/Products/Release/chanora_flutter.app
@ ______________________
chanora_bridge.framework/chanora_bridge inside Contents/Frameworks/
@ ______________________
TC-1 Connect + identity persistence [ pass / fail ] notes:
TC-2 Voice channel join [ pass / fail ] notes:
TC-3 PTT press / release (Input Monitor) [ 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 Binding persistence across restart [ pass / fail ] notes:
TC-9 Binding before voice-join [ pass / fail ] notes:
TC-10 No-permission channel rejection [ pass / fail ] notes:
TC-11 Reconnect [ pass / fail ] notes:
TC-12 Diagnostic export + privacy [ pass / fail ] notes:
TC-13 PTT bind dialog labels [ pass / fail ] notes:
TC-14 Capability badge gating [ pass / fail ] notes:
TC-15 Indicator label [ pass / fail ] notes:
Overall result: [ macOS rc.8 PASS / macOS rc.8 FAIL ]
Sign: ______________________
```
## Known gaps (not blockers)
- **Intel macOS** (`x86_64-apple-darwin`): the same Rust build targets it cleanly but no acceptance pass; ship as best-effort.
- **macOS Gatekeeper + notarization**: ad-hoc-signed `chanora_flutter.app` requires the user to right-click → Open the first time (or approve in Privacy & Security). Developer ID + notarization for distribution is a P1 follow-up.
- **`tools/macos-postbuild.sh` is a separate manual step**: Xcode build-phase auto-integration (so a plain `flutter build macos --release` produces a launchable bundle) is a P1 carryover, same shape as the analogous Windows + Linux gaps.
- **VAD** (`TransmitMode::VoiceActivity`) is intentionally disabled in P0 (DEC-030) on macOS as on every platform.
- **iOS** P0 acceptance is NOT covered here; see `ios-p0-acceptance.md`.
@@ -1,709 +0,0 @@
# Chanora SWE.4 Software Unit Verification Plan
**Document type:** Software Unit Verification Plan
**Process alignment:** ASPICE SWE.4 Software Unit Verification
**Version:** 0.9.14
**Status:** Baseline Candidate
**Language:** English
**Product:** Chanora
**Verification object:** Software units defined by SDD
**Direct source layer:** SDD only
**Repo path:** `docs/verification/swe4-unit-verification-plan.md` ---
## 1. Purpose
This document defines the software unit verification strategy for Chanora. It verifies that software units implement the Software Detailed Design.
This document is downstream of SDD and shall not bypass the document hierarchy.
```text
SysRS -> SysDes -> SRS -> SAD -> SDD -> SWE.4 Unit Verification
```
Direct source rule:
```text
SWE.4 unit verification items shall link directly to SDD only.
```
## 2. Verification Scope
In scope:
- Flutter unit tests
- Flutter widget-level unit verification where a widget is treated as a software unit
- Dart static analysis
- Dart code review
- Rust unit tests
- Rust static analysis
- Rust code review
- Unit-level regression verification
- Unit-level interface boundary tests
- Unit-level localization, Unicode, design-token, and diagnostics verification
Out of scope:
- Software integration verification across components; handled by SWE.5.
- Fully integrated software verification against SRS; handled by SWE.6.
- System integration verification against SysDes; handled by SYS.4.
## 3. Unit Verification Strategy
| Verification type | Applies to | Purpose |
|---|---|---|
| Static analysis | Dart, Rust | Detect structural, lint, safety, style, and maintainability issues |
| Code review | Dart, Rust, build scripts | Confirm design compliance and maintainability |
| Unit tests | Dart, Rust | Verify individual units against SDD behavior |
| Widget tests | Flutter widgets | Verify component rendering, semantics, and state behavior |
| Golden tests | Design-system components | Verify stable UI rendering where appropriate |
| Localization tests | Localization service and accessibility labels | Verify fallback, keys, and localized display strings |
| Unicode tests | Protocol/bridge/text-boundary units | Verify UTF-8 and multilingual content handling |
| Regression tests | Changed units | Confirm unchanged behavior remains valid after modifications |
## 4. Entry Criteria
| Criterion | Description |
|---|---|
| SDD baseline available | Unit design items and source SAD links are available. |
| Unit implementation available | The unit exists in source code or executable test double form. |
| Unit test environment available | Flutter and/or Rust test infrastructure is configured. |
| Verification measure defined | Test, review, or static analysis objective is defined. |
| Pass/fail criteria defined | Expected result is objective and reviewable. |
## 5. Exit Criteria
| Criterion | Description |
|---|---|
| Selected unit verification measures executed | Tests, reviews, and analyses required for the release scope are complete. |
| Results recorded | Verification results are stored in test reports or review records. |
| Nonconformances recorded | Failures are recorded for problem resolution. |
| Traceability complete | Each unit verification item traces to SDD and result evidence. |
| Regression scope complete | Changed units have selected regression measures executed. |
## 6. Unit Verification Measures
**SWE4-UV-001**: Verify `ChanoraApp` initialization order.
- Source SDD: SDD-001, SDD-045
- Verification method: Flutter unit/integration test
- Pass criteria: Theme, localization, platform services, routing, and adaptive shell initialize before feature rendering.
- Evidence: Test result
**SWE4-UV-002**: Verify `ChanoraThemeFactory` Material 3 theme creation.
- Source SDD: SDD-002, SDD-003
- Verification method: Dart unit test
- Pass criteria: Light and dark Material 3 ThemeData objects are created with valid ColorScheme and TextTheme values.
- Evidence: Test result
**SWE4-UV-003**: Verify `ChanoraSemanticColors` connection token behavior.
- Source SDD: SDD-004, SDD-006
- Verification method: Dart unit test
- Pass criteria: All connection semantic roles are present and do not require feature-screen hardcoding.
- Evidence: Test result
**SWE4-UV-004**: Verify voice semantic token behavior.
- Source SDD: SDD-005, SDD-006
- Verification method: Dart unit test
- Pass criteria: Speaking, muted, deafened, and push-to-talk active states are available through semantic tokens.
- Evidence: Test result
**SWE4-UV-005**: Verify feature widgets use theme accessors rather than hardcoded semantic values.
- Source SDD: SDD-010
- Verification method: Static inspection
- Pass criteria: Feature widgets do not construct product semantic token values directly.
- Evidence: Review record
**SWE4-UV-006**: Verify adaptive shell window classification.
- Source SDD: SDD-011, SDD-012
- Verification method: Dart unit test
- Pass criteria: Compact, medium, and expanded classes are selected deterministically.
- Evidence: Test result
**SWE4-UV-007**: Verify `CompactShell` layout behavior.
- Source SDD: SDD-013
- Verification method: Flutter widget test
- Pass criteria: Compact shell renders a single-column layout and mobile navigation entry points.
- Evidence: Test result
**SWE4-UV-008**: Verify `MediumShell` layout behavior.
- Source SDD: SDD-014
- Verification method: Flutter widget test
- Pass criteria: Medium shell renders side navigation or navigation rail behavior.
- Evidence: Test result
**SWE4-UV-009**: Verify `ExpandedShell` persistent pane behavior.
- Source SDD: SDD-015
- Verification method: Flutter widget test
- Pass criteria: Expanded shell renders persistent side pane and VoiceBar slot.
- Evidence: Test result
**SWE4-UV-010**: Verify connection status presentation uses non-color-only cues.
- Source SDD: SDD-016, SDD-022
- Verification method: Dart unit test, widget test
- Pass criteria: The presenter exposes label, icon, semantic label, and severity token.
- Evidence: Test result
**SWE4-UV-011**: Verify `ChanoraVoiceBar` exposed properties.
- Source SDD: SDD-017, SDD-018
- Verification method: Flutter widget test
- Pass criteria: Mute, deaf, push-to-talk, input meter, current channel, and latency properties render correctly.
- Evidence: Test result
**SWE4-UV-012**: Verify `ChanoraChannelTree` data and interaction behavior.
- Source SDD: SDD-019, SDD-020
- Verification method: Flutter widget test
- Pass criteria: Channel hierarchy, selected state, expansion state, join intent, and context-menu intent operate as designed.
- Evidence: Test result
**SWE4-UV-013**: Verify `ChanoraClientTile` state semantics.
- Source SDD: SDD-021
- Verification method: Widget test, accessibility test
- Pass criteria: Client status is available through text/icon/semantic cues and not color alone.
- Evidence: Test result
**SWE4-UV-014**: Verify localized semantic labels for icon-only buttons.
- Source SDD: SDD-024, SDD-031, SDD-032
- Verification method: Accessibility test, localization unit test
- Pass criteria: Icon-only controls expose localized semantic labels with fallback.
- Evidence: Test result
**SWE4-UV-015**: Verify keyboard focus traversal.
- Source SDD: SDD-025
- Verification method: Widget test
- Pass criteria: Primary interactive controls can be traversed in a logical order.
- Evidence: Test result
**SWE4-UV-016**: Verify text-scale resilience of critical controls.
- Source SDD: SDD-026
- Verification method: Accessibility widget test
- Pass criteria: Critical controls remain reachable under increased text scale.
- Evidence: Test result
**SWE4-UV-017**: Verify platform inset service unit behavior.
- Source SDD: SDD-027, SDD-030
- Verification method: Unit test with platform test doubles
- Pass criteria: Insets are normalized and exposed to the shell.
- Evidence: Test result
**SWE4-UV-018**: Verify back intent service behavior.
- Source SDD: SDD-028
- Verification method: Unit test with platform test doubles
- Pass criteria: Platform back events map to expected route-level intents.
- Evidence: Test result
**SWE4-UV-019**: Verify localization fallback.
- Source SDD: SDD-031, SDD-032, SDD-033
- Verification method: Unit test
- Pass criteria: Missing localized strings fall back deterministically.
- Evidence: Test result
**SWE4-UV-020**: Verify server-content pass-through.
- Source SDD: SDD-034
- Verification method: Unit test
- Pass criteria: Server-provided content is not translated by product localization.
- Evidence: Test result
**SWE4-UV-021**: Verify Unicode text boundary behavior.
- Source SDD: SDD-035, SDD-036, SDD-037
- Verification method: Unit test
- Pass criteria: Valid multilingual Unicode content is preserved; invalid external encodings are handled at boundaries.
- Evidence: Test result
**SWE4-UV-022**: Verify diagnostic Unicode preservation and redaction behavior.
- Source SDD: SDD-038, SDD-041, SDD-050, SDD-054
- Verification method: Unit test, security review
- Pass criteria: Secrets are redacted while multilingual content is preserved.
- Evidence: Test result, review record
**SWE4-UV-023**: Verify locale-aware formatting.
- Source SDD: SDD-040
- Verification method: Unit test
- Pass criteria: Dates, times, numbers, and timestamps format through localization utilities.
- Evidence: Test result
**SWE4-UV-024**: Verify traceability checker behavior.
- Source SDD: SDD-043, SDD-044, SDD-055, SDD-056, SDD-066, SDD-067
- Verification method: Unit test / script test
- Pass criteria: Invalid direct-layer references are detected.
- Evidence: Test result
**SWE4-UV-025**: Verify bridge DTO safety.
- Source SDD: SDD-046
- Verification method: Unit test, static inspection
- Pass criteria: Bridge DTOs do not expose internal Rust implementation types.
- Evidence: Test result, review record
**SWE4-UV-026**: Verify Rust core event mapping.
- Source SDD: SDD-047
- Verification method: Rust unit test
- Pass criteria: Core events map deterministically to view model deltas.
- Evidence: Test result
**SWE4-UV-027**: Verify voice view model separation from DSP.
- Source SDD: SDD-048
- Verification method: Code review, unit test
- Pass criteria: Voice UI receives status values and does not perform audio DSP.
- Evidence: Review record, test result
**SWE4-UV-028**: Verify storage facade isolation.
- Source SDD: SDD-049
- Verification method: Static inspection, unit test
- Pass criteria: Feature widgets do not directly call database APIs.
- Evidence: Review record, test result
**SWE4-UV-029**: Verify interface detail catalog completeness.
- Source SDD: SDD-059, SDD-060
- Verification method: Review
- Pass criteria: Unit-level interface entries include required details where applicable.
- Evidence: Review record
**SWE4-UV-030**: Verify unit construction and review records.
- Source SDD: SDD-064, SDD-065, SDD-068, SDD-069, SDD-070
- Verification method: Review
- Pass criteria: Unit construction, review, verification handoff, and registry records exist for release scope units.
- Evidence: Review record
## 7. Traceability Matrix
| SDD Range | SWE.4 Coverage |
|---|---|
| SDD-001 through SDD-010 | SWE4-UV-001 through SWE4-UV-005 |
| SDD-011 through SDD-018 | SWE4-UV-006 through SWE4-UV-011 |
| SDD-019 through SDD-026 | SWE4-UV-012 through SWE4-UV-016 |
| SDD-027 through SDD-037 | SWE4-UV-017 through SWE4-UV-021 |
| SDD-038 through SDD-050 | SWE4-UV-022 through SWE4-UV-028 |
| SDD-051 through SDD-070 | SWE4-UV-029 through SWE4-UV-030 |
## 8. Result Reporting
Unit verification results shall be summarized in a unit verification summary report including:
- verification scope
- selected verification measures
- skipped measures and rationale
- pass/fail result
- nonconformances
- regression scope
- residual risks
- release recommendation
## 9. Change History
| Version | Date | Description |
|---|---|---|
| 0.1.0 | 2026-05-14 | Initial SWE.4 unit verification plan derived from SDD v0.3. |
---
## 10. Platform Decision Unit Verification Addendum
**SWE4-UV-031**: Verify iOS and Android build configuration units.
- Source SDD: SDD-071, SDD-072, SDD-073
- Verification method: Static inspection, platform test
- Pass criteria: iOS minimum runtime, Apple SDK gate, Android minimum runtime, and Android target SDK configuration are inspectable and match release policy.
- Evidence: Test result, release inspection
**SWE4-UV-032**: Verify single active connection enforcement.
- Source SDD: SDD-074
- Verification method: Unit test
- Pass criteria: Attempting to create a second active MVP connection is rejected or routed through a controlled disconnect/switch policy.
- Evidence: Test result
**SWE4-UV-033**: Verify audio processing defaults and backend selection.
- Source SDD: SDD-075, SDD-076
- Verification method: Unit test, audio test
- Pass criteria: AEC, AGC, Noise Suppression, and High-Pass Filter default to enabled where supported and stable; backend selector prefers platform-native processing.
- Evidence: Test result
**SWE4-UV-034**: Verify local database, secret storage, bridge, and diagnostics privacy gate units.
- Source SDD: SDD-077, SDD-078, SDD-079, SDD-080
- Verification method: Unit test, security inspection
- Pass criteria: Non-secret storage, secret storage, typed bridge DTOs, and disabled automatic upload/crash behavior match detailed design.
- Evidence: Test result, security review
**SWE4-UV-035**: Verify `DesktopPttBackend` trait + `PttCapabilityLevel` enum + `FocusedPttBackend` units.
- Source SDD: SDD-081, SDD-082, SDD-087
- Verification method: Unit test
- Pass criteria: Trait surface compiles for every implementation; enum `as_str()` mapping is unambiguous; `FocusedPttBackend` reports `L0Focused` and forwards `set_ptt` events into `AudioTransmitGate` unchanged.
- Evidence: Test result
**SWE4-UV-036**: Verify Windows / macOS / Linux platform backend units in isolation.
- Source SDD: SDD-083, SDD-084, SDD-085, SDD-086
- Verification method: Unit test (with mocked OS surfaces), Platform Test (Windows / macOS / GNOME-Wayland reference hosts)
- Pass criteria: Each backend reports the expected `PttCapabilityLevel` for the current host configuration; the ladder falls through to `FocusedPttBackend` when its preferred OS API is unavailable.
- Evidence: Test result, platform-test trace
**SWE4-UV-037**: Verify `PttController` + `AudioTransmitGate` units.
- Source SDD: SDD-088, SDD-089
- Verification method: Unit test
- Pass criteria: `PttController::set_binding` rebinds the active backend without dropping `AudioTransmitGate`; `AudioTransmitGate::subscribe()` returns a `watch::Receiver` that delivers every transition the gate observes.
- Evidence: Test result
**SWE4-UV-038**: Verify `PttSanitizer` and `PttCapabilityBadge` units.
- Source SDD: SDD-090, SDD-091
- Verification method: Unit test, UI review
- Pass criteria: `PttSanitizer` drops records whose field names match the banned list (`key_code`, `scan_code`, `virtual_key`, `vk`, `keysym`, `keysym_string`, `key_sequence`); `PttCapabilityBadge` renders the correct label and explanation sheet for each `PttCapabilityLevel`.
- Evidence: Test result, UI screenshot
**SWE4-UV-039**: Verify `MissedKeyUpWatchdog` unit.
- Source SDD: SDD-092
- Verification method: Unit test (tokio time-paused)
- Pass criteria: After a simulated `false -> true -> (timeout elapsed)` sequence the watchdog calls `AudioTransmitGate::set(false)` exactly once and emits a sanitised diagnostic record naming only `capability_level` and `backend_id`.
- Evidence: Test result
| SDD Range | SWE.4 Coverage |
|---|---|
| SDD-071 through SDD-080 | SWE4-UV-031 through SWE4-UV-034 |
| SDD-081 through SDD-092 | SWE4-UV-035 through SWE4-UV-039 |
## Baseline Candidate 0.9.1 Update
| Version | Date | Description |
|---|---|---|
| 0.9.1 | 2026-05-14 | Updated baseline after product decision closure: Apple App Store SDK gate uses Xcode 26+ and iOS 26 / iPadOS 26 SDK+ since 2026-04-28, platform baselines and decision traceability propagated across the document set. |
## Baseline Candidate 0.9.2 Update
| Version | Date | Description |
|---|---|---|
| 0.9.2 | 2026-05-14 | Corrected Apple App Store Connect upload gate to 2026-04-28 and checked full-package naming, references, and coverage. |
## Baseline Candidate 0.9.3 Update
| Version | Date | Description |
|---|---|---|
| 0.9.3 | 2026-05-15 | Added desktop PTT unit-verification items SWE4-UV-035 through SWE4-UV-039: `DesktopPttBackend` trait + `PttCapabilityLevel`, platform backends, `PttController` + `AudioTransmitGate`, `PttSanitizer` + `PttCapabilityBadge`, `MissedKeyUpWatchdog`. |
## 11. Android P0 Unit Verification Addendum
**SWE4-UV-040**: Verify Android build configuration unit (`SDD-073` expanded).
- Source SDD: SDD-073
- Verification method: Gradle task assertion + static inspection of `android/app/build.gradle.kts`
- Pass criteria: `minSdk` literal equals `28` (DEC-004); `ndk.abiFilters` set equals `{arm64-v8a, armeabi-v7a, x86_64}` (per SDD-073 item 4 and SDD-118 item 3, `x86` 32-bit is explicitly out of scope and shall not be in the set); release `signingConfig` references the CI signing key reference (not debug); R8/ProGuard `minifyEnabled = true` on release; `bundle { abi.enableSplit = true }`; assemble target produces an AAB artifact at `app/build/outputs/bundle/release/app-release.aab`.
- Evidence: Gradle log + manifest dump
**SWE4-UV-041**: Verify `AndroidPermissionRequester` state machine (`SDD-106`).
- Source SDD: SDD-106
- Source SRS: SRS-209
- Verification method: Dart/Kotlin unit test with a permission-platform test double
- Pass criteria: State transitions are exhaustive for `{granted, denied, permanently_denied, revoked_mid_session}`; transitioning out of `granted` clamps `AudioTransmitGate::set(false)`; re-entering `granted` does NOT auto-transmit (requires explicit user gesture); permanently-denied surfaces a settings-deep-link intent rather than re-prompting.
- Evidence: Test result
**SWE4-UV-042**: Verify `BackIntentService` Android branches (`SDD-028` expanded).
- Source SDD: SDD-028
- Source SRS: SRS-163
- Verification method: Dart unit test with platform-channel test double for both `OnBackInvokedCallback` (API 33+) and `OnBackPressedDispatcher` (API ≤ 32) paths.
- Pass criteria: Modal-first rule — if a modal is open, back closes the modal and does not pop the route. PTT-active-ignore — while `transmit_active == true` the back event is consumed without effect. Route-pop ordering — otherwise the topmost non-modal route pops. The API 33+ branch registers a `OnBackInvokedCallback` at `PRIORITY_DEFAULT`; the pre-33 branch registers an `OnBackPressedCallback`. Switching API level in the test double switches the active branch deterministically.
- Evidence: Test result
**SWE4-UV-043**: Verify `AndroidJniBootstrap` library load and init ordering (`SDD-105`).
- Source SDD: SDD-105
- Verification method: Instrumented unit test on Android (or Robolectric where applicable)
- Pass criteria: `System.loadLibrary("chanora_bridge")` is invoked from the `Application` subclass `onCreate` (not from an Activity); `JNI_OnLoad` runs exactly once per process and captures the `JavaVM*`; bridge init occurs before any FFI call from Dart; double-init is a no-op.
- Evidence: Test result + logcat trace
**SWE4-UV-044**: Verify `AndroidVoiceForegroundService` lifecycle unit (`SDD-107`).
- Source SDD: SDD-107
- Source SRS: SRS-111
- Verification method: Kotlin unit test + Robolectric `ServiceController`
- Pass criteria: `startForeground` is called within 5 s of `onStartCommand` (API 26+ requirement); notification channel exists with `IMPORTANCE_LOW`; `foregroundServiceType` includes `microphone` on API 30+; service stops cleanly on disconnect intent and does not leak its wakelock; restart policy is `START_NOT_STICKY` (no auto-respawn after force-stop).
- Evidence: Test result
**SWE4-UV-045**: Verify `AndroidAudioModeController` snapshot/restore + refcount (`SDD-108`).
- Source SDD: SDD-108
- Source SRS: SRS-208
- Source SysRS: SysRS-305
- Verification method: Kotlin unit test with `AudioManager` test double
- Pass criteria: First acquire snapshots prior `AudioManager.getMode()` and sets `MODE_IN_COMMUNICATION`; nested acquires increment refcount without re-snapshotting; matched release decrements; final release restores the snapshotted prior mode exactly once; when `AudioManager` is unavailable (e.g. injected null), controller logs a sanitized warning and degrades to no-op without throwing.
- Evidence: Test result
**SWE4-UV-046**: Verify `AndroidPttCapability` reports `L0Focused` and pins `FocusedPttBackend` (`SDD-110`).
- Source SDD: SDD-110
- Verification method: Rust + Dart unit test
- Pass criteria: On `target_os = "android"` the `DesktopPttBackend` factory returns `FocusedPttBackend` unconditionally (no ladder evaluation); `PttCapabilityBadge` renders the localized `L0Focused` label with the Android-specific explanation string; no Android branch attempts to bind global hotkeys.
- Evidence: Test result + UI screenshot
| SDD Range | SWE.4 Coverage |
|---|---|
| SDD-028 (expanded) | SWE4-UV-042 |
| SDD-073 (refreshed) | SWE4-UV-040 |
| SDD-105 | SWE4-UV-043 |
| SDD-106 | SWE4-UV-041 |
| SDD-107 | SWE4-UV-044 |
| SDD-108 | SWE4-UV-045 |
| SDD-110 | SWE4-UV-046 |
### Android voice audio backend unit-verification items (SDD-111..SDD-116 wave)
**SWE4-UV-047**: Verify `AndroidVoiceStreamConfig` builder records requested vs. achieved fields.
- Source SDD: SDD-112 (config struct), SDD-111 (trait surface)
- Source SRS: SRS-210, SRS-211, SRS-213, SRS-214
- Source SAD: SAD-077, SAD-081
- Verification method: Rust unit test with a fake Oboe builder return path (no device)
- Pass criteria: Given a `MobileVoiceStreamConfig` requesting `PERFORMANCE_MODE_LOW_LATENCY`, input preset `VoiceCommunication`, output usage `VoiceCommunication`, content type `Speech`, and sharing mode `Exclusive`, the resulting `AndroidVoiceStreamConfig` records the requested values verbatim AND exposes `achieved_*` accessors populated from the fake builder's grant values. Mismatch between requested and achieved is observable via the accessors with no panics. Both input and output stream configs are independently recorded.
- Evidence: Rust unit test result
- SDD-116 matrix row satisfied: none directly (unit-level; matrix consumes the accessors per-device)
**SWE4-UV-048**: Verify input preset fallback ladder is observable at each step.
- Source SDD: SDD-111 (trait surface, open() error pathway), SDD-112 (preset field)
- Source SRS: SRS-211
- Source SAD: SAD-077
- Verification method: Rust unit test with a scripted Oboe builder mock that returns `ErrorIllegalArgument` for successive presets
- Pass criteria: With the mock denying `VoiceCommunication`, `open()` retries with `VoicePerformance`; with the mock denying both, `open()` retries with `Generic` (`Unprocessed`/default). Each retry is recorded in a structured field (e.g. `attempted_presets: Vec<InputPreset>`) and the final `achieved_input_preset()` returns the preset that was granted. If all presets fail, `open()` returns `BackendError::InputPresetUnavailable` (or the SDD-111 equivalent). No silent fallback.
- Evidence: Rust unit test result
- SDD-116 matrix row satisfied: none directly (matrix records `achieved_input_preset` per device)
**SWE4-UV-049**: Verify sharing-mode fallback Exclusive→Shared is observable.
- Source SDD: SDD-111, SDD-112 (sharing mode field)
- Source SRS: SRS-214
- Source SAD: SAD-077
- Verification method: Rust unit test with a fake Oboe builder return path
- Pass criteria: When the fake builder accepts `SharingMode::Exclusive`, `achieved_sharing_mode()` returns `Exclusive` and no retry occurs. When the fake builder rejects `Exclusive` (returns the documented Oboe error path for exclusive denial), the unit retries with `Shared`, and `achieved_sharing_mode()` returns `Shared`. The original request (`Exclusive`) is still recorded for diagnostics.
- Evidence: Rust unit test result
- SDD-116 matrix row satisfied: none directly (matrix records `achieved_sharing_mode` per device)
**SWE4-UV-050**: Verify hardware effects per-effect success/failure logic with software-AEC fallback.
- Source SDD: SDD-113 (per-effect construction + software fallback signal)
- Source SRS: SRS-212
- Source SAD: SAD-077, SAD-081
- Verification method: Rust unit test with a mocked JNI surface (`new_object`-style indirection) representing `AcousticEchoCanceler.create`, `NoiseSuppressor.create`, `AutomaticGainControl.create`
- Pass criteria: For each of AEC, NS, AGC: (a) success path — mock returns a non-null `GlobalRef`, effect is recorded as engaged, `setEnabled(true)` is invoked once; (b) failure path — mock returns null OR throws, the unit records the per-effect failure, does NOT abort the other two, and signals the engine to engage the software equivalent (per SDD-113 item 5 — for AEC failure the engine's software AEC must be flagged on). Order of effect construction does not affect outcome (no cross-effect coupling). `release()` is called on every engaged `GlobalRef` exactly once when the unit is torn down.
- Evidence: Rust unit test result
- SDD-116 matrix row satisfied: per-device AEC/NS/AGC availability column
**SWE4-UV-051**: Verify performance-mode achieved recording feeds SRS-210 latency tier classification.
- Source SDD: SDD-111 (`achieved_performance_mode`), SDD-112 (mode field)
- Source SRS: SRS-210
- Source SAD: SAD-077
- Verification method: Rust unit test
- Pass criteria: When the fake builder grants `PERFORMANCE_MODE_LOW_LATENCY`, `achieved_performance_mode()` returns `LowLatency` and the SRS-210 tier classifier (the unit-level pure function that maps achieved mode → latency target) returns the 150 ms tier. When the fake builder grants `PERFORMANCE_MODE_NONE` or `POWER_SAVING`, the classifier returns the 250 ms tier. The classifier is a pure function reachable from the engine without a device.
- Evidence: Rust unit test result
- SDD-116 matrix row satisfied: per-device `achieved_performance_mode` column
**SWE4-UV-052**: Verify output stream usage and content type are set correctly.
- Source SDD: SDD-112 (output config fields)
- Source SRS: SRS-213
- Source SAD: SAD-077
- Verification method: Rust unit test inspecting the builder calls captured by a mock Oboe builder
- Pass criteria: The output stream-construction path invokes `setUsage(VoiceCommunication)` and `setContentType(Speech)` exactly once on the builder before `openStream`. No other usage / content-type combination is reachable from the production path (the test asserts there is no code path that constructs the output stream with `Media` / `Music` defaults). `achieved_output_usage()` returns `VoiceCommunication` and `achieved_output_content_type()` returns `Speech` on a successful open.
- Evidence: Rust unit test result
- SDD-116 matrix row satisfied: per-device `achieved_output_usage` / `achieved_output_content_type` column
| SDD Range | SWE.4 Coverage |
|---|---|
| SDD-111 | SWE4-UV-047, SWE4-UV-048, SWE4-UV-049, SWE4-UV-051 |
| SDD-112 | SWE4-UV-047, SWE4-UV-048, SWE4-UV-049, SWE4-UV-051, SWE4-UV-052 |
| SDD-113 | SWE4-UV-050 |
| SDD-115 | (lifecycle sequencing — see SWE.5 integration items) |
| SDD-116 | (device matrix — see SWE.6 + android-p0-acceptance TC-14..TC-18) |
## Baseline Candidate 0.9.4 Update
| Version | Date | Description |
|---|---|---|
| 0.9.4 | 2026-05-18 | Added Android P0 unit-verification items SWE4-UV-040 through SWE4-UV-046 covering SDD-028 (expanded back-intent branches), SDD-073 (refreshed build config + AAB), SDD-105 (JNI bootstrap), SDD-106 (permission state machine + transmit clamp), SDD-107 (foreground service lifecycle), SDD-108 (audio mode controller refcount + restore), SDD-110 (PTT capability pin). |
| 0.9.5 | 2026-05-18 | Added Android voice audio backend unit-verification items SWE4-UV-047 through SWE4-UV-052 covering SDD-111 (trait surface), SDD-112 (config requested vs achieved), SDD-113 (per-effect hardware engagement + software fallback) and feeding SRS-210/211/213/214 unit-level classification. |
| 0.9.14 | 2026-05-18 | Wave 3 reviewer follow-up: allocated real SWE.4 IDs for two test sets that had been cited under provisional `SWE4-UV-NNN` placeholders. SWE4-UV-053..055 anchor the three `TransmitModeSelector` permission-state clamp tests in `crates/chanora_audio/src/transmit_selector.rs` (commit 635d160 was cited under the "SWE4-UV-026 family" without real anchors). SWE4-UV-056..057 anchor the two missing `DiagnosticExport::with_android_audio` rendering tests in `crates/chanora_diagnostics/src/lib.rs` (commit aea50a1 introduced the field/builder/`to_text` branch but the unit-test pair was queued for the commit-8 amend). Closes the Wave 3 traceability gap; downstream test cites in commits 4 and 8 are amended to reference these IDs. |
| 0.9.15 | 2026-05-18 | Closed SDD-120 §11 verification-engineer follow-up: allocated SWE4-UV-058 through SWE4-UV-062 anchoring the five realtime-audio bench functions landed in commit 3a7750a (`bench_capture_alloc_count` and `bench_capture_callback_wall_clock` in `crates/chanora_audio/benches/realtime_capture.rs`; `bench_opus_encode_latency` and `bench_opus_decode_latency` in `crates/chanora_audio/benches/opus_codec.rs`; `bench_resampler_throughput` in `crates/chanora_audio/benches/resampler.rs`) and feeding the SRS-216 metric families under the SRS-219 per-metric tolerance windows. Status PENDING_BASELINE: pass/fail evaluation cannot proceed until the first `bench-baseline-update.yml` `workflow_dispatch` invocation lands the SAD-089 baseline JSON; once landed, the SDD-120 §6 `bench-advisory.yml` workflow plus `examples/compare_baseline` perform automated 🔴/🟡/🟢 evaluation against the merge-base baseline snapshot. Strict-layered sourcing preserved (`SWE.4 -> SDD/SRS` only); no SDD, SRS, SAD, SysDes, or SysRS edits performed. |
## 12. Android Permission Clamp + Diagnostics Android-Audio Unit Verification Addendum (Wave 3 — v0.9.14)
**Background.** Wave 3 reviewer audit flagged two newly-landed test groups whose source comments cited verification IDs as `SWE4-UV-NNN` placeholders rather than real allocated anchors:
1. Three `TransmitModeSelector` permission-state tests at `crates/chanora_audio/src/transmit_selector.rs:343-435` (introduced by commit 635d160). The reviewer noted at `transmit_selector.rs:343` that these tests cite "SWE4-UV-026 family" without real IDs.
2. Two missing unit tests for the new `chanora_diagnostics` Android-audio surface (`android_audio: Option<String>` field, `with_android_audio` builder, `[audio.android]` `to_text` branch) introduced by commit aea50a1; queued for the commit-8 amend.
This addendum allocates the five required anchors. SWE4-UV-052 was the prior high-water mark; SWE4-UV-053..057 are the next free contiguous range.
**SWE4-UV-053**: Verify `TransmitModeSelector` clamps transmit to `false` when `permission_state == Denied` (or `PermanentlyDenied`/`Unknown`) regardless of PTT, channel, or transmit-mode state.
- Source SDD: SDD-106 §6
- Source SRS: SRS-209 (fail-safe listen-only default)
- Verification method: Rust unit test
- Test code path: `crates/chanora_audio/src/transmit_selector.rs::tests::permission_state_denied_clamps_transmit_to_false` (lines 348-384)
- Pass criteria: With `TransmitMode::Ptt`, `in_channel = true`, `ptt_held = true` and the default `Granted` permission state the gate evaluates `true`. Publishing `PermissionGate::Denied` flips the gate to `false`. `PermanentlyDenied` and `Unknown` likewise clamp `false`. The clamp wins over `TransmitMode::Continuous`.
- Evidence: `cargo test -p chanora_audio` result
- Notes: Establishes the fail-safe semantics: cold-launch `Unknown` is listen-only per SRS-209.
**SWE4-UV-054**: Verify `TransmitModeSelector` releases the permission clamp on transition back to `Granted` and resumes normal PTT-driven evaluation.
- Source SDD: SDD-106 §6
- Source SRS: SRS-209
- Verification method: Rust unit test
- Test code path: `crates/chanora_audio/src/transmit_selector.rs::tests::permission_state_granted_releases_clamp` (lines 389-409)
- Pass criteria: After `Denied` clamps the gate to `false`, transitioning to `PermissionGate::Granted` (with PTT held + in channel) restores the gate to `true` on the next state tick. Releasing the PTT key then drops the gate per normal PTT semantics — the clamp is no longer in effect.
- Evidence: `cargo test -p chanora_audio` result
- Notes: Pairs with SWE4-UV-053; together they cover the clamp on/off edges.
**SWE4-UV-055**: Verify `TransmitModeSelector` ignores `PermissionState` events for non-`RECORD_AUDIO` permissions (contract: bridge filters before reaching the selector).
- Source SDD: SDD-106 §5
- Verification method: Rust unit test (contract pin)
- Test code path: `crates/chanora_audio/src/transmit_selector.rs::tests::permission_state_for_other_permission_does_not_clamp` (lines 418-435)
- Pass criteria: The selector exposes no setter for non-`RECORD_AUDIO` permissions. With `TransmitMode::Continuous` + `in_channel = true` and no call to `set_permission_state`, the selector remains in its `Granted` default and the gate evaluates `true`. Demonstrates the contract that unrelated permission events (e.g. `POST_NOTIFICATIONS`) cannot reach this code path.
- Evidence: `cargo test -p chanora_audio` result
- Notes: This is a negative/contract test; the JNI bridge is responsible for filtering by permission name before invoking `set_permission_state`.
**SWE4-UV-056**: Verify `DiagnosticExport::with_android_audio(Some(yaml))` renders the `[audio.android]` section between `[metadata]` and `[recent logs]` in `to_text()`.
- Source SDD: SDD-116 item 3 (diagnostics matrix surface), SDD-112 item 10 / SDD-113 item 7 (producing crate)
- Verification method: Rust unit test
- Test code path: `crates/chanora_diagnostics/src/lib.rs::tests::android_audio_renders_between_metadata_and_logs` (to be added by commit-8 amend)
- Pass criteria: Building a `DiagnosticExport::from_sink(...)` and chaining `.with_android_audio(Some("perf: low_latency\n".into()))` produces a `to_text()` output where the substring `[audio.android]` appears (a) after the `[metadata]` header, (b) before the `[recent logs]` header, and (c) is immediately followed by the supplied YAML fragment verbatim. The YAML fragment is not re-redacted by the diagnostics bundle (SDD-090 places sanitisation in the producing crate).
- Evidence: `cargo test -p chanora_diagnostics` result
- Notes: Section ordering is part of the SDD-116 matrix contract; downstream tooling parses by header.
**SWE4-UV-057**: Verify `DiagnosticExport` with default `android_audio = None` omits the `[audio.android]` header entirely (negative test).
- Source SDD: SDD-116 item 3 (negative case — non-Android or pre-session)
- Verification method: Rust unit test
- Test code path: `crates/chanora_diagnostics/src/lib.rs::tests::android_audio_absent_omits_section` (to be added by commit-8 amend)
- Pass criteria: A `DiagnosticExport::from_sink(...)` constructed without `.with_android_audio(...)` (i.e. `android_audio == None`) produces a `to_text()` output that does **not** contain the substring `[audio.android]`. The `[metadata]` and `[recent logs]` sections remain present in order. Calling `.with_android_audio(None)` explicitly is equivalent (idempotent omission).
- Evidence: `cargo test -p chanora_diagnostics` result
- Notes: Pairs with SWE4-UV-056; together they cover the conditional-render branch in `DiagnosticExport::to_text`.
| SDD Range | SWE.4 Coverage |
|---|---|
| SDD-106 §5 | SWE4-UV-055 |
| SDD-106 §6 | SWE4-UV-053, SWE4-UV-054 |
| SDD-116 item 3 | SWE4-UV-056, SWE4-UV-057 |
## 13. Realtime-Audio Benchmark-Harness Unit Verification Addendum (SDD-120 §11 closure — v0.9.15)
**Background.** SDD-120 (`RealtimeAudioBenchmarkHarnessAndAdvisoryCI`, v0.9.16) §11 explicitly defers the SWE.4 allocation for the five bench functions it specifies in §3, noting: "Suggested SWE.4 forward allocation: SWE4-UV-058 through SWE4-UV-062 (verification-engineer follow-up; not authored by this SDD unit)." Commit 3a7750a landed the implementation across three bench files plus two `examples/` post-processor binaries. This addendum closes the §11 follow-up by authoring the five anchors. SWE4-UV-057 was the prior high-water mark; SWE4-UV-058..062 are the next free contiguous range.
**Status conventions.** All five items are marked **PENDING_BASELINE**: pass/fail evaluation requires a committed SAD-089 baseline JSON for the active host triple (`x86_64-unknown-linux-gnu`), which is produced only by the SDD-120 §7 `bench-baseline-update.yml` `workflow_dispatch` workflow. Until that workflow has been dispatched at least once and its PR merged, the SDD-120 §6 `bench-advisory.yml` short-circuits on missing baseline (per SRS-218 clause 4 always-success) and these verification items cannot return 🟢/🟡/🔴. Automated comparison thereafter is performed by `crates/chanora_audio/examples/compare_baseline.rs` (invoked via `cargo run --example compare_baseline`) against the merge-base baseline per SRS-219.
**SWE4-UV-058**: Verify the realtime capture path performs zero heap allocations per callback after the warmup window.
- Source SDD: SDD-120 §3 (bench function `bench_capture_alloc_count` with 100-callback warmup before the dhat measurement window); SDD-120 §8 (zero-tolerance metric collapses to 🔴/🟢 only — no 🟡 band).
- Source SRS: SRS-216 metric 1 (heap allocation count per realtime callback after warmup); SRS-219 clause a (zero tolerance — "any non-zero allocation count on a PR is flagged 🔴").
- Verification method: Criterion bench with dhat global allocator. Invocation: `cargo bench --bench realtime_capture -- bench_capture_alloc_count`.
- Test code path: `crates/chanora_audio/benches/realtime_capture.rs::bench_capture_alloc_count` (line 27).
- Pass criteria: After a 100-call `CaptureState::ingest` pre-warm (per SDD-120 §3), the dhat `HeapStats.total_blocks` delta across the subsequent 1000 `CaptureState::ingest` calls **SHALL equal zero**. Any non-zero count is a 🔴 regression per SRS-219 clause a (zero tolerance).
- Acceptance evidence: dhat global-allocator `HeapStats.total_blocks` delta == 0 captured by the bench; criterion `estimates.json` projected into the SRS-217 baseline schema by `examples/emit_baseline`; advisory marker rendered by `examples/compare_baseline` against the merge-base baseline.
- Cross-trace: SAD-088 (benchmark-instrumentation seam), SDD-094 (audio-engine lifecycle as the bench-seam container).
- Status: **PENDING_BASELINE** (zero-tolerance metric — first baseline run establishes the binary 🟢/🔴 evaluation; subsequent runs gate on any non-zero allocation regardless of baseline value).
**SWE4-UV-059**: Verify the realtime capture callback wall-clock stays within the SRS-219 p95 tolerance.
- Source SDD: SDD-120 §3 (bench function `bench_capture_callback_wall_clock` with criterion default warmup; measurement reports p50/p95/p99 as a fraction of the cpal stream period).
- Source SRS: SRS-216 metric 2 (per-callback wall-clock at p50/p95/p99 as a fraction of cpal stream period); SRS-219 clause b ("per-callback wall-clock duration at p95 — tolerance is +20% relative to the baseline (p50 and p99 are advisory data only and do not drive the 🔴 marker)").
- Verification method: Criterion timing bench. Invocation: `cargo bench --bench realtime_capture -- bench_capture_callback_wall_clock`.
- Test code path: `crates/chanora_audio/benches/realtime_capture.rs::bench_capture_callback_wall_clock` (line 95).
- Pass criteria: p95 wall-clock per callback **SHALL stay within +20% of the baseline-at-merge-base** per SRS-219 clause b. p50 and p99 are reported but **advisory only** (no tolerance gate; cannot drive 🔴). Automated 🟢/🟡/🔴 evaluation by `examples/compare_baseline` per SDD-120 §8 (🔴 if `delta_pct > 20%`, 🟡 if `10% < delta_pct ≤ 20%`, 🟢 otherwise).
- Acceptance evidence: criterion p95 estimate from `target/criterion/.../estimates.json` projected through `examples/emit_baseline`; advisory marker rendered by `examples/compare_baseline`.
- Cross-trace: SAD-088, SDD-094.
- Status: **PENDING_BASELINE**.
**SWE4-UV-060**: Verify Opus encode latency on a 960-sample / 20 ms frame stays within the SRS-219 mean tolerance.
- Source SDD: SDD-120 §3 (bench function `bench_opus_encode_latency` exercising `audiopus::coder::Encoder::encode_float` **directly**, not through `tsclientlib::AudioHandler`, to isolate encode latency from higher-layer mix/jitter handling).
- Source SRS: SRS-216 metric 3 (Opus encode latency on 960-sample/20 ms frame); SRS-219 clause c ("Opus encode latency on a 960-sample / 20 ms frame — tolerance is +15% relative to the baseline on the mean").
- Verification method: Criterion timing bench. Invocation: `cargo bench --bench opus_codec -- bench_opus_encode_latency`.
- Test code path: `crates/chanora_audio/benches/opus_codec.rs::bench_opus_encode_latency` (line 21).
- Pass criteria: `encoder.encode_float()` mean latency on a 960-sample (20 ms @ 48 kHz mono) deterministic-sine frame **SHALL stay within +15% of the baseline-at-merge-base** per SRS-219 clause c. Automated 🟢/🟡/🔴 evaluation by `examples/compare_baseline` per SDD-120 §8 (🔴 if `delta_pct > 15%`, 🟡 if `7.5% < delta_pct ≤ 15%`, 🟢 otherwise).
- Acceptance evidence: criterion mean estimate from `target/criterion/.../estimates.json` projected through `examples/emit_baseline`; advisory marker rendered by `examples/compare_baseline`.
- Cross-trace: SAD-088, SDD-094.
- Status: **PENDING_BASELINE**.
**SWE4-UV-061**: Verify Opus decode latency on a 20 ms Opus frame stays within the SRS-219 mean tolerance.
- Source SDD: SDD-120 §3 (bench function `bench_opus_decode_latency` exercising `audiopus::coder::Decoder::decode_float` **directly**, not `tsclientlib::AudioHandler::fill_buffer`; SDD-120 §3 pinned this explicitly because the `AudioHandler` path conflates Opus decode with jitter-buffer dequeue and PCM mix).
- Source SRS: SRS-216 metric 4 (Opus decode latency on 960-sample/20 ms frame); SRS-219 clause c ("Opus decode latency on a 960-sample / 20 ms frame — tolerance is +15% relative to the baseline on the mean").
- Verification method: Criterion timing bench. Invocation: `cargo bench --bench opus_codec -- bench_opus_decode_latency`.
- Test code path: `crates/chanora_audio/benches/opus_codec.rs::bench_opus_decode_latency` (line 37).
- Pass criteria: `decoder.decode_float()` mean latency on a single 20 ms (960-sample @ 48 kHz mono) Opus-encoded frame **SHALL stay within +15% of the baseline-at-merge-base** per SRS-219 clause c. Automated 🟢/🟡/🔴 evaluation by `examples/compare_baseline` per SDD-120 §8 (🔴 if `delta_pct > 15%`, 🟡 if `7.5% < delta_pct ≤ 15%`, 🟢 otherwise).
- Acceptance evidence: criterion mean estimate from `target/criterion/.../estimates.json` projected through `examples/emit_baseline`; advisory marker rendered by `examples/compare_baseline`.
- Cross-trace: SAD-088, SDD-094.
- Status: **PENDING_BASELINE**.
**SWE4-UV-062**: Verify resampler throughput on the three canonical rate pairs stays within the SRS-219 samples-per-second tolerance.
- Source SDD: SDD-120 §3 (bench function `bench_resampler_throughput` structured as a criterion `BenchmarkGroup` with three sub-benches: 44.1 kHz → 48 kHz, 16 kHz → 48 kHz, and 48 kHz → 48 kHz passthrough).
- Source SRS: SRS-216 metric 5 (resampler throughput on canonical rate pairs); SRS-219 clause d ("resampler throughput on the canonical rate pairs — tolerance is 10% relative to the baseline on samples-per-second (i.e. a throughput drop greater than 10% is flagged 🔴)").
- Verification method: Criterion throughput bench. Invocation: `cargo bench --bench resampler -- bench_resampler_throughput`.
- Test code path: `crates/chanora_audio/benches/resampler.rs::bench_resampler_throughput` (line 17).
- Pass criteria: Each of the three sub-benches (44.1→48, 16→48, 48→48-passthrough) samples-per-second throughput **SHALL stay no worse than 10% of the baseline-at-merge-base** per SRS-219 clause d. A throughput drop greater than 10% on any of the three sub-benches is a 🔴 regression. Automated 🟢/🟡/🔴 evaluation by `examples/compare_baseline` per SDD-120 §8 (🔴 if `delta_pct < 10%`, 🟡 if `10% ≤ delta_pct < 5%`, 🟢 otherwise; note the sign inversion versus latency metrics — for throughput, a *decrease* is the regression direction).
- Acceptance evidence: criterion samples-per-second estimates from `target/criterion/.../estimates.json` for each of the three sub-benches projected through `examples/emit_baseline`; advisory marker rendered by `examples/compare_baseline`.
- Cross-trace: SAD-088, SDD-094.
- Status: **PENDING_BASELINE**.
### 13.1 SDD-120 Coverage Index
| SDD Range | SWE.4 Coverage | Tolerance Source |
|---|---|---|
| SDD-120 §3 (bench `bench_capture_alloc_count`) | SWE4-UV-058 | SRS-219 clause a (zero-tolerance) |
| SDD-120 §3 (bench `bench_capture_callback_wall_clock`) | SWE4-UV-059 | SRS-219 clause b (+20% p95) |
| SDD-120 §3 (bench `bench_opus_encode_latency`) | SWE4-UV-060 | SRS-219 clause c (+15% mean) |
| SDD-120 §3 (bench `bench_opus_decode_latency`) | SWE4-UV-061 | SRS-219 clause c (+15% mean) |
| SDD-120 §3 (bench `bench_resampler_throughput`) | SWE4-UV-062 | SRS-219 clause d (10% samples/sec) |
### 13.2 SWE4-UV-058..062 Status Index
| SWE.4 ID | Bench function | Bench file | Tolerance | Status |
|---|---|---|---|---|
| SWE4-UV-058 | `bench_capture_alloc_count` | `crates/chanora_audio/benches/realtime_capture.rs` | zero | PENDING_BASELINE |
| SWE4-UV-059 | `bench_capture_callback_wall_clock` | `crates/chanora_audio/benches/realtime_capture.rs` | +20% p95 | PENDING_BASELINE |
| SWE4-UV-060 | `bench_opus_encode_latency` | `crates/chanora_audio/benches/opus_codec.rs` | +15% mean | PENDING_BASELINE |
| SWE4-UV-061 | `bench_opus_decode_latency` | `crates/chanora_audio/benches/opus_codec.rs` | +15% mean | PENDING_BASELINE |
| SWE4-UV-062 | `bench_resampler_throughput` | `crates/chanora_audio/benches/resampler.rs` | 10% samples/sec | PENDING_BASELINE |
@@ -1,377 +0,0 @@
# Chanora SWE.5 Software Integration and Integration Verification Plan
**Document type:** Software Integration Verification Plan
**Process alignment:** ASPICE SWE.5 Software Component Verification and Integration Verification
**Version:** 0.9.2
**Status:** Baseline Candidate
**Language:** English
**Product:** Chanora
**Verification object:** Integrated software elements
**Direct source layer:** SAD and SDD verification objects
**Repo path:** `docs/verification/swe5-software-integration-verification-plan.md` ---
## 1. Purpose
This document defines the software integration and integration verification strategy for Chanora. It verifies that software elements integrate correctly and interact according to the software architecture and detailed design.
```text
SysRS -> SysDes -> SRS -> SAD -> SDD
-> SWE.5 Software Integration Verification
```
SWE.5 verifies architecture interfaces, dynamic behavior, component behavior, and selected detailed-design interactions.
## 2. Integration Strategy
| Integration step | Integrated elements | Purpose |
|---|---|---|
| SWE5-INT-001 | Flutter App Shell + Design System | Confirm theme initialization and token availability |
| SWE5-INT-002 | Design System + UI Components | Confirm components consume tokens and expose semantics |
| SWE5-INT-003 | Adaptive Shell + Feature Modules | Confirm compact/medium/expanded layouts integrate with features |
| SWE5-INT-004 | Localization Service + UI Components | Confirm product strings and accessibility labels integrate |
| SWE5-INT-005 | Protocol Adapter + Rust Core | Confirm protocol events and errors integrate with core state |
| SWE5-INT-006 | Rust Core + Bridge Facade | Confirm commands, results, and event streams integrate |
| SWE5-INT-007 | Bridge Facade + Flutter State | Confirm event mapping and view model updates |
| SWE5-INT-008 | Audio Subsystem + Platform Audio | Confirm capture, processing, encode/decode, and playback interface behavior |
| SWE5-INT-009 | Diagnostics + Storage + Localization | Confirm diagnostics event keys, localized descriptions, redaction, and export integration |
| SWE5-INT-010 | Full Flutter UI + Rust Core | Confirm end-to-end software behavior before software qualification testing |
## 3. Integration Verification Measures
**SWE5-IV-001**: Verify Flutter App Shell and Design System integration.
- Source SAD: SAD-001, SAD-002, SAD-007
- Source SDD: SDD-001, SDD-002, SDD-003
- Verification method: Flutter integration test
- Pass criteria: App initializes Material 3 theme and Chanora semantic tokens before rendering feature screens.
- Evidence: Integration test result
**SWE5-IV-002**: Verify Design System and UI Component integration.
- Source SAD: SAD-002, SAD-003, SAD-014
- Source SDD: SDD-004 through SDD-010, SDD-021 through SDD-024
- Verification method: Widget integration test
- Pass criteria: Components consume design tokens and expose required accessibility semantics.
- Evidence: Integration test result
**SWE5-IV-003**: Verify adaptive shell and feature module integration.
- Source SAD: SAD-008 through SAD-011
- Source SDD: SDD-011 through SDD-015
- Verification method: Integration test
- Pass criteria: Compact, medium, and expanded layouts render correct feature regions and preserve critical voice/connection access.
- Evidence: Integration test result
**SWE5-IV-004**: Verify connection status and VoiceBar integration.
- Source SAD: SAD-012, SAD-013, SAD-016
- Source SDD: SDD-016, SDD-017, SDD-018, SDD-026
- Verification method: Integration test
- Pass criteria: Connection status and primary voice controls are visible or directly reachable across layout classes.
- Evidence: Integration test result
**SWE5-IV-005**: Verify localization and accessibility integration.
- Source SAD: SAD-014, SAD-020, SAD-026
- Source SDD: SDD-024, SDD-031, SDD-032, SDD-040
- Verification method: Integration test, accessibility test
- Pass criteria: Localized strings, semantic labels, fallback behavior, and locale-aware formatting work together.
- Evidence: Integration test result
**SWE5-IV-006**: Verify server-content text path integration.
- Source SAD: SAD-021, SAD-022, SAD-023
- Source SDD: SDD-034, SDD-035, SDD-036, SDD-037
- Verification method: Integration test
- Pass criteria: Server names, channel names, nicknames, and messages preserve Unicode and are not translated.
- Evidence: Integration test result
**SWE5-IV-007**: Verify Platform Services and App Shell integration.
- Source SAD: SAD-017, SAD-018, SAD-019
- Source SDD: SDD-027, SDD-028, SDD-029, SDD-030
- Verification method: Platform integration test
- Pass criteria: Insets, back intent, haptics, and keyboard avoidance integrate with shell behavior.
- Evidence: Platform test result
**SWE5-IV-008**: Verify Bridge Facade and Rust Core integration.
- Source SAD: SAD-040, SAD-041
- Source SDD: SDD-046, SDD-047
- Verification method: Integration test
- Pass criteria: Flutter commands and Rust events flow through stable DTOs and deterministic event mapping.
- Evidence: Integration test result
**SWE5-IV-009**: Verify Rust Core and Protocol Adapter integration.
- Source SAD: SAD-032, SAD-033, SAD-041
- Source SDD: SDD-046, SDD-047
- Verification method: Integration test with protocol test server or protocol test double
- Pass criteria: Connection, snapshot, delta events, and protocol errors integrate with core state.
- Evidence: Integration test result
**SWE5-IV-010**: Verify Audio Subsystem and Platform Audio integration.
- Source SAD: SAD-034
- Source SDD: SDD-048
- Verification method: Audio integration test
- Pass criteria: Audio status reaches UI while DSP remains isolated from UI units.
- Evidence: Audio integration test result
**SWE5-IV-011**: Verify Diagnostics integration.
- Source SAD: SAD-036, SAD-050
- Source SDD: SDD-038, SDD-041, SDD-050, SDD-054
- Verification method: Integration test, audit
- Pass criteria: Diagnostic keys, localized descriptions, redaction, Unicode preservation, and export flow operate together.
- Evidence: Integration test result, audit record
**SWE5-IV-012**: Verify storage and secure-storage integration boundaries.
- Source SAD: SAD-035
- Source SDD: SDD-049
- Verification method: Integration test, security review
- Pass criteria: Non-secret data uses storage facade and secrets use secure storage boundaries.
- Evidence: Integration test result, review record
**SWE5-IV-013**: Verify architecture-view and traceability-tooling integration.
- Source SAD: SAD-046 through SAD-060
- Source SDD: SDD-051 through SDD-070
- Verification method: Documentation/tooling integration check
- Pass criteria: Architecture views, ADRs, interface detail catalog, and traceability checker are mutually consistent.
- Evidence: Review record
## 4. Regression Strategy
Regression selection shall consider:
- changed software unit
- changed interface
- changed architecture item
- changed DTO
- changed localization resource
- changed audio pipeline behavior
- changed protocol adapter behavior
- changed diagnostics redaction behavior
- changed platform behavior
## 5. Traceability Matrix
| Architecture / Design Area | SWE.5 Coverage |
|---|---|
| SAD-001 through SAD-016 | SWE5-IV-001 through SWE5-IV-005 |
| SAD-017 through SAD-027 | SWE5-IV-005 through SWE5-IV-007, SWE5-IV-011 |
| SAD-028 through SAD-031 | SWE5-IV-013 |
| SAD-032 through SAD-041 | SWE5-IV-008 through SWE5-IV-012 |
| SAD-042 through SAD-050 | SWE5-IV-001, SWE5-IV-011, SWE5-IV-013 |
| SAD-051 through SAD-060 | SWE5-IV-013 |
| SDD-001 through SDD-030 | SWE5-IV-001 through SWE5-IV-007 |
| SDD-031 through SDD-050 | SWE5-IV-005 through SWE5-IV-012 |
| SDD-051 through SDD-070 | SWE5-IV-013 |
## 6. Result Reporting
Software integration verification results shall be summarized in an integration verification summary report including:
- integrated elements
- selected integration sequence
- selected verification measures
- regression scope
- test environment
- pass/fail result
- nonconformances
- interface issues
- residual risks
- release recommendation
## 7. Change History
| Version | Date | Description |
|---|---|---|
| 0.1.0 | 2026-05-14 | Initial SWE.5 software integration and integration verification plan. |
---
## 8. Platform Decision Integration Verification Addendum
**SWE5-IV-014**: Verify platform baseline, build configuration, single connection, audio defaults, storage, bridge, and diagnostics privacy integration.
- Source SAD: SAD-061 through SAD-070
- Source SDD: SDD-071 through SDD-080
- Verification method: Integration test, release inspection, security review
- Pass criteria: New product decisions integrate across build configuration, runtime behavior, audio, storage, bridge, diagnostics, and release readiness.
- Evidence: Integration verification result
| Architecture / Design Area | SWE.5 Coverage |
|---|---|
| SAD-061 through SAD-070 | SWE5-IV-014 |
| SDD-071 through SDD-080 | SWE5-IV-014 |
**SWE5-IV-015**: Verify integrated desktop PTT subsystem (audio engine, Rust core PTT controller, bridge, Flutter UI, diagnostics).
- Source SAD: SAD-071 through SAD-079
- Source SDD: SDD-081 through SDD-092
- Verification method: Software integration test on every supported desktop reference host (Windows, macOS, GNOME-on-Wayland Linux)
- Pass criteria: For each platform the live measurement of `PttCapabilityLevel` and `backend_id` matches the design expectation; pressing the bound input toggles `transmit_active` end-to-end; the missed-key-up watchdog clears `transmit_active` after the configured timeout; the user-initiated diagnostic export does not contain raw key data; the UI capability badge matches the active capability.
- Evidence: Integration test trace, diagnostic-export inspection, UI screenshot
| Architecture / Design Area | SWE.5 Coverage |
|---|---|
| SAD-061 through SAD-070 | SWE5-IV-014 |
| SDD-071 through SDD-080 | SWE5-IV-014 |
| SAD-071 through SAD-079 | SWE5-IV-015 |
| SDD-081 through SDD-092 | SWE5-IV-015 |
## Baseline Candidate 0.9.1 Update
| Version | Date | Description |
|---|---|---|
| 0.9.1 | 2026-05-14 | Updated baseline after product decision closure: Apple App Store SDK gate uses Xcode 26+ and iOS 26 / iPadOS 26 SDK+ since 2026-04-28, platform baselines and decision traceability propagated across the document set. |
## Baseline Candidate 0.9.2 Update
| Version | Date | Description |
|---|---|---|
| 0.9.2 | 2026-05-14 | Corrected Apple App Store Connect upload gate to 2026-04-28 and checked full-package naming, references, and coverage. |
## Baseline Candidate 0.9.3 Update
| Version | Date | Description |
|---|---|---|
| 0.9.3 | 2026-05-15 | Added desktop PTT software-integration verification item SWE5-IV-015 covering the integrated audio engine + Rust core PTT controller + bridge + Flutter UI + diagnostics path on Windows, macOS, and GNOME-on-Wayland Linux reference hosts. |
## 9. Android P0 Software Integration Verification Addendum
**SWE5-IV-016**: Verify Android JNI bootstrap → foreground service lifecycle integration.
- Source SAD: SAD-077, SAD-081
- Source SDD: SDD-105, SDD-107
- Verification method: Instrumented integration test on a physical API 28 device
- Pass criteria: `Application.onCreate` loads `libchanora_bridge.so`, `JNI_OnLoad` captures the `JavaVM`, the bridge is initialized, and `AndroidVoiceForegroundService` can be started by the Rust core (via JNI callback) without re-loading the library or racing init. Service start completes before the first audio frame is produced.
- Evidence: Integration test result + logcat trace
**SWE5-IV-017**: Verify permission denial → foreground service not started → listen-only stream open integration.
- Source SAD: SAD-077
- Source SDD: SDD-106, SDD-107
- Source SRS: SRS-209
- Verification method: Integration test using permission-state test double + real audio stack
- Pass criteria: When `RECORD_AUDIO` is denied (or revoked mid-session) the FGS is NOT started for capture; the playback stream remains open so the user hears other participants (listen-only fallback per SRS-209); UI surfaces the degraded state; re-granting permission re-enables capture without process restart.
- Evidence: Integration test result
**SWE5-IV-018**: Verify foreground service start triggers audio mode acquisition; service stop triggers mode restore.
- Source SAD: SAD-081, SAD-085
- Source SDD: SDD-107, SDD-108
- Source SRS: SRS-208
- Source SysRS: SysRS-305
- Verification method: Integration test on a physical device with `dumpsys audio` snapshots before / during / after
- Pass criteria: Before voice channel join, `AudioManager.mode` is the device baseline. After FGS start and join, mode is `MODE_IN_COMMUNICATION`. After leave/disconnect, FGS stops and the controller restores the snapshotted prior mode. Refcount holds across rapid join/leave/join cycles.
- Evidence: Integration test result + `dumpsys audio` traces
**SWE5-IV-019**: Verify `BackIntentService` integration with the Flutter Navigator route stack.
- Source SAD: SAD-018
- Source SDD: SDD-028 (expanded)
- Source SRS: SRS-163
- Verification method: Flutter integration test on Android with a non-trivial route stack (home → channel → settings → modal)
- Pass criteria: System back closes modal first (does not pop the route); subsequent back pops one route per press; at the home route system back backgrounds the activity. PTT-active-ignore: while the test driver asserts `transmit_active`, back is consumed. API 33+ and pre-33 code paths both produce identical end states (verified on at least one device per branch).
- Evidence: Integration test result
**SWE5-IV-020**: Verify AAB build pipeline integration (`SDD-109`).
- Source SAD: SAD-063
- Source SDD: SDD-073, SDD-109
- Source SRS: SRS-119, SRS-188
- Verification method: CI build job + `bundletool` validation
- Pass criteria: `flutter build appbundle --release` produces an AAB; `bundletool validate` reports no errors; `bundletool build-apks --connected-device` produces per-ABI splits for `{arm64-v8a, armeabi-v7a, x86_64}`; signing key fingerprint matches the CI release-key reference; `targetSdkVersion` matches the SRS-188 declared target; `minSdkVersion` == 28 (DEC-004); manifest contains `FOREGROUND_SERVICE` + `FOREGROUND_SERVICE_MICROPHONE` (API 34+) + `RECORD_AUDIO` permissions and no others outside the allow-list.
- Evidence: CI artifact + bundletool report
| Architecture / Design Area | SWE.5 Coverage |
|---|---|
| SAD-018, SAD-063, SAD-077, SAD-081, SAD-084, SAD-085, SAD-086 | SWE5-IV-016 through SWE5-IV-020 |
| SDD-028 (expanded), SDD-073 (refreshed), SDD-105, SDD-106, SDD-107, SDD-108, SDD-109, SDD-110 | SWE5-IV-016 through SWE5-IV-020 |
### Android voice audio backend integration items (SDD-111..SDD-116 wave)
**SWE5-IV-021**: Verify `MobileVoiceAudioBackend` trait + audio engine integration on voice_join / voice_leave.
- Source SAD: SAD-077, SAD-081
- Source SDD: SDD-111 (trait surface), SDD-112 (config), SDD-115 (lifecycle)
- Source SRS: SRS-210, SRS-211, SRS-213, SRS-214
- Verification method: Rust integration test on a physical Android device (API 31 mid-tier minimum) driving the engine through `voice_join` / `voice_leave` against the real `AndroidVoiceUnit`
- Pass criteria: `voice_join` constructs `AndroidVoiceStreamConfig` from engine state, calls `AndroidVoiceUnit::open(...)`, opens BOTH input and output streams (verified by `session_id()` being `Some(_)` and the output-stream handle being non-null), then calls `start()`. Engine state transitions are observed in order: `Idle → Opening → Running`. `voice_leave` calls `stop()` then `close()` in that order; engine state returns to `Idle`. No stream is left open after `voice_leave` (verified by a follow-up `voice_join` succeeding with a fresh session id).
- Evidence: Integration test result + logcat with engine state transition lines
- SDD-116 matrix rows satisfied: all (open/close path exercised on every matrix device)
**SWE5-IV-022**: Verify SDD-108 audio-mode engagement precedes SDD-113 effect binding (forward) and effects release precedes mode restore (reverse).
- Source SAD: SAD-077, SAD-081, SAD-085
- Source SDD: SDD-108, SDD-113, SDD-115 (sequencing)
- Source SRS: SRS-208, SRS-212
- Verification method: Integration test instrumented with ordered event sink (timestamped events for `mode_engage`, `stream_open`, `effect_attach`, `start`, `stop`, `effect_release`, `stream_close`, `mode_restore`)
- Pass criteria: On `voice_join`, the recorded order is exactly `FGS_start → stream_open → mode_engage → effect_attach → start`. On `voice_leave`, the recorded order is exactly `stop → effect_release → mode_restore → stream_close → FGS_stop` per SDD-115 item 3 (reverse-order teardown). Any deviation fails the case. Test repeats 3 rapid join/leave cycles to surface refcount or sequencing races.
- Evidence: Integration test result + event-sink dump
- SDD-116 matrix rows satisfied: all
**SWE5-IV-023**: Verify SDD-115 full lifecycle sequencing forward and reverse.
- Source SAD: SAD-077, SAD-081
- Source SDD: SDD-107, SDD-108, SDD-111, SDD-113, SDD-115
- Source SRS: SRS-208, SRS-210, SRS-212, SRS-215
- Verification method: Instrumented integration test on physical device
- Pass criteria: Forward sequence: `FGS_start → streams_open → mode_engage → effects_bind → start_capture+playback`. Reverse sequence: `stop → effects_release → mode_restore → streams_close → FGS_stop`. Test asserts strict ordering AND that no event from one step occurs before the previous step completes (no overlap). Test re-runs across at least one API 28 device and one API 34 device to cover the FGS-type tightening branch.
- Evidence: Integration test result + ordered event log
- SDD-116 matrix rows satisfied: API 28 row, API 31 row, API 34 row
**SWE5-IV-024**: Verify audio-focus loss handling.
- Source SAD: SAD-077, SAD-081, SAD-085
- Source SDD: SDD-115 (audio focus event response)
- Source SRS: SRS-208, SRS-215
- Verification method: Integration test using a controllable secondary audio source (test app that requests focus with the three loss kinds)
- Pass criteria: `AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK` → capture and playback continue at unchanged level (Chanora does not duck itself; voice content is exempt per SDD-115). `AUDIOFOCUS_LOSS_TRANSIENT` → capture is paused (no frames produced upstream) and playback is paused; on focus regain, both resume without `voice_leave`/`voice_join`. `AUDIOFOCUS_LOSS` (permanent) → the session is left cleanly (effects released, mode restored, streams closed) and the user is surfaced an "audio focus lost" state; no auto-rejoin.
- Evidence: Integration test result + logcat
- SDD-116 matrix rows satisfied: at least API 31 + API 34 rows
**SWE5-IV-025**: Verify `ErrorDisconnected` callback marshals to tokio task and triggers reopen without blocking the callback thread.
- Source SAD: SAD-077, SAD-081
- Source SDD: SDD-111 (error/disconnect callback via bounded channel/watch), SDD-115 (recovery on disconnect)
- Source SRS: SRS-210, SRS-215
- Verification method: Integration test injecting a forced disconnect (e.g. unplug Bluetooth headset mid-session, or invoke an Oboe error-callback fault injection point)
- Pass criteria: The Oboe `onErrorBeforeClose` / `onErrorAfterClose` callback path enqueues a message onto the bounded channel and returns within the callback's documented latency budget (assert: callback returns in < 5 ms). Reopen is performed on a tokio task on the engine runtime, not on the callback thread (verified by thread-id capture in test). Reopen completes (`voice_join` round-trip on the new device route) within 2 s. The callback never holds a lock during reopen.
- Evidence: Integration test result + thread-id capture log
- SDD-116 matrix rows satisfied: all (regression surface)
**SWE5-IV-026**: Verify permission-denied `voice_join` reports listen-only and does NOT start the foreground service for capture.
- Source SAD: SAD-077
- Source SDD: SDD-106, SDD-107, SDD-111, SDD-115
- Source SRS: SRS-209, SRS-215
- Verification method: Integration test with `RECORD_AUDIO` denied via permission test double
- Pass criteria: `voice_join` opens the output (playback) stream and engine state becomes `RunningListenOnly`. `AndroidVoiceForegroundService` is NOT started with `foregroundServiceType="microphone"` for capture (the service may still run as a non-mic FGS for playback longevity per SDD-107 policy — if SDD-107 declines this, the FGS is not started at all). Backend reports `capture_state = ListenOnly`. No `AndroidVoiceUnit` input stream is opened. UI badge reflects listen-only. On runtime permission grant, the engine performs an internal upgrade to full duplex without process restart and without losing the playback stream. Cross-link: builds on SWE5-IV-017.
- Evidence: Integration test result + logcat + UI screenshot
- SDD-116 matrix rows satisfied: API 28 row, API 34 row (FGS-type tightening branch)
| Architecture / Design Area | SWE.5 Coverage |
|---|---|
| SAD-077, SAD-081 (mobile voice audio backend allocation) | SWE5-IV-021 through SWE5-IV-026 |
| SDD-111, SDD-112, SDD-113, SDD-115 | SWE5-IV-021 through SWE5-IV-026 |
| SDD-116 (device matrix coverage entry points) | SWE5-IV-021, SWE5-IV-023 |
## Baseline Candidate 0.9.4 Update
| Version | Date | Description |
|---|---|---|
| 0.9.4 | 2026-05-18 | Added Android P0 software-integration items SWE5-IV-016..SWE5-IV-020 covering JNI+FGS, permission+FGS+listen-only fallback, FGS+audio-mode round-trip, BackIntentService + Flutter route stack, and AAB build pipeline (bundletool + ABI splits). |
| 0.9.5 | 2026-05-18 | Added Android voice audio backend integration items SWE5-IV-021..SWE5-IV-026 covering MobileVoiceAudioBackend trait + engine wiring (SDD-111), SDD-108↔SDD-113 ordering and reverse-order teardown (SDD-115), full lifecycle sequencing across SDD-107/108/111/113, audio-focus loss handling, ErrorDisconnected callback marshaling, and permission-denied listen-only fallback. Cross-links: SWE5-IV-017 extended by SWE5-IV-026 (listen-only fallback). |
@@ -1,400 +0,0 @@
# Chanora SWE.6 Software Verification Plan
**Document type:** Software Verification / Software Qualification Test Plan
**Process alignment:** ASPICE SWE.6 Software Verification
**Version:** 0.9.2
**Status:** Baseline Candidate
**Language:** English
**Product:** Chanora
**Verification object:** Fully integrated Chanora software
**Direct source layer:** SRS
**Repo path:** `docs/verification/swe6-software-verification-plan.md` ---
## 1. Purpose
This document defines the software verification strategy for Chanora. It verifies that the fully integrated software satisfies the Software Requirements Specification.
```text
SysRS -> SysDes -> SRS -> SAD -> SDD
-> SWE.6 Software Verification
```
SWE.6 verifies against SRS. It does not replace SWE.4 unit verification or SWE.5 software integration verification.
## 2. Software Verification Strategy
| Verification type | Purpose |
|---|---|
| Functional software tests | Verify integrated software behavior against SRS |
| Non-functional software tests | Verify performance, accessibility, security, diagnostics, localization, and reliability requirements |
| Platform software tests | Verify integrated software behavior on target platforms |
| Regression tests | Confirm changes did not break previously verified software requirements |
| Release-scope test selection | Select tests according to target release content |
| Summary reporting | Communicate software verification status |
## 3. Software Verification Measures
**SWE6-SV-001**: Verify server connection flow.
- Source SRS: SRS-001 through SRS-030
- Verification method: End-to-end software test
- Pass criteria: User can connect to a compatible server with configured connection data.
- Evidence: Software verification result
**SWE6-SV-002**: Verify channel tree, client list, and server state display.
- Source SRS: SRS-031 through SRS-060
- Verification method: End-to-end software test
- Pass criteria: Channel and client state is displayed and updates after server events.
- Evidence: Software verification result
**SWE6-SV-003**: Verify voice send and receive.
- Source SRS: SRS-061 through SRS-090
- Verification method: Audio software verification test
- Pass criteria: User can transmit and receive voice under supported conditions.
- Evidence: Software verification result
**SWE6-SV-004**: Verify push-to-talk, mute, deaf, and voice status.
- Source SRS: SRS-061 through SRS-090, SRS-156
- Verification method: End-to-end software test
- Pass criteria: Voice controls perform expected software behavior and display correct state.
- Evidence: Software verification result
**SWE6-SV-005**: Verify local storage and secure storage behavior.
- Source SRS: SRS-091 through SRS-110
- Verification method: Software security and persistence test
- Pass criteria: Non-secret data persists through approved storage and secrets use secure storage boundaries.
- Evidence: Software verification result
**SWE6-SV-006**: Verify diagnostics and redacted export behavior.
- Source SRS: SRS-111 through SRS-124, SRS-177, SRS-178
- Verification method: Software verification test, audit
- Pass criteria: Diagnostic export requires user action and redacts sensitive data while preserving safe Unicode content.
- Evidence: Software verification result, audit record
**SWE6-SV-007**: Verify deployment-sensitive software behavior.
- Source SRS: SRS-125 through SRS-134
- Verification method: Release build verification
- Pass criteria: Release build metadata, packaging behavior, and platform declarations are consistent with software requirements.
- Evidence: Release verification result
**SWE6-SV-008**: Verify traceability and documentation controls.
- Source SRS: SRS-135 through SRS-143, SRS-180 through SRS-183
- Verification method: Documentation verification
- Pass criteria: SRS, SAD, and SDD direct-source rules pass validation.
- Evidence: Traceability report
**SWE6-SV-009**: Verify Material 3 and Chanora Design System behavior.
- Source SRS: SRS-144 through SRS-150
- Verification method: UI software verification, review
- Pass criteria: Integrated app uses Material 3 baseline and Chanora semantic tokens without feature-screen hardcoding.
- Evidence: Software verification result
**SWE6-SV-010**: Verify adaptive layouts.
- Source SRS: SRS-151 through SRS-156
- Verification method: UI software verification
- Pass criteria: Compact, medium, and expanded layouts preserve connection status and primary voice controls.
- Evidence: Software verification result
**SWE6-SV-011**: Verify accessibility behavior.
- Source SRS: SRS-157 through SRS-161
- Verification method: Accessibility verification
- Pass criteria: Critical controls expose semantics, non-color-only state cues, focus behavior, and text-scale resilience.
- Evidence: Accessibility verification result
**SWE6-SV-012**: Verify platform behavior.
- Source SRS: SRS-162 through SRS-164
- Verification method: Platform software verification
- Pass criteria: Insets, safe areas, keyboard, Android back behavior, and iOS platform behavior operate as specified.
- Evidence: Platform verification result
**SWE6-SV-013**: Verify localization and fallback behavior.
- Source SRS: SRS-165 through SRS-169
- Verification method: Localization software verification
- Pass criteria: Product strings are externalized, English baseline exists, additional locales can be added, and fallback is deterministic.
- Evidence: Localization verification result
**SWE6-SV-014**: Verify Unicode and server-content behavior.
- Source SRS: SRS-170 through SRS-176
- Verification method: Internationalization software verification
- Pass criteria: Server content is preserved, displayed without translation, UTF-8 is used internally, and locale-aware formatting works.
- Evidence: Internationalization verification result
**SWE6-SV-015**: Verify software architecture and detailed design traceability controls.
- Source SRS: SRS-180 through SRS-184
- Verification method: Documentation/tooling verification
- Pass criteria: SAD derives from SRS and SDD derives from SAD; initialization sequence is verified.
- Evidence: Traceability validation result
## 4. Regression Strategy
Regression test selection shall consider:
- changed SRS item
- changed feature
- changed architecture component
- changed detailed design unit
- changed platform behavior
- changed localization or Unicode handling
- changed diagnostics or redaction
- changed audio behavior
- changed protocol adapter behavior
## 5. Traceability Matrix
| SRS Range | SWE.6 Coverage |
|---|---|
| SRS-001 through SRS-030 | SWE6-SV-001 |
| SRS-031 through SRS-060 | SWE6-SV-002 |
| SRS-061 through SRS-090 | SWE6-SV-003, SWE6-SV-004 |
| SRS-091 through SRS-110 | SWE6-SV-005 |
| SRS-111 through SRS-124 | SWE6-SV-006 |
| SRS-125 through SRS-134 | SWE6-SV-007 |
| SRS-135 through SRS-143 | SWE6-SV-008 |
| SRS-144 through SRS-150 | SWE6-SV-009 |
| SRS-151 through SRS-156 | SWE6-SV-010 |
| SRS-157 through SRS-161 | SWE6-SV-011 |
| SRS-162 through SRS-164 | SWE6-SV-012 |
| SRS-165 through SRS-169 | SWE6-SV-013 |
| SRS-170 through SRS-176 | SWE6-SV-014 |
| SRS-177 through SRS-184 | SWE6-SV-006, SWE6-SV-015 |
## 6. Result Reporting
Software verification results shall be summarized in a software verification summary report including:
- release scope
- selected software verification measures
- skipped measures and rationale
- platform/environment used
- pass/fail status
- nonconformances
- regression scope
- residual risks
- release recommendation
## 7. Change History
| Version | Date | Description |
|---|---|---|
| 0.1.0 | 2026-05-14 | Initial SWE.6 software verification plan derived from SRS v0.5. |
---
## 8. Platform Decision Software Verification Addendum
**SWE6-SV-016**: Verify platform baseline, release SDK gates, single connection MVP scope, audio defaults, storage/bridge policy, and diagnostics/crash reporting policy.
- Source SRS: SRS-185 through SRS-194
- Verification method: Software verification, release inspection, security/privacy review
- Pass criteria: Integrated software and release configuration satisfy the accepted product decisions.
- Evidence: Software verification result, release inspection record
| SRS Range | SWE.6 Coverage |
|---|---|
| SRS-185 through SRS-194 | SWE6-SV-016 |
**SWE6-SV-017**: Verify integrated software behaviour for desktop Push-to-Talk against SRS-195 through SRS-203.
- Source SRS: SRS-195, SRS-196, SRS-197, SRS-198, SRS-199, SRS-200, SRS-201, SRS-202, SRS-203
- Verification method: Software verification test (end-to-end) on Windows, macOS, GNOME-on-Wayland reference hosts
- Pass criteria: PTT trait abstraction is platform-agnostic from the caller's perspective; reported capability level equals live runtime behaviour; Windows ladder degrades correctly when Raw Input is denied; macOS Global ↔ Focused toggles with permission state changes; Linux honours portal availability; mouse-side-button binding works where the backend exposes it; `capture_active` and `transmit_active` are independently observable; diagnostic export contains zero raw key data; missed-key-up watchdog clears `transmit_active` after the configured timeout.
- Evidence: SWE.6 test report
| SRS Range | SWE.6 Coverage |
|---|---|
| SRS-185 through SRS-194 | SWE6-SV-016 |
| SRS-195 through SRS-203 | SWE6-SV-017 |
## Baseline Candidate 0.9.1 Update
| Version | Date | Description |
|---|---|---|
| 0.9.1 | 2026-05-14 | Updated baseline after product decision closure: Apple App Store SDK gate uses Xcode 26+ and iOS 26 / iPadOS 26 SDK+ since 2026-04-28, platform baselines and decision traceability propagated across the document set. |
## Baseline Candidate 0.9.2 Update
| Version | Date | Description |
|---|---|---|
| 0.9.2 | 2026-05-14 | Corrected Apple App Store Connect upload gate to 2026-04-28 and checked full-package naming, references, and coverage. |
## Baseline Candidate 0.9.3 Update
| Version | Date | Description |
|---|---|---|
| 0.9.3 | 2026-05-15 | Added desktop PTT software-verification item SWE6-SV-017 covering end-to-end SRS-195..203 verification across Windows, macOS, and GNOME-on-Wayland Linux reference hosts. |
## 9. Android P0 Software Verification Addendum
**SWE6-SV-018**: Verify Android voice foreground service satisfies SRS-111.
- Source SRS: SRS-111
- Verification method: Device-level software-qualification test on API 28 floor device + mid-tier + current target (per `docs/verification/android-p0-acceptance.md` TC-3)
- Pass criteria: While connected to a voice channel the foreground service runs with `foregroundServiceType="microphone"` (API 30+) and a non-dismissible notification; mic capture and playback continue with the screen off for ≥ 10 minutes without being killed; service stops cleanly on disconnect.
- Evidence: Device test log + screen-recording
- Cross-link (added in 0.9.5): SRS-215 device-matrix expansion is covered by SWE6-SV-030. SWE6-SV-018 remains the single-device baseline; SWE6-SV-030 collects per-SDD-116-row evidence.
**SWE6-SV-019**: Verify RECORD_AUDIO request timing and listen-only fallback satisfy SRS-209.
- Source SRS: SRS-209
- Source SysRS: SysRS-306
- Verification method: Device-level test (android-p0-acceptance.md TC-2)
- Pass criteria: The mic permission is requested at the user gesture that first needs it (not at app launch); denial drops capture but keeps playback open (listen-only); permanently-denied state opens the system settings deep-link instead of re-prompting; re-grant re-enables capture without process restart.
- Evidence: Device test log
**SWE6-SV-020**: Verify Android in-call audio mode round-trip satisfies SRS-208.
- Source SRS: SRS-208
- Source SysRS: SysRS-305
- Verification method: Device-level test with `dumpsys audio` (android-p0-acceptance.md TC-6)
- Pass criteria: Pre-join mode is recorded; during voice session mode is `MODE_IN_COMMUNICATION`; on leave/disconnect prior mode is restored exactly. No permanent mode lock across multiple sessions. No crash if a Bluetooth headset is connected/disconnected mid-session (P0 minimum).
- Evidence: Device test log + dumpsys traces
**SWE6-SV-021**: Verify Android back navigation satisfies SRS-163.
- Source SRS: SRS-163
- Verification method: Device-level test (android-p0-acceptance.md TC-11 + TC-12) on API 28, an API ≤ 32 device, and an API 33+ device.
- Pass criteria: Modal-first close; one-pop-per-press; home-route back backgrounds; PTT-active ignores back. Predictive-back animation present on API 33+ and absent on pre-33 with identical end behaviour.
- Evidence: Device test log + screen-recording
**SWE6-SV-022**: Verify Android minimum API satisfies SRS-187 and DEC-004.
- Source SRS: SRS-187
- Source SysRS: SysRS-288
- Verification method: Manifest inspection + install attempt on API 28 device
- Pass criteria: Merged manifest declares `minSdkVersion = 28`. Install on API 28 succeeds. Install attempt on API 27 simulator/device is rejected with a clean error.
- Evidence: Manifest dump + install logs
**SWE6-SV-023**: Verify Android Play target API and AAB packaging satisfy SRS-119 and SRS-188.
- Source SRS: SRS-119, SRS-188
- Verification method: AAB inspection + Play Console upload to Internal Testing (android-p0-acceptance.md TC-13)
- Pass criteria: `targetSdkVersion` matches the currently-required Play target per SRS-188; release artifact is an AAB (not a bare APK) per SRS-119; Play Console accepts the bundle; signing key is the CI release key.
- Evidence: AAB dump + Play Console acceptance screenshot
**SWE6-SV-024**: Verify Android PTT capability badge reports `L0Focused` per SAD-086 / SDD-110.
- Source SRS: SRS-208 (mode boundary), SRS-209
- Verification method: Device-level UI test (android-p0-acceptance.md TC-4 + TC-10)
- Pass criteria: Capability badge shows `L0Focused` with Android-specific explanatory copy. No global hotkey is bound. Backgrounding the app stops PTT transitions from being delivered.
- Evidence: UI screenshot + log
| SRS Range | SWE.6 Coverage |
|---|---|
| SRS-111 | SWE6-SV-018 |
| SRS-119 | SWE6-SV-023 |
| SRS-163 | SWE6-SV-021 |
| SRS-187 | SWE6-SV-022 |
| SRS-188 | SWE6-SV-023 |
| SRS-208 | SWE6-SV-020, SWE6-SV-024 |
| SRS-209 | SWE6-SV-019, SWE6-SV-024 |
### Android voice audio backend qualification items (SRS-210..SRS-215, SDD-111..SDD-116 wave)
**SWE6-SV-025**: Verify SRS-210 mouth-to-ear latency target per device-class on the SDD-116 device matrix.
- Source SRS: SRS-210
- Source SDD: SDD-111, SDD-112, SDD-116
- Verification method: Glass-to-glass acoustic measurement on each SDD-116 matrix device (API 28, API 31, API 34; at least one Pixel + one Samsung). See "Glass-to-glass latency measurement procedure" below.
- Pass criteria: On devices where `achieved_performance_mode() == LowLatency` the measured mouth-to-ear latency is **≤ 150 ms** (median of 20 samples, 95th percentile ≤ 180 ms). On devices where the achieved mode is `None`/`PowerSaving` the measured latency is **≤ 250 ms** (median of 20 samples, 95th percentile ≤ 300 ms). Tier classification is derived from the SWE4-UV-051 unit-level mapping evaluated against the per-device `achieved_performance_mode` from SWE6-SV-026.
- Evidence: Per-device measurement log (CSV: device, API, achieved_mode, sample_index, latency_ms) + reference audio recording for spot-check + acceptance form rows TC-17, TC-18
- SDD-116 matrix rows satisfied: all (one row per device)
**SWE6-SV-026**: Verify SRS-211 input preset achieved value recorded per device.
- Source SRS: SRS-211
- Source SDD: SDD-112 (`achieved_input_preset()`), SDD-116
- Verification method: Device-level test reading the backend's diagnostics export after `voice_join`
- Pass criteria: For each SDD-116 matrix device the diagnostics record one of `{VoiceCommunication, VoicePerformance, Generic}` and that value is consistent with the fallback ladder verified by SWE4-UV-048. `VoiceCommunication` is preferred and any deviation must be accompanied by a recorded fallback rationale (e.g. preset rejected by OEM stack). No device reports an unrecorded/unknown preset.
- Evidence: Per-device diagnostics export
- SDD-116 matrix rows satisfied: all
**SWE6-SV-027**: Verify SRS-212 hardware AEC engagement (and NS/AGC where supported) per device.
- Source SRS: SRS-212
- Source SDD: SDD-113, SDD-116
- Verification method: Device-level test inspecting backend diagnostics + audible regression check (echo audibility) under speakerphone-routed playback
- Pass criteria: For each matrix device the diagnostics record per-effect engagement: `{aec: hw|sw|unavailable, ns: hw|sw|unavailable, agc: hw|sw|unavailable}`. Where `AcousticEchoCanceler.isAvailable()` returns true the recorded value MUST be `hw`. Where it returns false the recorded value MUST be `sw` (software fallback per SDD-113 item 5) — never silent. Audible echo evaluation (test sequence: 10 s of speakerphone playback with mic open at conversational distance) yields no operator-perceptible echo on at least one Pixel device and at least one Samsung device.
- Evidence: Per-device diagnostics export + audible-check operator note
- SDD-116 matrix rows satisfied: all (AEC/NS/AGC availability column)
**SWE6-SV-028**: Verify SRS-213 output usage and content type achieved per device.
- Source SRS: SRS-213
- Source SDD: SDD-112 (`achieved_output_usage`, `achieved_output_content_type`), SDD-116
- Verification method: Device-level test reading backend diagnostics; cross-checked with `dumpsys media.audio_flinger` where queryable
- Pass criteria: For each matrix device the diagnostics record `achieved_output_usage == VoiceCommunication` AND `achieved_output_content_type == Speech`. Where `dumpsys` exposes the stream attributes, the dump corroborates. Mismatch fails the case. Also verifies the in-call routing side-effect: volume slider during the session adjusts the call-volume stream, not media volume (operator check on at least one device).
- Evidence: Per-device diagnostics export + dumpsys excerpt + operator volume-routing note
- SDD-116 matrix rows satisfied: all
**SWE6-SV-029**: Verify SRS-214 sharing mode achieved per device.
- Source SRS: SRS-214
- Source SDD: SDD-112 (`achieved_sharing_mode`), SDD-116
- Verification method: Device-level test reading backend diagnostics on each matrix device
- Pass criteria: For each matrix device the diagnostics record `achieved_sharing_mode ∈ {Exclusive, Shared}`. The requested value is always `Exclusive`; deviation to `Shared` is acceptable and must be recorded with no operational regression (assert: no callback errors in the first 60 s of the session when the achieved mode is `Shared`). The aggregate device-matrix table in the test report cross-tabulates `requested vs achieved` per device.
- Evidence: Per-device diagnostics export
- SDD-116 matrix rows satisfied: all
**SWE6-SV-030**: Verify SRS-215 background mic capture under foreground-service-type=microphone per device.
- Source SRS: SRS-215
- Source SDD: SDD-107, SDD-114 (manifest declarations), SDD-115 (FGS-first sequencing), SDD-116
- Verification method: Device-level test (extends SWE6-SV-018 / android-p0-acceptance.md TC-3) executed on each SDD-116 matrix device
- Pass criteria: With the screen off (device locked or screen timed out) for ≥ 10 minutes the input stream remains open AND frames continue to be produced by the AAudio input (verified by a periodic per-minute frame-count probe in the diagnostics) on every matrix device. On API 34 devices the FGS-type is `microphone` (verified by `dumpsys activity services` excerpt). No `ForegroundServiceDidNotStartInTimeException` or `MissingForegroundServiceTypeException` in logcat. Cross-link annotation: this item extends SWE6-SV-018 with per-matrix-row evidence; SWE6-SV-018 remains the single-device baseline and SWE6-SV-030 is the device-matrix expansion.
- Evidence: Per-device test log + dumpsys excerpt + frame-count probe export
- SDD-116 matrix rows satisfied: all
| SRS Range | SWE.6 Coverage |
|---|---|
| SRS-210 | SWE6-SV-025 |
| SRS-211 | SWE6-SV-026 |
| SRS-212 | SWE6-SV-027 |
| SRS-213 | SWE6-SV-028 |
| SRS-214 | SWE6-SV-029 |
| SRS-215 | SWE6-SV-018 (single-device baseline), SWE6-SV-030 (device-matrix expansion) |
#### Glass-to-glass latency measurement procedure (SWE6-SV-025)
Mouth-to-ear (glass-to-glass) latency for SRS-210 shall be measured end-to-end across the Android device under test acting as the speaker AND a second Android device (or reference Linux host) acting as the listener, both joined to a controlled test TeamSpeak 3 server on the same LAN (no internet hop; LAN RTT must be measured and recorded separately, and subtracted as a baseline only if > 5 ms).
Measurement setup:
1. Connect the DUT and the reference listener device to the test server. The DUT is the talker; the reference is the listener.
2. Drive a short acoustic impulse (a 1 kHz tone burst, 20 ms duration, or a click track) into the DUT microphone from a controlled acoustic source (small loudspeaker at fixed distance, OR a wired line-in adapter where the DUT supports one).
3. Capture the listener device's loudspeaker output with a reference microphone connected to a stereo audio interface; the same audio interface ALSO captures the source impulse on the second channel via a hardwired tap from the impulse generator. Both channels are recorded into a single stereo WAV at ≥ 48 kHz, 16-bit minimum.
4. Latency is the time delta between the impulse-onset sample on channel A (source tap) and the impulse-onset sample on channel B (listener loudspeaker capture), expressed in milliseconds. Onset detection: first sample exceeding 10× the RMS noise floor of the preceding 100 ms.
5. Required precision: ≤ 1 ms (achievable at 48 kHz with single-sample onset detection). The measurement tool is unspecified; any DAW, `sox`, or custom script that meets the precision requirement is acceptable. The script and raw WAV files are part of the evidence and shall be archived.
6. Per-device run: 20 impulses spaced ≥ 2 s apart. Record median and 95th percentile.
7. Baseline subtraction: LAN one-way latency to the test server is measured (e.g. via ICMP / TS3 control-channel ping) and recorded but NOT subtracted from the SRS-210 result, because SRS-210's "mouth-to-ear" definition includes the network hop on a representative network. The LAN result is the ceiling case; WAN regression is out of scope for SRS-210 on this matrix.
8. Operator notes record: DUT model, Android API, `achieved_performance_mode`, `achieved_sharing_mode`, `achieved_input_preset`, headset/loudspeaker routing, ambient noise estimate, ambient temperature only if extreme.
This procedure is reproducible without proprietary fixtures. A loopback-cable-only variant (DUT line-out → DUT line-in on a single device with a wired headset adapter) is acceptable as a screening test but is NOT sufficient for SRS-210 sign-off because it does not exercise the network path.
## Baseline Candidate 0.9.4 Update
| Version | Date | Description |
|---|---|---|
| 0.9.4 | 2026-05-18 | Added Android P0 software-qualification items SWE6-SV-018..SWE6-SV-024 mapped to SRS-111, SRS-119, SRS-163, SRS-187, SRS-188, SRS-208, SRS-209; cross-linked to `docs/verification/android-p0-acceptance.md` test cases. |
| 0.9.5 | 2026-05-18 | Added Android voice audio backend software-qualification items SWE6-SV-025..SWE6-SV-030 covering SRS-210 (latency tier per SDD-116 matrix, with glass-to-glass measurement procedure), SRS-211 (input preset achieved), SRS-212 (hardware AEC engagement + software fallback evidence), SRS-213 (output usage/content type achieved), SRS-214 (sharing mode achieved), SRS-215 (FGS-hosted background capture per matrix row). Annotated SWE6-SV-018 with cross-link to SWE6-SV-030 (device-matrix expansion). |
@@ -1,303 +0,0 @@
# Chanora SYS.4 System Integration and Integration Verification Plan
**Document type:** System Integration and Integration Verification Plan
**Process alignment:** ASPICE SYS.4 System Integration and Integration Verification
**Version:** 0.9.2
**Status:** Baseline Candidate
**Language:** English
**Product:** Chanora
**Verification object:** Integrated application system elements
**Direct source layer:** SysDes
**Repo path:** `docs/verification/sys4-system-integration-verification-plan.md` ---
## 1. Purpose
This document defines the system integration and integration verification strategy for the Chanora application system. It verifies that the system elements defined by SysDes are integrated and interact according to the system architecture.
```text
SysRS -> SysDes -> SYS.4 System Integration Verification
```
SYS.4 verifies system architecture integration. It does not replace SWE.4, SWE.5, or SWE.6.
## 2. System Integration Scope
System elements include:
- User/operator interaction
- Client device hardware
- Operating system services
- Network environment
- External compatible voice server
- Chanora application container
- Flutter UI
- Rust Core
- Protocol adapter
- Audio subsystem
- Platform adapters
- Storage
- Secure storage
- Diagnostics
- Deployment environment
- Localization and UI/UX architecture elements
## 3. System Integration Strategy
| Integration step | System elements | Purpose |
|---|---|---|
| SYS4-INT-001 | Chanora app + OS permissions | Verify microphone, notification, secure storage, lifecycle, and permission integration |
| SYS4-INT-002 | Chanora app + audio hardware | Verify input/output device integration and route changes |
| SYS4-INT-003 | Chanora app + network environment | Verify connection behavior, reconnect, and failure presentation |
| SYS4-INT-004 | Chanora app + external compatible server | Verify channels, clients, voice, and text behavior with real or representative server |
| SYS4-INT-005 | Flutter UI + platform services | Verify safe areas, system bars, keyboard, and platform navigation behavior |
| SYS4-INT-006 | Rust Core + external protocol server | Verify protocol integration at system level |
| SYS4-INT-007 | Diagnostics + user export target | Verify diagnostic export workflow at system level |
| SYS4-INT-008 | Deployment artifact + target OS | Verify packaged application launches and uses required services |
| SYS4-INT-009 | Localization + server multilingual content | Verify multilingual product UI and server-provided content handling |
| SYS4-INT-010 | Full application system | Verify integrated behavior across supported platform classes |
## 4. System Integration Verification Measures
**SYS4-SIV-001**: Verify OS permission integration.
- Source SysDes: SysDes-048 through SysDes-058, SysDes-118
- Verification method: Platform system integration test
- Pass criteria: Required permissions, lifecycle behavior, safe areas, keyboard behavior, and platform navigation behavior integrate with the application.
- Evidence: System integration test result
**SYS4-SIV-002**: Verify audio hardware integration.
- Source SysDes: SysDes-059 through SysDes-074
- Verification method: Audio system integration test
- Pass criteria: Audio capture, playback, route change handling, and processing feature availability work with supported devices.
- Evidence: System integration test result
**SYS4-SIV-003**: Verify network environment integration.
- Source SysDes: SysDes-075 through SysDes-087
- Verification method: Network system integration test
- Pass criteria: Connection, recoverable network loss, reconnect, and user-safe errors work under defined network conditions.
- Evidence: System integration test result
**SYS4-SIV-004**: Verify external compatible server integration.
- Source SysDes: SysDes-088 through SysDes-101
- Verification method: Server system integration test
- Pass criteria: Server connection, channel list, client list, voice, text, and disconnect behavior integrate correctly.
- Evidence: System integration test result
**SYS4-SIV-005**: Verify application functional system integration.
- Source SysDes: SysDes-102 through SysDes-120
- Verification method: System integration test
- Pass criteria: User-visible workflows operate across UI, Rust Core, protocol, state, audio, storage, and diagnostics elements.
- Evidence: System integration test result
**SYS4-SIV-006**: Verify protocol system integration.
- Source SysDes: SysDes-121 through SysDes-128
- Verification method: System integration test with compatible server
- Pass criteria: Protocol adapter integrates with Rust Core and external compatible server without exposing protocol internals to UI.
- Evidence: System integration test result
**SYS4-SIV-007**: Verify state synchronization system integration.
- Source SysDes: SysDes-129 through SysDes-132
- Verification method: System integration test
- Pass criteria: Snapshot, delta, reconnect, event ordering, and UI update behavior operate together.
- Evidence: System integration test result
**SYS4-SIV-008**: Verify storage and security system integration.
- Source SysDes: SysDes-001 through SysDes-132
- Verification method: Security and system integration test
- Pass criteria: Bookmarks, settings, secure storage, redaction, and privacy constraints integrate correctly.
- Evidence: System integration test result, audit record
**SYS4-SIV-009**: Verify diagnostics and operations system integration.
- Source SysDes: SysDes-001 through SysDes-132
- Verification method: System integration test
- Pass criteria: Logs, redaction, diagnostics, export, and support workflow operate as an integrated capability.
- Evidence: System integration test result
**SYS4-SIV-010**: Verify non-functional system behavior.
- Source SysDes: SysDes-001 through SysDes-132
- Verification method: Performance and reliability system integration test
- Pass criteria: Responsiveness, audio latency, memory growth controls, reconnect, and malformed-event behavior meet defined expectations.
- Evidence: System integration test result
**SYS4-SIV-011**: Verify deployment and release environment integration.
- Source SysDes: SysDes-001 through SysDes-132
- Verification method: Deployment system integration test
- Pass criteria: Target platform packages install, launch, and access required OS services as expected.
- Evidence: Deployment integration result
**SYS4-SIV-012**: Verify interface integration.
- Source SysDes: SysDes-001 through SysDes-132
- Verification method: Interface system integration test
- Pass criteria: User, bridge, protocol, audio, storage, secure storage, network, and diagnostic interfaces integrate correctly.
- Evidence: Interface integration result
**SYS4-SIV-013**: Verify constraints and assumptions integration impact.
- Source SysDes: SysDes-001 through SysDes-132
- Verification method: Review, inspection, system integration test
- Pass criteria: Constraints and assumptions are respected or deviations are documented.
- Evidence: Review record, system integration test result
**SYS4-SIV-014**: Verify UI/UX, Material 3, platform, and i18n system integration.
- Source SysDes: SysDes-111 through SysDes-132
- Verification method: System integration test
- Pass criteria: Material 3 design system, adaptive shell, accessibility, platform behavior, localization, Unicode, diagnostics localization, and traceability controls integrate correctly.
- Evidence: System integration test result
## 5. Regression Strategy
Regression selection shall consider:
- changed system element
- changed software release
- changed platform adapter
- changed audio hardware support
- changed compatible server behavior
- changed network behavior
- changed deployment package
- changed localization or Unicode handling
- changed diagnostics export behavior
- changed system architecture item
## 6. Traceability Matrix
| SysDes Range | SYS.4 Coverage |
|---|---|
| SysDes-001 through SysDes-034 | SYS4-SIV-005, SYS4-SIV-006, SYS4-SIV-007 |
| SysDes-035 through SysDes-058 | SYS4-SIV-001 |
| SysDes-059 through SysDes-074 | SYS4-SIV-002 |
| SysDes-075 through SysDes-087 | SYS4-SIV-003 |
| SysDes-088 through SysDes-101 | SYS4-SIV-004 |
| SysDes-102 through SysDes-110 | SYS4-SIV-005 |
| SysDes-111 through SysDes-132 | SYS4-SIV-014 |
| SysDes-121 through SysDes-128 | SYS4-SIV-006 |
| SysDes-129 through SysDes-132 | SYS4-SIV-007 |
| SysDes-above-current-baseline and above | Not applicable; no such IDs exist in current SysDes baseline. |
## 7. Result Reporting
System integration verification results shall be summarized in a system integration verification summary report including:
- release scope
- integrated system elements
- integration sequence
- selected verification measures
- regression scope
- platform/environment used
- pass/fail status
- nonconformances
- interface issues
- dynamic behavior issues
- residual risks
- release recommendation
## 8. Change History
| Version | Date | Description |
|---|---|---|
| 0.1.0 | 2026-05-14 | Initial SYS.4 system integration and integration verification plan derived from SysDes v0.6. |
---
## 9. Platform Decision System Integration Verification Addendum
**SYS4-SIV-015**: Verify platform baseline, Apple SDK gate, Android target gate, MVP single connection, audio defaults, storage, bridge, and diagnostics privacy system integration.
- Source SysDes: SysDes-133 through SysDes-141
- Verification method: System integration test, release inspection, security/privacy review
- Pass criteria: System-level integration reflects accepted product decisions and release gates.
- Evidence: System integration result, release readiness evidence
| SysDes Range | SYS.4 Coverage |
|---|---|
| SysDes-133 through SysDes-141 | SYS4-SIV-015 |
**SYS4-SIV-016**: Verify integrated system-level desktop Push-to-Talk behaviour against SysDes-142 through SysDes-148.
- Source SysDes: SysDes-142, SysDes-143, SysDes-144, SysDes-145, SysDes-146, SysDes-147, SysDes-148
- Verification method: System integration verification on Windows, macOS, GNOME-on-Wayland reference hosts
- Pass criteria: Platform PTT Backend layer is the only surface generating PTT state transitions; `PttCapabilityLevel` and `backend_id` propagate from backend through Rust Core through bridge to Flutter UI without modification; `transmit_active` and `capture_active` are observably independent at the system boundary; diagnostics sanitizer rejects raw key payloads; UI capability badge matches the active capability; release readiness record carries the live-measurement evidence per RR-PTT-001..008.
- Evidence: SYS.4 verification report
| SysDes Range | SYS.4 Coverage |
|---|---|
| SysDes-133 through SysDes-141 | SYS4-SIV-015 |
| SysDes-142 through SysDes-148 | SYS4-SIV-016 |
## Baseline Candidate 0.9.1 Update
| Version | Date | Description |
|---|---|---|
| 0.9.1 | 2026-05-14 | Updated baseline after product decision closure: Apple App Store SDK gate uses Xcode 26+ and iOS 26 / iPadOS 26 SDK+ since 2026-04-28, platform baselines and decision traceability propagated across the document set. |
## Baseline Candidate 0.9.2 Update
| Version | Date | Description |
|---|---|---|
| 0.9.2 | 2026-05-14 | Corrected Apple App Store Connect upload gate to 2026-04-28 and checked full-package naming, references, and coverage. |
## Baseline Candidate 0.9.3 Update
| Version | Date | Description |
|---|---|---|
| 0.9.3 | 2026-05-15 | Added desktop PTT system-integration verification item SYS4-SIV-016 covering SysDes-142..148 across Windows, macOS, and GNOME-on-Wayland Linux reference hosts. |
## 10. Android P0 System Integration Verification Addendum
SYS4-SIV-015 (Platform Decision gate) is strengthened to require the
Android-specific evidence below. Two new SYS.4 entries are added.
**SYS4-SIV-015 (strengthened — Android target gate)**: In addition to
the original platform-baseline checks, the Android target gate now
requires:
- A boot + connect + voice-channel run on a physical Android 9 (API 28) device per DEC-004 / SysRS-288.
- AAB upload acceptance on the Play Console Internal Testing track (or `bundletool` equivalent) per SRS-119 / SRS-188.
- Foreground service surviving lock-screen for ≥ 10 minutes per SRS-111.
- AudioManager mode snapshot/restore verified via `dumpsys audio` per SRS-208 / SysRS-305.
- Evidence: System integration result + Play Console acceptance record + dumpsys traces.
**SYS4-SIV-017**: Verify Android in-call audio mode round-trip with Bluetooth SCO route awareness.
- Source SysDes: SysDes-133 through SysDes-148 (Android-specific subset), SysRS-305
- Source SRS: SRS-208
- Verification method: System integration test on a physical Android device with at least one Bluetooth headset
- Pass criteria: Pre-session `AudioManager.mode` is captured; voice session sets `MODE_IN_COMMUNICATION`; disconnect restores the prior mode. Bluetooth headset connect/disconnect during a session does not crash the app and does not lock the audio route permanently (P0 minimum). Full SCO-route correctness is logged as a P1 follow-up.
- Evidence: Device test log + dumpsys + Bluetooth-event log
**SYS4-SIV-018**: Verify Android AAB install via Play Internal Track or bundletool.
- Source SysDes: SysDes-133 through SysDes-148 (Android-specific subset)
- Source SRS: SRS-119, SRS-187, SRS-188
- Verification method: Release-pipeline integration test
- Pass criteria: The exact AAB produced by the release CI job is accepted by Play Console Internal Testing (or by `bundletool install-apks --connected-device`). The installed app boots on an API 28 device and a current-target-API device. Signing key fingerprint matches the CI release-key fingerprint. ABI splits exist for `{arm64-v8a, armeabi-v7a, x86_64}`.
- Evidence: Play Console acceptance screenshot + bundletool report + adb install log
| SysDes Range | SYS.4 Coverage |
|---|---|
| SysDes-133 through SysDes-148 (Android subset) | SYS4-SIV-015 (strengthened), SYS4-SIV-017, SYS4-SIV-018 |
## Baseline Candidate 0.9.4 Update
| Version | Date | Description |
|---|---|---|
| 0.9.4 | 2026-05-18 | Strengthened SYS4-SIV-015 Android target gate (API 28 boot, FGS lock-screen longevity, AudioManager round-trip, Play AAB acceptance) and added SYS4-SIV-017 (in-call mode + Bluetooth SCO awareness) and SYS4-SIV-018 (AAB install via Play Internal Track / bundletool). |
@@ -1,83 +0,0 @@
# Chanora Verification Master Plan
**Document type:** Verification Master Plan
**Version:** 0.9.2
**Status:** Baseline Candidate
**Language:** English
**Product:** Chanora
**Repo path:** `docs/verification/verification-master-plan.md` ---
## 1. Purpose
This document defines how Chanora verification documents relate to the engineering hierarchy.
Core engineering hierarchy:
```text
SysRS -> SysDes -> SRS -> SAD -> SDD
```
Verification work products are downstream evidence-producing artifacts and do not replace the hierarchy.
## 2. Verification Layers
| Process | Verification target | Direct verification source | Purpose |
|---|---|---|---|
| SWE.4 | Software units | SDD | Verify software units against detailed design |
| SWE.5 | Integrated software elements | SAD and SDD | Verify software architecture integration, interfaces, dynamic behavior, and component interaction |
| SWE.6 | Fully integrated software | SRS | Verify integrated software satisfies software requirements |
| SYS.4 | Integrated application system elements | SysDes | Verify system elements integrate according to system architecture |
## 3. Verification Flow
```text
SDD -> SWE.4 Unit Verification
SAD + SDD -> SWE.5 Software Integration Verification
SRS -> SWE.6 Software Verification
SysDes -> SYS.4 System Integration Verification
```
## 4. Regression Rule
A change in any design or requirement artifact shall trigger impact analysis for the corresponding verification layer:
| Changed artifact | Verification impact |
|---|---|
| SDD | SWE.4 and SWE.5 impact analysis |
| SAD | SDD, SWE.5, and possibly SWE.4 impact analysis |
| SRS | SAD, SDD, SWE.6, and related lower-level verification impact analysis |
| SysDes | SRS/SAD/SDD impact analysis and SYS.4 impact analysis |
| SysRS | SysDes/SRS/SAD/SDD impact analysis and system/software verification impact analysis |
## 5. Required Verification Reports
| Report | Source |
|---|---|
| Unit Verification Summary Report | SWE.4 |
| Software Integration Verification Summary Report | SWE.5 |
| Software Verification Summary Report | SWE.6 |
| System Integration Verification Summary Report | SYS.4 |
| Regression Verification Report | Any affected verification layer |
| Problem Resolution Report | Failed verification requiring defect handling |
## 6. Change History
| Version | Date | Description |
|---|---|---|
| 0.1.0 | 2026-05-14 | Initial verification master plan covering SWE.4, SWE.5, SWE.6, and SYS.4. |
## Baseline Candidate 0.9.1 Update
| Version | Date | Description |
|---|---|---|
| 0.9.1 | 2026-05-14 | Updated baseline after product decision closure: Apple App Store SDK gate uses Xcode 26+ and iOS 26 / iPadOS 26 SDK+ since 2026-04-28, platform baselines and decision traceability propagated across the document set. |
## Baseline Candidate 0.9.2 Update
| Version | Date | Description |
|---|---|---|
| 0.9.2 | 2026-05-14 | Corrected Apple App Store Connect upload gate to 2026-04-28 and checked full-package naming, references, and coverage. |
-246
View File
@@ -1,246 +0,0 @@
# Windows P0 acceptance — human-must test cases (v1.0.0-rc.8)
This document captures the human-side verification pass for the
Windows P0 ship of Chanora. Automated tests (cargo + flutter
analyze + windows-smoke.cmd) sign off the mechanical side; this
checklist signs off the user-experience side. A failure on any
row blocks the rc.8 tag.
Source: this checklist supersedes the inline plan in the
conversation that produced the v1.0.0-rc.8 baseline. The auto-test
sign-off lives in this document's "Auto-test sign-off" section.
## 0. Pre-flight
| # | Step | Expected |
|---|---|---|
| 0.1 | RDP into `100.84.219.45` as `admin` / `jkorisno1` | Desktop visible |
| 0.2 | Wipe stale install: delete `%LOCALAPPDATA%\app.chanora\` and `%APPDATA%\app.chanora\` if they exist | First-launch from clean state |
| 0.3 | Launch `C:\Users\admin\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\chanora_flutter.exe` | Window opens, no crash |
| 0.4 | Open About / version label | Shows **`v1.0.0-rc.8`** (not rc.1) |
| 0.5 | Open `C:\Users\admin\AppData\Local\app.chanora\chanora_flutter\logs\chanora.log` | Contains `bridge initialised`, `log file path …`, `identity store initialised`, `bookmark store initialised`. File size <1 MB. |
## TC-1: Connect + identity persistence (DEC-006, SRS-201)
| # | Step | Expected |
|---|---|---|
| 1.1 | Enter `cn.teamspeak.app` (or your own test server), nickname, blank password | "Connect" button enabled |
| 1.2 | Click Connect | Server snapshot appears: channel tree on left, client list visible |
| 1.3 | Note the connected server name + the channel tree | Tree is sorted (parents above children); no duplicate roots |
| 1.4 | Disconnect, reconnect with the same nickname | Server-visible UID is the same as previous session (identity persisted) |
| 1.5 | Disconnect | Returns to connect form, no error |
## TC-2: Voice channel join (SRS-204, SDD-094)
| # | Step | Expected |
|---|---|---|
| 2.1 | After connecting, observe the UI | **No "Start audio" button anywhere** |
| 2.2 | Click a non-default voice channel in the tree | Voice Bar widget appears at top |
| 2.3 | Voice Bar shows: channel-name pill on left, speaker-mute + mic-mute on right, mode badge ("PTT"), capability badge — expected `L2GlobalHoldToTalk (raw-input)` or `L3GlobalWithMouseButtons (raw-input)` after a successful Raw Input register | All five visible; capability NOT `L0Focused` on this Win 11 host |
| 2.4 | Tap a different voice channel | Voice Bar's channel-name pill updates to the new channel; other clients see us move |
| 2.5 | There is **no** "Leave voice" button | TS3 model: you are always in *some* channel |
## TC-3: PTT mode press / release (SDD-088, SDD-096, SAD-083)
| # | Step | Expected |
|---|---|---|
| 3.1 | In a voice channel, click the gear "Voice settings" | Dialog opens with three mode radio rows (PTT selected, Voice activity disabled "Coming soon"), bind-key button, release-tail slider at 200 ms |
| 3.2 | Click "Bind PTT key", press Space, observe Captured value, click Save | "Captured: **Space**" (not blank); after Save the Voice Bar's PTT line reads **"PTT: Space"** + release tail value |
| 3.3 | Press and hold Space, watch the stats line | Within ~100 ms: **"Mic on"** appears; level meter goes active |
| 3.4 | Release Space | After ~200 ms: **"Mic off"** appears; level meter returns to idle |
| 3.5 | Repeat 3.33.4 ten times rapidly | Each press toggles Mic on/off; no stuck-on state |
| 3.6 | Hold Space for 60 seconds continuously while saying something | **Mic stays on the entire time** (watchdog is disabled per DEC-031; no mid-sentence cut-off) |
| 3.7 | Alt-tab to another application, press Space (no mouse focus on chanora) | Mic on toggles even though chanora is not focused (verifies L2/L3 global path); other clients hear you |
## TC-4: Release tail (SDD-096, SRS-206)
| # | Step | Expected |
|---|---|---|
| 4.1 | Open Voice settings, slide release tail to 0 ms, save | Slider shows "0 ms" |
| 4.2 | Hold Space briefly then release | "Mic off" appears within ~100 ms of release |
| 4.3 | Set release tail to 500 ms, save | Slider shows "500 ms" |
| 4.4 | Hold Space briefly then release | "Mic on" persists for ~500 ms after release, then "Mic off" |
| 4.5 | Set release tail back to 200 ms | Slider shows "200 ms" |
## TC-5: Continuous mode (SAD-083, SRS-205, regression: DEC-031)
| # | Step | Expected |
|---|---|---|
| 5.1 | Open Voice settings → select "Continuous" radio → Save | Mode badge reads "Continuous"; **no "PTT: …" line**; **no release-tail line**; **no capability badge** (all PTT-only surfaces hidden) |
| 5.2 | Observe the stats line | "Mic on" immediately and **stays on** indefinitely |
| 5.3 | Wait 60 seconds continuously | **Mic stays on** the whole time (the old bug — watchdog disabling Continuous after 30 s — must not reproduce) |
| 5.4 | Switch back to PTT mode | Mode badge reads "PTT: Space"; PTT-only lines reappear; Mic off (no key held) |
## TC-6: Mic mute toggle (SRS-207, regression: log spam)
| # | Step | Expected |
|---|---|---|
| 6.1 | In Continuous mode, click the mic-mute icon | Icon toggles to crossed-out; Mic shows off immediately |
| 6.2 | Have a second client check our status | Other client sees a microphone-off icon next to your nickname |
| 6.3 | Wait 30 seconds while muted, then check log file size | `chanora.log` stays <500 KB (no "Sending audio while muted" flood) |
| 6.4 | Click mic-mute again to unmute | Icon clears; Mic returns to on |
| 6.5 | In PTT mode with key held, click mic-mute | Mic immediately goes off mid-press; key release is ignored as long as muted |
| 6.6 | Unmute, key still held | Mic returns to on |
## TC-7: Speaker mute (server-broadcast)
| # | Step | Expected |
|---|---|---|
| 7.1 | Have a second client talk to you | You hear them |
| 7.2 | Click speaker-mute (headset icon) | Icon toggles; their voice is silent |
| 7.3 | Other client checks our status | They see a headset-off icon next to our nickname |
| 7.4 | Click speaker-mute again | Voice returns |
## TC-8: Binding persistence across restart (SDD-095)
| # | Step | Expected |
|---|---|---|
| 8.1 | Bind PTT to F10 → Save | Voice Bar shows "PTT: F10" |
| 8.2 | Close the app entirely | Window dismissed |
| 8.3 | Relaunch | Connect form appears |
| 8.4 | Connect + join a voice channel | Voice Bar shows **"PTT: F10"** (binding survived restart) |
| 8.5 | Press F10 | Mic on |
## TC-9: Binding before voice-join (SDD-094 follow-up)
| # | Step | Expected |
|---|---|---|
| 9.1 | Wipe storage again (steps 0.2) | Clean state |
| 9.2 | Launch, connect, but do not join a voice channel yet | Channel tree visible |
| 9.3 | Open Voice settings, bind a key, Save | No `audio not started` error |
| 9.4 | Join a voice channel | Voice Bar reflects the binding |
## TC-10: No-permission channel rejection (issue 3 from rc.7 review)
| # | Step | Expected |
|---|---|---|
| 10.1 | Identify a channel where your account lacks join permission | Server tree shows it as locked or similar |
| 10.2 | Tap it | After ~1.5 s, **red error banner** appears: "channel move rejected by server (no permission, wrong password, or channel full)" |
| 10.3 | Voice Bar's channel-name pill does **not** update | We did NOT join the channel locally |
| 10.4 | Other clients confirm we are NOT in the requested channel | Server-side reality matches the error banner |
## TC-11: Reconnect (real disconnect, not transient blip)
| # | Step | Expected |
|---|---|---|
| 11.1 | Connect, join a voice channel | Voice Bar visible |
| 11.2 | Force a hard disconnect (server-side kick, firewall block, or `nm-cli con down` on Linux RDP host) | UI shows red "Connection lost" banner with a spinning indicator and reconnect attempt counter |
| 11.3 | Restore network | Reconnect succeeds; voice channel rejoined automatically; banner disappears |
## TC-12: Diagnostic export + privacy (DEC-027)
| # | Step | Expected |
|---|---|---|
| 12.1 | Trigger a settings change (e.g. toggle mode) | Activity in log |
| 12.2 | Trigger the diagnostic export (if a button exists) or copy `%LOCALAPPDATA%\app.chanora\chanora_flutter\logs\chanora.log` | Multi-line text |
| 12.3 | Read it — privacy invariant | Contains `capability_level`, `backend_id`, `bound_input_class` (`keyboard` / `mouse-side-button`); **does NOT** contain any `vk`, `scan_code`, `keysym`, the actual key label, or any timing sequence of presses |
| 12.4 | File size at end of session | <5 MB (rotation: previous session is at `chanora.log.1`; the one before at `chanora.log.2`) |
## TC-13: PTT bind dialog labels (regression)
| # | Step | Expected |
|---|---|---|
| 13.1 | Open binding dialog | Initial state: "Captured: " is blank or shows a placeholder |
| 13.2 | Press Space | "Captured: **Space**" (not blank — the rc.7 regression) |
| 13.3 | Press Enter | "Captured: Enter" |
| 13.4 | Press Tab | "Captured: Tab" |
| 13.5 | Press A | "Captured: A" |
| 13.6 | Press F12 | "Captured: F12" |
| 13.7 | Press Shift alone | Captured field unchanged (modifier-only press ignored) |
| 13.8 | Press a mouse side button (Mouse4 / Back) | "Captured: mouse-side-button:8" |
## TC-14: Capability badge gating
| # | Step | Expected |
|---|---|---|
| 14.1 | In PTT mode | Capability badge visible (`L2GlobalHoldToTalk (raw-input)` or similar) |
| 14.2 | Switch to Continuous mode | **Capability badge disappears** (no info-icon, no level label) |
| 14.3 | Switch to "Voice activity (coming soon)" | Cannot select (disabled); confirms the radio row is non-interactive |
## TC-15: Indicator label (regression)
| # | Step | Expected |
|---|---|---|
| 15.1 | In any mode, look at the stats line | Reads "Mic on" / "Mic off" (mode-neutral); **not** "PTT on" / "PTT off" |
## Auto-test sign-off
These rows do not require human interaction. The values are filled
in from CI / local invocation.
| Layer | Result on Linux | Result on Korean Win 11 |
|---|---|---|
| `cargo check --workspace` | clean | clean |
| `cargo test --workspace --lib` | 80 / 0 / 3 | 126 / 0 / 1 |
| `flutter analyze` | clean (6 pre-existing Radio.groupValue infos) | n/a |
| `python3 tools/validate_docs.py` | strict layered sourcing OK; pre-existing 35-filename warning | n/a |
| `tools/windows-smoke.cmd` | n/a | exit 0 |
The Windows host runs 44 more tests than Linux because of the
`#[cfg(target_os = "windows")]`-gated PTT-backend, dispatcher, and
full-chain tests in `crates/chanora_audio/src/ptt_backends/windows.rs`
and `core/chanora_core/src/ptt.rs::windows_full_chain_tests`.
## Sign-off form
```
chanora v1.0.0-rc.8 — Windows P0 acceptance
Host: Korean Win 11 (HP LAPTOP-37KAQO05, ko-KR locale)
Tester: ______________________
Date: ______________________
Build: chanora_flutter.exe @ ______________________
chanora_bridge.dll @ ______________________
data\app.so @ ______________________
TC-1 Connect + identity persistence [ pass / fail ] notes:
TC-2 Voice channel join [ pass / fail ] notes:
TC-3 PTT press / release [ 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 Binding persistence across restart [ pass / fail ] notes:
TC-9 Binding before voice-join [ pass / fail ] notes:
TC-10 No-permission channel rejection [ pass / fail ] notes:
TC-11 Reconnect [ pass / fail ] notes:
TC-12 Diagnostic export + privacy [ pass / fail ] notes:
TC-13 PTT bind dialog labels [ pass / fail ] notes:
TC-14 Capability badge gating [ pass / fail ] notes:
TC-15 Indicator label [ pass / fail ] notes:
Overall result: [ rc.8 PASS / rc.8 FAIL ]
Sign: ______________________
```
## Known gaps (not blockers)
- **macOS / iOS / Linux** P0 acceptance is NOT covered here; this
document is Windows-only. Tagging rc.8 ships Linux from a separate
pass and remains Windows-gated for macOS / iOS / Android.
- **VAD** (`TransmitMode::VoiceActivity`) is intentionally disabled
per DEC-030 (deferred to P1). UI shows it as a "Coming soon" radio
row.
- **Missed-key-up watchdog** is disabled on P0 per DEC-031 (long PTT
speech would have been cut off at 30 s). P1 reintroduces a
redesigned variant.
- **Audio level meter** is binary (`pttActive` only). Real RMS meter
is a P1 follow-up.