chore: restore product scaffold to rollback baseline

This commit is contained in:
Edison Jwa
2026-05-29 14:02:04 +09:00
parent 2896f14ec9
commit fe6e07353e
434 changed files with 27278 additions and 63230 deletions
-31
View File
@@ -1,31 +0,0 @@
# AI Agent Router — Chanora Minimal Spec
Purpose: keep AI context small. Do not load the old full bundle unless explicitly asked for audit reconstruction.
## Load Order
1. Always load this file first.
2. Load exactly one phase file: `P0_MVP_AGENT_SPEC.md`, `P1_BETA_AGENT_SPEC.md`, or `P2_PRODUCTION_AGENT_SPEC.md`.
3. Load `SOP_AGENT_OPERATIONS.md` only when changing architecture, release, security, privacy, diagnostics, build, or test gates.
4. Use `phase_index.json` for machine lookup. Do not paste full source docs into context.
## Phase Meaning
- P0 = MVP buildable product. Default implementation target.
- P1 = beta hardening and extended quality. Do not implement unless asked.
- P2 = production/public-release readiness. Do not implement until P0 and P1 gates pass.
- SOP = standing operating rules for agents, reviews, testing, release, traceability, and refusal conditions.
## Non-Negotiable Product Contract
- Product: Chanora, a cross-platform TeamSpeak 3-compatible client application.
- Stack: Flutter UI + Rust core + typed Flutter/Rust bridge + `tsclientlib` adapter.
- Platforms: Windows, macOS, Linux, Android, iOS.
- MVP server model: external TS3-compatible servers only; no Chanora-operated backend.
- MVP connection model: one active server connection per client instance.
- Privacy: no telemetry, no automatic diagnostic upload, no crash reporting unless later approved.
- Diagnostics: local, user-initiated export only.
- Storage: secrets in platform secure storage; non-secrets in bundled SQLite via `rusqlite`.
- Localization: English + Chinese Simplified at MVP; preserve server content verbatim.
- Design: Material 3 + Chanora Design System.
- License: Apache-2.0 OR MIT.
## Hard Stop Rules
Stop and ask for explicit approval before adding: telemetry, crash reporting, automatic upload, multiple active server connections, a Chanora backend, unsupported platform targets, unapproved hotkey/audio packages, or legal/trademark wording changes.
-136
View File
@@ -1,136 +0,0 @@
# 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 = 24` and `cargo-ndk -P 26`; product code in `apps/chanora_flutter` uses `minSdk = 28` and 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 = `cpal` for desktop (empirically verified on Linux/PipeWire by `poc/audio-capture-playback-spike` on 2026-05-13) and direct `oboe-rs` for 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 explicit `VoiceCommunication` input preset, `VoiceCommunication`/`Speech` output 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 `tsclientlib` license 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 = `rusqlite` with the `bundled` feature (SQLite statically linked into the binary; no system libsqlite3 dependency); verified by `poc/sqlite-storage-spike` on 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-spike` on 2026-05-13
- DEC-014: Bridge choice = Use a stable typed Flutter/Rust bridge with generated or schema-controlled DTOs; **`flutter_rust_bridge` 2.x pinned** (empirically verified at 2.12.0 by `poc/flutter_rust_bridge_hello` on 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 (`tsclientlib` MIT-OR-Apache-2.0, `flutter_rust_bridge` MIT, `cpal` Apache-2.0, `rusqlite` MIT, `keyring` MIT-OR-Apache-2.0, etc.) and with the Flutter framework's BSD-3-Clause. The license texts ship as `LICENSE-APACHE` and `LICENSE-MIT` at the repository root; an aggregator `LICENSE` points to both. `NOTICE` enumerates 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 `L0Focused` without 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.GlobalShortcuts` interface 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`, and `fallback_exercised`; the user's actual key value (scan code, virtual key, keysym) shall never be logged, persisted, or exported. `PttSanitizer` enforces 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_active` after 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 `DesktopPttBackend` trait + per-OS implementations (already specified in SDD-083 / SDD-084 / SDD-085); do not adopt `hotkey_manager`, `super_hot_key`, or any equivalent pub.dev package for PTT.** Rationale: those packages wrap the OS `RegisterHotKey` / `RegisterEventHotKey` semantics — 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::VoiceActivity` ships 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 the `webrtc-vad` crate — frozen-but-stable C++ BSD-3 dep, ~200 KB binary, industry baseline; Silero VAD via ONNX Runtime — best quality but ~816 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
1. App shell: routing, startup, theme, localization, adaptive layout, platform-service initialization.
2. UI/design: Material 3, Chanora design tokens, responsive desktop/tablet/mobile layouts, accessible controls, keyboard focus, safe areas/insets.
3. Protocol: TS3-compatible adapter through `tsclientlib`; no direct UI coupling to protocol internals.
4. Connection: one active server connection; visible connecting/connected/reconnecting/disconnected/error states.
5. Channel/user tree: preserve Unicode server content; do not translate server names/messages.
6. Voice basics: capture/playback path, push-to-talk where platform-supported, mute/deafen indicators, native processing defaults.
7. Audio defaults: AEC on where stable, AGC on with user toggle, noise suppression on with user toggle, high-pass filter on.
8. Storage: SQLite for non-secret state; platform secure storage for identities/tokens/password-equivalent secrets.
9. Diagnostics: local export only; redact secrets; machine fields remain language-neutral.
10. Builds: reproducible platform builds for Windows, macOS, Linux, Android, and iOS with staged release allowed.
11. 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`: 178
- `architecture/sysdes.md`: 153
- `architecture/sdd.md`: 98
- `architecture/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.
-58
View File
@@ -1,58 +0,0 @@
# P1 Beta Agent Spec — Harden After MVP
## Objective
Improve reliability, beta readiness, accessibility coverage, diagnostics usefulness, and platform quality after P0 works end-to-end.
## Entry Gate
Do not start P1 until P0 MVP acceptance is passing or blockers are formally recorded.
## P1 Scope
- Harden reconnection/error handling beyond MVP happy path.
- Expand integration/system tests and beta acceptance evidence.
- Improve accessibility, keyboard workflows, adaptive layout edge cases, and localization completeness.
- Improve diagnostics quality without changing the privacy model.
- Prepare external beta release notes, support workflows, and known-issue tracking.
## P1 Non-Goals
- No telemetry by default.
- No automatic diagnostic upload.
- No multiple active server connections unless a new accepted decision overrides DEC-006.
- No production/store launch gates unless explicitly promoted to P2.
## P1 Definition of Done
- P0 regressions remain passing.
- Beta blockers are triaged and traceable.
- Diagnostic exports are useful for support while still redacted and local.
- Platform-specific beta risks have owner/status records.
## P1 Requirement Index
- Count: 81
- ID groups: SAD-035..SAD-066 (18 ids); SDD-051..SDD-109 (20 ids); SRS-028..SRS-191 (38 ids); SysDes-122, SysDes-123, SysDes-124, SysDes-125, SysDes-138
## P1 Source Weighting
- `requirements/srs.md`: 38
- `architecture/sdd.md`: 20
- `architecture/sad.md`: 18
- `architecture/sysdes.md`: 5
## P1 Allocation Hotspots
- Unallocated: 20
- Diagnostics: 14
- Audio Subsystem: 13
- Flutter UI: 9
- Platform Adapters: 9
- Deployment Scripts: 7
- Software Architecture: 6
- Application Container: 6
- Storage: 5
- Protocol Adapter: 5
- Localization Service: 5
- Bridge: 4
- Flutter App Shell: 4
- Software Design: 4
- Platform Adapter: 4
- Platform Audio Adapter: 3
- State Sync: 3
- Rust Core: 3
- Verification Support: 2
- Flutter State: 2
-34
View File
@@ -1,34 +0,0 @@
# P2 Production Agent Spec — Public Release Readiness
## Objective
Prepare Chanora for public/store release only after P0 implementation and P1 beta hardening are complete.
## Entry Gate
Do not begin P2 until P0 and P1 evidence is available. Public release is blocked by legal/trademark/OSS review and store-policy readiness.
## P2 Scope
- Final release go/no-go records.
- Store metadata and platform upload compliance.
- Legal review: TeamSpeak non-affiliation wording, trademark usage, OSS license posture, `tsclientlib` license posture.
- Final privacy policy confirmation against actual app behavior.
- Production build signing, artifact policy, and staged rollout policy.
## P2 Non-Goals
- Do not add telemetry/crash reporting to satisfy production readiness unless privacy/legal/product explicitly approve it.
- Do not expand server/backend scope.
- Do not add new features that destabilize P0/P1 acceptance.
## P2 Definition of Done
- Release manager has signed go/no-go evidence.
- Legal/compliance blockers are closed.
- Platform upload SDK/target requirements are current on upload date.
- Release artifacts are reproducible, signed where required, and mapped to a version/tag.
## P2 Requirement Index
- Count: 11
- ID groups: SAD-045, SAD-046, SAD-047, SAD-048, SAD-049, SAD-050, SAD-055; SDD-063; SRS-117, SRS-154, SRS-175
## P2 Source Weighting
- `architecture/sad.md`: 7
- `requirements/srs.md`: 3
- `architecture/sdd.md`: 1
-11
View File
@@ -1,11 +0,0 @@
# Chanora AI-Agent Minimal Phased Requirements
This bundle intentionally removes the large rewritten archive and trace CSV. It is designed for AI agents with limited context.
Recommended loading:
1. `00_AGENT_ROUTER.md`
2. One phase file: P0, P1, or P2
3. `SOP_AGENT_OPERATIONS.md` only when process/release/security rules matter
4. `phase_index.json` only for exact ID lookup
Do not load the old full bundle unless performing audit reconstruction.
-46
View File
@@ -1,46 +0,0 @@
# SOP Agent Operations — Standing Rules
## Core Agent Rules
1. Default to P0. Do not implement P1/P2 unless explicitly requested.
2. Preserve traceability: commit messages, PR descriptions, tests, and review notes must cite decision/requirement IDs.
3. Make the smallest compliant change. Avoid architecture invention.
4. If requirements conflict, precedence is: Product Decision Register > SysRS > SysDes > SRS > SAD > SDD > Verification/Release/Security docs.
5. If a task requires exact historical detail, inspect `phase_index.json`; do not load the obsolete full bundle.
## Required Review Gates
- Architecture boundary review for Flutter/Rust/bridge/protocol/storage/audio changes.
- Privacy review for diagnostics, logging, crash handling, analytics, upload, identifiers, or support artifacts.
- Security review for secure storage, redaction, secrets, dependencies, and supply chain.
- Release review for platform build, signing, SDK, store, and artifact policy.
- Localization review for product strings and server-content preservation.
## Testing SOP
- Unit tests: Rust core, DTO transforms, storage, redaction, decision logic.
- Widget tests: Material 3 components, state tokens, localization, accessibility semantics, adaptive layouts.
- Integration tests: bridge events/errors, protocol adapter state, diagnostics export, storage migration.
- Platform tests: secure storage, audio capture/playback, permissions, hotkeys/PTT, packaging.
- Acceptance tests: use platform P0 acceptance docs before any release promotion.
## Build SOP
- Keep repo layout canonical: `apps/chanora_flutter/`, `core/chanora_core/`, `crates/chanora_*`.
- Pin `flutter_rust_bridge` 2.x.
- Use bundled `rusqlite` for embedded SQLite.
- Android minSdk = 28; target API must match current Google Play requirement on upload date.
- iOS minimum = 13; App Store upload SDK gate must be checked on actual upload date.
## Privacy/Security SOP
- No telemetry in MVP.
- No crash reporting unless approved.
- No automatic diagnostic upload.
- Diagnostics are local export only and must redact secrets.
- Product-owned strings may be localized; server-originated content must be preserved verbatim.
- Secrets never enter logs, diagnostics, SQLite, screenshots, or crash-like artifacts.
## Release SOP
- Release sequence: Internal Alpha -> External Beta -> MVP Public.
- Public/store release requires legal/trademark/OSS license confirmation.
- Staged platform release is allowed when documented.
- Every release must include version, commit/tag, artifact list, known issues, test evidence, and go/no-go result.
## Agent Refusal / Escalation Conditions
Stop implementation and ask for owner approval when asked to add or change: telemetry, crash reporting, diagnostic uploads, backend service, multi-server active sessions, platform scope, license, trademark/non-affiliation text, secret-storage policy, or target SDK/minimum OS baseline.
@@ -0,0 +1,43 @@
# Chanora Desktop Push-to-Talk Architecture
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
**Related documents:** `docs/architecture/sad.md`, `docs/architecture/sdd.md`, `docs/release/dv-waiver-register.md`
## 1. Purpose
This document records the desktop push-to-talk architecture advertised by the README and connects it to the SWE.2/SWE.3 baselines.
## 2. Architecture Summary
Desktop PTT is implemented as a platform-capability feature. The application must detect the active backend, expose the resulting `PttCapabilityLevel`, and avoid claiming global PTT support when the runtime falls back to focused-input behavior.
## 3. Platform Backends
| Platform | Backend strategy | Release claim rule |
|---|---|---|
| Windows | Raw Input first, low-level keyboard hook fallback, focused fallback if unavailable | Claim only the detected runtime capability |
| macOS | Event Tap where permission and OS policy allow; focused fallback otherwise | Claim Global PTT only with permission/backend evidence |
| Linux | Freedesktop GlobalShortcuts portal where available; focused fallback otherwise | State portal/fallback behavior clearly |
## 4. Safety Rules
| Rule | Purpose |
|---|---|
| Missed-key-up watchdog clears transmit after timeout | Prevents stuck transmit when an OS suppresses key-up |
| Capability is surfaced to UI and release record | Prevents over-claiming platform support |
| Mouse side-button support is platform-dependent | Avoids blocking release on Linux portal limitations |
| Focused fallback remains available | Preserves usable PTT when global backends are unavailable |
## 5. Verification Handoff
| Evidence | Required result |
|---|---|
| Per-platform smoke | Active backend and fallback behavior recorded |
| UI inspection | PTT capability badge matches runtime backend |
| Release readiness | Release notes mirror actual capability per platform |
| Safety test | Watchdog prevents stuck transmit after missed key-up |
## 6. DV Conclusion
The desktop PTT architecture is documented for DV navigation. Public release claims still require per-platform PTT evidence attached to the release-readiness record.
+186
View File
@@ -0,0 +1,186 @@
# Chanora Software Architecture Description
**Lifecycle:** SWE.2 Software Architectural Design
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
**Direct upstream source:** `docs/srs.md`
**Related system allocation:** `docs/sysdes.md`
## 1. Purpose
This Software Architecture Description defines Chanora's software architecture for DV review. It bridges SRS software requirements to SWE.3 detailed design and to SWE.5/SWE.6 verification planning.
This baseline captures the architecture visible in the current repository. It is sufficient for DV traceability review, while deeper per-module algorithms remain in `docs/architecture/sdd.md` and source-level design.
## 2. Architectural Scope
Chanora is a Flutter application with a Rust core. Flutter owns the user-facing shell, Material 3 widgets, localization, permission UX, and platform service presentation. Rust owns connection orchestration, protocol isolation, audio processing, storage coordination, diagnostics, server resolution, prefetch policy, and bridge DTOs.
## 3. Upstream SRS Allocation
| SRS group | Architectural allocation |
|---|---|
| SRS-003, SRS-008 through SRS-016 | Cross-platform app shell, Flutter UI, Rust core, platform adapters |
| SRS-017 through SRS-030 | Flutter UI, state presentation, connection and voice controls |
| SRS-031 through SRS-035 | Bridge layer and typed DTO boundary |
| SRS-036 through SRS-043 | Rust core connection lifecycle and state behavior |
| SRS-044 through SRS-053 | Protocol adapter and TeamSpeak-compatible server boundary |
| SRS-054 through SRS-061 | State synchronization and replay/reducer verification hooks |
| SRS-062 through SRS-083 | Audio subsystem, DSP, codec, PTT, mute/deaf, metering |
| SRS-084 through SRS-095 | Storage, secure storage, identity, diagnostics-sensitive data |
| SRS-096 through SRS-102 | Diagnostics, export, redaction, troubleshooting hooks |
| SRS-103 through SRS-123 | Platform adapters, packaging, release behavior |
| SRS-124 through SRS-143 | Verification support, analysis requirements, traceability rules |
| SRS-144 through SRS-184 | Material 3, adaptive UI, accessibility, localization, Unicode, app initialization |
| SRS-185 through SRS-218 | Platform baselines, PTT capability, transmit mode, no automatic telemetry, benchmark advisory |
## 4. Component Architecture
| Component | Repository location | Responsibility | Direct architectural dependencies |
|---|---|---|---|
| Flutter app shell | `apps/chanora_flutter/lib/main.dart`, services, widgets | App startup, screen composition, user actions, localization, Material 3 UI | Generated Rust bridge, platform plugins, Flutter services |
| Flutter service layer | `apps/chanora_flutter/lib/services/` | Permission flows, lifecycle policy, host prefetch debounce, link trust, state mapping, platform back intent | Flutter app shell, generated bridge APIs, platform plugins |
| Flutter widget layer | `apps/chanora_flutter/lib/widgets/` | Connect UI, channel tree, chat, voice controls, settings, diagnostics surfaces | Flutter services, generated DTOs, design tokens |
| Bridge layer | `crates/chanora_bridge`, `apps/chanora_flutter/lib/src/rust/` | Typed Flutter/Rust boundary and generated bindings | Rust core, Flutter generated code |
| Rust core | `core/chanora_core` | Connection lifecycle, orchestration, reconnect behavior, storage coordination, voice state | Protocol, audio, storage, diagnostics, state, resolver/prefetch |
| Protocol adapter | `crates/chanora_protocol` | Isolate `tsclientlib`, expose typed protocol DTOs/errors | Rust core, external compatible server |
| State sync | `crates/chanora_state` | Snapshot/delta model, channel join helpers, reducer behavior | Rust core, protocol DTOs |
| Audio subsystem | `crates/chanora_audio` | Capture/playback, Opus, DSP, PTT, voice activity reservation, platform units | Rust core, platform APIs, protocol audio path |
| Storage | `crates/chanora_storage` | Bookmarks, identities, encrypted local data, platform keyring integration | Rust core, platform secure storage |
| Diagnostics | `crates/chanora_diagnostics` | Redaction, log sink, export bundle, known-secret registry | Rust core, Flutter diagnostics UI |
| Server resolver | `crates/chanora_resolver` | SRV/TSDNS/DNS fallback resolution | Rust core, prefetch crate |
| Server prefetch | `crates/chanora_server_prefetch`, Flutter `prefetch_debouncer.dart` | Invisible host-field resolution warming, TTL cache, generation safety | Resolver, Flutter connect UI, Rust core |
## 5. Static Architecture View
```text
Flutter UI/widgets/services
-> generated Dart bridge API
-> chanora_bridge
-> chanora_core
-> chanora_protocol -> tsclientlib -> external compatible server
-> chanora_state
-> chanora_audio -> platform audio APIs / Opus / DSP
-> chanora_storage -> platform secure storage / SQLite
-> chanora_diagnostics
-> chanora_server_prefetch -> chanora_resolver -> network DNS/TSDNS
```
The bridge is the trust and type boundary between Flutter and Rust. Flutter must not directly depend on protocol-library internals. Rust core must not expose platform-specific storage or audio details to UI code except through stable DTOs and capability fields.
## 6. Runtime Flow Architecture
### 6.1 Connect Flow
```text
User enters host/bookmark
-> Flutter connect widgets
-> optional prefetch debounce
-> bridge connect command
-> Rust core supervisor
-> resolver / prefetch cache
-> protocol adapter
-> external compatible server
-> state snapshot/events
-> bridge event stream
-> Flutter state mapper and widgets
```
### 6.2 Voice Flow
```text
Microphone / platform input
-> audio capture unit
-> DSP chain: HPF, NS, AEC, AGC where active
-> PTT/mute/transmit gate
-> Opus encode
-> protocol adapter
-> external compatible server
External server voice
-> protocol adapter
-> jitter/decode path
-> mixer / per-user controls
-> platform output
```
### 6.3 Diagnostics Flow
```text
Runtime event or error
-> diagnostic log sink / known-secret registry
-> redactor
-> user-initiated export bundle
-> Flutter share/export surface
```
## 7. Interface Catalogue
| Interface | Producer | Consumer | Architectural rule |
|---|---|---|---|
| Bridge command DTOs | Flutter generated API | `chanora_bridge`, Rust core | Stable typed DTOs; no raw protocol-library types cross to Flutter |
| Bridge event DTOs | Rust core / bridge | Flutter services/widgets | User-safe errors and capability fields are explicit |
| Protocol DTOs | `chanora_protocol` | Rust core, state sync | Protocol adapter isolates `tsclientlib` |
| Audio configuration | Flutter settings / Rust core | `chanora_audio` | Voice modes and processing flags are explicit; VAD remains disabled/deferred |
| Storage records | Storage crate | Rust core / Flutter UI via bridge | Secrets stay behind secure-storage abstraction |
| Diagnostic bundles | Diagnostics crate | Flutter diagnostics UI | Redaction runs before export or display |
| Platform capability records | Platform adapters/audio/PTT backends | UI and release record | UI/release wording must not over-claim capability |
## 8. Dependency Rules
| Rule | Rationale |
|---|---|
| Flutter UI depends on generated bridge APIs, not Rust internals | Keeps UI stable across Rust implementation changes |
| Rust core orchestrates crates but protocol/audio/storage crates remain separately testable | Supports SWE.4 unit verification and bounded responsibilities |
| Protocol adapter is the only component that owns `tsclientlib` coupling | Protects the app from protocol-library leakage |
| Diagnostics redaction must be reusable by runtime logging and export | Prevents split redaction behavior |
| Platform-specific behavior stays in platform adapters or audio platform units | Keeps cross-platform logic testable and reduces conditional sprawl |
| Release claims consume capability records and release evidence | Prevents over-claiming PTT, signing, packaging, or secure-storage behavior |
## 9. Non-Functional Allocation
| Concern | Architectural mechanism | Verification owner |
|---|---|---|
| Real-time audio responsiveness | Rust audio subsystem, benchmark advisory, bounded callback behavior | Audio / Platform QA |
| Privacy and no automatic telemetry | User-initiated diagnostics, no automatic upload policy | Security / Privacy QA |
| Secure secret handling | Platform secure-storage abstraction and encrypted local storage | Security / QA |
| Cross-platform UI | Flutter Material 3, design tokens, responsive widgets | Software QA / UX |
| Protocol compatibility | `tsclientlib` adapter isolation and compatible-server matrix | Protocol / Integration QA |
| Release reproducibility | CI, build scripts, artifact hashes, release-readiness record | Release / Operations QA |
## 10. Architectural Decisions Captured by This Baseline
| Decision | Architectural outcome |
|---|---|
| Flutter + Rust split | Flutter owns presentation; Rust owns protocol/audio/storage/diagnostics core behavior |
| `tsclientlib` isolation | Protocol compatibility is behind `chanora_protocol` |
| Secure storage abstraction | Platform storage details do not leak into UI or unrelated crates |
| Advisory audio benchmarks | Performance regressions are surfaced without making CI a hard release gate at this stage |
| PTT capability levels | Platform PTT support is represented as capability data and must match release wording |
| VoiceActivity deferral | `VoiceActivity` remains reserved/disabled until a later baseline allocates implementation |
| No automatic diagnostic upload in MVP | Diagnostics are local and user-initiated unless future approved requirements change policy |
## 11. Verification Handoff
| Verification plan | SAD handoff |
|---|---|
| SWE.4 | Component boundaries define unit-test ownership for Flutter services/widgets and Rust crates |
| SWE.5 | Interface catalogue and runtime flows define integration paths |
| SWE.6 | SRS allocation and acceptance flows define software acceptance evidence |
| SYS.4 | Platform capability and external-server boundaries define system integration evidence |
## 12. Traceability to SRS
This SAD derives only from `docs/srs.md`. The broad SRS group-to-component allocation in section 3 is the controlling SWE.2 trace for DV. Detailed item-level trace is represented by the SRS coverage matrix and `docs/governance/traceability-matrix.md`.
## 13. Open Architecture Risks
| Risk | Impact | Control |
|---|---|---|
| SAD item numbering from historical status references is not reconstructed in this baseline | Existing references such as `SAD-043` and `SAD-046` are not itemized here | Treat this as a DV baseline SAD; add itemized SAD IDs in a follow-up if process requires strict ID-level review |
| Some architecture views are textual rather than C4 diagrams | Reviewers may request visual C4 views | Record as documentation hardening, not a blocker for DV baseline if textual views are accepted |
| Release/platform architecture evidence is incomplete | Public release remains blocked | Controlled by release-readiness and waiver records |
## 14. DV Conclusion
This SWE.2 baseline is sufficient to remove the missing-SAD traceability gap for DV review. It does not replace candidate test evidence or final release approval.
+154
View File
@@ -0,0 +1,154 @@
# Chanora Software Detailed Design
**Lifecycle:** SWE.3 Software Detailed Design and Unit Construction Handoff
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
**Direct upstream source:** `docs/architecture/sad.md`
**Related software requirements:** `docs/srs.md`
## 1. Purpose
This Software Detailed Design defines the module-level design details needed for SWE.4 unit verification and SWE.5 integration verification. It is based on the current repository layout and the SWE.2 architecture baseline.
## 2. Module Catalogue
| SDD module | Source location | Primary responsibility | Upstream SAD component |
|---|---|---|---|
| SDD-MOD-001 Flutter app bootstrap | `apps/chanora_flutter/lib/services/app_bootstrap.dart`, `main.dart` | Initialize Rust bridge, localization, app services, theme/design baseline | Flutter app shell |
| SDD-MOD-002 Connect UI | `apps/chanora_flutter/lib/widgets/connect_widgets.dart` | Host/bookmark inputs, connect actions, pre-request UX | Flutter widget layer |
| SDD-MOD-003 Snapshot and channel UI | `snapshot_view.dart`, `snapshot_state_mapper.dart`, `channel_spacer.dart` | Present channel tree, clients, and mapped state | Flutter widget/service layer |
| SDD-MOD-004 Chat UI | `chat_views.dart`, `bbcode_text.dart` | Channel text rendering and BBCode-safe display | Flutter widget layer |
| SDD-MOD-005 Voice UI | `voice_bar.dart`, `voice_compact.dart`, `voice_settings*.dart`, `voice_level_meter.dart`, `ptt_capability_badge.dart` | Voice controls, processing settings, metering, PTT capability | Flutter widget layer |
| SDD-MOD-006 Platform services | `android_permissions_service.dart`, `ios_permissions_service.dart`, `audio_lifecycle_service.dart`, `back_intent_*`, `link_trust_service.dart` | Permission, lifecycle, navigation, route/link trust behavior | Flutter service layer |
| SDD-MOD-007 Bridge API | `crates/chanora_bridge/src/api.rs`, generated Dart/Rust bridge files | Typed command/event boundary | Bridge layer |
| SDD-MOD-008 Rust core supervisor | `core/chanora_core/src/lib.rs`, `ptt.rs` | Connection orchestration, reconnect, PTT state, storage coordination | Rust core |
| SDD-MOD-009 Protocol adapter | `crates/chanora_protocol/src/` | `tsclientlib` isolation, DTO/error mapping | Protocol adapter |
| SDD-MOD-010 State sync | `crates/chanora_state/src/lib.rs`, `channel_join.rs` | Snapshot/delta model, reducer, channel join support | State sync |
| SDD-MOD-011 Audio subsystem | `crates/chanora_audio/src/` | Audio capture/playback, DSP, Opus, PTT, mode stack, platform units | Audio subsystem |
| SDD-MOD-012 Storage | `crates/chanora_storage/src/lib.rs` | Bookmarks, identity storage, encrypted local records, keyring abstraction | Storage |
| SDD-MOD-013 Diagnostics | `crates/chanora_diagnostics/src/lib.rs` | Redaction, log sink, known-secret registry, export bundle | Diagnostics |
| SDD-MOD-014 Resolution and prefetch | `crates/chanora_resolver/src/lib.rs`, `crates/chanora_server_prefetch/src/lib.rs`, `prefetch_debouncer.dart` | SRV/TSDNS/DNS fallback and generation-safe resolution warming | Server resolver / prefetch |
| SDD-MOD-015 Build and release hooks | `.github/workflows/`, `tools/`, platform project files | CI, unsigned iOS build, benchmark advisory, platform smoke procedures | Release / platform architecture |
## 3. Bridge Boundary Design
The bridge boundary is the only supported Flutter-to-Rust command path. Dart code uses generated APIs under `apps/chanora_flutter/lib/src/rust/`; Rust exposes bridge functions through `crates/chanora_bridge/src/api.rs`.
Design rules:
| Rule | Detail |
|---|---|
| DTO stability | DTO fields must be explicit and serializable through Flutter Rust Bridge generation |
| Error safety | Rust errors exposed to Flutter must be user-safe or mapped before display |
| Secret handling | Secrets may cross only as command inputs or protected DTO fields and must be registered for diagnostic redaction where relevant |
| Capability reporting | Platform and PTT capability fields must reflect actual active backend state |
| Regeneration control | Generated bridge files are implementation artifacts and must be regenerated when bridge API signatures change |
## 4. Connection and State Design
| Detail | Design |
|---|---|
| Connection lifecycle | Rust core owns connect/disconnect/reconnect decisions and suppresses reconnect after user disconnect |
| Backoff | Reconnect uses exponential backoff as described in implementation status, capped at 60 seconds |
| Server resolution | Resolver performs SRV/TSDNS/DNS fallback; prefetch cache may warm but must not be required for connect success |
| Snapshot mapping | Rust state and bridge DTOs are mapped into Flutter view models by `snapshot_state_mapper.dart` |
| Channel join | Channel join logic and errors are represented through Rust state/protocol handling and Flutter error mapper service |
| Reducers | `chanora_state` owns snapshot/delta reducer design with unit coverage for snapshot, delta, reconnect, duplicate normalization, disconnected/lost suppression, unknown-client voice activity, deterministic ordering, and channel-delete/client cleanup. Current runtime UI refresh still flows through `chanora_core` snapshot/probe paths; full live-event folding through `chanora_state::reduce` is an integration follow-up. |
## 5. Audio Detailed Design
| Audio element | Design detail |
|---|---|
| Capture/playback | Platform-specific units handle Android, iOS, desktop/fallback paths behind Rust audio abstractions |
| Codec | Opus encode/decode lives in `opus_voice.rs` and associated audio modules |
| DSP chain | High-pass filter, noise suppression, echo cancellation, and AGC are represented by audio processing modules/backends |
| Transmit control | `TransmitMode` supports `Ptt`, `Continuous`, and reserved `VoiceActivity`; `VoiceActivity` has no active MVP implementation |
| PTT | Desktop/mobile backends expose capability level and active backend; missed-key-up watchdog prevents stuck transmit |
| Release tail | Tail handling prevents abrupt cutoffs after PTT release where configured |
| Benchmarks | Realtime capture, Opus, and resampler benchmarks provide advisory baseline evidence |
## 6. Storage and Secret Design
| Storage item | Design detail |
|---|---|
| Bookmarks | Stored locally through the storage crate and surfaced in Flutter connect UI |
| Identity references | Stored through `IdentityFileStore` and platform secure storage where available |
| Passwords/secrets | Encrypted at rest using the current storage design; Android Keystore-backed DEK is deferred and must be disclosed |
| CI keyring behavior | CI disables real keyring access with `CHANORA_DISABLE_KEYRING=1` to avoid headless blocking |
| Fallback behavior | Platform fallback modes must be represented as limitations in release/security evidence |
## 7. Diagnostics Detailed Design
| Diagnostic element | Design detail |
|---|---|
| Log sink | Runtime logs can be captured by diagnostic sinks for export |
| Known-secret registry | Runtime secrets are registered for redaction where applicable |
| Redactor | Redacts configured sensitive patterns before export |
| Export bundle | Diagnostic export is JSON-based and user-initiated |
| Upload policy | MVP has no automatic diagnostic, telemetry, or crash upload |
## 8. Flutter UI Detailed Design
| UI area | Design detail |
|---|---|
| Design tokens | `chanora_tokens.dart` centralizes product styling over Material 3 |
| Platform capability display | `platform_capabilities.dart` and PTT capability widgets expose platform-specific support honestly |
| Localization | Generated localization files provide English and Simplified Chinese resources |
| Responsive behavior | Current widgets support compact/mobile-oriented layouts; expanded side-pane hardening remains P1/P2 as recorded |
| Accessibility | Critical status should use text/icons/semantics and not color alone; verification remains through UI tests/audit |
| UI settings persistence | `UiPreferencesService` persists host, nickname, permission explanation state, and theme mode through `shared_preferences`; invalid stored theme values fall back to system theme |
## 9. Build and Release Detailed Design
| Build/release item | Design detail |
|---|---|
| Rust CI | `.github/workflows/ci.yml` runs cargo check/test and advisory clippy |
| Flutter CI | `.github/workflows/ci.yml` runs Flutter pub get, analyze, and tests |
| Supply chain | CI runs cargo-deny and license inventory checks |
| iOS unsigned build | CI runs `flutter build ios --release --no-codesign` |
| Audio benchmarks | `bench-advisory.yml` runs audio benchmarks and posts advisory evidence |
| Platform packages | Public binary packaging/signing/notarization remains release-gated |
## 10. Verification Hook Design
| Module | SWE.4 unit hooks | SWE.5/SWE.6 integration hooks |
|---|---|---|
| Flutter services/widgets | Dart unit/widget tests under `apps/chanora_flutter/test/` | Widget/system demos and candidate device smoke |
| Bridge | API compile/generation checks | Flutter-to-Rust command/event smoke |
| Rust core | Cargo tests | Compatible-server lifecycle demo |
| Protocol | DTO/error mapping tests | Protocol compatibility matrix and server demo |
| State sync | Reducer tests | Snapshot/delta/reconnect integration evidence |
| Audio | DSP/codec/PTT tests and benchmarks | Platform audio loopback/device demo |
| Storage | Repository/encryption/keyring-disabled tests | Platform secure-storage audit |
| Diagnostics | Redaction/export tests | User-initiated export inspection |
| Release hooks | CI workflow validation | Release readiness record and artifact evidence |
## 11. Traceability to SAD
| SAD component | SDD modules |
|---|---|
| Flutter app shell | SDD-MOD-001 |
| Flutter service layer | SDD-MOD-006, SDD-MOD-014 |
| Flutter widget layer | SDD-MOD-002 through SDD-MOD-005 |
| Bridge layer | SDD-MOD-007 |
| Rust core | SDD-MOD-008 |
| Protocol adapter | SDD-MOD-009 |
| State sync | SDD-MOD-010 |
| Audio subsystem | SDD-MOD-011 |
| Storage | SDD-MOD-012 |
| Diagnostics | SDD-MOD-013 |
| Server resolver/prefetch | SDD-MOD-014 |
| Release/platform architecture | SDD-MOD-015 |
## 12. Open Detailed-Design Risks
| Risk | Impact | Control |
|---|---|---|
| Detailed item IDs from historical SDD references are not reconstructed | Existing references such as `SDD-109` are not itemized in this baseline | Treat this as a DV baseline SDD and add strict item numbering later if required |
| Some module designs are summarized rather than API-by-API | May be insufficient for final process audit | Use this as DV baseline; deepen high-risk modules before final release gate |
| Android Keystore-backed DEK is not implemented | Limits storage/security design claims | Controlled by waiver and release-readiness records |
| Full event replay tooling and live reducer integration evidence are absent | Limits state verification design beyond reducer unit behavior | Controlled as P1 gap and runtime-integration follow-up |
## 13. DV Conclusion
This SWE.3 baseline is sufficient to remove the missing-SDD traceability gap for DV review and to feed SWE.4/SWE.5 verification plans. It does not close release evidence gaps or replace source-level tests.
+21
View File
@@ -0,0 +1,21 @@
# System Design Specification
**Document status:** DV entry-point record
**Canonical document:** `../sysdes.md`
The canonical Chanora System Design Specification currently lives at `docs/sysdes.md`. This file preserves the README-advertised path `docs/architecture/sysdes.md` for DV navigation.
Reviewers shall use `docs/sysdes.md` as the authoritative SysDes baseline until the repository migration moves the canonical file into this directory.
## DV Review Summary
| Topic | Canonical source |
|---|---|
| System element allocation | `docs/sysdes.md` sections 4 through 8 |
| Verification handoff | `docs/sysdes.md` section 12, SysDes-102 through SysDes-107 |
| SysRS-to-SysDes allocation matrix | `docs/sysdes.md` Appendix A |
| Change-control and traceability rules | `docs/sysdes.md` SysDes-108 through SysDes-110 |
## DV Position
The SysDes baseline is reviewable for DV. SRS, SAD, SDD, and verification documents derive from or consume this allocation layer.
-767
View File
@@ -1,767 +0,0 @@
{
"document_id": "CHANORA_P1_VOICE_AGENT_SPEC",
"version": "1.1.0",
"language": "en",
"audience": "ai_agents",
"scope": "P1_voice_only",
"mode": "modify_existing_scaffold_do_not_rewrite",
"based_on": {
"archive": "chanora-product-scaffold-v0.zip",
"previous_spec": "chanora_p1_voice_design_from_current_scaffold.json"
},
"project_context": {
"ui": "Flutter",
"core": "Rust",
"network": "tsclientlib",
"audio_crate": "crates/chanora_audio",
"bridge_crate": "crates/chanora_bridge",
"flutter_app": "apps/chanora_flutter",
"phase_1_platform": "iOS",
"future_platforms": [
"Android",
"Windows",
"macOS",
"Linux"
]
},
"current_scaffold_facts": {
"ios_audio_backend_exists": true,
"ios_current_backend": "VoiceProcessingIO AudioUnit",
"android_audio_backend_exists": true,
"android_current_backend": "Oboe",
"desktop_audio_backend_exists": true,
"desktop_current_backend": "cpal / SDL depending on platform",
"current_network_frame": {
"sample_rate_hz": 48000,
"channels": 1,
"frame_ms": 20,
"samples": 960,
"codec": "Opus VoIP"
},
"current_effects_api_exists": true,
"current_effects_are_real_dsp": false,
"current_voice_activity_is_real_vad": false,
"current_voice_activity_behavior": "VoiceActivity behaves like Continuous",
"current_ios_has_platform_voice_processing": true,
"current_ios_platform_processing_likely_includes": [
"AEC",
"NS",
"AGC"
],
"current_rust_software_ns_exists": false,
"current_rust_software_aec_exists": false,
"current_rust_software_agc_exists": false
},
"p1_primary_goals": [
"Add real VoiceActivity mode using ML VAD.",
"Keep iOS VoiceProcessingIO as default shipping voice-processing path.",
"Add optional iOS Sonora mode for experimental raw Rust AEC3/NS/AGC2/HPF.",
"Make AEC/NS/AGC route-aware and backend-aware.",
"Expose voice processing configuration and stats to Flutter.",
"Preserve current 20ms Opus/tsclientlib transport behavior.",
"Add internal 10ms processing frames without changing network framing."
],
"non_goals": [
"Do not rewrite P0.",
"Do not move PCM processing to Flutter.",
"Do not replace tsclientlib.",
"Do not require Android/Desktop implementation in Phase 1.",
"Do not require CoreML conversion for P1.",
"Do not require GPU/NPU execution providers for correctness.",
"Do not ship AI noise suppression as P1 default.",
"Do not use energy VAD as production transmit gate."
],
"hard_invariants": {
"INV_001": "Flutter MUST NOT process realtime PCM.",
"INV_002": "Rust AudioEngine MUST own voice state, VAD state, processing config, route policy, stats, and diagnostics.",
"INV_003": "Platform audio layer MUST only provide audio I/O, device/session config, and route detection.",
"INV_004": "tsclientlib MUST remain the voice network/session layer.",
"INV_005": "Keep existing 20ms Opus frame/network contract in P1.",
"INV_006": "Add internal 10ms processing frames by splitting 20ms frames into two 10ms frames.",
"INV_007": "Realtime callbacks MUST NOT call Flutter, block on network, perform file I/O, allocate unbounded memory, or run ML inference.",
"INV_008": "Rust panics MUST NOT cross FFI/bridge boundaries.",
"INV_009": "Never enable platform AEC and Rust AEC simultaneously on the same route.",
"INV_010": "Never enable VoiceProcessingIO and Sonora AEC3 simultaneously.",
"INV_011": "Software AEC-capable backends MUST receive both capture and render-reference streams.",
"INV_012": "Render reference MUST be copied from decoded/mixed remote PCM before playout.",
"INV_013": "VAD MUST NOT stop capture or playback; VAD may only gate transmit and update speaking/debug state."
},
"canonical_audio_model": {
"network_frame": {
"sample_rate_hz": 48000,
"channels": 1,
"frame_ms": 20,
"samples": 960,
"sample_format": "i16_or_f32_internal_conversion",
"status": "keep_existing"
},
"processing_frame": {
"sample_rate_hz": 48000,
"channels": 1,
"frame_ms": 10,
"samples": 480,
"sample_format": "f32",
"status": "new_internal_contract"
},
"vad_model_input": {
"sample_rate_hz": 16000,
"channels": 1,
"sample_format": "f32",
"source": "processed_capture",
"resample_from": "48kHz"
}
},
"target_architecture": {
"layers": [
"Flutter UI",
"Typed Bridge / FFI",
"Rust Application Core",
"tsclientlib Adapter",
"Rust AudioEngine",
"AudioDevice backend",
"AudioProcessor backend",
"VoiceActivityDetector backend",
"VoiceTransmitStateMachine",
"AudioStats",
"WavDebugRecorder"
],
"capture_path": [
"Platform mic input",
"Platform audio callback",
"Minimal realtime-safe push into Rust",
"Canonical conversion to 48kHz mono f32",
"20ms network frame split into 2x10ms processing frames",
"Raw mic debug tap",
"AudioProcessor.process_capture",
"Processed mic debug tap",
"ML VAD worker consumes processed capture",
"VoiceActivityStateMachine updates transmit state",
"If transmitting, collect 10ms frames into 20ms Opus frame",
"tsclientlib / existing send path"
],
"render_path": [
"tsclientlib receive/decode/jitter/mix",
"Remote mixer output",
"Copy render_reference before playout",
"If software AEC backend active, call AudioProcessor.process_render on 10ms slices",
"Platform output callback",
"Speaker / receiver / headset"
]
},
"modules_to_add": {
"crates/chanora_audio/src/frame.rs": "AudioFrame10ms, AudioFrame20ms, split/merge helpers, i16/f32 conversion, dBFS helpers.",
"crates/chanora_audio/src/processor.rs": "AudioProcessor trait, AudioBackend enum, backend selector.",
"crates/chanora_audio/src/processor/platform.rs": "PlatformVoiceProcessing backend marker/policy.",
"crates/chanora_audio/src/processor/sonora.rs": "Optional Sonora backend: HPF, AEC3, NS, AGC2, limiter hook.",
"crates/chanora_audio/src/processor/noop.rs": "No-op backend for tests/headset/debug.",
"crates/chanora_audio/src/vad/mod.rs": "VoiceActivityDetector trait, VadOutput, VadBackend enum.",
"crates/chanora_audio/src/vad/silero_onnx.rs": "Silero ONNX VAD runtime wrapper.",
"crates/chanora_audio/src/vad/webrtc.rs": "WebRTC VAD fallback.",
"crates/chanora_audio/src/vad/energy_debug.rs": "Debug-only energy VAD.",
"crates/chanora_audio/src/voice_activity.rs": "VoiceActivity state machine.",
"crates/chanora_audio/src/stats.rs": "Expanded AudioStats schema.",
"crates/chanora_audio/src/debug_wav.rs": "Async WAV dump writer.",
"crates/chanora_audio/src/route_policy.rs": "AudioRoute enum and route-to-processing policy.",
"crates/chanora_audio/src/ios_raw_unit.rs": "Optional raw iOS RemoteIO path for Sonora mode."
},
"modules_to_modify": {
"crates/chanora_audio/src/lib.rs": [
"Export AudioProcessingConfig, AudioStats, AudioRoute, AudioBackend, VadBackend.",
"Replace no-op effects semantics with backend-aware processing config."
],
"crates/chanora_audio/src/engine.rs": [
"Own AudioProcessingConfig.",
"Own AudioProcessor backend.",
"Own VoiceActivityDetector backend.",
"Own VoiceActivityStateMachine.",
"Expose expanded stats.",
"Preserve existing 20ms Opus send/receive behavior."
],
"crates/chanora_audio/src/transmit_selector.rs": [
"Remove VoiceActivity == Continuous behavior.",
"Accept VoiceActivityStateMachine result for VoiceActivity transmit mode."
],
"crates/chanora_audio/src/ios_voice_unit.rs": [
"Keep as platform VoiceProcessingIO backend.",
"Ensure Rust AEC/NS/AGC disabled when this backend is active.",
"Feed captured audio into Rust VAD/state pipeline.",
"Do not run ML inference in callback.",
"Expose render_reference for debug/future software AEC validation."
],
"crates/chanora_audio/src/android_voice_unit.rs": [
"Keep current Oboe backend.",
"Future: adapt to same AudioProcessor/VAD config and stats."
],
"crates/chanora_bridge/src/api.rs": [
"Add set_audio_processing_config.",
"Add audio_processing_stats.",
"Add set_vad_model_path or bundled model discovery.",
"Add enable_audio_debug_wav_dump."
],
"apps/chanora_flutter/ios/Runner/AppDelegate.swift": [
"Keep AVAudioSession ownership.",
"Add/verify mediaServicesWereReset handling.",
"Send detailed route class to Rust.",
"Do not send PCM to Flutter."
]
},
"voice_processing_backends": {
"platform_voice_processing": {
"description": "OS/platform voice processing.",
"ios_implementation": "VoiceProcessingIO AudioUnit",
"ios_default": true,
"owns": [
"AEC",
"NS",
"AGC"
],
"rust_aec": "disabled",
"rust_ns": "disabled_by_default",
"rust_agc": "disabled_by_default"
},
"sonora": {
"description": "Optional Rust-native WebRTC-style voice processor.",
"ios_status": "experimental_optional",
"future_cross_platform_status": "candidate",
"owns": [
"HPF",
"AEC3",
"NS",
"AGC2",
"limiter_hook"
],
"requires": [
"raw_or_near_raw_capture",
"render_reference",
"48kHz mono 10ms frames"
],
"must_not_run_with": [
"VoiceProcessingIO",
"platform AEC"
],
"shipping_default": false
},
"webrtc_apm": {
"description": "Fallback/candidate WebRTC APM backend.",
"status": "candidate_or_fallback",
"requires": [
"render_reference_for_AEC"
]
},
"noop": {
"description": "No processing.",
"status": "debug_or_headset_policy"
}
},
"ios_processing_modes": {
"platform_default": {
"mode_id": "ios_platform_voice_processing",
"shipping_default": true,
"audio_io": "VoiceProcessingIO AudioUnit",
"av_audio_session": {
"category": "playAndRecord",
"mode": "voiceChat",
"preferred_sample_rate_hz": 48000,
"preferred_io_buffer_duration_ms": [
5,
10
],
"options": [
"defaultToSpeaker_when_speaker_route",
"allowBluetoothHFP",
"allowBluetoothA2DP_only_for_output_policy"
]
},
"processing": {
"aec": "platform",
"ns": "platform",
"agc": "platform",
"hpf": "platform_or_safe_rust_optional",
"limiter": "safe_rust_optional",
"sonora": "disabled"
},
"vad": {
"backend": "silero_vad_onnx",
"input_source": "platform_processed_capture"
}
},
"sonora_experimental": {
"mode_id": "ios_raw_sonora",
"shipping_default": false,
"audio_io": "RemoteIO_or_raw_input_output_path",
"av_audio_session": {
"category": "playAndRecord",
"mode": "default_or_measurement_candidate",
"avoid": [
"voiceChat",
"videoChat",
"VoiceProcessingIO",
"setVoiceProcessingEnabled(true)",
"setPrefersEchoCancelledInput(true)"
]
},
"processing": {
"backend": "sonora",
"order": [
"hpf",
"aec3",
"ns",
"agc2",
"limiter",
"vad_input_tap"
],
"requires_render_reference": true,
"requires_raw_or_near_raw_capture": true
},
"vad": {
"backend": "silero_vad_onnx",
"input_source": "sonora_processed_capture"
},
"fallback": {
"on_backend_failure": "switch_to_ios_platform_voice_processing",
"on_render_reference_missing": "reject_sonora_aec_activation"
}
}
},
"route_policy": {
"ios": {
"speaker": {
"default": "platform_voice_processing",
"optional": "sonora_experimental",
"aec": "platform_by_default",
"ns": "platform_by_default",
"agc": "platform_by_default",
"vad": "silero_vad_onnx",
"sonora_allowed_if": [
"raw_io_available",
"render_reference_available"
]
},
"earpiece": {
"default": "platform_voice_processing",
"optional": "sonora_experimental",
"vad": "silero_vad_onnx"
},
"wired_headset": {
"default": "noop_or_conservative",
"aec": "off",
"ns": "conservative_optional",
"agc": "conservative_optional",
"optional": "sonora_ns_agc_without_aec",
"vad": "silero_vad_onnx"
},
"bluetooth_hfp": {
"default": "route_managed",
"aec": "app_side_off",
"ns": "route_managed_or_conservative",
"agc": "route_managed_or_conservative",
"sonora": "disabled_by_default",
"vad": "silero_vad_onnx"
},
"bluetooth_a2dp": {
"default": "invalid_for_duplex_voice",
"transmit_allowed": false,
"vad": "disabled"
},
"unknown": {
"default": "safe_fallback",
"aec": "off_until_classified",
"vad": "silero_vad_onnx"
}
},
"future_platforms": {
"android": {
"default": "platform_effects_if_available_else_rust_backend",
"audio_io": "Oboe",
"vad": "silero_vad_onnx"
},
"windows": {
"default": "rust_backend_candidate",
"audio_io": "WASAPI_or_CPAL",
"vad": "silero_vad_onnx"
},
"macos": {
"default": "platform_or_rust_policy",
"audio_io": "CoreAudio_or_CPAL",
"vad": "silero_vad_onnx"
},
"linux": {
"default": "rust_backend_candidate",
"audio_io": "CPAL_with_ALSA_PulseAudio_PipeWire",
"vad": "silero_vad_onnx"
}
}
},
"vad_runtime_strategy": {
"owner": "Rust AudioEngine",
"default_backend": "silero_vad_onnx",
"model_artifact": "silero_vad.onnx",
"primary_runtime": "onnxruntime_c_api",
"rust_binding": "ort_or_onnxruntime_wrapper",
"fallback_backend": "webrtc_vad",
"debug_backend": "energy_vad",
"flutter_onnx_plugins": {
"allowed_in_realtime_voice_path": false,
"blocked_packages_for_core_path": [
"onnxruntime_v2",
"fonnx"
],
"reason": "Flutter must not process realtime PCM or own VAD inference."
},
"coreml": {
"p1_primary": false,
"p1_conversion_required": false,
"p2_candidate": true,
"allowed_p1_use": "CoreML Execution Provider benchmark only, not correctness dependency"
},
"execution_provider_policy": {
"correctness_provider": "CPU",
"acceleration_providers_optional": true,
"ios_optional": [
"CoreML_EP_after_benchmark"
],
"android_optional": [
"NNAPI_after_benchmark"
],
"windows_optional": [
"DirectML_after_benchmark",
"CUDA_after_benchmark"
],
"macos_optional": [
"CoreML_EP_after_benchmark"
],
"linux_optional": [
"OpenVINO_after_benchmark",
"CUDA_after_benchmark",
"ROCm_after_benchmark"
]
},
"runtime_rules": {
"create_session_once": true,
"reuse_session": true,
"run_outside_realtime_callback": true,
"do_not_create_session_per_frame": true,
"fallback_if_model_missing": true,
"fallback_if_inference_lag_exceeds_budget": true,
"fallback_if_memory_budget_exceeded": true
},
"model_input": {
"sample_rate_hz": 16000,
"channels": 1,
"source": "processed_capture",
"resample_from_hz": 48000
},
"defaults": {
"open_threshold": 0.55,
"close_threshold": 0.35,
"open_after_ms": 40,
"hangover_ms": 500,
"pre_roll_ms": 160,
"min_tx_ms": 200,
"max_inference_lag_ms": 60
},
"ios_memory_budget": {
"target_extra_rss_mb": 30,
"max_acceptable_extra_rss_mb": 50,
"fail_threshold_extra_rss_mb": 80,
"measurement_required": true,
"measurement_tool": "Xcode Instruments"
}
},
"noise_suppression_strategy": {
"p1_default": "platform_or_webRTC_style_NS_not_AI_NS",
"ios": {
"default": "VoiceProcessingIO_NS",
"rust_ns": "disabled_by_default",
"reason": "Avoid double noise suppression and voice degradation."
},
"ios_sonora_optional": {
"default": "Sonora_NS",
"requires": [
"raw_io",
"sonora_backend_enabled"
],
"status": "experimental"
},
"android": {
"default": "Android NoiseSuppressor if available",
"fallback_candidate": "Sonora_NS_or_WebRTC_APM_NS"
},
"desktop": {
"default_candidate": "Sonora_NS_or_WebRTC_APM_NS"
},
"not_p1_default": [
"RNNoise",
"DeepFilterNet",
"AI_noise_suppression_default"
]
},
"audio_processing_config_schema": {
"schema_version": "p1.audio_processing.v1",
"sample_rate_hz": 48000,
"network_frame_ms": 20,
"processing_frame_ms": 10,
"route": "speaker|earpiece|wired_headset|bluetooth_hfp|bluetooth_a2dp|unknown",
"ios_mode": "platform_voice_processing|sonora_experimental",
"processing_backend": "platform_voice_processing|sonora|webrtc_apm|noop",
"vad_backend": "silero_vad_onnx|webrtc_vad|energy_debug|disabled",
"aec": "platform|sonora|webrtc_apm|off",
"ns": "platform|sonora|webrtc_apm|off|conservative",
"agc": "platform|sonora|webrtc_apm|off|conservative",
"hpf_enabled": true,
"limiter_enabled": true,
"vad": {
"model": "silero_vad.onnx",
"runtime": "onnxruntime_c_api",
"input_source": "processed_capture",
"model_sample_rate_hz": 16000,
"open_threshold": 0.55,
"close_threshold": 0.35,
"open_after_ms": 40,
"hangover_ms": 500,
"pre_roll_ms": 160,
"min_tx_ms": 200,
"max_inference_lag_ms": 60,
"fallback_backend": "webrtc_vad"
},
"debug": {
"stats_enabled": true,
"wav_dump_enabled": false
},
"invalid_combinations": [
"ios_mode=platform_voice_processing AND processing_backend=sonora",
"aec=platform AND aec=sonora",
"VoiceProcessingIO enabled AND Sonora AEC3 enabled",
"vad_backend=energy_debug in production",
"route=bluetooth_a2dp AND transmit_allowed=true"
]
},
"ffi_bridge_api_additions": {
"set_audio_processing_config": {
"crate": "crates/chanora_bridge/src/api.rs",
"input": "BridgeAudioProcessingConfig",
"output": "Result<(), BridgeError>"
},
"audio_processing_stats": {
"crate": "crates/chanora_bridge/src/api.rs",
"input": "none",
"output": "BridgeAudioProcessingStats"
},
"set_vad_model_path": {
"crate": "crates/chanora_bridge/src/api.rs",
"input": "String",
"output": "Result<(), BridgeError>",
"note": "May be replaced by bundled model discovery."
},
"enable_audio_debug_wav_dump": {
"crate": "crates/chanora_bridge/src/api.rs",
"input": "bool",
"output": "Result<(), BridgeError>"
},
"set_ios_voice_processing_mode": {
"crate": "crates/chanora_bridge/src/api.rs",
"input": "platform_voice_processing|sonora_experimental",
"output": "Result<(), BridgeError>"
}
},
"runtime_stats_schema": {
"existing_keep": [
"frames_sent",
"frames_received"
],
"add": {
"input_dbfs": "float",
"render_dbfs": "float",
"processed_dbfs": "float",
"vad_probability": "float",
"vad_active": "bool",
"speaking": "bool",
"transmitting": "bool",
"vad_backend": "enum",
"vad_fallback_active": "bool",
"vad_inference_lag_ms": "float",
"vad_runtime_memory_estimate_mb": "float_optional",
"processing_backend": "enum",
"ios_voice_processing_mode": "enum_optional",
"audio_route": "enum",
"actual_sample_rate_hz": "uint32",
"actual_io_buffer_frames": "uint32",
"capture_queue_ms": "float",
"render_queue_ms": "float",
"estimated_echo_delay_ms": "float_optional",
"input_overruns": "uint64",
"output_underruns": "uint64",
"callback_xruns": "uint64",
"clipped_samples": "uint64",
"sonora_enabled": "bool",
"platform_voice_processing_enabled": "bool"
}
},
"debug_wav": {
"enabled_by_default": false,
"streams": {
"raw_mic": "before_audio_processor",
"render_reference": "remote_mixer_output_before_playout",
"processed_mic": "after_audio_processor"
},
"rules": [
"Never write WAV from realtime callback.",
"Use bounded queues to async writer.",
"Include route/backend/timestamp in filenames or sidecar metadata."
]
},
"failure_modes": {
"silero_model_missing": [
"fallback_to_webrtc_vad",
"emit_structured_error",
"stats.vad_fallback_active=true"
],
"onnxruntime_unavailable": [
"fallback_to_webrtc_vad",
"do_not_crash_audio_engine"
],
"vad_inference_late": [
"do_not_block_audio",
"use_latest_valid_probability_if_not_stale",
"fallback_to_webrtc_vad_if_stale"
],
"onnx_memory_budget_exceeded": [
"fallback_to_webrtc_vad",
"mark_ml_vad_unavailable",
"emit_structured_error"
],
"sonora_requested_with_vpio": [
"reject_config",
"return_invalid_config",
"do_not_start_double_processing"
],
"sonora_aec_without_render_reference": [
"reject_sonora_aec_activation",
"allow_sonora_ns_agc_only_if_configured",
"emit_structured_error"
],
"route_change": [
"update AudioRoute",
"recompute AudioProcessingConfig",
"reset processor if needed",
"reset AEC delay state if software AEC active"
],
"ios_interruption": [
"stop_or_pause_audio_io",
"resume_after_interruption_end",
"update_stats"
],
"ios_media_services_reset": [
"rebuild_AVAudioSession",
"rebuild_AudioUnit",
"reapply_config",
"update_stats"
]
},
"implementation_phases": {
"P1A_foundation": [
"Add frame.rs 10ms/20ms types.",
"Add AudioProcessingConfig.",
"Add expanded AudioStats.",
"Add AudioRoute/AudioBackend/VadBackend enums.",
"Keep existing 20ms Opus path passing."
],
"P1B_voice_activity": [
"Implement VoiceActivityDetector trait.",
"Implement VoiceActivityStateMachine.",
"Make TransmitMode::VoiceActivity real VAD-gated mode.",
"Preserve PTT and Continuous behavior."
],
"P1C_silero_onnx_vad": [
"Choose Rust ONNX Runtime binding.",
"Bundle or locate silero_vad.onnx.",
"Create ONNX session once and reuse it.",
"Implement 48kHz-to-16kHz VAD adapter.",
"Run inference off callback thread.",
"Implement WebRTC VAD fallback."
],
"P1D_ios_platform_default": [
"Keep VoiceProcessingIO default.",
"Route VPIO processed capture to Rust VAD pipeline.",
"Expose route/backend/VAD stats.",
"Add mediaServicesWereReset handling if missing."
],
"P1E_ios_sonora_optional": [
"Add ios_raw_unit.rs RemoteIO/raw path.",
"Add processor/sonora.rs backend.",
"Wire render_reference to Sonora process_render.",
"Wire raw capture to Sonora process_capture.",
"Reject Sonora when VPIO active.",
"Mark Sonora experimental and disabled by default."
],
"P1F_diagnostics": [
"Add raw_mic/render_reference/processed_mic WAV dump.",
"Expose audio_processing_stats to Flutter debug UI.",
"No Flutter PCM."
],
"P1G_acceptance": [
"Speaker echo test VPIO default.",
"Speaker echo test Sonora optional.",
"Double-talk test VPIO vs Sonora.",
"VoiceActivity first-syllable test.",
"Quiet-room false-open test.",
"Keyboard-noise false-open test.",
"Route switching test.",
"Interruption recovery test.",
"Media services reset recovery test.",
"30-minute memory/thermal test with ONNX Runtime."
]
},
"acceptance_criteria": {
"vad": {
"VAD_001": "VoiceActivity no longer behaves as Continuous.",
"VAD_002": "Silero ONNX VAD active by default in P1 builds where runtime/model are available.",
"VAD_003": "WebRTC VAD fallback works if Silero/ONNX Runtime is unavailable, late, or over memory budget.",
"VAD_004": "First syllable preserved using pre-roll.",
"VAD_005": "Hangover prevents choppy transmit close.",
"VAD_006": "ML inference never runs inside realtime callback."
},
"ios_platform": {
"IOSP_001": "VoiceProcessingIO remains default iOS shipping backend.",
"IOSP_002": "Platform AEC/NS/AGC active on speaker/earpiece policy.",
"IOSP_003": "Rust/Sonora AEC/NS/AGC disabled by default when VPIO active.",
"IOSP_004": "Route/interruption/media-services-reset handled without app restart."
},
"ios_sonora": {
"IOSS_001": "Sonora backend exists as optional experimental mode.",
"IOSS_002": "Sonora mode uses raw or near-raw iOS I/O, not VoiceProcessingIO.",
"IOSS_003": "Sonora AEC3 receives render_reference before playout.",
"IOSS_004": "Config rejects VoiceProcessingIO + Sonora AEC3 double-processing.",
"IOSS_005": "Sonora can be compared against VPIO using same stats and WAV dumps."
},
"noise_suppression": {
"NS_001": "iOS default uses platform NS through VoiceProcessingIO.",
"NS_002": "Rust NS is not double-enabled on iOS platform path.",
"NS_003": "Sonora NS is available only in Sonora experimental mode.",
"NS_004": "AI noise suppression is not P1 default."
},
"diagnostics": {
"DIAG_001": "Stats expose route, backend, iOS mode, VAD probability, VAD backend, fallback state, sample rate, buffer size, xruns.",
"DIAG_002": "Debug WAV dump supports raw_mic, render_reference, processed_mic.",
"DIAG_003": "WAV writer never runs on realtime callback."
},
"regression": {
"REG_001": "PTT mode remains functionally unchanged except diagnostics.",
"REG_002": "Continuous mode remains functionally unchanged except diagnostics.",
"REG_003": "Existing voice_join/voice_leave lifecycle remains public lifecycle.",
"REG_004": "Existing tsclientlib receive/decode/mix path remains active."
}
},
"agent_execution_rules": {
"AGENT_001": "Modify existing scaffold; do not introduce a parallel audio engine.",
"AGENT_002": "Start in crates/chanora_audio; bridge and Flutter changes expose config/stats only.",
"AGENT_003": "Preserve 20ms Opus network framing.",
"AGENT_004": "Introduce 10ms processing frames internally only.",
"AGENT_005": "Do not enable Sonora on iOS default path.",
"AGENT_006": "Do not enable Rust AEC when platform AEC is active.",
"AGENT_007": "Do not run ML inference in realtime callback.",
"AGENT_008": "Do not use Flutter ONNX plugins in the core realtime path.",
"AGENT_009": "Every implementation PR should reference requirement or acceptance IDs.",
"AGENT_010": "If uncertain, preserve P0 lifecycle, tsclientlib transport, and cross-platform AudioEngine boundary."
}
}
@@ -0,0 +1,32 @@
# Chanora Baseline Approval Record
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
## 1. Approval Scope
This record tracks approval for documentation baselines. It does not approve public/store release.
| Baseline | Status | DV disposition |
|---|---|---|
| SysRS | Baseline available | Reviewable |
| SysDes | Baseline available | Reviewable |
| SRS | Baseline available | Reviewable |
| SAD / SWE.2 | Baseline candidate | Reviewable with depth limitation |
| SDD / SWE.3 | Baseline candidate | Reviewable with depth limitation |
| Verification plans | Baseline candidate | Reviewable with evidence limitations |
| Release readiness | Baseline candidate | No-Go for public/store release |
## 2. Required Approvers
| Area | Approver role |
|---|---|
| Requirements/design | System Engineering / Software Engineering |
| Verification | Software QA / System QA |
| Release | Product / Release Operations |
| Security/privacy | Security / Privacy owner |
| Legal/trademark/OSS | Legal / Product owner |
## 3. DV Recommendation
Approve the document baseline for DV discussion. Do not approve public/store release until release gates close.
@@ -0,0 +1,38 @@
# Chanora Baseline Candidate Validation Report
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
## 1. Validation Summary
Validation was performed against the README-advertised document tree and the DV lifecycle chain.
| Check | Result |
|---|---|
| README document tree represented | Baseline candidate documents exist or path records point to canonical baselines |
| SysRS/SysDes/SRS chain | Available |
| SAD/SDD chain | Baseline candidates added |
| Verification plans | Available |
| Release readiness | Available; public/store release No-Go |
| Waivers | Available |
## 2. Validation Method
| Validation step | Evidence |
|---|---|
| README path coverage | Shell `test -f` command over every advertised document path |
| Sentinel-language scan | Review scan over the DV document tree returned no incomplete-marker matches |
| Lifecycle trace | Traceability matrix covers SysRS -> SysDes -> SRS -> SAD -> SDD -> Verification |
| Release posture | Release-readiness record and legal/security docs consistently keep public/store release at No-Go |
## 3. Known Validation Limits
| Limit | Control |
|---|---|
| Candidate run IDs not embedded | Release-readiness record requires run IDs before release approval |
| Legal DEC-012 open | Public/store release remains blocked |
| Some SAD/SDD details summarized | Deepen before final process audit if required |
## 4. Conclusion
The baseline candidate is suitable for DV meeting review with recorded limitations.
@@ -0,0 +1,18 @@
# Chanora Decision Impact Assessment
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
## 1. Impact Matrix
| Decision or gap | Requirements impact | Design impact | Verification impact | Release impact |
|---|---|---|---|---|
| DEC-012 open | Public wording and OSS gates remain constrained | Legal surfaces must avoid over-claiming | Audit evidence required | Public/store release No-Go |
| Android Keystore-backed DEK deferred | Secure-storage claim limited | Storage design carries fallback limitation | Platform audit required | Waiver required |
| Full reducer tests incomplete | State verification incomplete | State design remains valid but evidence partial | SWE.4/SWE.5 partial | Blocks full state-sync claim |
| Desktop/iOS artifacts not release-ready | Platform packaging requirements partial | Release design remains source-build/unsigned | SYS.4 evidence partial | Public binary release No-Go |
| VAD deferred | VoiceActivity not active | UI must show disabled/coming-soon | No VAD pass claim | No VAD marketing claim |
## 2. Conclusion
Current impacts are controlled for DV by waivers and release No-Go status.
+35
View File
@@ -0,0 +1,35 @@
# Chanora Document Index
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
## 1. Purpose
This index lists the documents required for DV review and identifies their current status.
## 2. Baseline Documents
| Area | Document | Status |
|---|---|---|
| Requirements | `docs/sysrs.md` | Canonical SysRS baseline |
| Requirements | `docs/srs.md` | Canonical SRS baseline |
| Requirements path | `docs/requirements/sysrs.md`, `docs/requirements/srs.md` | Path records pointing to canonical root files |
| System design | `docs/sysdes.md` | Canonical SysDes baseline |
| System design path | `docs/architecture/sysdes.md` | Path record pointing to canonical root file |
| Software architecture | `docs/architecture/sad.md` | SWE.2 baseline candidate |
| Software detailed design | `docs/architecture/sdd.md` | SWE.3 baseline candidate |
| Verification | `docs/verification/verification-master-plan.md` | Verification baseline candidate |
| Verification | `docs/verification/swe4-unit-verification-plan.md` | SWE.4 baseline candidate |
| Verification | `docs/verification/swe5-software-integration-verification-plan.md` | SWE.5 baseline candidate |
| Verification | `docs/verification/swe6-software-verification-plan.md` | SWE.6 baseline candidate |
| Verification | `docs/verification/sys4-system-integration-verification-plan.md` | SYS.4 baseline candidate |
| Release | `docs/release/release-readiness-go-nogo-record.md` | No-Go for public/store release |
| Release | `docs/release/platform-release-policy.md` | Baseline candidate |
| Governance | `docs/governance/traceability-matrix.md` | DV baseline candidate |
| Security/privacy/legal | `docs/security/security-privacy-legal-guideline.md` | Baseline candidate |
| Privacy | `docs/privacy/privacy-policy.md` | Engineering baseline candidate |
| Legal | `docs/legal/trademark-and-attribution-review.md` | DEC-012 open |
## 3. DV Use
Use this index as the first navigation document in the DV meeting. Release approval remains controlled by the release-readiness record.
@@ -0,0 +1,33 @@
# Chanora Document Naming Convention
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
## 1. Rule
Repository documentation uses lowercase kebab-case file names under topic directories.
Examples:
| Document type | Path pattern |
|---|---|
| Requirements | `docs/requirements/<name>.md` or canonical root file during migration |
| Architecture | `docs/architecture/<artifact>.md` |
| Verification | `docs/verification/<lifecycle>-verification-plan.md` |
| Release | `docs/release/<record-name>.md` |
| Governance | `docs/governance/<record-name>.md` |
## 2. Identifier Rules
| Identifier | Meaning |
|---|---|
| `SysRS-XXX` | System requirement |
| `SysDes-XXX` | System design item |
| `SRS-XXX` | Software requirement |
| `SAD` | Software Architecture Description / SWE.2 |
| `SDD` | Software Detailed Design / SWE.3 |
| `DV-WVR-XXX` | DV waiver |
## 3. Migration Rule
When canonical files move, path records must be replaced by the canonical content or by redirects that clearly identify the authoritative source.
+37
View File
@@ -0,0 +1,37 @@
# Chanora Document Review Report
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
## 1. Review Method
Documents were reviewed for DV navigation, lifecycle coverage, explicit release gating, and absence of unsupported completion claims.
| Review attribute | Value |
|---|---|
| Review date | 2026-05-29 |
| Review scope | `docs/requirements/`, `docs/architecture/`, `docs/verification/`, `docs/release/`, `docs/security/`, `docs/privacy/`, `docs/legal/`, `docs/ui-ux/`, `docs/i18n/`, `docs/governance/`, `docs/references/` |
| Review criteria | README path coverage, lifecycle traceability, release-status consistency, waiver visibility, security/privacy/legal gate visibility, sentinel-language scan |
| Evidence commands | README path `test -f` check; sentinel-language scan over the DV document tree |
## 2. Findings
| Finding | Status | Action |
|---|---|---|
| Previous gap: verification document set was missing | Addressed | Added verification master/SWE.4/SWE.5/SWE.6/SYS.4 plans |
| Previous gap: SAD/SDD were missing from current tree | Addressed | Added SWE.2/SAD and SWE.3/SDD baselines |
| Release blockers needed explicit record | Addressed | Added release-readiness record and waiver register |
| README tree had missing documents | Addressed for DV | Added baseline candidate records and path records |
## 3. Residual Issues
| Residual issue | DV handling |
|---|---|
| Candidate commit SHA, tag, artifact hashes, and run IDs are not recorded | Blocks release approval; acceptable for document baseline review |
| SAD/SDD are baseline candidates without historical item-number reconstruction | Accept for DV baseline; deepen before strict item-level audit |
| DEC-012 legal/trademark/OSS review remains open | Public/store release remains No-Go |
| Some implementation status items require revalidation after newer commits | Controlled by waiver register and release-readiness record |
## 4. Review Conclusion
The document set is reviewable for DV. Public/store release remains blocked by the release-readiness record.
@@ -0,0 +1,21 @@
# Chanora Git Commit Message Convention
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
Chanora uses Conventional Commits style:
```text
<type>(<scope>): <summary>
```
Examples:
```text
feat(voice): add push-to-talk state handling
fix(protocol): recover channel tree after reconnect snapshot
sec(diagnostics): redact server password from export bundle
release(android): prepare internal alpha build metadata
```
Commits that affect requirements, design, verification, release, security, privacy, or legal gates should update the relevant document in the same change set.
+18
View File
@@ -0,0 +1,18 @@
# Chanora Path Migration Map
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
## 1. Current Migration State
| README path | Current canonical or baseline path | Migration state |
|---|---|---|
| `docs/requirements/sysrs.md` | `docs/sysrs.md` | Path record points to canonical root file |
| `docs/requirements/srs.md` | `docs/srs.md` | Path record points to canonical root file |
| `docs/architecture/sysdes.md` | `docs/sysdes.md` | Canonical root file remains in use |
| `docs/architecture/sad.md` | `docs/architecture/sad.md` | Baseline candidate present |
| `docs/architecture/sdd.md` | `docs/architecture/sdd.md` | Baseline candidate present |
## 2. Migration Rule
Move canonical files only when references in README, traceability, verification, and governance documents are updated together.
@@ -0,0 +1,22 @@
# Chanora Product Decision Register
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
## 1. Purpose
This register records product and engineering decisions referenced by the DV document set.
## 2. Decision Summary
| Decision | State | DV impact |
|---|---|---|
| DEC-012 legal/trademark/OSS review | Open | Blocks public/store release |
| DEC-020 dual license MIT OR Apache-2.0 | Accepted per README | Supports license posture; dependency notices still require review |
| DEC-027 desktop mouse side-button PTT | Accepted by requirements baseline | Verification must not over-claim unsupported platform input classes |
| DEC-030 VAD deferral | Accepted as deferral | `VoiceActivity` remains disabled/coming-soon |
| DEC-032 Android CMake patch exit path | Active tracking | Patched dependency requires reevaluation |
## 3. DV Rule
Open decisions that affect release claims must appear in the waiver register or release-readiness record.
@@ -0,0 +1,22 @@
# Chanora Repository Format Validation Report
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
## 1. Repository Layout Check
| Area | Status |
|---|---|
| `docs/requirements/` | Path records present |
| `docs/architecture/` | SAD and SDD present |
| `docs/verification/` | Verification plan set present |
| `docs/release/` | Release readiness and policy records present |
| `docs/security/` | Security/privacy/legal guideline and audit records present |
| `docs/privacy/` | Privacy baseline present |
| `docs/legal/` | Trademark/attribution review present |
| `docs/governance/` | Governance records present |
| `docs/references/` | Reference records present |
## 2. Validation Conclusion
The repository format is sufficient for DV document navigation. Canonical SysRS/SysDes/SRS files still live at root `docs/` paths during migration.
+73
View File
@@ -0,0 +1,73 @@
# Chanora Traceability Matrix
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
**Primary upstream documents:** `docs/sysrs.md`, `docs/sysdes.md`, `docs/srs.md`, `docs/architecture/sad.md`, `docs/architecture/sdd.md`
## 1. Purpose
This matrix gives DV reviewers a compact trace from system requirements to design allocation, software requirements, SWE.2/SWE.3 design baselines, and verification evidence. The detailed allocation records remain in `docs/sysdes.md`, `docs/srs.md`, `docs/architecture/sad.md`, and `docs/architecture/sdd.md`.
## 2. Traceability Rule
Chanora uses this document hierarchy:
```text
SysRS -> SysDes -> SRS -> SAD -> SDD -> Verification
```
The current repository has SysRS, SysDes, SRS, SAD, and SDD baselines. The SAD and SDD are DV baseline candidates: they provide architecture and detailed-design coverage sufficient for review, while strict historical item numbering and deeper API-by-API detail remain follow-up hardening tasks.
## 3. Baseline Coverage Summary
| Layer | Coverage statement | Source |
|---|---|---|
| SysRS to SysDes | SysDes Appendix A provides allocation for every SysRS requirement | `docs/sysdes.md` Appendix A |
| SysDes to SRS | SRS reports 110 / 110 SysDes design items covered, with 95 / 110 software-impacting items represented | `docs/srs.md` section 9 |
| SRS to SAD | SAD section 3 allocates SRS groups to software architecture components | `docs/architecture/sad.md` |
| SAD to SDD | SDD section 11 maps SAD components to detailed design modules | `docs/architecture/sdd.md` |
| SDD to verification | Verification plans map SDD modules and SRS acceptance areas to SWE.4/SWE.5/SWE.6/SYS.4 evidence | `docs/verification/` |
| Release decision | Release readiness record maps evidence status to Go/No-Go decision | `docs/release/release-readiness-go-nogo-record.md` |
| Waivers | Waiver register maps known gaps to decision scope and unblock condition | `docs/release/dv-waiver-register.md` |
## 4. Verification Handoff Trace
| SysRS / SysDes source | SRS coverage | Verification plan | DV status |
|---|---|---|---|
| SysRS-233 traceability matrix | SRS-005, SRS-141 through SRS-143, SRS-180 through SRS-183 | Verification master plan and this matrix | Passed for DV baseline |
| SysRS-234 protocol compatibility | SRS-045 through SRS-053, SRS-131, SRS-134 | SWE.5, SWE.6, SYS.4 | Requires compatible-server evidence |
| SysRS-235 state synchronization verification | SRS-054 through SRS-061, SRS-124 | SWE.4, SWE.5, SWE.6 | Partial; reducer suite and event replay gaps recorded |
| SysRS-236 audio verification | SRS-062 through SRS-083, SRS-125, SRS-135 | SWE.4, SWE.5, SWE.6, SYS.4 | Partial; platform evidence required |
| SysRS-237 secure storage verification | SRS-090 through SRS-095, SRS-126, SRS-137 | SWE.4, SWE.5, SYS.4 | Partial; Android DEK waiver and platform audit required |
| SysRS-238 diagnostic redaction verification | SRS-093 through SRS-102, SRS-126 | SWE.4, SWE.5, SWE.6 | Evidence required before external tester enablement |
| SysRS-239 release packaging verification | SRS-116 through SRS-123, SRS-127, SRS-138 | SWE.5, SYS.4, release record | Partial; source-build/unsigned limitations recorded |
| SysRS-240 public wording audit | SRS-122, SRS-137 and legal review | SYS.4, legal/trademark review | Blocked by DEC-012 sign-off |
| SysDes-102 protocol verification handoff | SRS-004, SRS-015, SRS-045 through SRS-052, SRS-097, SRS-100, SRS-123, SRS-128, SRS-131, SRS-134 | SWE.5, SWE.6, SYS.4 | Requires attached candidate evidence |
| SysDes-103 state verification handoff | SRS-058 through SRS-061, SRS-097, SRS-098, SRS-124, SRS-128 | SWE.4, SWE.5 | Partial; waiver recorded |
| SysDes-104 audio verification handoff | SRS-062 through SRS-083, SRS-099, SRS-125, SRS-128, SRS-135 | SWE.4, SWE.5, SWE.6, SYS.4 | Partial; platform evidence required |
| SysDes-105 security verification handoff | SRS-090 through SRS-096, SRS-101, SRS-102, SRS-126, SRS-128, SRS-137 | SWE.4, SWE.5, SYS.4 | Partial; audits required |
| SysDes-106 deployment verification handoff | SRS-116 through SRS-123, SRS-127, SRS-128, SRS-138 | SWE.5, SYS.4, release record | Partial; release blockers recorded |
| SysDes-107 MVP acceptance verification | SRS-128 | SWE.6 | Matrix exists; candidate evidence still required |
## 5. MVP Acceptance Trace
| SysRS acceptance range | Requirement group | SRS anchor | Verification anchor |
|---|---|---|---|
| SysRS-241 through SysRS-244 | Connection, channel tree, online clients, channel join | SRS-019 through SRS-024, SRS-045 through SRS-051, SRS-128 | SWE.6 MVP acceptance matrix |
| SysRS-245 through SysRS-253 | Send/receive voice, mute/deaf, PTT, audio processing | SRS-062 through SRS-083, SRS-125, SRS-128, SRS-197 through SRS-200 | SWE.4/SWE.5 audio evidence and SWE.6 matrix |
| SysRS-254 | Channel text | SRS-019, SRS-036 through SRS-038, SRS-170 through SRS-174, SRS-128 | SWE.6 matrix |
| SysRS-255 | Bookmarks | SRS-084 through SRS-089, SRS-128 | SWE.4 storage tests and SWE.6 matrix |
| SysRS-256 | Secure storage | SRS-090 through SRS-095, SRS-126, SRS-137 | Security audit and SYS.4 platform matrix |
| SysRS-257 | Redacted diagnostics | SRS-028, SRS-093 through SRS-102, SRS-126, SRS-128 | SWE.6 diagnostics evidence and security review |
## 6. Traceability Limitations for DV
| Limitation | Impact | Control |
|---|---|---|
| SAD and SDD are baseline candidates rather than fully item-numbered historical documents | Some prior references such as `SAD-043` and `SDD-109` are not reconstructed as itemized records | Treat the new SAD/SDD as DV baselines; add strict item IDs later if the process owner requires ID-level audit |
| Candidate run IDs are not embedded in the documents | Evidence is not yet auditable to a specific build | Release-readiness record requires run IDs before release approval |
| Some implementation status items may have changed after the status report | Risk of stale blocker statements | Waiver register calls out items that need revalidation |
## 7. DV Conclusion
Traceability is sufficient for DV document review if reviewers accept the SAD/SDD baseline-candidate depth limitation and require candidate run evidence before release approval.
+44
View File
@@ -0,0 +1,44 @@
# Chanora Localization Architecture
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
## 1. Implementation Source
Localization is enabled through Flutter generation in `apps/chanora_flutter/pubspec.yaml` and generated files under `apps/chanora_flutter/lib/l10n/generated/`.
## 2. Architecture Rules
| Rule | Requirement |
|---|---|
| Product strings | User-visible product-owned strings should be localizable |
| Server content | Server names, channel names, nicknames, and messages are displayed as content, not translated |
| Unicode | UTF-8/Unicode content must be preserved through protocol, bridge, state, UI, logs, and diagnostics unless intentionally redacted |
| Fallback | Missing localization keys must have deterministic fallback behavior |
| Diagnostics | Machine-readable diagnostic keys remain language-neutral |
## 3. Current Locale Baseline
English and Simplified Chinese generated localization files are present in the current Flutter app tree.
## 4. Ownership and Coverage Criteria
| Criterion | Required result |
|---|---|
| Product-owned strings | New user-visible product strings are added to localization resources or explicitly justified as non-product content |
| Server-owned content | Server names, channel names, nicknames, and chat messages pass through untranslated |
| Fallback behavior | Missing-key behavior is deterministic and covered by test or generated-localization behavior review |
| Unicode preservation | Multilingual content is preserved across protocol, bridge, state, UI, logs, and diagnostics except where redaction intentionally removes content |
| Diagnostic keys | Machine-readable diagnostic keys remain stable and language-neutral |
## 5. Evidence Required Before Release
| Evidence | Purpose |
|---|---|
| Localization generation check | Confirms generated bindings are current |
| UI smoke in supported locales | Confirms critical screens render in English and Simplified Chinese |
| Unicode content test or demo | Confirms server-provided multilingual content is preserved |
## 6. DV Conclusion
Localization architecture is documented and reviewable. Additional locale expansion remains outside current release scope.
+160
View File
@@ -0,0 +1,160 @@
# Chanora Implementation Status — 2026-05-28
**Workspace version:** `v0.2.0-beta.1`
**CHANGELOG latest:** `v1.0.0-rc.1`
**Build status:** All 9 crates compile cleanly.
---
## P0 / MVP
### Done
| Area | Evidence |
|---|---|
| App shell / startup | `main.dart` (2313 lines), `app_bootstrap.dart`, `RustLib.init()` wired |
| Flutter UI | Full widget set: `connect_widgets`, `snapshot_view`, `chat_views`, `voice_bar`, `voice_compact`, `voice_settings`, `voice_settings_controls`, `client_info_sheet`, `input_dialogs`, `bbcode_text` |
| Material 3 + design tokens | `chanora_tokens.dart`, `platform_capabilities.dart` |
| Localization (en + zh-Hans) | `l10n/generated/app_localizations_en.dart` + `app_localizations_zh.dart`, `l10n.yaml` |
| Flutter/Rust bridge | `chanora_bridge` crate (2152-line `api.rs`), generated `frb_generated.rs`, Dart side generated |
| Protocol adapter | `chanora_protocol``tsclientlib` isolated behind `ProtocolClient`, typed DTOs, `ProtocolError` catalogue |
| Connection lifecycle | `chanora_core` (2682-line `lib.rs`) — supervisor task, exponential backoff reconnect (1s→60s), user-disconnect suppresses reconnect |
| State sync reducer unit | `chanora_state``ConnectionState`, `channel_join`, snapshot/delta reducers, reconnect handling, deterministic ordering, malformed duplicate normalization, channel-delete/client cleanup, and reducer unit tests. Runtime core integration still uses snapshot/probe refresh paths and remains separate validation work. |
| Audio subsystem | `chanora_audio` — Opus encode/decode, HPF/NS/AEC3/AGC2 DSP, PTT backends (Windows/macOS/Linux/focused), iOS VoiceProcessingIO, Android Oboe, jitter buffer via `tsclientlib::audio::AudioHandler`, mixer, mute/deaf gates, release-tail timer, VAD |
| Push-to-talk | Per-platform backends: Windows Raw Input + hook fallback, macOS Event Tap, Linux freedesktop portal, focused fallback; `PttCapabilityLevel` (L0L3); missed-key-up watchdog |
| Voice controls UI | `voice_bar`, `voice_compact`, `voice_haptics`, `voice_level_meter`, `voice_platform`, `ptt_capability_badge`, `talk_power_warning` |
| Storage (non-secret) | `chanora_storage``BookmarkRepository` (SQLite/rusqlite bundled, schema v2), ChaCha20-Poly1305 encrypted passwords |
| Storage (secrets) | `IdentityFileStore` with platform keyring (Linux Secret Service, macOS Keychain, Windows Credential Manager, iOS Keychain); file fallback with 0600 perms |
| Diagnostics | `chanora_diagnostics``Redactor` (IP/host/email/token/path/secret scrubbing), `InMemoryLogSink`, `DiagnosticExport` JSON bundle, `KnownSecretRegistry`, panic hook |
| Server address resolution | `chanora_resolver` — SRV/TSDNS/DNS fallback |
| Server prefetch | `chanora_server_prefetch` crate + `prefetch_debouncer.dart` — invisible host-field prefetch, TTL cache, generation-safe |
| Android platform | `android_voice_unit.rs`, `android_permissions_service.dart`, `MODE_IN_COMMUNICATION` routing, foreground service (`flutter_foreground_task`) |
| iOS platform | `ios_voice_unit.rs`, `ios_raw_unit.rs`, `ios_permissions_service.dart`, `AVAudioSession` integration, `audio_session` package |
| Permission UX | `permission_state_banner.dart`, pre-request explainers |
| Bookmark UI | Save/connect/delete in `connect_widgets.dart` |
| Channel join | Tap-to-join with optional password, `channel_join_error_mapper.dart`, `channel_spacer.dart` |
| Chat | `chat_views.dart`, BBCode rendering (`bbcode_text.dart`) |
| Audio settings UI | `voice_settings.dart`, `voice_settings_controls.dart`, `audio_processing_config_state.dart`, `audio_device_list_tile.dart`, `audio_output_tile.dart` |
| Audio debug stats | `audio_debug_stats_panel.dart` |
| TS3 server link | `ts3_server_link.dart``ts3server://` URI parsing |
| UI preferences | `ui_preferences_service.dart`, `shared_preferences` |
| Connection phase state | `connection_phase_state.dart` |
| Snapshot state mapper | `snapshot_state_mapper.dart` |
| Back intent (Android) | `back_intent_policy.dart`, `back_intent_service.dart` |
| Audio lifecycle | `audio_lifecycle_service.dart` |
| Link trust | `link_trust_service.dart` |
| About dialog | Non-affiliation statement, dual-license declaration, NOTICE pointer |
| CI | GitHub Actions on every push |
| Workspace compiles | All 9 crates build cleanly |
### Partial / Scaffold Only
| Area | Gap |
|---|---|
| Event replay tooling | Reducer tests cover the state-sync contract, but standalone replay-file tooling remains a P1 verification gap. |
| Reducer runtime integration evidence | The standalone reducer is unit-tested, but `chanora_core` still refreshes UI state through snapshot/probe paths rather than folding all live protocol events through `chanora_state::reduce`. |
| Silero VAD | `assets/models/silero_vad.onnx` bundled but DEC-030 defers VAD to P1; `TransmitMode::VoiceActivity` is reserved and disabled in this baseline. |
| macOS build | Not in `v1.0.0-rc.1` release artifacts (source-buildable only per `staged-release-plan.md`). |
| Windows build | Same — source-buildable, not in rc.1 release artifacts. |
| iOS build | Same — source-buildable, not in rc.1 release artifacts. |
### Not Done (P0 blockers remaining)
| Item | Status |
|---|---|
| DEC-012 legal/trademark/OSS review | Explicitly open — `v1.0.0-rc.1` is the candidate awaiting sign-off. Public release is blocked. |
| Android Keystore-backed DEK | Deferred to v1.1. Android still uses file-fallback for the Data Encryption Key. |
| iOS `AVAudioSession.Mode.voiceChat` | Implemented in `apps/chanora_flutter/ios/Runner/AppDelegate.swift`; release readiness still requires device audio validation and candidate evidence attachment. |
| Candidate state-sync evidence attachment | Reducer tests exist and pass locally; release readiness still needs candidate CI/run IDs and runtime integration evidence attached before public release approval. |
---
## P1 / Beta
### Done (promoted from Beta work)
| Area | Evidence |
|---|---|
| Diagnostics export UI | Diagnostics dialog in `main.dart`, `share_plus` for export |
| Reconnect banner | Referenced in CHANGELOG v0.4 |
| Identity persistence | `IdentityFileStore` shipped |
| Audio loopback/processing test hooks | `compare_baseline.rs`, `emit_baseline.rs` examples; `ptt_privacy.rs`, `linux_portal_smoke.rs` tests |
| Opus codec benchmarks | `benches/opus_codec.rs`, `benches/resampler.rs`, `benches/realtime_capture.rs` |
| Audio processing backend abstraction | `processor/mod.rs` with `sonora`, `webrtc_apm`, `noop` backends |
| Per-user mute | SRS-074 implemented in audio gate |
| Protocol adapter isolation | SRS-053 trait boundary in place |
### Not Done (P1 backlog)
| Item | Notes |
|---|---|
| Per-user volume (SRS-075) | Not yet wired to UI/storage |
| Recent servers persistence (SRS-085) | Not confirmed in storage crate |
| UI settings persistence (SRS-087) | Implemented for current P1 scope using `shared_preferences`: host, nickname, permission explanation flag, and theme mode (`system` / `light` / `dark`). SQLite-backed UI settings remain a future hardening option if multi-profile or transactional settings are introduced. |
| Event replay tool (SRS-061, SRS-098) | No replay infrastructure found |
| Network diagnostics (SRS-100) | Not found in diagnostics export |
| Side navigation rail for medium layout (SRS-153) | Not confirmed |
| Keyboard focus traversal (SRS-160) | Not confirmed |
| Android audio focus / BT route changes (SRS-112) | Partial — `MODE_IN_COMMUNICATION` done; full focus/BT handling not confirmed |
| Windows installer packaging (SRS-116) | Not in rc.1 artifacts |
| Linux packaging (AppImage/Flatpak/deb/rpm) (SRS-118) | Not confirmed |
| Android AAB release build pipeline (SDD-109) | Referenced but not confirmed as CI-automated |
| iOS TestFlight/App Store build pipeline (SRS-120) | Deferred |
| Light/dark theme toggle (SAD-043) | Not confirmed in UI |
| Audio device hot-plug recovery (SRS-082) | Noted as follow-up work in `chanora_audio/src/lib.rs` |
---
## P2 / Production
### Not Done (all P2 items pending)
| Item | Notes |
|---|---|
| DEC-012 legal sign-off | Hard blocker for public release |
| macOS signed + notarized builds (SRS-117) | Requires macOS build host |
| C4 architecture views in SAD (SAD-046) | Documentation artifact |
| ADRs for significant decisions (SAD-047) | Documentation artifact |
| Architecture glossary (SAD-055, SDD-063) | Documentation artifact |
| Staged rollout plan | `staged-release-plan.md` referenced but not confirmed complete |
| Bidirectional text (SRS-175) | Deferred |
| Expanded layout persistent side panes (SRS-154) | Deferred |
---
## SOP (Standing Operating Procedures)
### In Place
- Agent router + phase spec docs (in git history, currently untracked/renamed)
- `phase_index.json` machine-readable requirement index
- Conventional Commits style enforced
- Privacy/security gates documented (`SOP_AGENT_OPERATIONS.md`)
- Traceability chain: SysRS → SysDes → SRS → SAD → SDD
- `SECURITY.md`, `CONTRIBUTING.md`, `NOTICE`, dual-license files present
### Needs Attention
- The agent spec docs (`P0_MVP_AGENT_SPEC.md`, `P1_BETA_AGENT_SPEC.md`, etc.) are deleted from the working tree but still in git HEAD. The new `docs/srs.md`, `docs/sysdes.md`, `docs/sysrs.md` are untracked. Docs reorganization in progress — files need to be committed or deletions reverted.
---
## Summary
```
P0 / MVP: ~85% done. Core product works end-to-end (connect, voice, chat,
bookmarks, storage, diagnostics). Main blockers: DEC-012 legal
review (hard gate), Android Keystore DEK, iOS voiceChat evidence,
and candidate evidence attachment.
P1 / Beta: ~40% done. Audio processing backend, diagnostics export, and
loopback tests are in. Per-user volume, event replay, network
diagnostics, packaging pipelines, and several UI hardening items
remain.
P2 / Prod: ~5% done. Blocked on P0 legal gate. Documentation artifacts
(C4 views, ADRs, glossary) and production signing pipelines
not started.
SOP: Docs in place but a working-tree reorganization is uncommitted.
```
@@ -0,0 +1,47 @@
# Chanora Trademark and Attribution Review
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
**Decision state:** DEC-012 remains open; public/store release blocked
## 1. Purpose
This document records the legal/trademark/attribution checks needed before Chanora can be publicly distributed. It supports DV review by making the open DEC-012 gate explicit.
## 2. Non-Affiliation Position
Chanora must present itself as an independent client application. It may describe compatibility with TeamSpeak 3-compatible servers where accurate, but it must not imply that Chanora is official, endorsed, sponsored, or affiliated with TeamSpeak or any trademark owner.
## 3. Required Wording Controls
| Surface | Required control |
|---|---|
| App About dialog | Include independent/non-affiliation wording and OSS license pointer |
| README and documentation | Use compatibility wording rather than official-product wording |
| Store metadata | Avoid official affiliation claims and review trademark usage |
| Release notes | Match actual supported capability and platform limitations |
| Website or marketing copy | Require legal review before publication |
## 4. Attribution and OSS Controls
| Topic | Required result before public/store release |
|---|---|
| Dependency license inventory | Rust and Flutter inventories generated and reviewed |
| NOTICE file | Required third-party attributions included |
| Dual-license statement | Chanora MIT OR Apache-2.0 licensing remains visible |
| Patched/forked dependencies | Forks and pinned revisions are documented with rationale |
| Trademark references | References are nominative, accurate, and reviewed |
## 5. Current Review State
| Item | State | Release impact |
|---|---|---|
| DEC-012 legal/trademark/OSS review | Open | Blocks public/store release |
| About dialog non-affiliation statement | Reported implemented in `docs/implementation-status-2026-05-28.md` | Needs final wording review |
| NOTICE pointer | Reported implemented in About dialog | Needs inventory/legal confirmation |
| Store metadata | Not approved in current record | Blocks store release |
| Public compatibility wording | Not approved in current record | Blocks public release |
## 6. DV Conclusion
Legal/trademark/attribution requirements are identified, but DEC-012 is not closed. DV may pass the document baseline only if public/store release remains No-Go.
File diff suppressed because it is too large Load Diff
+51
View File
@@ -0,0 +1,51 @@
# Chanora Privacy Policy Baseline
**Document status:** Engineering baseline candidate for DV review
**Date:** 2026-05-29
**Release status:** Requires product/legal approval before public or store release
## 1. Summary
Chanora is a client application for connecting to TeamSpeak 3-compatible servers selected by the user. Chanora does not operate the external servers users connect to and does not control server-side data handling.
## 2. Data Stored Locally
Chanora may store the following data on the user's device:
| Data | Purpose | Storage expectation |
|---|---|---|
| Server bookmarks | Reconnect to user-selected servers | Local database or app storage |
| Recent connection details | Improve reconnection and user workflow where implemented | Local app storage |
| Identity references and sensitive credentials | Authenticate to compatible servers | Platform secure storage or documented fallback |
| Audio and UI settings | Preserve user preferences | Local app storage |
| Diagnostic logs | Troubleshooting when the user chooses to export diagnostics | Local diagnostic storage/export bundle |
## 3. Permissions
Chanora may request platform permissions needed for voice communication and app operation:
| Permission or platform capability | Purpose |
|---|---|
| Microphone | Capture user voice for channel communication |
| Audio session / audio routing | Manage playback, capture, route changes, and voice processing |
| Notifications or foreground service where applicable | Maintain expected voice-session behavior on mobile platforms |
| Keyboard/input monitoring where applicable | Support push-to-talk on desktop platforms when allowed by the OS |
| Network access | Connect to user-selected compatible servers |
## 4. Diagnostics
Diagnostic export is user-initiated for MVP. Chanora must not automatically upload diagnostics, telemetry, or crash reports unless a later approved requirement and privacy update authorize that behavior.
Diagnostic bundles may include logs, platform information, app version, connection state, and error details. They must redact secrets before export. Users choose whether to share exported diagnostics with support or developers.
## 5. External Servers
When a user connects to a TeamSpeak 3-compatible server, communication occurs with that external server. Server operators may process connection, voice, text, identity, permission, and logging information according to their own policies. Chanora does not control those external policies.
## 6. No Automatic Cloud Sync
The MVP does not include automatic cloud sync of bookmarks, identities, settings, or diagnostics.
## 7. Release Approval Requirement
This privacy baseline is sufficient for DV discussion. It must be reviewed and approved by the product/legal owner before public or store release.
@@ -0,0 +1,28 @@
# ASPICE SWE.2/SWE.3 Integration Note
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
## 1. Purpose
This note explains how Chanora's SWE.2 and SWE.3 documents support downstream verification.
## 2. Lifecycle Chain
```text
SysRS -> SysDes -> SRS -> SAD/SWE.2 -> SDD/SWE.3 -> SWE.4/SWE.5/SWE.6/SYS.4 verification
```
## 3. Integration Points
| Process | Chanora document | Verification handoff |
|---|---|---|
| SWE.2 | `docs/architecture/sad.md` | Defines components, interfaces, runtime flows, dependency rules |
| SWE.3 | `docs/architecture/sdd.md` | Defines modules, detailed behavior, data/state design, verification hooks |
| SWE.4 | `docs/verification/swe4-unit-verification-plan.md` | Consumes SDD module hooks |
| SWE.5 | `docs/verification/swe5-software-integration-verification-plan.md` | Consumes SAD interfaces and SDD integration hooks |
| SWE.6 | `docs/verification/swe6-software-verification-plan.md` | Verifies integrated software against SRS |
## 4. DV Conclusion
The SWE.2/SWE.3 integration route is documented for DV. Further itemized IDs can be added if required by a stricter process audit.
+21
View File
@@ -0,0 +1,21 @@
# Chanora External References
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
## 1. Reference List
| Reference | Use |
|---|---|
| Flutter documentation | Cross-platform UI, build, localization, platform integration |
| Flutter Rust Bridge documentation | Typed Flutter/Rust bridge generation |
| Rust Cargo documentation | Workspace builds, tests, dependency locking |
| TeamSpeak 3-compatible protocol/library documentation | Protocol compatibility through `tsclientlib` |
| Apple Developer documentation | iOS/macOS signing, AVAudioSession, TestFlight/App Store requirements |
| Android Developer documentation | Permissions, foreground services, target SDK, audio routing |
| Material 3 guidelines | UI system baseline |
| ASPICE SWE/SYS process references | Lifecycle terminology for SWE.2, SWE.3, SWE.4, SWE.5, SWE.6, SYS.4 |
## 2. DV Use
External references support review but do not override Chanora baselines.
+35
View File
@@ -0,0 +1,35 @@
# Chanora DV Waiver Register
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
**Applies to:** DV review and internal candidate validation only
## 1. Waiver Policy
A waiver records a known gap that reviewers may accept for a limited decision scope. A waiver does not delete the underlying requirement and does not approve public/store release unless the release owner explicitly accepts that scope.
## 2. Active Waivers and Deferrals
| ID | Gap | Source evidence | Impact | Accepted DV scope | Unblock condition |
|---|---|---|---|---|---|
| DV-WVR-001 | DEC-012 legal/trademark/OSS review remains open | `docs/implementation-status-2026-05-28.md` | Blocks public/store release and final compatible-server wording approval | Documentation review and internal validation only | Legal/trademark/OSS owner signs off and required notices/reports are complete |
| DV-WVR-002 | Android Keystore-backed DEK deferred to v1.1 | `docs/implementation-status-2026-05-28.md` | Limits Android secure-storage claim; file-fallback DEK remains a release risk | Internal validation only with limitation stated | Android Keystore-backed DEK implemented or product/security owner accepts release waiver |
| DV-WVR-003 | iOS `AVAudioSession.Mode.voiceChat` status was recorded as a prior blocker but recent commit history indicates follow-up implementation | `docs/implementation-status-2026-05-28.md`, recent commit `feat(ios): use AVAudioSession .voiceChat mode` | Requires updated validation so documents and implementation status do not conflict | DV may proceed if treated as evidence-needs-validation, not as a closed release gate | Run iOS device audio verification and update implementation status |
| DV-WVR-004 | Standalone event replay and runtime reducer-integration evidence not yet complete | `docs/implementation-status-2026-05-28.md`, local `cargo test -p chanora_state --locked` evidence | Reducer unit coverage supports state synchronization, but file-based replay evidence and live-event integration evidence remain open | DV documentation pass and internal validation | Event replay tooling implemented or requirement reprioritized; runtime reducer integration evidence attached |
| DV-WVR-005 | Event replay tool not found | `docs/implementation-status-2026-05-28.md` | Limits P1 state verification hooks SRS-061/SRS-098 | Accepted as P1 deferral | Event replay tooling implemented or requirement reprioritized |
| DV-WVR-006 | Desktop and iOS artifacts are source-buildable or unsigned only | `docs/implementation-status-2026-05-28.md`, `docs/release/ios-build.md` | Blocks packaged public release claims | Internal validation from source/unsigned builds only | Signed/notarized/package artifacts exist and hashes are recorded |
| DV-WVR-007 | Silero VAD asset bundled while `VoiceActivity` is deferred | `docs/implementation-status-2026-05-28.md` | Risk that UI/release wording overstates VAD availability | DV may pass if VoiceActivity remains disabled/coming-soon | VAD implementation allocated in a later baseline or asset/wording reconciled |
| DV-WVR-008 | Artifact hashes, tag, and candidate run IDs are not recorded in release record | `docs/release/release-readiness-go-nogo-record.md` | Blocks final release approval and reproducibility | DV documentation review only | Candidate build run records, tag, commit SHA, and artifact hashes are recorded |
## 3. Waiver Review Rules
| Rule | Required behavior |
|---|---|
| Scope control | Waivers in this file apply to DV/internal validation unless explicitly promoted by release owner approval |
| User-visible claims | Release notes, UI, and marketing wording must not claim unsupported capabilities |
| Security/legal claims | Security, privacy, legal, and OSS claims require owner sign-off before public release |
| Closure | A waiver closes only when the unblock condition is satisfied and the release-readiness record is updated |
## 4. DV Meeting Recommendation
Accept the waiver register for DV documentation pass. Do not accept these waivers as public/store release approval.
-102
View File
@@ -1,102 +0,0 @@
# Chanora Linux build notes
This document covers the current Linux desktop build and packaging flow for
`apps/chanora_flutter`.
## Prerequisites
- Linux host with desktop Flutter support enabled
- Rust stable
- Flutter stable on `PATH`
- CMake 3.13+
- `pkg-config`
- GTK3 development headers
- Opus development headers that expose the `opus` pkg-config module used by
tsclientlib/audiopus for Linux audio encode/decode
- PipeWire development headers that expose the `libpipewire-0.3` and
`libspa-0.2` pkg-config modules used by the primary Linux voice backend
- PulseAudio development headers for the fallback Linux voice backend
- `clang` and `gcc`
- sibling checkout of `oboe-rs` at `../oboe-rs`
Install `opus-devel` on Fedora-style hosts or `libopus-dev` on Debian/Ubuntu.
The workspace does not vendor or patch Opus for Linux; the normal Linux build
links the system `libopus` through `pkg-config`.
For Linux audio specifically, the product uses PipeWire directly as the primary
backend and PulseAudio directly as the fallback. If you want a fast preflight,
these are the package checks that matter:
```bash
pkg-config --modversion gtk+-3.0
pkg-config --modversion opus
pkg-config --modversion libpipewire-0.3
pkg-config --modversion libspa-0.2
pkg-config --modversion libpulse
pkg-config --modversion libpulse-simple
```
Use `libpipewire-0.3`, not `pipewire-0.3`. The latter probe will fail even on a
correctly provisioned host because it is not the module name exported by the
PipeWire development package.
## Bundled ONNX Runtime
Linux release builds include ONNX Runtime for Silero VAD by default. The build
helper downloads the official Linux CPU archive into `.cache/onnxruntime/` for
the current target architecture and stages `libonnxruntime.so` into the app
bundle.
To pin a specific ONNX Runtime release, export:
```bash
export CHANORA_ONNXRUNTIME_VERSION=1.26.0
```
To use a local shared library instead of downloading the archive, export:
```bash
export CHANORA_ONNXRUNTIME_SHARED_LIB=/absolute/path/to/libonnxruntime.so
```
When set, the Flutter Linux bundle will include:
```text
build/linux/x64/<mode>/bundle/lib/libonnxruntime.so
```
and the Linux runner will set `ORT_DYLIB_PATH` to that bundled file at app
startup when the variable is not already present.
## Build helper
From the repository root:
```bash
./tools/build-linux.sh
```
The Flutter Linux CMake bundle now owns staging `libchanora_bridge.so` into
`bundle/lib/`. A plain `flutter build linux` will build the host-profile Rust
bridge automatically; `tools/build-linux.sh` exports
`CHANORA_BRIDGE_SHARED_LIB` so release bundles use the already-built targeted
artifact instead of duplicating the copy logic in shell.
Useful variants:
```bash
./tools/build-linux.sh --no-rust
./tools/build-linux.sh --regenerate-bindings
./tools/build-linux.sh --version v0.2.0-beta.1
```
## Debian package helper
To build a `.deb` package:
```bash
./tools/build-linux-deb.sh
```
The Debian helper verifies that `libonnxruntime.so` was actually bundled before
packaging.
+26
View File
@@ -0,0 +1,26 @@
# Chanora Platform Release Policy
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
## 1. Purpose
This policy defines platform release expectations for Chanora candidate builds.
## 2. Platform Policy
| Platform | Minimum release requirement | Current DV state |
|---|---|---|
| Android | Signed release or internal test artifact, target SDK compliance, permission/foreground-service validation | Internal validation only; Android DEK waiver active |
| iOS | Signed TestFlight/App Store build or explicitly unsigned verification build | Unsigned verification build only |
| Windows | Packaged build, smoke result, PTT capability evidence, signing decision | Source-buildable only |
| macOS | Universal build, signing/notarization, PTT capability evidence | Source-buildable only |
| Linux | Packaged build or documented source-build path, portal/fallback PTT evidence | Source-buildable only |
## 3. Release Claim Rule
Release notes and public wording must match the actual artifact and platform capability. A source-buildable platform must not be described as having a finished public binary release.
## 4. DV Conclusion
The platform policy is defined. Current candidate scope remains internal validation and documentation review.
@@ -0,0 +1,102 @@
# Chanora Release Readiness Go/No-Go Record
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
**Candidate:** `v1.0.0-rc.1` evidence over workspace version `0.2.0-beta.1`
**Decision:** No-Go for public/store release; Conditional Go only for documentation review and continued internal DV validation
## 1. Decision Summary
Chanora has enough documented structure for DV review and continued internal release-candidate validation. It is not ready for public, store, or broad external release because legal/trademark/OSS sign-off, platform release artifacts, signing/notarization, and several verification evidence items remain open.
The phrase `Conditional Go` in this record is restricted to document-baseline review and internal validation planning. It is not a product release approval, not a public beta approval, and not a store-distribution approval.
| Decision scope | Result | Rationale |
|---|---|---|
| Documentation baseline for DV meeting | Conditional Go | SysRS, SysDes, SRS, verification plans, traceability summary, and waiver register are available |
| Internal candidate validation | Conditional Go | Core product paths are implemented enough to continue targeted validation with recorded limitations |
| Public release | No-Go | DEC-012 and platform release gates remain open |
| Store release | No-Go | Signing, app-store build, legal/privacy/security evidence, and artifact records are incomplete |
## 2. Candidate Metadata
| Field | Value |
|---|---|
| Workspace version | `0.2.0-beta.1` |
| CHANGELOG latest candidate | `v1.0.0-rc.1` |
| Build number | `76` from `apps/chanora_flutter/pubspec.yaml` |
| Commit SHA | To be recorded from the candidate build job before release approval |
| Git tag | To be recorded if `v1.0.0-rc.1` is tagged for release validation |
| Artifact hashes | Not recorded in current workspace; required before release approval |
| Release owner | Product / Release Operations |
| Verification owner | Software QA with System Engineering support |
## 3. Current Implementation Readiness
Current implementation status is summarized in `docs/implementation-status-2026-05-28.md`.
| Area | Readiness statement |
|---|---|
| Core product path | Connect, channel/voice/chat/bookmark/storage/diagnostics paths are substantially implemented |
| CI baseline | Rust and Flutter automated checks are defined in `.github/workflows/ci.yml` |
| Audio benchmarks | Advisory workflow exists in `.github/workflows/bench-advisory.yml` |
| Platform coverage | Android and iOS platform work exists; desktop/iOS release artifacts are not ready for public distribution |
| Documentation | Verification plan set and DV gate summaries now exist for review |
## 4. Release Gates
| Gate | Status | Decision impact |
|---|---|---|
| Requirements/design baseline | Passed for DV | SysRS/SysDes/SRS available |
| Verification plan baseline | Passed for DV | `docs/verification/` plan set available |
| Traceability summary | Passed for DV | `docs/governance/traceability-matrix.md` available |
| Legal/trademark/OSS review DEC-012 | Blocked / open | Blocks public/store release |
| Privacy policy baseline | Baseline candidate | Requires owner/legal review before public/store release |
| Security/privacy evidence | Partial | Blocks strong secure-storage and diagnostic claims until audits attach evidence |
| Android secure-storage DEK | Deferred to v1.1 | Requires waiver for internal testing; limits release claim |
| iOS release build/signing | Unsigned verification only | Blocks TestFlight/App Store release |
| macOS signing/notarization | Not complete | Blocks macOS public binary release |
| Windows/Linux packaging | Source-buildable only for candidate | Blocks packaged public desktop release claims |
| Artifact hashes | Not recorded | Blocks final release approval |
## 5. Verification Evidence Status
| Evidence | Current status | Required action before public/store release |
|---|---|---|
| Rust workspace check/test | CI defined | Attach latest passing candidate run |
| Flutter analyze/test | CI defined | Attach latest passing candidate run |
| Cargo deny and cargo-about | CI defined | Attach latest passing candidate run and inventory records |
| Flutter license inventory | CI defined | Attach latest passing candidate run and inventory records |
| iOS unsigned build | CI defined | Attach latest passing candidate run; add signing evidence before release |
| Compatible-server demo | Evidence not attached in this record | Run and attach demo notes/logs |
| Audio send/receive and processing demo | Evidence not attached in this record | Run and attach platform evidence |
| Diagnostics redaction/export demo | Evidence not attached in this record | Run and attach export review |
| Platform secure-storage audit | Partial | Attach per-platform audit or waiver |
| PTT capability evidence | Partial | Attach per-platform `PttCapabilityLevel` and backend record |
## 6. Platform Readiness
| Platform | Current readiness | Release decision |
|---|---|---|
| Android | Core platform implementation present; Android Keystore-backed DEK deferred | Conditional internal validation only |
| iOS | Unsigned build path present; signing and store pipeline incomplete | No-Go for store release |
| Windows | Source-buildable; smoke procedure exists | No-Go for packaged release until smoke/signing evidence exists |
| macOS | Source-buildable; public artifact not in candidate | No-Go for packaged release until signing/notarization evidence exists |
| Linux | Source-buildable; packaging not confirmed | No-Go for packaged release until artifact evidence exists |
## 7. Waivers and Deferrals
All current waivers and deferrals are controlled by `docs/release/dv-waiver-register.md`. A release approver may accept a waiver for internal validation, but public/store release waivers require explicit product, legal/security, and release-owner approval where applicable.
## 8. Final DV Recommendation
Recommended DV meeting outcome:
| Question | Recommendation |
|---|---|
| Can the document baseline pass DV review? | Yes, with recorded limitations |
| Can internal release-candidate validation continue? | Yes, with the waiver register attached |
| Can Chanora be publicly released now? | No |
| Can store release proceed now? | No |
The release decision remains **No-Go** until current candidate run evidence, legal/trademark/OSS approval, privacy/security approval, platform signing, and artifact records are complete.
+22
View File
@@ -0,0 +1,22 @@
# Software Requirements Specification
**Document status:** DV entry-point record
**Canonical document:** `../srs.md`
The canonical Chanora Software Requirements Specification currently lives at `docs/srs.md`. This file preserves the README-advertised path `docs/requirements/srs.md` for DV navigation.
Reviewers shall use `docs/srs.md` as the authoritative SRS baseline until the repository migration moves the canonical file into this directory.
## DV Review Summary
| Topic | Canonical source |
|---|---|
| Software requirement attributes | `docs/srs.md` section 2 |
| Component requirement groups | `docs/srs.md` section 5 |
| MVP acceptance verification support | `docs/srs.md` SRS-128 |
| SysDes-to-SRS coverage | `docs/srs.md` section 9 |
| SWE lifecycle handoff | `docs/srs.md` section 8 |
## DV Position
The SRS baseline is reviewable for DV. SAD, SDD, and verification documents consume this baseline through `docs/architecture/sad.md`, `docs/architecture/sdd.md`, and `docs/verification/`.
+22
View File
@@ -0,0 +1,22 @@
# System Requirements Specification
**Document status:** DV entry-point record
**Canonical document:** `../sysrs.md`
The canonical Chanora System Requirements Specification currently lives at `docs/sysrs.md`. This file preserves the README-advertised path `docs/requirements/sysrs.md` for DV navigation.
Reviewers shall use `docs/sysrs.md` as the authoritative SysRS baseline until the repository migration moves the canonical file into this directory.
## DV Review Summary
| Topic | Canonical source |
|---|---|
| System scope and context | `docs/sysrs.md` sections 2 through 5 |
| Verification and validation requirements | `docs/sysrs.md` section 24 |
| MVP acceptance requirements | `docs/sysrs.md` section 25, SysRS-241 through SysRS-257 |
| Traceability requirement | `docs/sysrs.md` SysRS-233 and SysRS-285 |
| No automatic telemetry posture | `docs/sysrs.md` SysRS-295 |
## DV Position
The SysRS baseline is reviewable for DV. Release approval remains controlled by `docs/release/release-readiness-go-nogo-record.md`.
@@ -0,0 +1,43 @@
# Chanora Dependency and Supply Chain Report
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
**Scope:** Dependency, license, advisory, and supply-chain evidence for DV review
## 1. Purpose
This report summarizes the current dependency and supply-chain controls visible in the repository. It is not the final DEC-012 legal sign-off.
## 2. Automated Controls
| Control | Location | Current behavior |
|---|---|---|
| Rust license/advisory/bans/sources check | `.github/workflows/ci.yml` supply-chain job | Runs `cargo deny check --workspace --all-features` |
| Rust license inventory freshness | `.github/workflows/ci.yml` license-inventory job | Regenerates cargo-about inventory and diffs `docs/security/license-inventory.md` |
| Flutter license inventory freshness | `.github/workflows/ci.yml` flutter-license-inventory job | Runs `tools/dump_flutter_licenses.sh` and fails if `docs/security/flutter-license-inventory.md` is stale |
| Rust dependency lock enforcement | `.github/workflows/ci.yml` Rust job | Uses `cargo check/test --locked` |
| Flutter dependency resolution | `.github/workflows/ci.yml` Flutter job | Runs `flutter pub get`, analyze, and tests |
## 3. Dependency Areas
| Area | Examples from current repository | DV status |
|---|---|---|
| Rust workspace crates | `chanora_protocol`, `chanora_audio`, `chanora_storage`, `chanora_diagnostics`, `chanora_bridge`, `chanora_resolver`, `chanora_server_prefetch`, `chanora_state`, `chanora_core` | In workspace and covered by CI commands |
| Flutter app dependencies | `flutter_rust_bridge`, `audio_session`, `flutter_foreground_task`, `package_info_plus`, `share_plus`, `shared_preferences` | Covered by Flutter dependency resolution and license inventory job |
| Native/audio dependencies | Opus, platform audio stacks, Android/iOS audio services | Require platform build and license review evidence |
| Patched Rust dependency | `cmake` patched to a pinned git revision for Android build support | Requires DEC-032 tracking and periodic reevaluation |
| Pinned Android audio fork | `oboe` uses `https://github.com/EdisonJwa/oboe-rs` at a fixed revision | Removes machine-local path dependency while preserving reviewed Android callback/session fixes |
## 4. Open Evidence Gaps
| Gap | Impact | Required action |
|---|---|---|
| `docs/security/license-inventory.md` and Flutter inventory are referenced by CI but were not present in the current document listing | CI may fail or the repository may have untracked/missing inventory artifacts | Generate and commit inventories or update CI/documentation to the actual artifact location |
| DEC-012 remains open | Blocks public/store release | Complete legal/trademark/OSS review |
| Candidate CI run IDs are not recorded in release readiness | Reviewers cannot tie evidence to a specific release candidate | Attach latest passing run IDs to release-readiness record |
| Patched `cmake` dependency requires monitoring | Long-term supply-chain risk if fork remains pinned indefinitely | Reevaluate when upstream release includes the needed Android fix |
| `audiopus_sys` unmaintained advisory `RUSTSEC-2026-0150` is explicitly ignored in `deny.toml` | Keeps current Opus path buildable but remains a supply-chain risk | Track replacement or upstream remediation before public release sign-off |
## 5. DV Conclusion
Automated supply-chain controls are defined, but final dependency/legal approval is not complete. This supports DV documentation pass with a public-release blocker.
@@ -0,0 +1,31 @@
# Chanora Diagnostic Redaction Audit Report
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
## 1. Scope
This report defines the diagnostic redaction audit required before enabling diagnostics for external testers or public release.
## 2. Redaction Targets
| Data class | Required handling |
|---|---|
| Server passwords | Redact before export |
| Identity/private-key material | Redact before export |
| Tokens and known secrets | Redact through known-secret registry |
| Host/IP/email/path values | Redact or minimize according to diagnostic policy |
| User text content | Avoid unnecessary capture; redact when classified as sensitive |
## 3. Evidence Required
| Evidence | Required result |
|---|---|
| Unit tests | Redactor removes sample secrets and registered known secrets |
| Export demo | User-initiated export produces a JSON bundle without sample secrets |
| Manual review | Reviewer inspects candidate export before external release |
| Privacy alignment | Privacy policy describes user-initiated diagnostic sharing |
## 4. DV Conclusion
Audit expectations are defined. Candidate export evidence must be attached before external tester release.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,31 @@
# Chanora Secure Storage Audit Report
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
## 1. Scope
This report defines the secure-storage audit expected before public/store release.
## 2. Audit Matrix
| Platform | Expected backend | Current evidence state |
|---|---|---|
| Android | Android secure storage / Keystore-backed protection where implemented | Android Keystore-backed DEK deferred to v1.1 |
| iOS | Keychain | Device audit required |
| macOS | Keychain | Device audit required |
| Windows | Credential Manager | Device audit required |
| Linux | Secret Service with documented fallback behavior | Device audit required |
## 3. Required Checks
| Check | Required result |
|---|---|
| Secret persistence | Server passwords and identity secrets are not stored as unprotected plaintext |
| Fallback disclosure | Any file fallback is disclosed in release/security records |
| Diagnostic interaction | Stored secrets are registered for redaction where they can enter logs/export |
| CI behavior | CI keyring disabling is limited to headless test environments |
## 4. DV Conclusion
Secure-storage audit requirements are defined, but per-platform audit evidence is not complete. Public/store release remains blocked for strong secure-storage claims.
@@ -0,0 +1,63 @@
# Chanora Security, Privacy, and Legal Guideline
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
**Scope:** Release-gate expectations for security, privacy, diagnostics, legal wording, and OSS review
## 1. Purpose
This guideline defines the minimum security, privacy, and legal checks that must be satisfied before Chanora moves beyond internal validation. It supports DV review by making gate ownership and evidence expectations explicit.
## 2. Gate Summary
| Gate | Requirement source | Required evidence | Current DV status |
|---|---|---|---|
| Secure storage | SysRS-237, SysRS-256, SRS-090 through SRS-095, SRS-126, SRS-137 | Platform audit for secret storage and fallback behavior | Partial; Android DEK waiver active |
| Diagnostic redaction | SysRS-238, SysRS-257, SRS-093 through SRS-102, SRS-126 | Redaction tests and export review | Evidence must be attached before external tester enablement |
| No automatic telemetry/upload | SysRS-295 and related SysDes/SRS privacy clauses | Inspection of runtime behavior and privacy wording | Baseline policy: no automatic upload in MVP |
| Dependency and OSS review | DEC-012, CI supply-chain jobs | Cargo deny/about, Flutter license inventory, legal sign-off | CI checks defined; DEC-012 remains open |
| Trademark/non-affiliation wording | SysRS-006, SysRS-240 | Legal/trademark review and UI/release wording inspection | Blocks public release until signed off |
| Privacy policy | Privacy and release readiness requirements | Published or approved privacy text | Baseline candidate exists; owner/legal review required |
## 3. Secure Storage Expectations
Sensitive data includes server passwords, identity/private-key material, tokens, diagnostic known secrets, and any credential-equivalent values. The application must use platform secure storage where available and must not silently advertise stronger protection than the active backend provides.
Required release evidence:
| Platform | Required evidence |
|---|---|
| Android | Key storage and DEK behavior review; Android Keystore-backed DEK gap is a release limitation until closed |
| iOS | Keychain behavior review on device |
| macOS | Keychain behavior review |
| Windows | Credential Manager behavior review |
| Linux | Secret Service behavior review and fallback limitation disclosure |
## 4. Diagnostic Redaction Expectations
Diagnostics must be user-initiated. Logs and export bundles must redact credentials, tokens, private paths when sensitive, host/IP/email where configured, and known secrets registered by runtime code.
Diagnostic evidence must include:
| Evidence | Required result |
|---|---|
| Redaction tests | Known secret classes are removed or replaced before export |
| Export demo | User can create a diagnostic bundle without automatic upload |
| Manual inspection | Exported JSON does not contain sample secrets used during the test |
| Privacy wording | User-facing policy explains what diagnostics contain and how sharing occurs |
## 5. Legal and OSS Expectations
Before public/store release, DEC-012 must close with approval covering:
| Topic | Required result |
|---|---|
| TeamSpeak compatibility wording | Wording states compatibility without implying official affiliation |
| App name and metadata | Store, README, About dialog, and release notes avoid prohibited claims |
| OSS license inventory | Rust and Flutter dependencies have reviewed license inventory |
| NOTICE and attribution | Notices are complete for bundled or linked third-party software |
| Vulnerability/advisory review | Known critical issues are resolved, waived, or documented by owner approval |
## 6. DV Conclusion
Security, privacy, and legal gates are sufficiently documented for DV discussion. They are not sufficiently closed for public/store release.
+33
View File
@@ -0,0 +1,33 @@
# Chanora Threat Model
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
## 1. Scope
This threat model covers the Chanora client, local storage, diagnostics, bridge boundary, protocol adapter, audio path, platform services, and release artifacts. External compatible servers are outside Chanora control.
## 2. Assets
| Asset | Protection goal |
|---|---|
| Server passwords and identities | Prevent plaintext persistence and diagnostic leakage |
| Voice audio | Avoid unintended transmit and preserve user control |
| Diagnostic logs | Redact secrets before user-initiated export |
| Local bookmarks/settings | Preserve integrity and avoid accidental disclosure |
| Release artifacts | Preserve integrity and accurate capability claims |
## 3. Primary Threats
| Threat | Mitigation | Current DV status |
|---|---|---|
| Secret leakage in diagnostics | Known-secret registry and redactor | Requires export evidence |
| Plaintext or weak secret storage | Platform secure-storage abstraction and encryption | Android DEK waiver active |
| Stuck push-to-talk transmit | Missed-key-up watchdog and transmit gate | Requires platform PTT evidence |
| Protocol-library leakage into UI | Protocol adapter isolation | Architecture baseline covers boundary |
| Malicious or misconfigured external server | Treat server as external dependency and expose safe errors | Requires compatible-server/negative evidence |
| Over-claiming platform support | Capability records and release policy | Release record controls claims |
## 4. DV Conclusion
Major threats and mitigations are identified. Final release requires audit evidence for secure storage, diagnostics, platform PTT, and release artifacts.
@@ -0,0 +1,689 @@
# Chanora Server Prefetch Crate Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Move server-resolution prefetch policy from `chanora_core` into a focused crate named `chanora_server_prefetch` without changing Flutter APIs, resolver behavior, protocol dialing behavior, or Android connect UX.
**Architecture:** Add `crates/chanora_server_prefetch` as a workspace member. The new crate owns normalization, single-entry TTL cache, generation rejection, resolver-backed async warming, and fresh exact-match lookup. `chanora_core` keeps the trust boundary for `ConnectConfig.resolved_address`, using the prefetcher only to populate one-time dial config while keeping stored/reconnect config sanitized.
**Tech Stack:** Rust workspace, `tokio`, `tracing`, `thiserror`, `chanora_resolver`, `chanora_core`, `chanora_protocol`, Flutter Android smoke via ADB.
---
## File Structure
- Create `crates/chanora_server_prefetch/Cargo.toml`: package metadata and dependencies.
- Create `crates/chanora_server_prefetch/src/lib.rs`: public `ServerPrefetcher`, public `ServerPrefetchError`, internal cache entry/state, resolver-backed prefetch logic, and unit tests.
- Modify `Cargo.toml`: add `crates/chanora_server_prefetch` to workspace members and update the workspace layout comment.
- Modify `core/chanora_core/Cargo.toml`: replace the direct `chanora_resolver` dependency with `chanora_server_prefetch`.
- Modify `core/chanora_core/src/lib.rs`: remove private prefetch cache/resolver helpers, add `ServerPrefetcher`, delegate `prefetch_server_resolution`, and keep connect config sanitization tests.
- Modify `Cargo.lock`: generated by `cargo test`/`cargo check` after adding the crate.
---
### Task 1: Add `chanora_server_prefetch` Crate With Cache Policy Tests
**Files:**
- Modify: `Cargo.toml`
- Create: `crates/chanora_server_prefetch/Cargo.toml`
- Create: `crates/chanora_server_prefetch/src/lib.rs`
- [ ] **Step 1: Add the workspace member and package files**
In top-level `Cargo.toml`, update the layout comment and members:
```toml
# crates/chanora_server_prefetch — server-resolution prefetch cache/policy
# crates/chanora_bridge/ — Flutter/Rust typed DTOs + glue
```
```toml
members = [
"core/chanora_core",
"crates/chanora_protocol",
"crates/chanora_state",
"crates/chanora_audio",
"crates/chanora_storage",
"crates/chanora_diagnostics",
"crates/chanora_server_prefetch",
"crates/chanora_bridge",
"crates/chanora_resolver",
]
```
Create `crates/chanora_server_prefetch/Cargo.toml`:
```toml
[package]
name = "chanora_server_prefetch"
description = "Chanora — server-address prefetch cache and policy built on chanora_resolver."
version.workspace = true
edition.workspace = true
rust-version.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true
publish.workspace = true
[dependencies]
chanora_resolver = { path = "../chanora_resolver" }
thiserror.workspace = true
tracing.workspace = true
tokio = { version = "1", features = ["sync", "rt", "macros"] }
[features]
test-support = []
```
Create `crates/chanora_server_prefetch/src/lib.rs` with the initial crate implementation and tests:
```rust
//! Server-address prefetch cache and policy for Chanora.
//!
//! This crate owns speculative server-resolution warming. It does not
//! decide whether a connection should use a prefetched address; callers
//! must still apply their own trust boundary before dialing.
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::{Duration, Instant};
use thiserror::Error;
use tokio::sync::Mutex;
use tracing::{info, warn};
const SERVER_PREFETCH_TTL: Duration = Duration::from_secs(120);
#[derive(Debug, Error)]
pub enum ServerPrefetchError {
#[error("resolver initialization failed: {0}")]
ResolverInit(String),
}
#[derive(Debug, Clone)]
struct ServerPrefetchEntry {
normalized_host: String,
resolved_address: SocketAddr,
completed_at: Instant,
generation: u64,
}
#[derive(Debug, Default)]
struct ServerPrefetchCache {
latest_generation: u64,
entry: Option<ServerPrefetchEntry>,
last_failure: Option<String>,
}
impl ServerPrefetchCache {
fn begin(&mut self, host: &str) -> u64 {
if normalize_host(host).is_empty() {
return self.latest_generation;
}
self.latest_generation = self.latest_generation.saturating_add(1);
self.last_failure = None;
self.latest_generation
}
fn store_success(
&mut self,
generation: u64,
host: &str,
resolved_address: SocketAddr,
completed_at: Instant,
) {
if generation != self.latest_generation {
return;
}
self.entry = Some(ServerPrefetchEntry {
normalized_host: normalize_host(host),
resolved_address,
completed_at,
generation,
});
self.last_failure = None;
}
fn store_failure(&mut self, generation: u64, error: String) {
if generation != self.latest_generation {
return;
}
self.last_failure = Some(error);
}
fn fresh_match(&self, host: &str, now: Instant) -> Option<SocketAddr> {
let normalized = normalize_host(host);
let entry = self.entry.as_ref()?;
if entry.normalized_host != normalized {
return None;
}
if entry.generation != self.latest_generation {
return None;
}
if now.duration_since(entry.completed_at) > SERVER_PREFETCH_TTL {
return None;
}
Some(entry.resolved_address)
}
}
#[derive(Debug, Clone, Default)]
pub struct ServerPrefetcher {
cache: Arc<Mutex<ServerPrefetchCache>>,
}
impl ServerPrefetcher {
pub fn new() -> Self {
Self::default()
}
pub async fn prefetch(&self, host: String) -> Result<(), ServerPrefetchError> {
let normalized = normalize_host(&host);
if normalized.is_empty() {
return Ok(());
}
let generation = {
let mut cache = self.cache.lock().await;
cache.begin(&normalized)
};
let cache = self.cache.clone();
tokio::spawn(async move {
info!(target: "chanora_server_prefetch", host = %normalized, "resolution prefetch started");
let result = resolve_socket(&normalized).await;
let mut guard = cache.lock().await;
match result {
Ok(addr) => {
info!(
target: "chanora_server_prefetch",
host = %normalized,
resolved = %addr,
"resolution prefetch result"
);
guard.store_success(generation, &normalized, addr, Instant::now());
}
Err(err) => {
warn!(
target: "chanora_server_prefetch",
host = %normalized,
error = %err,
"resolution prefetch failed"
);
guard.store_failure(generation, err.to_string());
}
}
});
Ok(())
}
pub async fn fresh_match(&self, host: &str) -> Option<SocketAddr> {
let resolved = {
let cache = self.cache.lock().await;
cache.fresh_match(host, Instant::now())
};
match resolved {
Some(addr) => {
info!(
target: "chanora_server_prefetch",
host = %host,
resolved = %addr,
"connect using prefetched resolution"
);
Some(addr)
}
None => {
info!(target: "chanora_server_prefetch", host = %host, "connect prefetch miss or stale");
None
}
}
}
#[cfg(any(test, feature = "test-support"))]
pub async fn begin_for_test(&self, host: &str) -> u64 {
let mut cache = self.cache.lock().await;
cache.begin(host)
}
#[cfg(any(test, feature = "test-support"))]
pub async fn store_success_for_test(
&self,
generation: u64,
host: &str,
resolved_address: SocketAddr,
completed_at: Instant,
) {
let mut cache = self.cache.lock().await;
cache.store_success(generation, host, resolved_address, completed_at);
}
#[cfg(any(test, feature = "test-support"))]
pub async fn latest_generation_for_test(&self) -> u64 {
let cache = self.cache.lock().await;
cache.latest_generation
}
}
fn normalize_host(host: &str) -> String {
host.trim().to_lowercase()
}
async fn resolve_socket(host: &str) -> Result<SocketAddr, ServerPrefetchError> {
let resolver = chanora_resolver::ChanoraResolver::new()
.map_err(|err| ServerPrefetchError::ResolverInit(err.to_string()))?;
let resolved = resolver
.resolve_client_address(host)
.await
.map_err(|err| ServerPrefetchError::ResolverInit(err.to_string()))?;
Ok(resolved.connection_addr())
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn fresh_exact_match_returns_socket_address() {
let prefetcher = ServerPrefetcher::new();
let generation = prefetcher.begin_for_test(" Example.COM ").await;
let addr = "127.0.0.1:9987".parse().unwrap();
prefetcher
.store_success_for_test(generation, "example.com", addr, Instant::now())
.await;
assert_eq!(prefetcher.fresh_match("example.com").await, Some(addr));
assert_eq!(prefetcher.fresh_match(" EXAMPLE.com ").await, Some(addr));
}
#[tokio::test]
async fn stale_entries_are_ignored() {
let prefetcher = ServerPrefetcher::new();
let generation = prefetcher.begin_for_test("example.com").await;
let addr = "127.0.0.1:9987".parse().unwrap();
prefetcher
.store_success_for_test(
generation,
"example.com",
addr,
Instant::now() - SERVER_PREFETCH_TTL - Duration::from_secs(1),
)
.await;
assert_eq!(prefetcher.fresh_match("example.com").await, None);
}
#[tokio::test]
async fn different_hosts_are_ignored() {
let prefetcher = ServerPrefetcher::new();
let generation = prefetcher.begin_for_test("example.com").await;
let addr = "127.0.0.1:9987".parse().unwrap();
prefetcher
.store_success_for_test(generation, "example.com", addr, Instant::now())
.await;
assert_eq!(prefetcher.fresh_match("other.example.com").await, None);
}
#[tokio::test]
async fn stale_generation_completions_are_ignored() {
let prefetcher = ServerPrefetcher::new();
let old_generation = prefetcher.begin_for_test("old.example.com").await;
let _new_generation = prefetcher.begin_for_test("new.example.com").await;
let old_addr = "127.0.0.1:9987".parse().unwrap();
prefetcher
.store_success_for_test(old_generation, "old.example.com", old_addr, Instant::now())
.await;
assert_eq!(prefetcher.fresh_match("old.example.com").await, None);
}
#[tokio::test]
async fn blank_hosts_do_not_update_generation() {
let prefetcher = ServerPrefetcher::new();
let before = prefetcher.latest_generation_for_test().await;
prefetcher.prefetch(" ".to_string()).await.unwrap();
assert_eq!(prefetcher.latest_generation_for_test().await, before);
}
}
```
- [ ] **Step 2: Run new crate tests**
Run: `cargo test -p chanora_server_prefetch --lib`
Expected: the new crate compiles and all 5 tests pass.
- [ ] **Step 3: Commit Task 1**
```bash
git add Cargo.toml Cargo.lock crates/chanora_server_prefetch
git commit -m "feat(prefetch): add server prefetch crate"
```
---
### Task 2: Wire Core to `ServerPrefetcher`
**Files:**
- Modify: `core/chanora_core/Cargo.toml`
- Modify: `core/chanora_core/src/lib.rs`
- [ ] **Step 1: Replace the core dependency**
In `core/chanora_core/Cargo.toml`, replace:
```toml
chanora_resolver = { path = "../../crates/chanora_resolver" }
```
with:
```toml
chanora_server_prefetch = { path = "../../crates/chanora_server_prefetch" }
```
- [ ] **Step 2: Remove private prefetch cache implementation from core**
In `core/chanora_core/src/lib.rs`, remove these private items:
```rust
const RESOLUTION_PREFETCH_TTL: Duration = Duration::from_secs(120);
#[derive(Debug, Clone)]
struct ResolutionPrefetchEntry { ... }
#[derive(Debug, Default)]
struct ResolutionPrefetchCache { ... }
impl ResolutionPrefetchCache { ... }
fn normalize_prefetch_host(host: &str) -> String { ... }
async fn resolve_prefetch_socket(host: &str) -> Result<std::net::SocketAddr, CoreError> { ... }
```
Add this import near the other crate imports:
```rust
use chanora_server_prefetch::ServerPrefetcher;
```
- [ ] **Step 3: Replace the session field and constructor initialization**
Change the `ChanoraSession` field from:
```rust
resolution_prefetch: Arc<Mutex<ResolutionPrefetchCache>>,
```
to:
```rust
server_prefetch: ServerPrefetcher,
```
Change the constructor initialization from:
```rust
resolution_prefetch: Arc::new(Mutex::new(ResolutionPrefetchCache::default())),
```
to:
```rust
server_prefetch: ServerPrefetcher::new(),
```
- [ ] **Step 4: Delegate prefetch and fresh-match lookup**
Replace `prefetch_server_resolution` with:
```rust
pub async fn prefetch_server_resolution(&self, host: String) -> Result<(), CoreError> {
self.server_prefetch
.prefetch(host)
.await
.map_err(|err| CoreError::Protocol(chanora_protocol::ProtocolError::DnsFailed {
host: "prefetch".to_string(),
reason: err.to_string(),
}))
}
```
Replace `apply_prefetched_resolution` with:
```rust
async fn apply_prefetched_resolution(&self, cfg: &mut ConnectConfig) {
cfg.resolved_address = None;
let host = cfg.address.trim();
if host.is_empty() {
return;
}
if let Some(addr) = self.server_prefetch.fresh_match(host).await {
cfg.resolved_address = Some(addr);
}
}
```
Keep `prepare_connect_configs` unchanged except that it calls the updated `apply_prefetched_resolution`.
- [ ] **Step 5: Update core tests to use the prefetcher seam**
Keep these existing core tests:
```rust
session_clears_untrusted_prefetched_resolution_on_cache_miss
connect_config_without_prefetched_resolution_clears_only_resolved_address
connect_config_preparation_keeps_prefetched_address_out_of_stored_config
```
Replace any direct access to `session.resolution_prefetch` with calls to public test-support methods on `ServerPrefetcher`. In `crates/chanora_server_prefetch/src/lib.rs`, keep these methods gated with `#[cfg(any(test, feature = "test-support"))]`:
```rust
#[cfg(any(test, feature = "test-support"))]
pub async fn store_success_for_test(
&self,
generation: u64,
host: &str,
resolved_address: SocketAddr,
completed_at: Instant,
)
```
and:
```rust
#[cfg(any(test, feature = "test-support"))]
pub async fn begin_for_test(&self, host: &str) -> u64
```
Enable the feature for `chanora_core` tests by adding this dev-dependency in `core/chanora_core/Cargo.toml`:
```toml
[dev-dependencies]
chanora_server_prefetch = { path = "../../crates/chanora_server_prefetch", features = ["test-support"] }
```
Then core test setup should look like:
```rust
let generation = session.server_prefetch.begin_for_test("example.com").await;
session
.server_prefetch
.store_success_for_test(generation, "example.com", cached, std::time::Instant::now())
.await;
```
- [ ] **Step 6: Run core tests and fix compile errors**
Run: `cargo test -p chanora_core --lib`
Expected: all core lib tests pass.
- [ ] **Step 7: Commit Task 2**
```bash
git add Cargo.toml Cargo.lock core/chanora_core/Cargo.toml core/chanora_core/src/lib.rs crates/chanora_server_prefetch
git commit -m "refactor(core): use server prefetch crate"
```
---
### Task 3: Remove Duplicated Core Cache Tests and Verify Protocol Is Unchanged
**Files:**
- Modify: `core/chanora_core/src/lib.rs`
- Inspect only: `crates/chanora_protocol/src/adapter.rs`
- [ ] **Step 1: Remove core tests moved to the prefetch crate**
Delete these tests from `core/chanora_core/src/lib.rs` because they now belong in `chanora_server_prefetch`:
```rust
resolution_prefetch_cache_returns_fresh_exact_match
resolution_prefetch_cache_ignores_stale_entries
resolution_prefetch_cache_ignores_different_hosts
resolution_prefetch_cache_ignores_stale_generation_completion
```
Do not delete core trust-boundary tests:
```rust
session_uses_fresh_prefetched_resolution_for_connect_config
session_clears_untrusted_prefetched_resolution_on_cache_miss
connect_config_without_prefetched_resolution_clears_only_resolved_address
connect_config_preparation_keeps_prefetched_address_out_of_stored_config
```
- [ ] **Step 2: Confirm protocol source is untouched**
Run: `git diff -- crates/chanora_protocol/src/adapter.rs`
Expected: no diff. If there is a diff, revert only accidental protocol edits by manually restoring the changed lines from `HEAD`; do not use destructive git checkout/reset.
- [ ] **Step 3: Run combined Rust tests**
Run: `cargo test -p chanora_server_prefetch -p chanora_core -p chanora_protocol --lib`
Expected: all tests pass.
- [ ] **Step 4: Commit Task 3**
```bash
git add core/chanora_core/src/lib.rs Cargo.lock
git commit -m "test(core): keep prefetch tests at crate boundary"
```
---
### Task 4: Final Verification and Android Smoke
**Files:**
- No source edits expected.
- [ ] **Step 1: Run full focused Rust verification**
Run: `cargo test -p chanora_resolver -p chanora_server_prefetch -p chanora_protocol -p chanora_core --lib`
Expected: all tests pass.
- [ ] **Step 2: Run focused Flutter tests**
Run from `apps/chanora_flutter`:
```bash
flutter test test/services/server_resolution_prefetch_scheduler_test.dart test/services/connection_phase_state_test.dart test/services/ts3_server_link_test.dart
```
Expected: all tests pass.
- [ ] **Step 3: Build Android APK**
Run from `apps/chanora_flutter`:
```bash
flutter build apk --debug
```
Expected: build succeeds and produces `build/app/outputs/flutter-apk/app-debug.apk`.
- [ ] **Step 4: Run fresh-launch Android prefetch/connect smoke**
Run from `apps/chanora_flutter` with an emulator/device attached:
```bash
adb install -r "build/app/outputs/flutter-apk/app-debug.apk"
adb shell am force-stop app.chanora.chanora_flutter
adb logcat -c
adb shell monkey -p app.chanora.chanora_flutter -c android.intent.category.LAUNCHER 1
sleep 5
adb shell input tap 354 1363
sleep 8
adb logcat -d -v time | rg "resolution prefetch|prefetched|client request resolution|server address resolved|FATAL|AndroidRuntime|ANR"
adb shell uiautomator dump /sdcard/window.xml
adb exec-out cat /sdcard/window.xml
```
Expected logs include:
```text
resolution prefetch started
resolution prefetch result
connect using prefetched resolution
using prefetched server address
```
Expected UI hierarchy includes:
```text
Vigorous Pro
Leave server
Default Channel
ChanoraBeta
```
No app `FATAL`, app `AndroidRuntime` crash, or `ANR` lines should appear. `AndroidRuntime` lines from `monkey` or `uiautomator` are not app crashes.
- [ ] **Step 5: Run one channel switch smoke**
With the app still connected, run:
```bash
adb logcat -c
adb shell input tap 300 1705
sleep 2
adb shell input tap 300 1465
sleep 2
adb logcat -d -v time | rg "voice_join|client_move|FATAL|AndroidRuntime|ANR"
adb shell uiautomator dump /sdcard/window.xml
adb exec-out cat /sdcard/window.xml
```
Expected logs include successful moves to channel IDs similar to:
```text
voice_join accepted by server
client_move resolved by authoritative self channel change
```
Expected final UI has `ChanoraBeta` under `Default Channel`.
- [ ] **Step 6: Check final git state**
Run:
```bash
git status --short
git log --oneline -5
```
Expected: only unrelated untracked `config.json` remains, unless the user has added other unrelated work. The latest commits should be the prefetch crate split commits.
---
## Self-Review
- Spec coverage: the plan adds `chanora_server_prefetch`, moves cache policy and resolver-backed warming there, keeps core as trust boundary, preserves Flutter/protocol behavior, and includes Android connect smoke verification.
- Placeholder scan: no `TBD`, `TODO`, or unspecified edge handling remains.
- Type consistency: the plan uses `ServerPrefetcher`, `ServerPrefetchError`, `prefetch`, and `fresh_match` consistently across crate and core tasks.
@@ -0,0 +1,899 @@
# Server Resolution Prefetch Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Add invisible server-address resolution prefetch so the active host field can warm Rust resolver state before Connect without changing connection semantics.
**Architecture:** Flutter schedules debounced prefetch calls for the active host field only. Rust owns resolution prefetch state, TTL, exact-match validation, and connect-time reuse. The protocol layer accepts an optional already-resolved socket address so Connect can skip resolver work only when the core cache says it is safe.
**Tech Stack:** Flutter/Dart, flutter_rust_bridge generated bindings, Rust async Tokio, `chanora_core`, `chanora_protocol`, `chanora_resolver`, Flutter widget/service tests, Rust unit tests.
---
## File Structure
- Modify `core/chanora_core/src/lib.rs`: add a session-owned prefetch cache, prefetch API, connect-time cache lookup, and unit tests for TTL/exact-match/generation behavior.
- Modify `crates/chanora_protocol/src/adapter.rs`: add `resolved_address: Option<SocketAddr>` to `ConnectConfig` and skip `resolve_server_socket()` when present.
- Modify `crates/chanora_bridge/src/api.rs`: add `prefetch_server_resolution(host: String)` bridge function and pass cache-aware connects through `ChanoraSession`.
- Regenerate `crates/chanora_bridge/src/frb_generated.rs`: generated Rust bridge bindings.
- Regenerate `apps/chanora_flutter/lib/src/rust/api.dart`, `api.freezed.dart`, and `frb_generated.dart`: generated Dart bridge bindings.
- Create `apps/chanora_flutter/lib/services/server_resolution_prefetch_scheduler.dart`: small testable debounce helper for host-field prefetch scheduling.
- Modify `apps/chanora_flutter/lib/main.dart`: wire `_hostCtl` listener, settings-loaded prefetch, and disposal.
- Create `apps/chanora_flutter/test/services/server_resolution_prefetch_scheduler_test.dart`: Flutter/Dart tests for debounce and empty-host behavior.
Keep all prefetch UI invisible. Do not prefetch bookmarks in bulk.
---
### Task 1: Protocol Config Accepts Prefetched Socket
**Files:**
- Modify: `crates/chanora_protocol/src/adapter.rs`
- [ ] **Step 1: Add a failing protocol config test**
Add this test inside the existing `#[cfg(test)] mod tests` in `crates/chanora_protocol/src/adapter.rs`:
```rust
#[test]
fn connect_config_can_carry_prefetched_socket_address() {
let addr: std::net::SocketAddr = "127.0.0.1:9987".parse().unwrap();
let cfg = ConnectConfig {
address: "example.com".to_string(),
nickname: "Tester".to_string(),
password: None,
identity: None,
ready_timeout: Duration::from_secs(1),
resolved_address: Some(addr),
};
assert_eq!(cfg.resolved_address, Some(addr));
}
```
- [ ] **Step 2: Run the test to verify it fails**
Run: `cargo test -p chanora_protocol connect_config_can_carry_prefetched_socket_address`
Expected: FAIL to compile with a message like `struct ConnectConfig has no field named resolved_address`.
- [ ] **Step 3: Add the minimal config field**
Update `ConnectConfig` in `crates/chanora_protocol/src/adapter.rs`:
```rust
#[derive(Debug, Clone)]
pub struct ConnectConfig {
/// Server address: `hostname[:port]` or TSDNS name.
pub address: String,
/// Optional already-resolved socket address from core's invisible
/// prefetch cache. When present, the protocol layer skips address
/// resolution but still opens a normal TS3 connection only after
/// the user requested Connect.
pub resolved_address: Option<std::net::SocketAddr>,
/// Nickname to use on the server.
pub nickname: String,
/// Optional server password.
pub password: Option<String>,
/// Optional pre-existing identity (base64 string accepted by
/// `tsclientlib::Identity::new_from_str`). If `None`, a fresh
/// identity is generated and **not persisted** — production callers
/// should provide one from secure identity storage.
pub identity: Option<String>,
/// How long to wait for the initial state snapshot before
/// returning `ProtocolError::Timeout`.
pub ready_timeout: Duration,
}
impl Default for ConnectConfig {
fn default() -> Self {
Self {
address: String::new(),
resolved_address: None,
nickname: "Chanora".to_string(),
password: None,
identity: None,
ready_timeout: Duration::from_secs(10),
}
}
}
```
- [ ] **Step 4: Run the focused test**
Run: `cargo test -p chanora_protocol connect_config_can_carry_prefetched_socket_address`
Expected: PASS.
- [ ] **Step 5: Commit**
```bash
git add crates/chanora_protocol/src/adapter.rs
git commit -m "feat(protocol): accept prefetched server address"
```
---
### Task 2: Protocol Connect Skips Resolver On Prefetch Hit
**Files:**
- Modify: `crates/chanora_protocol/src/adapter.rs`
- [ ] **Step 1: Add a failing helper test**
Add a private helper next to `resolve_server_socket()` only after this failing test is added. First add this test in `#[cfg(test)] mod tests`:
```rust
#[test]
fn server_socket_from_config_prefers_prefetched_address() {
let addr: std::net::SocketAddr = "127.0.0.1:9987".parse().unwrap();
let cfg = ConnectConfig {
address: "example.com".to_string(),
resolved_address: Some(addr),
nickname: "Tester".to_string(),
password: None,
identity: None,
ready_timeout: Duration::from_secs(1),
};
assert_eq!(server_socket_from_config(&cfg), Some(addr));
}
```
- [ ] **Step 2: Run the test to verify it fails**
Run: `cargo test -p chanora_protocol server_socket_from_config_prefers_prefetched_address`
Expected: FAIL to compile with `cannot find function server_socket_from_config`.
- [ ] **Step 3: Add the helper and wire connection task**
Add this helper near `resolve_server_socket()`:
```rust
fn server_socket_from_config(cfg: &ConnectConfig) -> Option<SocketAddr> {
cfg.resolved_address
}
```
Find the call in `connection_task` that currently resolves the address, shaped like:
```rust
let server = resolve_server_socket(&cfg.address).await?;
```
Replace it with:
```rust
let server = match server_socket_from_config(&cfg) {
Some(addr) => {
info!(
target: "chanora_protocol",
input = %cfg.address,
resolved = %addr,
"using prefetched server address"
);
addr
}
None => resolve_server_socket(&cfg.address).await?,
};
```
- [ ] **Step 4: Run protocol tests**
Run: `cargo test -p chanora_protocol server_socket_from_config_prefers_prefetched_address`
Expected: PASS.
- [ ] **Step 5: Run broader protocol tests**
Run: `cargo test -p chanora_protocol`
Expected: PASS.
- [ ] **Step 6: Commit**
```bash
git add crates/chanora_protocol/src/adapter.rs
git commit -m "fix(protocol): reuse prefetched server address"
```
---
### Task 3: Core Prefetch Cache Data Model
**Files:**
- Modify: `core/chanora_core/src/lib.rs`
- [ ] **Step 1: Write failing cache tests**
Add these tests inside `#[cfg(test)] mod tests` in `core/chanora_core/src/lib.rs`:
```rust
#[test]
fn resolution_prefetch_cache_returns_fresh_exact_match() {
let mut cache = ResolutionPrefetchCache::default();
let now = std::time::Instant::now();
let addr: std::net::SocketAddr = "127.0.0.1:9987".parse().unwrap();
let generation = cache.begin(" Example.COM ");
cache.store_success(generation, " Example.COM ", addr, now);
assert_eq!(cache.fresh_match("example.com", now), Some(addr));
}
#[test]
fn resolution_prefetch_cache_ignores_stale_entries() {
let mut cache = ResolutionPrefetchCache::default();
let now = std::time::Instant::now();
let addr: std::net::SocketAddr = "127.0.0.1:9987".parse().unwrap();
let generation = cache.begin("example.com");
cache.store_success(generation, "example.com", addr, now - RESOLUTION_PREFETCH_TTL - std::time::Duration::from_secs(1));
assert_eq!(cache.fresh_match("example.com", now), None);
}
#[test]
fn resolution_prefetch_cache_ignores_different_hosts() {
let mut cache = ResolutionPrefetchCache::default();
let now = std::time::Instant::now();
let addr: std::net::SocketAddr = "127.0.0.1:9987".parse().unwrap();
let generation = cache.begin("example.com");
cache.store_success(generation, "example.com", addr, now);
assert_eq!(cache.fresh_match("other.example.com", now), None);
}
#[test]
fn resolution_prefetch_cache_ignores_stale_generation_completion() {
let mut cache = ResolutionPrefetchCache::default();
let now = std::time::Instant::now();
let first: std::net::SocketAddr = "127.0.0.1:9987".parse().unwrap();
let second: std::net::SocketAddr = "127.0.0.2:9987".parse().unwrap();
let old_generation = cache.begin("example.com");
let new_generation = cache.begin("example.com");
cache.store_success(new_generation, "example.com", second, now);
cache.store_success(old_generation, "example.com", first, now);
assert_eq!(cache.fresh_match("example.com", now), Some(second));
}
```
- [ ] **Step 2: Run tests to verify they fail**
Run: `cargo test -p chanora_core resolution_prefetch_cache --lib`
Expected: FAIL to compile with missing `ResolutionPrefetchCache` and `RESOLUTION_PREFETCH_TTL`.
- [ ] **Step 3: Add the cache model**
Add near `NetworkDiagnostics` in `core/chanora_core/src/lib.rs`:
```rust
const RESOLUTION_PREFETCH_TTL: Duration = Duration::from_secs(120);
#[derive(Debug, Clone)]
struct ResolutionPrefetchEntry {
normalized_host: String,
resolved_address: std::net::SocketAddr,
completed_at: std::time::Instant,
generation: u64,
}
#[derive(Debug, Default)]
struct ResolutionPrefetchCache {
latest_generation: u64,
entry: Option<ResolutionPrefetchEntry>,
last_failure: Option<String>,
}
impl ResolutionPrefetchCache {
fn begin(&mut self, host: &str) -> u64 {
self.latest_generation = self.latest_generation.saturating_add(1);
self.last_failure = None;
let _ = normalize_prefetch_host(host);
self.latest_generation
}
fn store_success(
&mut self,
generation: u64,
host: &str,
resolved_address: std::net::SocketAddr,
completed_at: std::time::Instant,
) {
if generation != self.latest_generation {
return;
}
self.entry = Some(ResolutionPrefetchEntry {
normalized_host: normalize_prefetch_host(host),
resolved_address,
completed_at,
generation,
});
self.last_failure = None;
}
fn store_failure(&mut self, generation: u64, error: String) {
if generation != self.latest_generation {
return;
}
self.last_failure = Some(error);
}
fn fresh_match(&self, host: &str, now: std::time::Instant) -> Option<std::net::SocketAddr> {
let normalized = normalize_prefetch_host(host);
let entry = self.entry.as_ref()?;
if entry.normalized_host != normalized {
return None;
}
if now.duration_since(entry.completed_at) > RESOLUTION_PREFETCH_TTL {
return None;
}
Some(entry.resolved_address)
}
}
fn normalize_prefetch_host(host: &str) -> String {
host.trim().to_lowercase()
}
```
- [ ] **Step 4: Run focused core tests**
Run: `cargo test -p chanora_core resolution_prefetch_cache --lib`
Expected: all four tests PASS.
- [ ] **Step 5: Commit**
```bash
git add core/chanora_core/src/lib.rs
git commit -m "feat(core): add resolution prefetch cache"
```
---
### Task 4: Core Prefetch API And Connect Cache Lookup
**Files:**
- Modify: `core/chanora_core/src/lib.rs`
- [ ] **Step 1: Write failing session tests**
Add this test in `#[cfg(test)] mod tests` in `core/chanora_core/src/lib.rs`:
```rust
#[tokio::test]
async fn session_uses_fresh_prefetched_resolution_for_connect_config() {
let session = ChanoraSession::new();
let addr: std::net::SocketAddr = "127.0.0.1:9987".parse().unwrap();
{
let mut cache = session.resolution_prefetch.lock().await;
let generation = cache.begin("example.com");
cache.store_success(generation, "example.com", addr, std::time::Instant::now());
}
let mut cfg = ConnectConfig::default();
cfg.address = " example.com ".to_string();
session.apply_prefetched_resolution(&mut cfg).await;
assert_eq!(cfg.resolved_address, Some(addr));
}
```
- [ ] **Step 2: Run the test to verify it fails**
Run: `cargo test -p chanora_core session_uses_fresh_prefetched_resolution_for_connect_config --lib`
Expected: FAIL to compile with missing `resolution_prefetch` and `apply_prefetched_resolution`.
- [ ] **Step 3: Add session field and constructor initialization**
Add to `ChanoraSession`:
```rust
/// Invisible server-address prefetch cache. Warmed by Flutter typing
/// but validated by Rust before Connect can reuse it.
resolution_prefetch: Arc<Mutex<ResolutionPrefetchCache>>,
```
Initialize in `ChanoraSession::new()`:
```rust
resolution_prefetch: Arc::new(Mutex::new(ResolutionPrefetchCache::default())),
```
- [ ] **Step 4: Add connect-time cache lookup helper**
Add methods inside `impl ChanoraSession` before `connect()`:
```rust
async fn apply_prefetched_resolution(&self, cfg: &mut ConnectConfig) {
let host = cfg.address.trim();
if host.is_empty() {
return;
}
let resolved = {
let cache = self.resolution_prefetch.lock().await;
cache.fresh_match(host, std::time::Instant::now())
};
match resolved {
Some(addr) => {
info!(
target: "chanora_core",
host = %host,
resolved = %addr,
"connect using prefetched resolution"
);
cfg.resolved_address = Some(addr);
}
None => {
info!(target: "chanora_core", host = %host, "connect prefetch miss or stale");
}
}
}
```
In `connect()`, after identity resolution and before `ProtocolClient::connect(cfg.clone())`, add:
```rust
self.apply_prefetched_resolution(&mut cfg).await;
```
- [ ] **Step 5: Add prefetch scheduling API in core**
Add this method in `impl ChanoraSession`:
```rust
pub async fn prefetch_server_resolution(&self, host: String) -> Result<(), CoreError> {
let normalized = normalize_prefetch_host(&host);
if normalized.is_empty() {
return Ok(());
}
let generation = {
let mut cache = self.resolution_prefetch.lock().await;
cache.begin(&normalized)
};
let cache = self.resolution_prefetch.clone();
tokio::spawn(async move {
info!(target: "chanora_core", host = %normalized, "resolution prefetch started");
let result = resolve_prefetch_socket(&normalized).await;
let mut guard = cache.lock().await;
match result {
Ok(addr) => {
info!(
target: "chanora_core",
host = %normalized,
resolved = %addr,
"resolution prefetch result"
);
guard.store_success(generation, &normalized, addr, std::time::Instant::now());
}
Err(err) => {
warn!(
target: "chanora_core",
host = %normalized,
error = %err,
"resolution prefetch failed"
);
guard.store_failure(generation, err.to_string());
}
}
});
Ok(())
}
```
Add this helper outside `impl ChanoraSession`:
```rust
async fn resolve_prefetch_socket(host: &str) -> Result<std::net::SocketAddr, CoreError> {
let resolver = chanora_resolver::ChanoraResolver::new()
.map_err(|err| CoreError::Protocol(chanora_protocol::ProtocolError::DnsFailed {
host: host.to_string(),
reason: format!("resolver initialization failed: {err}"),
}))?;
let resolved = resolver
.resolve_client_address(host)
.await
.map_err(|err| CoreError::Protocol(chanora_protocol::ProtocolError::DnsFailed {
host: host.to_string(),
reason: err.to_string(),
}))?;
resolved
.parse::<std::net::SocketAddr>()
.map_err(|err| CoreError::Protocol(chanora_protocol::ProtocolError::DnsFailed {
host: host.to_string(),
reason: format!("resolver returned invalid socket address '{resolved}': {err}"),
}))
}
```
- [ ] **Step 6: Run focused core tests**
Run: `cargo test -p chanora_core session_uses_fresh_prefetched_resolution_for_connect_config --lib`
Expected: PASS.
- [ ] **Step 7: Run core tests**
Run: `cargo test -p chanora_core --lib`
Expected: PASS.
- [ ] **Step 8: Commit**
```bash
git add core/chanora_core/src/lib.rs
git commit -m "feat(core): prefetch server resolution"
```
---
### Task 5: Bridge API Exposes Prefetch
**Files:**
- Modify: `crates/chanora_bridge/src/api.rs`
- Regenerate: `crates/chanora_bridge/src/frb_generated.rs`
- Regenerate: `apps/chanora_flutter/lib/src/rust/api.dart`
- Regenerate: `apps/chanora_flutter/lib/src/rust/api.freezed.dart`
- Regenerate: `apps/chanora_flutter/lib/src/rust/frb_generated.dart`
- [ ] **Step 1: Add bridge function**
Add after `connect()` in `crates/chanora_bridge/src/api.rs`:
```rust
/// Warm server address resolution for the active host field. This is
/// intentionally fire-and-forget from the UI perspective: it schedules
/// Rust-side prefetch work and never opens a TS3 session.
pub async fn prefetch_server_resolution(host: String) -> Result<(), BridgeError> {
runtime()
.spawn(async move { session().prefetch_server_resolution(host).await })
.await
.map_err(|e| task_join_error("prefetch_server_resolution", e))??;
Ok(())
}
```
- [ ] **Step 2: Regenerate flutter_rust_bridge bindings**
Run from repo root:
```bash
flutter_rust_bridge_codegen generate
```
Expected: generated files update and `apps/chanora_flutter/lib/src/rust/api.dart` contains:
```dart
Future<void> prefetchServerResolution({required String host}) => RustLib
.instance
.api
.crateApiPrefetchServerResolution(host: host);
```
- [ ] **Step 3: Run bridge/core compile check**
Run: `cargo test -p chanora_bridge --lib`
Expected: PASS.
- [ ] **Step 4: Run Flutter analyzer smoke check**
Run: `cd apps/chanora_flutter && flutter analyze`
Expected: no new errors related to generated bindings.
- [ ] **Step 5: Commit**
```bash
git add crates/chanora_bridge/src/api.rs crates/chanora_bridge/src/frb_generated.rs apps/chanora_flutter/lib/src/rust/api.dart apps/chanora_flutter/lib/src/rust/api.freezed.dart apps/chanora_flutter/lib/src/rust/frb_generated.dart
git commit -m "feat(bridge): expose resolution prefetch"
```
---
### Task 6: Flutter Prefetch Scheduler Helper
**Files:**
- Create: `apps/chanora_flutter/lib/services/server_resolution_prefetch_scheduler.dart`
- Create: `apps/chanora_flutter/test/services/server_resolution_prefetch_scheduler_test.dart`
- [ ] **Step 1: Write failing scheduler tests**
Create `apps/chanora_flutter/test/services/server_resolution_prefetch_scheduler_test.dart`:
```dart
import 'package:flutter_test/flutter_test.dart';
import 'package:chanora_flutter/services/server_resolution_prefetch_scheduler.dart';
void main() {
test('debounces host edits and prefetches latest trimmed host', () async {
final calls = <String>[];
final scheduler = ServerResolutionPrefetchScheduler(
delay: const Duration(milliseconds: 20),
prefetch: (host) async => calls.add(host),
);
scheduler.schedule(' first.example.com ');
scheduler.schedule(' second.example.com ');
await Future<void>.delayed(const Duration(milliseconds: 35));
expect(calls, ['second.example.com']);
scheduler.dispose();
});
test('skips empty hosts', () async {
final calls = <String>[];
final scheduler = ServerResolutionPrefetchScheduler(
delay: const Duration(milliseconds: 10),
prefetch: (host) async => calls.add(host),
);
scheduler.schedule(' ');
await Future<void>.delayed(const Duration(milliseconds: 25));
expect(calls, isEmpty);
scheduler.dispose();
});
test('dispose cancels pending prefetch', () async {
final calls = <String>[];
final scheduler = ServerResolutionPrefetchScheduler(
delay: const Duration(milliseconds: 30),
prefetch: (host) async => calls.add(host),
);
scheduler.schedule('example.com');
scheduler.dispose();
await Future<void>.delayed(const Duration(milliseconds: 45));
expect(calls, isEmpty);
});
}
```
- [ ] **Step 2: Run tests to verify they fail**
Run: `cd apps/chanora_flutter && flutter test test/services/server_resolution_prefetch_scheduler_test.dart`
Expected: FAIL to compile with missing `server_resolution_prefetch_scheduler.dart`.
- [ ] **Step 3: Implement scheduler**
Create `apps/chanora_flutter/lib/services/server_resolution_prefetch_scheduler.dart`:
```dart
import 'dart:async';
typedef ServerResolutionPrefetch = Future<void> Function(String host);
class ServerResolutionPrefetchScheduler {
ServerResolutionPrefetchScheduler({
required this.prefetch,
this.delay = const Duration(milliseconds: 700),
});
final ServerResolutionPrefetch prefetch;
final Duration delay;
Timer? _timer;
bool _disposed = false;
void schedule(String rawHost) {
if (_disposed) return;
_timer?.cancel();
final host = rawHost.trim();
if (host.isEmpty) return;
_timer = Timer(delay, () {
if (_disposed) return;
unawaited(prefetch(host));
});
}
void dispose() {
_disposed = true;
_timer?.cancel();
_timer = null;
}
}
```
- [ ] **Step 4: Run scheduler tests**
Run: `cd apps/chanora_flutter && flutter test test/services/server_resolution_prefetch_scheduler_test.dart`
Expected: PASS.
- [ ] **Step 5: Format Dart files**
Run: `cd apps/chanora_flutter && dart format lib/services/server_resolution_prefetch_scheduler.dart test/services/server_resolution_prefetch_scheduler_test.dart`
Expected: files formatted, no errors.
- [ ] **Step 6: Commit**
```bash
git add apps/chanora_flutter/lib/services/server_resolution_prefetch_scheduler.dart apps/chanora_flutter/test/services/server_resolution_prefetch_scheduler_test.dart
git commit -m "feat(ui): add resolution prefetch scheduler"
```
---
### Task 7: Wire Prefetch Scheduler Into Home UI
**Files:**
- Modify: `apps/chanora_flutter/lib/main.dart`
- [ ] **Step 1: Add imports and state fields**
In `apps/chanora_flutter/lib/main.dart`, add:
```dart
import 'services/server_resolution_prefetch_scheduler.dart';
```
Inside `_BetaHomeState`, add:
```dart
late final ServerResolutionPrefetchScheduler _resolutionPrefetch;
```
- [ ] **Step 2: Initialize scheduler and listener**
In `initState()`, before `_eventsSub = rust.eventsStream().listen(_onEvent);`, add:
```dart
_resolutionPrefetch = ServerResolutionPrefetchScheduler(
prefetch: (host) => rust.prefetchServerResolution(host: host),
);
_hostCtl.addListener(_onHostEdited);
```
Add method in `_BetaHomeState`:
```dart
void _onHostEdited() {
_resolutionPrefetch.schedule(_hostCtl.text);
}
```
- [ ] **Step 3: Schedule prefetch after settings load**
In `_loadUiSettings()`, after setting host/nickname from settings and before the `catch`, add:
```dart
if (settings.host.isNotEmpty) {
_resolutionPrefetch.schedule(settings.host);
}
```
Ensure this is not duplicated if an existing `_hostCtl.text = settings.host;` listener already schedules it. If both paths fire, keep only the explicit `_resolutionPrefetch.schedule(settings.host);` and temporarily remove/re-add the listener around `_hostCtl.text = settings.host`, or accept the duplicate because scheduler debounce collapses it. Prefer accepting the duplicate for minimal change.
- [ ] **Step 4: Dispose scheduler and listener**
In `dispose()`, before `_hostCtl.dispose();`, add:
```dart
_hostCtl.removeListener(_onHostEdited);
_resolutionPrefetch.dispose();
```
- [ ] **Step 5: Run focused Flutter tests**
Run: `cd apps/chanora_flutter && flutter test test/services/server_resolution_prefetch_scheduler_test.dart test/services/connection_phase_state_test.dart`
Expected: PASS.
- [ ] **Step 6: Run analyzer**
Run: `cd apps/chanora_flutter && flutter analyze`
Expected: no new analyzer errors.
- [ ] **Step 7: Commit**
```bash
git add apps/chanora_flutter/lib/main.dart
git commit -m "feat(ui): prefetch active server address"
```
---
### Task 8: End-To-End Verification On Android
**Files:**
- No source changes expected unless verification finds a bug.
- [ ] **Step 1: Run focused Rust tests**
Run: `cargo test -p chanora_resolver -p chanora_protocol -p chanora_core --lib`
Expected: PASS.
- [ ] **Step 2: Run focused Flutter tests**
Run: `cd apps/chanora_flutter && flutter test test/services/server_resolution_prefetch_scheduler_test.dart test/services/connection_phase_state_test.dart test/services/ts3_server_link_test.dart`
Expected: PASS.
- [ ] **Step 3: Build Android debug APK**
Run: `cd apps/chanora_flutter && flutter build apk --debug`
Expected: `✓ Built build/app/outputs/flutter-apk/app-debug.apk`.
- [ ] **Step 4: Install and launch through ADB**
Run from `apps/chanora_flutter`:
```bash
adb install -r "build/app/outputs/flutter-apk/app-debug.apk"
adb shell am force-stop app.chanora.chanora_flutter
adb logcat -c
adb shell monkey -p app.chanora.chanora_flutter -c android.intent.category.LAUNCHER 1
```
Expected: install success and app launches.
- [ ] **Step 5: Allow prefetch, connect, and inspect logs**
Run from repo root:
```bash
sleep 2
adb shell input tap 354 1363
sleep 8
adb logcat -d -v time | rg "resolution prefetch|prefetched|client request resolution|server address resolved|FATAL|AndroidRuntime"
```
Expected:
- No app `FATAL` lines.
- Logs include `resolution prefetch started` and either `resolution prefetch result` or safe failure.
- If prefetch completed before tap, logs include `connect using prefetched resolution` and protocol logs include `using prefetched server address`.
- If prefetch did not complete before tap, connect still succeeds through normal resolver path.
- [ ] **Step 6: Inspect UI hierarchy**
Run:
```bash
adb shell uiautomator dump /sdcard/window.xml
adb exec-out cat /sdcard/window.xml
```
Expected: connected server view with channel count, not stuck on `Synchronizing...`.
- [ ] **Step 7: Final status check**
Run: `git status --short`
Expected: no unintended source changes. Unrelated existing `config.json` may remain untracked and must not be committed.
---
## Self-Review
Spec coverage:
- Active-field-only prefetch: Tasks 6 and 7.
- Rust-owned resolver cache and exact-match validation: Tasks 3 and 4.
- 2-minute TTL: Task 3.
- Invisible UI: Tasks 6 and 7 avoid any UI status changes.
- No bookmark fan-out: Task 7 wires only `_hostCtl` and settings-loaded host.
- No TS3 session before Connect: Task 4 only resolves address; Task 2 only uses resolved address during actual protocol connect.
- Diagnostics: Tasks 2 and 4 add logs for prefetch and cache hit/miss.
- Testing and Android smoke: Task 8.
Completeness scan: no incomplete markers are used. Each code-changing task includes concrete code snippets and commands.
Type consistency: `resolved_address` is added to protocol `ConnectConfig`, core uses `cfg.resolved_address`, bridge exposes `prefetch_server_resolution`, and Dart uses generated `prefetchServerResolution`.
@@ -0,0 +1,114 @@
# DV Evidence Pack Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Create a document set that lets a DV meeting review Chanora's current verification posture, traceability, release blockers, waivers, and evidence without implying incomplete work is complete.
**Architecture:** The pack is documentation-only. Verification plans live under `docs/verification/`; release decision evidence lives under `docs/release/`; cross-document traceability lives under `docs/governance/`; security, privacy, and legal gate summaries live in their existing README-advertised folders.
**Tech Stack:** Markdown, existing SysRS/SysDes/SRS baselines, implementation status report, CI workflow definitions.
---
### Task 1: Create Verification Plan Set
**Files:**
- Create: `docs/verification/verification-master-plan.md`
- Create: `docs/verification/swe4-unit-verification-plan.md`
- Create: `docs/verification/swe5-software-integration-verification-plan.md`
- Create: `docs/verification/swe6-software-verification-plan.md`
- Create: `docs/verification/sys4-system-integration-verification-plan.md`
- [x] **Step 1: Write master plan**
Create `docs/verification/verification-master-plan.md` with lifecycle scope, evidence rules, entry/exit criteria, current evidence sources, open gates, and reviewer decision framing.
- [x] **Step 2: Write SWE.4 plan**
Create `docs/verification/swe4-unit-verification-plan.md` with unit verification scope for Rust crates, Flutter services/widgets, diagnostics, storage, state reducers, audio DSP, and known unit-test gaps.
- [x] **Step 3: Write SWE.5 plan**
Create `docs/verification/swe5-software-integration-verification-plan.md` with cross-component integration scope for Flutter-bridge-core, protocol-state, audio-platform, secure storage, diagnostics export, resolver prefetch, and packaging hooks.
- [x] **Step 4: Write SWE.6 plan**
Create `docs/verification/swe6-software-verification-plan.md` with SRS-level acceptance scope and the SysRS-241 through SysRS-257 MVP acceptance matrix.
- [x] **Step 5: Write SYS.4 plan**
Create `docs/verification/sys4-system-integration-verification-plan.md` with system-level integration scope for external compatible servers, OS services, hardware, network, app stores, diagnostics, and release evidence.
### Task 2: Create Release and DV Decision Records
**Files:**
- Create: `docs/release/release-readiness-go-nogo-record.md`
- Create: `docs/release/dv-waiver-register.md`
- [x] **Step 1: Write release readiness record**
Create `docs/release/release-readiness-go-nogo-record.md` with current candidate metadata, decision state, platform readiness, verification status, legal/security/privacy gates, blockers, and meeting recommendation.
- [x] **Step 2: Write waiver register**
Create `docs/release/dv-waiver-register.md` with explicit waivers for DEC-012, Android Keystore DEK, iOS voice-processing mode, source-build-only desktop/iOS artifacts, state reducer coverage, and VAD deferral.
### Task 3: Create Traceability and Gate Summaries
**Files:**
- Create: `docs/governance/traceability-matrix.md`
- Create: `docs/security/security-privacy-legal-guideline.md`
- Create: `docs/security/dependency-and-supply-chain-report.md`
- Create: `docs/privacy/privacy-policy.md`
- Create: `docs/legal/trademark-and-attribution-review.md`
- [x] **Step 1: Write traceability matrix**
Create `docs/governance/traceability-matrix.md` summarizing SysRS to SysDes to SRS to verification coverage, including the MVP acceptance and verification handoff items.
- [x] **Step 2: Write security/privacy/legal guideline**
Create `docs/security/security-privacy-legal-guideline.md` summarizing release gates and evidence expectations for secure storage, diagnostics redaction, dependency review, privacy policy, and affiliation wording.
- [x] **Step 3: Write dependency report**
Create `docs/security/dependency-and-supply-chain-report.md` summarizing current CI checks and open evidence gaps without claiming DEC-012 completion.
- [x] **Step 4: Write privacy policy baseline**
Create `docs/privacy/privacy-policy.md` as an engineering release-candidate privacy baseline covering local storage, permissions, diagnostics, and the no automatic telemetry posture.
- [x] **Step 5: Write trademark review**
Create `docs/legal/trademark-and-attribution-review.md` with current non-affiliation wording requirement and open legal sign-off state.
### Task 4: Verify Documentation Pack
**Files:**
- Inspect all created files.
- [x] **Step 1: Search for forbidden sentinel text**
Run the sentinel-language scan over `docs/verification`, `docs/release`, `docs/governance`, `docs/security`, `docs/privacy`, and `docs/legal`.
Expected: no matches introduced by the DV evidence pack except intentional historical references in source documents outside these folders.
- [x] **Step 2: Confirm expected files exist**
Run: `ls docs/verification docs/release docs/governance docs/security docs/privacy docs/legal`
Expected: all DV evidence pack files are listed.
- [x] **Step 3: Inspect git status and diff**
Run: `git diff -- docs/verification docs/release docs/governance docs/security docs/privacy docs/legal docs/superpowers/plans/2026-05-29-dv-evidence-pack.md`
Expected: only intended Markdown additions are present.
---
## Self-Review
- Spec coverage: covers verification plan set, release decision evidence, waiver register, traceability matrix, and minimum security/privacy/legal gate summaries.
- Placeholder scan: plan contains no incomplete instructions.
- Type consistency: document paths match the README-advertised folders and task file list.
@@ -0,0 +1,79 @@
# Finish DV Document Tree Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Fill the README-advertised document tree with baseline candidate documents so DV reviewers can navigate all required gates.
**Architecture:** Keep canonical large baselines at current root paths and add README-path stubs or summaries where needed. Governance, release, security, privacy, legal, UI/UX, i18n, and references each get explicit baseline documents that state current evidence and open gates honestly.
**Tech Stack:** Markdown documentation aligned to SysRS, SysDes, SRS, SAD, SDD, verification, release, and implementation status.
---
### Task 1: Add Requirements Path Wrappers
**Files:**
- Create: `docs/requirements/sysrs.md`
- Create: `docs/requirements/srs.md`
- [x] **Step 1: Create README entry records that point to canonical root documents and summarize DV review anchors**
These wrappers preserve README paths without duplicating the canonical baselines.
### Task 2: Add Governance Baselines
**Files:**
- Create: `docs/governance/document-index.md`
- Create: `docs/governance/document-naming-convention.md`
- Create: `docs/governance/baseline-approval-record.md`
- Create: `docs/governance/baseline-candidate-validation-report.md`
- Create: `docs/governance/document-review-report.md`
- Create: `docs/governance/product-decision-register.md`
- Create: `docs/governance/decision-impact-assessment.md`
- Create: `docs/governance/git-commit-message-convention.md`
- Create: `docs/governance/repo-format-validation-report.md`
- Create: `docs/governance/path-migration-map.md`
- [x] **Step 1: Write governance records**
Each record summarizes status for DV, identifies owner expectations, and avoids claiming final release approval.
### Task 3: Add Remaining Release, Security, UI/UX, I18n, Reference Docs
**Files:**
- Create: `docs/release/platform-release-policy.md`
- Create: `docs/security/threat-model.md`
- Create: `docs/security/secure-storage-audit-report.md`
- Create: `docs/security/diagnostic-redaction-audit-report.md`
- Create: `docs/ui-ux/material3-guideline.md`
- Create: `docs/ui-ux/material3-design-tokens.md`
- Create: `docs/ui-ux/material3-component-catalog.md`
- Create: `docs/ui-ux/adaptive-layout-platform-guide.md`
- Create: `docs/i18n/localization-architecture.md`
- Create: `docs/references/external-references.md`
- Create: `docs/references/aspice-swe2-swe3-integration-note.md`
- [x] **Step 1: Write remaining baseline docs**
Use concise DV-ready records that reference current implementation and open gaps.
### Task 4: Verify Complete Tree
**Files:**
- Inspect all created documents.
- [x] **Step 1: Check README paths exist**
Run a shell `test -f` command over every README-advertised path.
- [x] **Step 2: Sentinel-language scan**
Run the sentinel-language scan over `docs/requirements`, `docs/architecture`, `docs/verification`, `docs/release`, `docs/security`, `docs/privacy`, `docs/legal`, `docs/ui-ux`, `docs/i18n`, `docs/governance`, and `docs/references`; expect no matches.
---
## Self-Review
- Spec coverage: fills all README-advertised document paths except already existing files.
- Placeholder scan: plan contains no incomplete document instructions.
- Type consistency: file paths match README tree.
@@ -0,0 +1,108 @@
# State Sync and UI Settings Validation Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Complete validation-backed state-sync evidence and UI settings persistence while updating DV documents.
**Architecture:** State reducer work stays in `crates/chanora_state/src/lib.rs` with Rust unit tests. UI settings persistence stays in `apps/chanora_flutter/lib/services/ui_preferences_service.dart` with Flutter service tests; app-level theme application is wired in `apps/chanora_flutter/lib/main.dart` only if needed by the persisted setting.
**Tech Stack:** Rust/cargo tests, Flutter/Dart, shared_preferences, Markdown documentation.
---
### Task 1: State Reducer Validation
**Files:**
- Modify: `crates/chanora_state/src/lib.rs`
- [x] **Step 1: Write failing regression test**
Add `channel_delete_removes_clients_in_deleted_channel` proving channel deletion removes clients assigned to that channel and emits client-removal deltas before the channel-removal delta.
- [x] **Step 2: Verify RED**
Run: `cargo test -p chanora_state channel_delete_removes_clients_in_deleted_channel --locked`
Expected: FAIL because deleted-channel clients remain in state.
- [x] **Step 3: Implement minimal reducer fix**
In `StateEvent::ChannelDeleted`, collect clients whose `client.channel == id`, remove them from `clients` and `client_order`, then emit deterministic `ClientRemoved` deltas before `ChannelRemoved`.
- [x] **Step 4: Verify GREEN**
Run: `cargo test -p chanora_state channel_delete_removes_clients_in_deleted_channel --locked`
Expected: PASS.
- [x] **Step 5: Run full state crate tests**
Run: `cargo test -p chanora_state --locked`
Expected: all state crate tests pass.
### Task 2: UI Settings Persistence
**Files:**
- Modify: `apps/chanora_flutter/lib/services/ui_preferences_service.dart`
- Modify: `apps/chanora_flutter/test/services/ui_preferences_service_test.dart`
- Modify: `apps/chanora_flutter/lib/main.dart`
- [x] **Step 1: Add failing tests for theme persistence**
Add tests for default `system` theme mode, saving `dark`, saving `light`, and invalid stored value fallback to `system`.
- [x] **Step 2: Verify RED**
Run: `flutter test test/services/ui_preferences_service_test.dart`
Expected: FAIL because `UiThemeMode`, `themeMode`, and `saveThemeMode` do not exist.
- [x] **Step 3: Implement minimal service changes**
Add `UiThemeMode`, `UiSettings.themeMode`, persisted key `ui.theme_mode`, and `saveThemeMode`.
- [x] **Step 4: Verify GREEN**
Run: `flutter test test/services/ui_preferences_service_test.dart`
Expected: PASS.
- [x] **Step 5: Wire app theme mode**
Make `ChanoraApp` load persisted theme mode and pass `themeMode` into `MaterialApp`.
- [x] **Step 6: Run focused Flutter tests**
Run: `flutter test test/services/ui_preferences_service_test.dart`
Expected: PASS.
### Task 3: Documentation Updates
**Files:**
- Modify: `docs/implementation-status-2026-05-28.md`
- Modify: `docs/release/dv-waiver-register.md`
- Modify: `docs/verification/swe4-unit-verification-plan.md`
- Modify: `docs/verification/swe6-software-verification-plan.md`
- Modify: `docs/architecture/sdd.md`
- [x] **Step 1: Update implementation status**
Mark `chanora_state` scaffold statement as superseded by reducer implementation/tests and mark UI settings persistence implemented for SharedPreferences scope.
- [x] **Step 2: Update waiver and verification docs**
Record reducer evidence and leave event replay as the remaining P1 state-sync gap.
- [x] **Step 3: Verify docs**
Run sentinel-language scan over edited docs.
---
## Self-Review
- Spec coverage: covers reducer evidence, UI settings persistence, and document updates.
- Placeholder scan: plan contains no incomplete implementation instructions.
- Type consistency: `UiThemeMode`, `themeMode`, and `saveThemeMode` names are used consistently.
@@ -0,0 +1,68 @@
# SWE.2/SWE.3 Baselines Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Add reviewable SWE.2/SAD and SWE.3/SDD baselines so the DV document chain is no longer missing the architecture and detailed design layers.
**Architecture:** Keep SWE.2 in `docs/architecture/sad.md` and SWE.3 in `docs/architecture/sdd.md`, matching the README document tree. Update DV traceability and verification-plan wording to consume these baselines while preserving honest limitations for areas that still need deeper detail.
**Tech Stack:** Markdown, existing SysDes/SRS baselines, current Flutter/Rust workspace structure.
---
### Task 1: Write SWE.2 SAD Baseline
**Files:**
- Create: `docs/architecture/sad.md`
- [x] **Step 1: Create SAD with architecture views**
Write sections for purpose, upstream sources, components, static view, runtime flows, interface catalogue, dependency rules, non-functional allocation, architectural decisions, verification handoff, traceability, and open architecture risks.
### Task 2: Write SWE.3 SDD Baseline
**Files:**
- Create: `docs/architecture/sdd.md`
- [x] **Step 1: Create SDD with module designs**
Write sections for purpose, upstream sources, module catalogue, detailed API/data/state design, persistence, diagnostics, platform adapters, build/release design, verification hooks, traceability, and open detailed-design risks.
### Task 3: Update DV Traceability
**Files:**
- Modify: `docs/governance/traceability-matrix.md`
- Modify: `docs/verification/verification-master-plan.md`
- [x] **Step 1: Remove SAD/SDD missing limitation**
Update traceability text so it says SAD and SDD baselines exist, with known depth limitations instead of missing-document limitations.
- [x] **Step 2: Update verification plan inputs**
Update verification master plan to reference SAD and SDD as current inputs for SWE.4/SWE.5.
### Task 4: Verify SWE.2/SWE.3 Pack
**Files:**
- Inspect created and updated docs.
- [x] **Step 1: Sentinel-language scan**
Run the sentinel-language scan over `docs/architecture`, `docs/governance`, and `docs/verification`.
Expected: no matches introduced by this baseline pack.
- [x] **Step 2: File presence check**
Run: `ls docs/architecture`
Expected: `sad.md` and `sdd.md` are listed.
---
## Self-Review
- Spec coverage: creates SAD and SDD baselines and updates DV traceability consumers.
- Placeholder scan: no incomplete instructions are present.
- Type consistency: document names match README paths.
@@ -0,0 +1,116 @@
# Chanora Server Prefetch Crate Design
Date: 2026-05-28
## Goal
Move server-resolution prefetch policy out of `chanora_core` into a focused Rust crate named `chanora_server_prefetch`, without changing connection behavior, Flutter APIs, or protocol dialing semantics.
## Non-Goals
- Do not change resolver behavior or DNS/SRV/TSDNS ordering.
- Do not change `ConnectConfig.resolved_address` semantics in `chanora_protocol`.
- Do not expose prefetch state in the UI.
- Do not prefetch bookmarks or additional hosts.
- Do not persist prefetched addresses.
## Architecture
Add a workspace member at `crates/chanora_server_prefetch`.
Responsibilities:
- Normalize server host keys by trimming and lowercasing.
- Track one active prefetch generation.
- Store at most one successful prefetched socket address.
- Reject stale async completions by generation.
- Return a prefetched address only for an exact normalized host match.
- Enforce the 2-minute freshness TTL.
- Resolve server addresses by calling `chanora_resolver::ChanoraResolver::resolve_client_address`.
- Log prefetch start, success, miss/stale, and failure diagnostics.
Dependencies:
- `chanora_resolver` for actual server address resolution.
- `tokio` for `Mutex` and spawned prefetch tasks.
- `tracing` for diagnostics.
- `thiserror` for a narrow `ServerPrefetchError` public error type.
## Public API
The crate exposes this small async owner type:
```rust
pub struct ServerPrefetcher { ... }
impl ServerPrefetcher {
pub fn new() -> Self;
pub async fn prefetch(&self, host: String) -> Result<(), ServerPrefetchError>;
pub async fn fresh_match(&self, host: &str) -> Option<std::net::SocketAddr>;
}
```
`prefetch` returns after scheduling work, preserving the current invisible, non-blocking behavior. Empty normalized hosts are ignored successfully. Resolution failures are stored only as diagnostics and do not affect connect semantics.
## Core Integration
`chanora_core` replaces its private prefetch cache fields and helpers with `ServerPrefetcher`.
Core remains the trust boundary for connection config:
- It clears any caller-provided `ConnectConfig.resolved_address` before lookup.
- It asks `ServerPrefetcher::fresh_match` for the current host.
- It sets `dial_cfg.resolved_address` only from a fresh exact cache hit.
- It stores supervisor/reconnect config with `resolved_address: None`.
The Flutter bridge keeps calling the same core API, `prefetch_server_resolution(host)`. No Dart API change is intended.
## Data Flow
1. Flutter host editing schedules `ChanoraSession::prefetch_server_resolution(host)`.
2. Core delegates to `ServerPrefetcher::prefetch(host)`.
3. The prefetcher normalizes the host, increments generation, and spawns resolver work.
4. On success, the prefetcher stores the resolved socket address if the generation is still current.
5. On connect, core prepares `(stored_cfg, dial_cfg)`.
6. Core clears untrusted `resolved_address`, asks the prefetcher for a fresh exact match, and applies the result only to `dial_cfg`.
7. Protocol uses `dial_cfg.resolved_address` if present; otherwise it resolves normally.
## Error Handling
- Prefetch failures remain invisible to users.
- Prefetch failures are logged through `tracing`.
- If prefetch misses, is stale, or fails, connect falls back to normal protocol resolution.
- A caller-supplied `resolved_address` is never trusted by core.
## Testing
Move cache-policy tests from `chanora_core` into `chanora_server_prefetch`:
- fresh exact match returns the socket address.
- stale entries are ignored.
- different hosts are ignored.
- stale generation completions are ignored.
- blank normalized hosts return `Ok(())` and do not update generation or spawn resolver work.
Keep core tests for connection trust-boundary behavior:
- untrusted `resolved_address` is cleared on cache miss.
- prepared stored/supervisor config has `resolved_address: None`.
- prepared dial config can receive a fresh prefetched address.
Run at minimum:
- `cargo test -p chanora_server_prefetch --lib`
- `cargo test -p chanora_core --lib`
- `cargo test -p chanora_protocol --lib`
For final confidence, rerun the Android server connect smoke path that verifies prefetch logs and connected UI.
## Acceptance Criteria
- Workspace builds with the new crate member.
- `chanora_core` no longer owns the prefetch cache implementation.
- `chanora_server_prefetch` owns prefetch normalization, TTL, generation, storage, and resolver-backed warming.
- Public Flutter and Rust protocol behavior is unchanged.
- Existing server connect and reconnect safety tests pass.
- Android connect still reaches the connected server UI and does not get stuck in `Connecting` or `Synchronizing`.
@@ -0,0 +1,203 @@
# Server Resolution Prefetch Design
Date: 2026-05-28
## Purpose
Reduce perceived server join latency by resolving the active TeamSpeak server address before the user taps Connect. Prefetch must be invisible, conservative, and safe: it may warm resolver state, but it must not change connection semantics or surface background errors to the user.
Recent Android testing showed resolver latency can dominate the first part of the connect flow. A prior fix bounded slow TS3 SRV discovery and removed Android's forced Cloudflare resolver. Prefetch builds on that by hiding remaining address-resolution work when the user has already entered or loaded a likely server address.
## Goals
- Prefetch only the active server address field.
- Keep the feature invisible to users.
- Reuse prefetched results only for exact normalized host matches.
- Keep prefetched results fresh for 2 minutes.
- Preserve today's Connect behavior when prefetch misses, fails, or is stale.
- Avoid prefetching all bookmarks.
- Avoid opening a TS3 session before the user taps Connect.
## Non-Goals
- No visible resolving, ready, or failed UI state.
- No bookmark fan-out prefetch.
- No persisted resolver cache across app launches.
- No password, channel, or permission validation during prefetch.
- No server reachability probe beyond address resolution.
- No connection warm-up or pre-authentication.
## Chosen Approach
Use a Rust-owned resolver prefetch cache with Flutter-owned scheduling.
Flutter knows when the active host field changes, so it schedules prefetch requests. Rust owns resolver correctness, normalization, cache validity, and connect-time reuse. This keeps Flutter from depending on resolver internals and ensures Connect can independently decide whether a prefetched result is safe to use.
Other approaches considered:
- Flutter-only prefetch: rejected because it pushes resolver state into Dart and creates a weaker boundary between UI and connection behavior.
- Resolver-internal repeated-call cache only: rejected because it does not hide first-click latency from the active host field.
## Behavior
Prefetch starts for the active host value in two cases:
- After `_loadUiSettings()` loads the last-used host into `_hostCtl`.
- After the user stops editing the host field for about 700 ms.
The feature is invisible:
- No SnackBars.
- No inline status text.
- No disabled Connect button.
- No user-facing error if prefetch fails.
Connect behavior:
- If the current normalized host exactly matches a fresh prefetched entry, Connect uses the cached resolved address.
- If the cache is missing, stale, failed, or for a different host, Connect resolves normally.
- Connect remains the only operation that opens a TS3 session.
## Architecture
### Flutter Scheduling
`_BetaHomeState` owns the host text field. It should add a listener to `_hostCtl` and manage a short debounce timer.
Responsibilities:
- Trim the host input before scheduling.
- Skip empty values.
- Reset the debounce timer on each edit.
- Call a bridge prefetch API after about 700 ms of idle typing.
- Schedule one prefetch after settings load if the loaded host is non-empty.
- Dispose the listener and timer with the widget state.
Flutter does not store resolved addresses and does not decide whether Connect can use a prefetched result.
### Bridge API
Add a fire-and-forget bridge function shaped like:
```text
prefetch_server_resolution(host: String) -> Result<(), BridgeError>
```
The bridge call should return after the prefetch task has been accepted by the Rust runtime. It must not wait for resolution to complete. Background completion or failure is reported only through diagnostics/logging.
### Rust Resolver Cache
Rust stores a small prefetch cache owned near the session/resolver boundary. A single latest-host entry is enough for v1, because the design only prefetches the active field.
Cache entry fields:
- Normalized input host.
- Resolved `host:port` address.
- Resolution method.
- Completion timestamp.
- Generation or request id.
- Optional sanitized failure metadata for diagnostics.
The cache TTL is 2 minutes.
### Connect Integration
Connect should ask Rust for a fresh exact-match prefetched result before running normal resolution.
Rules:
- Exact normalized host match is required.
- Entry age must be at most 2 minutes.
- Failed entries must not block normal connect resolution.
- Stale entries must be ignored.
- Missing cache must behave exactly like today.
## Data Flow
1. App starts.
2. `_loadUiSettings()` loads the last-used host into `_hostCtl`.
3. Flutter schedules invisible prefetch for that host.
4. User edits the host field.
5. Flutter cancels the pending debounce timer and starts a new one.
6. After 700 ms idle, Flutter calls Rust prefetch with the latest trimmed host.
7. Rust normalizes and resolves the host through the same resolver path used by Connect.
8. Rust stores the result if it still matches the latest generation for that normalized host.
9. User taps Connect.
10. Rust Connect checks the cache for a fresh exact-match result.
11. Cache hit: Connect uses the prefetched address.
12. Cache miss/stale/failure: Connect resolves normally.
## Cancellation And Staleness
Cancellation can be logical rather than hard task cancellation.
- Flutter prevents obsolete debounce timers from firing.
- Rust tags requests by normalized host and generation.
- Late completions for stale generations must not replace newer successful entries.
- Duplicate prefetches for the same normalized host may coalesce or refresh the same entry.
This avoids complexity while preventing old input values from poisoning the cache.
## Error Handling
Prefetch failures are diagnostic-only.
- Empty host: skip prefetch.
- Invalid host shape: skip or fail silently with debug diagnostics.
- Resolver failure: store optional sanitized failure metadata for diagnostics only.
- Connect after failure: normal connect path runs and surfaces errors as it does today.
- App resume and network changes: no special invalidation in v1; TTL handles staleness.
- Disconnect: cache may remain because it is independent of the TS3 session.
## Diagnostics
Add privacy-safe logs for:
- Prefetch started.
- Prefetch result.
- Prefetch failed.
- Connect using prefetched resolution.
- Connect prefetch miss or stale entry.
Do not log passwords, channel passwords, or nickname. Host and resolved address are acceptable because resolver/connect logging already includes them today.
## Testing
Rust tests:
- Fresh exact-match prefetched result is reusable.
- Stale prefetched result is ignored.
- Different normalized host is ignored.
- Failed prefetch does not block normal resolution.
- Late stale generation cannot overwrite a newer cache entry.
Flutter tests should cover the scheduling logic through a small testable helper if wiring directly through `_BetaHomeState` would be brittle:
- Host edits debounce prefetch scheduling.
- Empty host does not prefetch.
- Settings-loaded host schedules one prefetch.
Manual Android smoke test:
- Install debug APK.
- Launch app.
- Wait for last-used host prefetch or type host and wait past debounce.
- Tap Connect.
- Confirm UI reaches connected server view.
- Confirm logcat shows either a prefetch cache hit or safe fallback behavior.
## Acceptance Criteria
- Typing or loading a valid host can warm resolver state before Connect.
- Connect never fails because prefetch failed.
- Connect never uses a prefetched result for a different normalized host.
- Prefetched entries older than 2 minutes are ignored.
- No visible UI is added for prefetch state.
- Bookmarks are not prefetched in bulk.
- Android debug build and focused resolver/Flutter tests pass.
## Implementation Notes
- Prefer a single latest-host cache unless implementation reveals an existing cache abstraction that makes a tiny map simpler.
- Prefer minimal bridge API surface: one prefetch call and connect-time internal cache lookup.
- Keep the resolver cache near existing Rust session/connect code so future non-Flutter clients can benefit from the same behavior.
@@ -0,0 +1,70 @@
# State Sync and UI Settings Validation Design
**Date:** 2026-05-29
**Status:** Approved for implementation
**Scope:** P0/P1 validation-based completion for state-sync evidence and UI settings persistence
## 1. Goal
Close the current DV/P0-P1 gaps for reducer/state-sync evidence and UI settings persistence with tests first, minimal behavior changes, and updated documentation evidence.
## 2. State-Sync Design
`chanora_state` remains the reducer owner. The validation pass adds focused tests for known reducer contracts rather than broad refactoring. Missing behavior is implemented only when a test proves a gap.
Required evidence covers:
| Contract | Evidence |
|---|---|
| Snapshot creates ready state and deterministic normalized order | Existing and expanded reducer tests |
| Reconnect discards stale state and reconnect snapshot replaces state | Existing reducer tests |
| Disconnected/lost states suppress live deltas | Existing reducer tests |
| Duplicate IDs are normalized deterministically | Existing reducer tests |
| Unknown client voice activity is ignored | Existing reducer tests |
| Channel deletion removes clients in deleted channel | New reducer regression test and implementation |
| Same event sequence produces same state and deltas | Existing reducer determinism test |
## 3. UI Settings Design
`UiPreferencesService` remains a Flutter service backed by `shared_preferences`. This is the minimal P0/P1-complete implementation because the current app already uses SharedPreferences and no current behavior requires SQLite-backed UI settings.
`UiSettings` gains a typed `themeMode` field with values:
| Value | Meaning |
|---|---|
| `system` | Follow platform theme |
| `light` | Force light theme |
| `dark` | Force dark theme |
The service persists the selected theme mode, falls back to `system` for invalid stored values, and preserves independent saves for host and nickname.
## 4. App Wiring
`ChanoraApp` becomes stateful enough to load and apply persisted theme mode. `_BetaHome` continues to load/save host and nickname through `UiPreferencesService`. UI controls for selecting theme mode are out of this slice unless already present; this slice provides persistence and app-level application.
## 5. Documentation Updates
After tests pass:
| Document | Update |
|---|---|
| `docs/implementation-status-2026-05-28.md` | Mark reducer scaffold statement stale/resolved and UI settings persistence implemented for SharedPreferences scope |
| `docs/release/dv-waiver-register.md` | Close or soften reducer waiver; keep event replay as P1 gap |
| `docs/verification/swe4-unit-verification-plan.md` | Record reducer test evidence and UI settings tests |
| `docs/verification/swe6-software-verification-plan.md` | Update state sync and UI settings DV status |
| `docs/architecture/sdd.md` | Record UI settings persistence design |
## 6. Validation
Run focused tests:
```text
cargo test -p chanora_state --locked
flutter test test/services/ui_preferences_service_test.dart
```
Run wider checks if touched app-shell behavior requires it:
```text
flutter test --exclude-tags e2e
```
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,27 @@
# Chanora Adaptive Layout and Platform Guide
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
## 1. Layout Policy
Chanora prioritizes compact/mobile layout for MVP while preserving a path to medium and expanded layouts.
| Layout class | Current expectation |
|---|---|
| Compact | Primary supported layout for mobile and narrow windows |
| Medium | Must preserve connection and voice control visibility; side navigation rail remains a P1 gap where not implemented |
| Expanded | Persistent side panes are deferred beyond current candidate |
## 2. Platform Policy
| Platform concern | Required behavior |
|---|---|
| Safe areas/system bars | Critical controls remain reachable |
| Android back | Back intent service mediates app behavior |
| iOS gestures/haptics | Platform services handle iOS-specific behavior |
| Desktop PTT | UI displays actual PTT capability level and fallback state |
## 3. DV Conclusion
Adaptive layout policy is documented. Medium/expanded hardening remains a tracked post-MVP/P1 gap where not implemented.
+38
View File
@@ -0,0 +1,38 @@
# Chanora Material 3 Component Catalog
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
## 1. Component Catalogue
| Component area | Source files | Purpose |
|---|---|---|
| Connect and bookmarks | `connect_widgets.dart`, `input_dialogs.dart` | Server input, bookmarks, connect actions |
| Channel and client view | `snapshot_view.dart`, `client_info_sheet.dart`, `channel_spacer.dart` | Channel tree, clients, client details |
| Chat | `chat_views.dart`, `bbcode_text.dart` | Channel text and BBCode rendering |
| Voice controls | `voice_bar.dart`, `voice_compact.dart`, `voice_settings*.dart` | Mute, deaf, PTT, levels, processing controls |
| Platform/permission indicators | `permission_state_banner.dart`, `ptt_capability_badge.dart`, `talk_power_warning.dart` | Platform readiness and safety warnings |
| Diagnostics | `audio_debug_stats_panel.dart` and diagnostics surfaces in app shell | Runtime/debug evidence and export support |
## 2. Acceptance Criteria
| Component area | DV acceptance criterion |
|---|---|
| Connect and bookmarks | User can enter server details, save/reuse bookmarks, and receive user-safe errors |
| Channel and client view | Channel tree and online clients remain visible after snapshot updates |
| Chat | Channel text renders without corrupting Unicode or unsafe BBCode display |
| Voice controls | Mute, deaf, PTT, metering, and processing controls expose current state clearly |
| Platform/permission indicators | Permission and capability limitations are visible and not color-only |
| Diagnostics | Diagnostic/debug surfaces do not expose unredacted secrets in release evidence |
## 3. Evidence Required Before Release
| Evidence | Purpose |
|---|---|
| Flutter widget test run | Confirms component behavior remains stable |
| Accessibility review | Confirms critical states are not color-only and controls are reachable |
| Candidate screenshots or demo notes | Supports DV review and release readiness |
## 4. DV Conclusion
The component catalog is sufficient for DV navigation. Final UI audit should attach screenshots or test evidence for each critical component.
+21
View File
@@ -0,0 +1,21 @@
# Chanora Material 3 Design Tokens
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
## 1. Source
Implementation token source is `apps/chanora_flutter/lib/design/chanora_tokens.dart`.
## 2. Token Areas
| Token area | Purpose |
|---|---|
| Color roles | Connection, voice, permission, diagnostic, warning, and neutral states |
| Typography roles | App shell, status, channel/client lists, chat, settings, diagnostics |
| Spacing roles | Compact/mobile layout, control grouping, list density |
| Shape/elevation roles | Material 3 cards, sheets, banners, controls |
## 3. Verification
Token usage is verified through Flutter widget tests, visual review, and accessibility checks that critical state is not represented by color alone.
+8
View File
@@ -0,0 +1,8 @@
# Chanora Material 3 Guideline
**Document status:** DV path record
**Canonical source:** `../material3-guideline.md`
The current Material 3 guideline lives at `docs/material3-guideline.md`. This file preserves the README-advertised path `docs/ui-ux/material3-guideline.md` for DV navigation.
Reviewers shall use `docs/material3-guideline.md` as the canonical UI/UX guideline until the migration moves it into `docs/ui-ux/`.
@@ -0,0 +1,57 @@
# SWE.4 Unit Verification Plan
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
**Scope:** Unit-level verification for Chanora software components
## 1. Purpose
This plan defines unit verification coverage for the software modules that implement Chanora behavior. The plan is based on the current repository state and the SRS verification hooks. It is acceptable for DV only when open unit gaps remain visible in the waiver register.
## 2. Unit Verification Scope
| Area | Components | Verification method | Current evidence |
|---|---|---|---|
| Rust core orchestration | `core/chanora_core` | Cargo unit tests, integration-oriented crate tests | `cargo test --workspace --locked --no-fail-fast` in CI |
| Protocol adapter | `crates/chanora_protocol` | Protocol DTO and error mapping tests | Workspace tests; protocol compatibility still needs SWE.5/SYS.4 evidence |
| State sync | `crates/chanora_state` | Snapshot, delta, reducer, malformed-event, reconnect, deterministic ordering, unknown-client, and channel-delete/client cleanup unit tests | Local `cargo test -p chanora_state --locked` passes with 27 tests |
| Audio subsystem | `crates/chanora_audio` | DSP, Opus, mixer, gate, PTT logic, baseline examples, benches | Workspace tests and benchmark harnesses; platform loopback evidence is integration/system scope |
| Storage | `crates/chanora_storage` | Bookmark repository, identity store, encryption behavior, keyring-disabled CI mode | Workspace tests with `CHANORA_DISABLE_KEYRING=1` in CI |
| Diagnostics | `crates/chanora_diagnostics` | Redaction, known-secret registry, diagnostic export JSON, log sink | Workspace tests and security audit plan |
| Resolver and prefetch | `crates/chanora_resolver`, `crates/chanora_server_prefetch`, Flutter prefetch debouncer | Resolver fallback, cache TTL, generation safety, debouncer tests | Workspace tests and `apps/chanora_flutter/test/services/prefetch_debouncer_test.dart` |
| Flutter services | `apps/chanora_flutter/lib/services` | Dart unit tests | Service tests under `apps/chanora_flutter/test/services/` |
| Flutter widgets | `apps/chanora_flutter/lib/widgets` and screen widgets | Widget tests | Widget tests under `apps/chanora_flutter/test/widgets/` |
## 3. Required CI Commands
| Command | Owner | Expected use |
|---|---|---|
| `cargo check --workspace --locked` | Software | Rust compile verification |
| `CHANORA_DISABLE_KEYRING=1 cargo test --workspace --locked --no-fail-fast` | Software / QA | Rust unit and crate tests without host keyring dependency |
| `cargo clippy --workspace --all-targets -- -D warnings` | Software | Advisory static analysis in current CI |
| `flutter analyze` in `apps/chanora_flutter` | Software | Dart static analysis |
| `flutter test --exclude-tags e2e` in `apps/chanora_flutter` | Software / QA | Flutter unit and widget tests |
## 4. SRS Unit Coverage Focus
| SRS group | Unit focus | Status for DV |
|---|---|---|
| SRS-036 through SRS-043 | Rust core and state behavior | Reducer unit evidence present; candidate CI/run ID still required for release record |
| SRS-054 through SRS-061 | State sync reducers and replay support | Reducer unit coverage present; standalone event replay support remains P1/not complete |
| SRS-062 through SRS-083 | Audio capture, processing, codec, playback, controls | Partial; unit and benchmark evidence exists, full platform loopback is SWE.5/SYS.4 |
| SRS-084 through SRS-095 | Storage, secure storage, diagnostics | Partial; CI avoids real keyring and must be supplemented by platform audits |
| SRS-165 through SRS-178 | Localization and Unicode handling | Partial; generated localization exists and targeted tests should be reviewed |
| SRS-184 and platform behavior items | App initialization and platform services | Partial; service tests exist for back intent, permissions, bootstrap, and lifecycle |
## 5. Known SWE.4 Gaps
| Gap | Impact | Required DV handling |
|---|---|---|
| Standalone event replay tool is not yet complete | Limits P1 replay-based state-sync verification claims | Keep event replay waiver; reducer unit evidence is available |
| Event replay tool not found | P1 verification hook is incomplete | Mark deferred/P1 in SWE.6 and waiver register if discussed |
| Platform keyring behavior not exercised in CI | Secure-storage unit evidence is incomplete for real OS services | Cover through platform audit/SYS.4, not CI-only claims |
| Audio device hot-plug recovery follow-up exists | Limits reliability evidence for SRS-082 | Mark as P1 gap |
## 6. SWE.4 DV Decision Rule
SWE.4 can pass for documentation readiness if all existing unit commands are identified and open unit gaps are listed as limitations. SWE.4 cannot be used to approve public release until required unit evidence is executed on the release candidate and attached to the release-readiness record.
@@ -0,0 +1,58 @@
# SWE.5 Software Integration Verification Plan
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
**Scope:** Integration verification across Chanora software components
## 1. Purpose
This plan defines how Chanora verifies that independently tested software components work together. It covers the integration paths identified by SRS section 8 and SysDes verification handoff items.
## 2. Integration Paths
| Integration path | Covered components | Primary requirements | Evidence method |
|---|---|---|---|
| Flutter to bridge to Rust core | Flutter UI/state, generated FRB bindings, `chanora_bridge`, `chanora_core` | SRS-031 through SRS-035, SRS-103 | Flutter integration tests, Rust bridge tests, manual candidate smoke |
| Core to protocol adapter | `chanora_core`, `chanora_protocol`, `tsclientlib` | SRS-044 through SRS-053, SRS-131, SRS-134 | Protocol integration tests and compatible-server demo |
| Protocol to state | Protocol events, Rust core state handling, `chanora_state`, Flutter snapshot mapper | SRS-054 through SRS-061, SRS-124 | Reducer tests, snapshot mapper tests, reconnect/error tests |
| Audio to platform | `chanora_audio`, OS audio APIs, Flutter voice controls, platform permissions | SRS-062 through SRS-083, SRS-104 through SRS-115, SRS-135 | Audio loopback tests, platform smoke, mobile device tests |
| Storage to platform secure storage | `chanora_storage`, keyring adapters, Flutter bookmark/identity UI | SRS-084 through SRS-095, SRS-126, SRS-137 | Storage tests, platform secure-storage audit |
| Diagnostics export | Diagnostics crate, Flutter diagnostics UI, redaction, share/export surface | SRS-095 through SRS-102, SRS-126, SRS-128 | Redaction tests, diagnostic export review, manual export demo |
| Server resolution prefetch | Flutter host-field debouncer, `chanora_server_prefetch`, `chanora_resolver`, connect path | Resolver/prefetch design specs and connection requirements | Unit tests, Android connect smoke, manual connect demo |
| Packaging and release build | Flutter build, Rust cdylib/framework/DLL/SO packaging, CI workflows | SRS-116 through SRS-123, SRS-127 | CI build evidence, unsigned iOS build, platform-specific smoke |
## 3. Required Integration Evidence
| Evidence | Current state | DV conclusion |
|---|---|---|
| CI Rust workspace test run | Defined in `.github/workflows/ci.yml` | Accept as automated baseline when latest run is attached |
| CI Flutter analyze/test run | Defined in `.github/workflows/ci.yml` | Accept as automated baseline when latest run is attached |
| iOS unsigned release build | Defined in `.github/workflows/ci.yml` and `docs/release/ios-build.md` | Accept for unsigned build verification only |
| Audio benchmark advisory | Defined in `.github/workflows/bench-advisory.yml` | Accept as advisory performance evidence, not a release blocker |
| Windows smoke procedure | Defined in `tools/windows-smoke.md` | Accept only when an executed result is attached |
| Android connect and permission smoke | Referenced by implementation status and tests | Requires executed evidence for release decision |
## 4. Integration Acceptance Criteria
An integration path passes when:
| Criterion | Requirement |
|---|---|
| Buildability | Integrated components compile in CI or on the declared target build host |
| Data contract stability | DTOs and bridge boundaries preserve required fields and error states |
| Error behavior | Integration failures produce user-safe errors, not crashes or secret leakage |
| Platform behavior | Platform-specific permission, audio, and lifecycle behavior matches the target platform policy |
| Evidence attachment | Test logs, CI run IDs, smoke records, or manual demo notes are referenced in the release-readiness record |
## 5. Known SWE.5 Gaps
| Gap | Impact | Required DV handling |
|---|---|---|
| Desktop release artifacts are source-buildable only for current candidate | Blocks binary distribution readiness for Windows/macOS/Linux | Release record must state source-build-only scope |
| iOS build is unsigned | Blocks App Store/TestFlight release approval | Release record must state unsigned verification only |
| Android Keystore-backed DEK deferred | Limits secure-storage integration claim on Android | Waiver required for internal testing; public release claim blocked |
| Event replay infrastructure not found | Limits protocol-state integration stress evidence | Mark P1 gap |
## 6. SWE.5 DV Decision Rule
SWE.5 can pass for DV documentation readiness if every integration path has an identified evidence method and every incomplete path has a waiver or follow-up. SWE.5 does not pass for public release until current candidate integration runs are attached to the release-readiness record.
@@ -0,0 +1,62 @@
# SWE.6 Software Verification Plan
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
**Scope:** Integrated software verification against `docs/srs.md`
## 1. Purpose
This plan verifies the integrated Chanora software against the SRS. It focuses on SRS-128 and the MVP acceptance requirements inherited from SysRS-241 through SysRS-257.
## 2. SRS Verification Strategy
| SRS area | Verification approach | Evidence source |
|---|---|---|
| Software process and traceability | Review and inspection | `docs/srs.md`, `docs/governance/traceability-matrix.md` |
| UI and app shell | Flutter widget/service tests and demo | `apps/chanora_flutter/test/` |
| Bridge and Rust core | Workspace tests and integration smoke | CI Rust job, bridge/core tests |
| Protocol and connection | Protocol integration and compatible-server demo | Protocol tests, manual server demo |
| State sync | Reducer tests, snapshot mapper tests, reconnect/error scenarios, and runtime integration evidence | Rust state tests, Flutter mapper tests, compatible-server runtime evidence |
| Audio | Audio tests, processing tests, benchmark advisory, platform demo | `chanora_audio` tests/benches and manual platform evidence |
| Storage and secure storage | Storage tests and platform secure-storage audit | Rust storage tests, platform audit |
| Diagnostics and redaction | Redaction tests, diagnostic export demo, security review | Diagnostics tests, export review |
| Platform and release | Platform smoke and release build inspection | CI, platform smoke docs, release record |
## 3. MVP Acceptance Matrix
| SysRS | Acceptance requirement | SRS coverage | Required evidence | Current DV status |
|---|---|---|---|---|
| SysRS-241 | Connect to TeamSpeak 3-compatible server using `tsclientlib` | SRS-045, SRS-046, SRS-049, SRS-050, SRS-128, SRS-134 | Compatible-server connection demo and protocol integration evidence | Passed with limitation when demo log is attached; not sufficient for public release alone |
| SysRS-242 | Display server channel tree | SRS-019 through SRS-022, SRS-054 through SRS-058, SRS-128 | UI demo, snapshot/state tests | Reducer unit evidence present; candidate UI/demo and runtime reducer-integration evidence must still be attached |
| SysRS-243 | Display online clients | SRS-019 through SRS-022, SRS-054 through SRS-058, SRS-128 | UI demo, snapshot/state tests | Reducer unit evidence present; candidate UI/demo and runtime reducer-integration evidence must still be attached |
| SysRS-244 | Allow user to join a voice channel | SRS-023, SRS-024, SRS-045 through SRS-051, SRS-128 | Channel join demo, error mapper tests | Passed with limitation when candidate demo is attached |
| SysRS-245 | Send voice | SRS-062 through SRS-071, SRS-077, SRS-079, SRS-128 | Audio loopback/platform demo | Partial; platform evidence must be attached |
| SysRS-246 | Receive voice | SRS-071 through SRS-076, SRS-078, SRS-081, SRS-128 | Audio loopback/platform demo | Partial; platform evidence must be attached |
| SysRS-247 | Support microphone mute | SRS-025, SRS-077, SRS-079, SRS-128 | UI/audio gate demo or test | Passed with limitation when candidate demo is attached |
| SysRS-248 | Support output deaf | SRS-025, SRS-078, SRS-128 | UI/audio gate demo or test | Passed with limitation when candidate demo is attached |
| SysRS-249 | Support push-to-talk | SRS-077 through SRS-079, SRS-197 through SRS-200, SRS-128 | PTT backend tests, platform capability evidence | Partial; release record must state platform capability level |
| SysRS-250 | Support Echo Canceller | SRS-064 through SRS-067, SRS-125, SRS-128 | Audio processing test/demo | Partial; attach audio evidence |
| SysRS-251 | Support Automatic Gain Control | SRS-064 through SRS-067, SRS-125, SRS-128 | Audio processing test/demo | Partial; attach audio evidence |
| SysRS-252 | Support Noise Suppression | SRS-064 through SRS-067, SRS-125, SRS-128 | Audio processing test/demo | Partial; attach audio evidence |
| SysRS-253 | Support High-Pass Filter | SRS-064 through SRS-067, SRS-125, SRS-128 | Audio processing test/demo | Partial; attach audio evidence |
| SysRS-254 | Send and receive channel text messages | SRS-019, SRS-036 through SRS-038, SRS-170 through SRS-174, SRS-128 | Chat UI tests and compatible-server demo | Passed with limitation when candidate demo is attached |
| SysRS-255 | Save and reuse server bookmarks | SRS-084 through SRS-089, SRS-128 | Storage tests and UI demo | Passed with limitation when candidate demo is attached |
| SysRS-256 | Use secure storage for sensitive data | SRS-090 through SRS-095, SRS-126, SRS-137 | Security audit and platform storage evidence | Partial; Android Keystore-backed DEK deferred |
| SysRS-257 | Export redacted diagnostic logs | SRS-028, SRS-093 through SRS-102, SRS-126, SRS-128 | Redaction tests and export demo | Passed with limitation when redaction/export evidence is attached |
## 4. Candidate Software Verification Runs
For a release decision, the following run records must be attached or linked from `docs/release/release-readiness-go-nogo-record.md`:
| Run record | Command or method | Required for |
|---|---|---|
| Rust workspace CI | `cargo check --workspace --locked` and `cargo test --workspace --locked --no-fail-fast` | All Rust-backed SRS behavior |
| Flutter CI | `flutter analyze` and `flutter test --exclude-tags e2e` | UI, service, and widget SRS behavior |
| Candidate compatible-server demo | Manual or scripted demo against a controlled TeamSpeak-compatible server | SysRS-241 through SysRS-246, SysRS-254 |
| Candidate audio demo | Manual or scripted loopback/platform run | SysRS-245 through SysRS-253 |
| Candidate diagnostics demo | Export and inspect redacted diagnostic bundle | SysRS-257, SRS-093 through SRS-102 |
| Candidate secure-storage audit | Platform inspection for Windows, macOS, Linux, iOS, Android | SysRS-256, SRS-090 through SRS-095 |
## 5. SWE.6 DV Decision Rule
SWE.6 can pass the DV meeting if the acceptance matrix is accepted as the controlling checklist and each partial item is carried into the release-readiness record. SWE.6 cannot approve public release while DEC-012, Android secure-storage limitation, platform signing, and incomplete candidate evidence remain open.
@@ -0,0 +1,57 @@
# SYS.4 System Integration Verification Plan
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
**Scope:** System integration verification against `docs/sysdes.md`
## 1. Purpose
This plan verifies Chanora as an integrated application system, including software, operating-system services, hardware, network dependencies, external compatible servers, diagnostics, and release operations.
## 2. System Elements Under Verification
| Element | Integration concern | Verification method |
|---|---|---|
| External TeamSpeak 3-compatible server | Connection, authentication, channel tree, clients, voice, channel text, server permissions | Compatible-server demo and protocol compatibility matrix |
| OS audio services | Capture, playback, route changes, permissions, mobile audio session, foreground behavior | Platform audio tests and manual device smoke |
| Audio hardware | Microphone/headset/speaker availability and failure handling | Platform smoke and user-safe error checks |
| Platform secure storage | Keychain/Credential Manager/Secret Service/mobile key stores and fallback behavior | Platform security audit |
| Network | DNS/SRV/TSDNS resolution, unreachable server behavior, reconnect | Resolver tests and network failure smoke |
| Platform packaging | Android, iOS, Windows, macOS, Linux build and packaging behavior | CI build, unsigned iOS build, platform smoke, release inspection |
| Diagnostics export | User-initiated export and secret redaction | Export demo and security review |
| Public wording | Non-affiliation and compatible-server claims | Legal/trademark review |
## 3. SysDes Verification Handoff Coverage
| SysDes item | Handoff | SYS.4 handling |
|---|---|---|
| SysDes-102 | Protocol verification via protocol probe and compatibility matrix | Requires compatible-server evidence and protocol adapter review |
| SysDes-103 | State verification via reducer and event replay tests | Reducer evidence handled in SWE.4/SWE.5; event replay remains P1 gap |
| SysDes-104 | Audio loopback and processing tests | Requires platform audio evidence and processing tests |
| SysDes-105 | Security audit for storage, keys, passwords, validation, redaction | Requires security/privacy/legal gate review |
| SysDes-106 | Deployment package, signing, notarization, app-store build, metadata | Release-readiness record controls final decision |
| SysDes-107 | MVP acceptance verification for SysRS-241 through SysRS-257 | SWE.6 acceptance matrix controls software evidence; SYS.4 adds environment evidence |
## 4. Platform Matrix
| Platform | Current candidate status | Required SYS.4 evidence before public/store release |
|---|---|---|
| Android | Implemented features include permissions, foreground service, Oboe audio, MODE_IN_COMMUNICATION; Android Keystore-backed DEK deferred | Device smoke, permission flow, foreground voice, secure-storage limitation waiver, Play target SDK inspection |
| iOS | Source-buildable and unsigned; AVAudioSession work exists; public artifact not ready | Unsigned build evidence, device audio session smoke, signing/TestFlight evidence before release |
| Windows | Source-buildable; smoke procedure exists | Executed smoke result, PTT capability evidence, packaging/signing evidence before release |
| macOS | Source-buildable; not in current release artifacts | Build evidence, PTT capability evidence, signing/notarization evidence before release |
| Linux | Source-buildable; GlobalShortcuts portal behavior depends on environment | Build/smoke evidence, portal/focused fallback capability evidence, packaging evidence before release |
## 5. SYS.4 Acceptance Criteria
| Criterion | Required result |
|---|---|
| External server compatibility | Candidate connects to a controlled compatible server and exercises connection, channel, voice, and text flows |
| Platform behavior transparency | Release notes and UI do not overstate platform PTT, packaging, or secure-storage capabilities |
| Security/privacy/legal readiness | DEC-012 and privacy/security gates are signed off or explicitly block release |
| Release artifact integrity | Build number, commit SHA, tag, artifact hashes, and signing status are recorded |
| Environmental limitations | Source-build-only, unsigned, or platform-specific limitations are listed in release readiness |
## 6. SYS.4 DV Decision Rule
SYS.4 can pass the documentation baseline if system integration responsibilities, platform evidence needs, and release blockers are explicit. SYS.4 cannot pass for public/store release until platform-specific evidence and legal/signing gates are complete.
@@ -0,0 +1,90 @@
# Chanora Verification Master Plan
**Document status:** DV meeting baseline candidate
**Date:** 2026-05-29
**Applies to:** Chanora `0.2.0-beta.1` workspace / `v1.0.0-rc.1` release-candidate evidence
**Primary upstream documents:** `docs/sysrs.md`, `docs/sysdes.md`, `docs/srs.md`, `docs/architecture/sad.md`, `docs/architecture/sdd.md`, `docs/implementation-status-2026-05-28.md`
## 1. Purpose
This plan defines the verification evidence Chanora must present at the DV meeting. It does not approve release by itself. It gives reviewers a single route from requirements and design baselines to available tests, demos, audits, waivers, and release gates.
The DV meeting may approve the verification posture for continued internal candidate work if the open gates are accepted as explicit waivers. Public or store release remains blocked until the release-readiness record reaches `Go`.
## 2. Verification Lifecycle Mapping
| Lifecycle level | Plan | Primary input | Verification focus |
|---|---|---|---|
| SWE.4 | `docs/verification/swe4-unit-verification-plan.md` | `docs/architecture/sdd.md` module behavior plus current crate and Flutter tests | Unit behavior of reducers, protocol mappers, audio processors, storage, diagnostics, UI services, and utility logic |
| SWE.5 | `docs/verification/swe5-software-integration-verification-plan.md` | `docs/architecture/sad.md` component interfaces and `docs/architecture/sdd.md` module integration hooks | Integration across Flutter, bridge, Rust core, protocol, audio, storage, diagnostics, resolver, and packaging boundaries |
| SWE.6 | `docs/verification/swe6-software-verification-plan.md` | `docs/srs.md` | Integrated software verification against SRS, especially SRS-128 MVP acceptance |
| SYS.4 | `docs/verification/sys4-system-integration-verification-plan.md` | `docs/sysdes.md` and external environment assumptions | System integration with compatible servers, OS services, hardware, network, app stores, diagnostics, and release operations |
## 3. Verification Policy
Verification evidence shall be recorded as one of these states:
| State | Meaning | DV handling |
|---|---|---|
| Passed | Evidence exists and satisfies the stated criterion | Accept as supporting evidence |
| Passed with limitation | Evidence exists, but scope is narrower than final release scope | Accept only with documented limitation |
| Not run | Criterion is defined but no run is recorded for the candidate | Requires waiver or follow-up action |
| Failed | Evidence exists and does not satisfy the criterion | Blocks the covered release scope unless waived by the release owner |
| Deferred | Requirement is intentionally outside current candidate scope | Requires requirement stage or waiver reference |
| Blocked | External decision, platform access, legal sign-off, or environment prevents completion | Requires owner and unblock condition |
No document in this pack may convert an implementation gap into a pass. Gaps must be represented as `Not run`, `Deferred`, or `Blocked`.
## 4. Current Evidence Sources
| Evidence source | Current content | DV use |
|---|---|---|
| `.github/workflows/ci.yml` | Rust workspace check/test, advisory clippy, cargo-deny, cargo-about inventory check, Flutter analyze/test, unsigned iOS release build | Build, unit, static-analysis, supply-chain, Flutter test, and iOS unsigned build evidence |
| `.github/workflows/bench-advisory.yml` | Advisory-only realtime audio benchmark workflow for PR/push events | Performance trend evidence, not a hard quality gate |
| `docs/implementation-status-2026-05-28.md` | Current implementation status, blockers, partial areas, and P1/P2 gaps | Primary readiness and waiver input |
| `apps/chanora_flutter/test/` | Flutter widget/service/e2e-labeled tests | SWE.4/SWE.5/SWE.6 evidence depending on test type |
| Rust crate tests and benches | Workspace tests plus audio benchmark harnesses | SWE.4/SWE.5 performance and component evidence |
| `tools/windows-smoke.md` | Windows source-build smoke procedure | SYS.4/SWE.5 manual platform evidence when executed |
| `docs/release/ios-build.md` | Unsigned iOS verification build note | Release and platform build evidence |
## 5. Entry Criteria for DV Review
| Criterion | Status | Evidence |
|---|---|---|
| SysRS baseline available | Met | `docs/sysrs.md` |
| SysDes baseline available | Met | `docs/sysdes.md` |
| SRS baseline available | Met | `docs/srs.md` |
| SAD baseline available | Met | `docs/architecture/sad.md` |
| SDD baseline available | Met | `docs/architecture/sdd.md` |
| Verification plans available | Met by this pack | `docs/verification/` |
| Traceability summary available | Met by this pack | `docs/governance/traceability-matrix.md` |
| Release decision record available | Met by this pack | `docs/release/release-readiness-go-nogo-record.md` |
| Open gates represented as waivers or blockers | Met by this pack | `docs/release/dv-waiver-register.md` |
## 6. Exit Criteria for DV Meeting
The DV meeting can pass the documentation baseline if reviewers agree that:
| Exit criterion | Required result |
|---|---|
| Requirements-to-verification route is reviewable | SysRS/SysDes/SRS items map to verification plans and acceptance evidence |
| MVP acceptance criteria are explicit | SysRS-241 through SysRS-257 appear in SWE.6 with evidence status |
| Verification scope is honest | Known gaps are not marked as passed |
| Waivers are explicit | Each release-affecting gap has owner, impact, mitigation, and unblock condition |
| Release recommendation is clear | Current candidate is not represented as public-release-ready while DEC-012 and other gates remain open |
## 7. Open Gates Affecting Release
| Gate | Status | Release impact | Owning document |
|---|---|---|---|
| DEC-012 legal/trademark/OSS sign-off | Blocked / open | Blocks public or store release | `docs/legal/trademark-and-attribution-review.md`, `docs/release/dv-waiver-register.md` |
| Android Keystore-backed DEK | Deferred to v1.1 | Blocks claim that Android secrets use hardware-backed DEK protection | `docs/release/dv-waiver-register.md` |
| Full state reducer test suite | Partial | Blocks full claim for SysRS-235/SysDes-103/SRS-059 through SRS-061 | `docs/release/dv-waiver-register.md` |
| Desktop and iOS release artifacts | Source-buildable / unsigned only | Blocks broad binary distribution claims | `docs/release/release-readiness-go-nogo-record.md` |
| Store signing and notarization | Not complete for candidate | Blocks production release | `docs/release/release-readiness-go-nogo-record.md` |
## 8. DV Recommendation
Recommended DV outcome: **Pass documentation baseline with release waivers recorded**.
This means the project has enough document structure to conduct DV review and continue internal candidate validation, but it does not mean the candidate is approved for public release. The release decision remains **No-Go for public/store release** until legal, signing, platform, and evidence gates are closed or formally waived by the accountable owners.