15 KiB
15 KiB
P0 MVP Agent Spec — Build This First
Objective
Create the smallest complete Chanora MVP that can connect to one TS3-compatible server, render channel/user state, support basic voice workflow, persist local non-secret settings, protect secrets, and pass platform P0 acceptance gates.
Scope
Implement only P0/MVP. Treat P1/P2 items as backlog unless the user explicitly requests them.
Accepted MVP Decisions
- DEC-001: Release type = Internal Alpha first, then External Beta, then MVP Public / Store Release
- DEC-002: MVP platform scope = MVP target remains Windows, macOS, Linux, Android, and iOS; first release may be staged by channel/platform
- DEC-003: Minimum iOS version = iOS 13 minimum for Flutter support baseline; test latest iOS release separately
- DEC-004: Minimum Android version = Android API 28 (Android 9.0) minimum, raised from the original recommendation of API 24 by explicit owner ruling on 2026-05-14. Rationale: simplifies the audio path (AAudio is unconditionally available from API 26+ and stable from API 28), narrows the TLS / privacy / scoped-storage compatibility surface, and matches typical 2026 Android baselines. The original cpal-on-Oboe Android spike was built with
minSdk = 24andcargo-ndk -P 26; product code inapps/chanora_flutterusesminSdk = 28and the Android product audio backend is now direct Oboe, so Chanora no longer carries the API 24/25 OpenSL ES fallback branch. - DEC-005: Android target SDK = Target the Google Play-required API level on the upload date; current release gate uses API 35+ unless newer Google policy applies
- DEC-006: Multiple server connections in MVP = Not in MVP; support one active server connection per client instance
- DEC-007: AEC default state = Enabled by default on platforms/audio backends where supported and stable
- DEC-008: AGC default state = Enabled by default, with user setting to disable
- DEC-009: Noise suppression default state = Enabled by default, with user setting to disable
- DEC-010: High-pass filter default state = Enabled by default
- DEC-011: Audio processing implementation path = Use platform-native audio processing first where available; use Rust/WebRTC-style processing as controlled fallback or later architecture option
- DEC-011.1: Audio crate choice =
cpalfor desktop (empirically verified on Linux/PipeWire bypoc/audio-capture-playback-spikeon 2026-05-13) and directoboe-rsfor Android product code. The earlier cpal-on-Oboe spike remains evidence that Rust audio reaches Oboe/AAudio on a Motorola Moto G Stylus 5G (2023) running Android 14 arm64-v8a, but product code chooses direct Oboe because P0 Android needs explicitVoiceCommunicationinput preset,VoiceCommunication/Speechoutput attributes, low-latency/exclusive stream requests with shared fallback, audio-session ids for hardware AEC/NS/AGC binding, and diagnostics of requested/achieved stream values. iOS crate TBD pending an iOS spike that requires macOS + Xcode hardware - DEC-012: Official SDK / trademark / licensing review = Public/store release is blocked until legal confirms TeamSpeak non-affiliation wording, trademark usage, OSS licenses, and
tsclientliblicense posture - DEC-013: Local database choice = Use SQLite or equivalent embedded local database for non-secret local state; secrets remain in platform secure storage
- DEC-013.1: SQLite crate =
rusqlitewith thebundledfeature (SQLite statically linked into the binary; no system libsqlite3 dependency); verified bypoc/sqlite-storage-spikeon 2026-05-13 - DEC-013.2: Linux secure-storage backend policy = Prefer Secret Service (libsecret / gnome-keyring / kwallet / KeePassXC) on Linux; if the default collection is locked or D-Bus is unavailable, fall back to kernel keyutils with a clear user notice. Both backends are "equivalent" per SysRS-053 / SysRS-162; verified by
poc/secure-storage-spikeon 2026-05-13 - DEC-014: Bridge choice = Use a stable typed Flutter/Rust bridge with generated or schema-controlled DTOs;
flutter_rust_bridge2.x pinned (empirically verified at 2.12.0 bypoc/flutter_rust_bridge_helloon 2026-05-13) - DEC-015: Product language for MVP = English + Chinese (Simplified) for MVP, raised from the original recommendation of English-only by explicit owner ruling on 2026-05-14. Rationale: the demonstrated test-server population (verified live against
cn.teamspeak.app) and broader TS3 audience include substantial Chinese-speaking users; shipping zh-Hans alongside en at MVP avoids a launch-window UX gap. Architecture remains i18n-ready so additional languages can be added later mechanically. Server-provided content is preserved verbatim and never translated (ADR-008 UTF-8 boundary, DEC-015 server-content rule retained). - DEC-016: Diagnostics upload policy = No automatic upload for MVP; user-initiated local diagnostic export only
- DEC-017: Crash reporting = Disabled for MVP unless explicit opt-in provider and privacy policy are approved
- DEC-018: Public product name = Chanora
- DEC-021: Apple App Store submission SDK = Use Xcode 26 or later and the iOS 26 / iPadOS 26 SDK or later for App Store submission on or after 2026-04-28, unless Apple publishes a newer applicable requirement before upload
- DEC-020: License model = Dual-licensed under Apache-2.0 OR MIT (recipient's choice), the standard Rust-ecosystem permissive license model. Accepted on 2026-05-14. Compatible with every direct dependency in the PoC tree (
tsclientlibMIT-OR-Apache-2.0,flutter_rust_bridgeMIT,cpalApache-2.0,rusqliteMIT,keyringMIT-OR-Apache-2.0, etc.) and with the Flutter framework's BSD-3-Clause. The license texts ship asLICENSE-APACHEandLICENSE-MITat the repository root; an aggregatorLICENSEpoints to both.NOTICEenumerates current direct-dependency attributions. The full OSS legal review (transitive deps, license obligations, OSS notices) remains under DEC-012 and is still required before public release. - DEC-022: Canonical implementation directory layout = Accept the README's sketch as canonical:
apps/chanora_flutter/,core/chanora_core/,crates/chanora_protocol/,crates/chanora_audio/,crates/chanora_state/,crates/chanora_storage/,crates/chanora_diagnostics/,crates/chanora_bridge/. Matches SAD §7.2 module decomposition - DEC-023: Windows desktop Global PTT priority = P0 in MVP. Resolves PTT-OPEN-001 from
gen2/chanora-desktop-ptt-review-summary-v0.9.2.md. The Windows backend ladder (Raw Input → low-level keyboard hook → Focused fallback) is mandatory for the first public release; release notes shall not claim Global PTT support on Windows until live measurement confirms a Global level from a non-fallback rung. - DEC-024: macOS desktop Global PTT priority = P0 in MVP, with explicit permission UX flow. Resolves PTT-OPEN-002. The macOS backend shall request the Input Monitoring / Accessibility permission, accept user denial gracefully (continue at
L0Focusedwithout functional regression), and upgrade to Global asynchronously when the user grants the permission. - DEC-025: Officially-tested Linux environment for first public release = GNOME on Wayland (only). Resolves PTT-OPEN-003. The Linux backend shall use the freedesktop
org.freedesktop.portal.GlobalShortcutsinterface on GNOME-on-Wayland and fall back to Focused PTT on every other Linux environment (X11, sway, KDE, untested compositors). Release notes shall not claim Global PTT support outside the tested compositor. - DEC-026: Mouse side-button support in first desktop PTT release = Supported on Windows and macOS; Linux follows whatever the GlobalShortcuts portal exposes. Resolves PTT-OPEN-004. The Raw Input backend (Windows) and the Event Tap backend (macOS) shall accept Mouse4 / Mouse5 bindings; the Linux portal binding accepts whatever the session exposes and the release notes shall not over-claim.
- DEC-027: PTT diagnostics privacy posture = Capability and availability state only — no raw key codes ever leave the device. Resolves PTT-OPEN-005. The diagnostic export shall name only
PttCapabilityLevel,backend_id,bound_input_class, andfallback_exercised; the user's actual key value (scan code, virtual key, keysym) shall never be logged, persisted, or exported.PttSanitizerenforces this at write time. - DEC-028: Missed-key-up watchdog requirement = P0. Resolves PTT-OPEN-006. The audio engine shall include a missed-key-up watchdog that clears
transmit_activeafter a configured ceiling (default 30 s) when no key-up event arrives. The watchdog is a P0 release-gate item rather than an implementation-level concern because the failure mode (stuck transmission after the user has released the binding) is user-visible and reputation-relevant. - DEC-029: Flutter global-hotkey packages rejected for PTT = Use the native Rust
DesktopPttBackendtrait + per-OS implementations (already specified in SDD-083 / SDD-084 / SDD-085); do not adopthotkey_manager,super_hot_key, or any equivalent pub.dev package for PTT. Rationale: those packages wrap the OSRegisterHotKey/RegisterEventHotKeysemantics — they consume the key (suppressing it from other applications), they do not deliver a key-up event, and they do not support mouse side-buttons. PTT requires the opposite primitive (observe, do not consume, deliver both up and down). - DEC-030: Voice activity detection deferred to P1 =
TransmitMode::VoiceActivityships as a reserved enum variant with no v1 implementation. The settings UI shows it as a disabled "coming soon" option. The actual implementation choice is deferred to a future baseline. Rationale: three viable backends were compared (RMS energy threshold — trivial but quality-poor; WebRTC VAD via thewebrtc-vadcrate — frozen-but-stable C++ BSD-3 dep, ~200 KB binary, industry baseline; Silero VAD via ONNX Runtime — best quality but ~8–16 MB binary uplift per platform plus an ONNX-runtime dependency surface). The v1 dependency-surface budget does not have room for the trade-off review required to pick correctly. Choosing too early risks either user-visible quality regression (RMS) or a forced ONNX adoption (Silero) before there is a comparable need for ML inference elsewhere (noise suppression, AEC). - DEC-001: Product Owner = Release type sequence
- DEC-002: Product Owner / Engineering = MVP platform strategy
- DEC-003: Product Owner / iOS Owner = Minimum iOS version
- DEC-004: Product Owner / Android Owner = Minimum Android version
- DEC-005: Android Owner / Release Manager = Android target SDK
- DEC-006: Product Owner / Software Architect = Multiple server connections in MVP
- DEC-007: Audio Owner / Product Owner = AEC default
- DEC-008: Audio Owner / Product Owner = AGC default
- DEC-009: Audio Owner / Product Owner = Noise suppression default
- DEC-010: Audio Owner = High-pass filter default
- DEC-011: Software Architect / Audio Owner = Audio processing path
- DEC-011.1: Software Architect / Audio Owner = Audio crate (desktop / mobile)
- DEC-012: Legal / Compliance = SDK/trademark/licensing review
- DEC-013: Software Architect / Storage Owner = Local database
- DEC-013.1: Software Architect / Storage Owner = SQLite crate
- DEC-013.2: Software Architect / Storage Owner / Security Reviewer = Linux secure-storage backend policy
- DEC-014: Software Architect = Bridge choice
- DEC-015: Product Owner = Product language for MVP
- DEC-016: Product Owner / Legal / Security = Diagnostics upload policy
- DEC-017: Product Owner / Legal / Security = Crash reporting
- DEC-018: Product Owner = Public product name
- DEC-020: Product Owner / Legal = License model
- DEC-021: iOS Owner / Release Manager = Apple App Store SDK gate
- DEC-022: Software Architect = Canonical implementation directory layout
- DEC-023: Product Owner / Windows Platform Owner = Windows desktop Global PTT priority
- DEC-024: Product Owner / macOS Platform Owner = macOS desktop Global PTT priority
- DEC-025: Product Owner / Linux Platform Owner = Officially-tested Linux environment
- DEC-026: Product Owner / UX Owner = Mouse side-button support
- DEC-027: Security / Privacy Reviewer = PTT diagnostics privacy posture
- DEC-028: Audio Owner / Software Architect = Missed-key-up watchdog
- DEC-029: Software Architect / Audio Owner = Flutter global-hotkey packages rejected for PTT
- DEC-030: Audio Owner / Product Owner = Voice activity detection deferred to P1
Required P0 Capability Groups
- App shell: routing, startup, theme, localization, adaptive layout, platform-service initialization.
- UI/design: Material 3, Chanora design tokens, responsive desktop/tablet/mobile layouts, accessible controls, keyboard focus, safe areas/insets.
- Protocol: TS3-compatible adapter through
tsclientlib; no direct UI coupling to protocol internals. - Connection: one active server connection; visible connecting/connected/reconnecting/disconnected/error states.
- Channel/user tree: preserve Unicode server content; do not translate server names/messages.
- Voice basics: capture/playback path, push-to-talk where platform-supported, mute/deafen indicators, native processing defaults.
- Audio defaults: AEC on where stable, AGC on with user toggle, noise suppression on with user toggle, high-pass filter on.
- Storage: SQLite for non-secret state; platform secure storage for identities/tokens/password-equivalent secrets.
- Diagnostics: local export only; redact secrets; machine fields remain language-neutral.
- Builds: reproducible platform builds for Windows, macOS, Linux, Android, and iOS with staged release allowed.
- Verification: unit, widget, integration, platform acceptance, and release go/no-go evidence.
P0 Architecture Boundaries
- Flutter owns UI, navigation, Material 3 theming, accessibility, and adaptive presentation.
- Rust core owns domain state, protocol/session orchestration, storage, diagnostics, and platform-abstracted services.
- Bridge owns typed DTOs/events/errors only. Do not leak Rust internals into Flutter or Flutter objects into Rust core.
- Platform adapters own OS-specific audio, hotkey, secure storage, build, and permission behavior.
P0 Definition of Done
- User can launch app, enter/connect to a TS3-compatible server, see server/channel/user state, and disconnect cleanly.
- App supports the selected P0 voice path without telemetry or automatic upload.
- Secrets are never stored in SQLite/plaintext.
- English and zh-Hans product strings are available; server-originated Unicode remains unchanged.
- All diagnostic exports are local/user-triggered and redacted.
- Platform P0 acceptance docs pass or contain explicit blocker records.
- Every code change cites at least one requirement or decision ID.
P0 Requirement Index
- Count: 500
- ID groups: SAD-001..SAD-096 (71 ids); SDD-001..SDD-121 (98 ids); SRS-001..SRS-219 (178 ids); SysDes-001..SysDes-158 (153 ids)
P0 Source Weighting
requirements/srs.md: 178architecture/sysdes.md: 153architecture/sdd.md: 98architecture/sad.md: 71
P0 Allocation Hotspots
- Unallocated: 98
- Flutter UI: 68
- Rust Core: 52
- Verification: 43
- Audio Subsystem: 42
- Software: Audio Subsystem: 39
- System Engineering: 33
- Protocol Adapter: 31
- Diagnostics: 28
- State Sync: 24
- Design System: 22
- Bridge: 21
- Storage: 21
- Software: Diagnostics: 21
- Platform: 20
- Software: Protocol Adapter: 20
- Platform Adapters: 19
- Software: Rust Core: 19
- Audio: 18
- Operations: 17
Agent Compression Rule
Do not paste all P0 requirement statements into prompt context. Use this file as the execution contract and phase_index.json only when an exact ID/source lookup is required.