feat(alpha): wire connect→snapshot→disconnect end-to-end (v0.1.0-alpha.1)
First Internal Alpha build per DEC-001. Closes the milestone of
'Flutter UI calls Rust via the typed bridge, Rust connects to a
TeamSpeak-compatible server through tsclientlib, returns a typed
snapshot, and disconnects cleanly.' Audio remains Beta scope.
Promotions from PoC:
poc/tsclientlib-connect-spike → crates/chanora_protocol/
New product code:
crates/chanora_protocol/src/{dto.rs,adapter.rs} — typed boundary
over tsclientlib. Tokio task owns the Connection; public
handle communicates via mpsc/oneshot. No tsclientlib types
cross out of the crate (SAD-067 / SysDes-011 / SysDes-029).
core/chanora_core/src/lib.rs — ChanoraSession composes the
protocol crate, enforces the DEC-006 single-connection
invariant.
crates/chanora_bridge/src/{api.rs,frb_generated.rs} — FRB 2.12.0
bridge per DEC-014. cdylib + staticlib + rlib. Typed
BridgeSnapshot / BridgeChannel / BridgeClient / BridgeError
DTOs. Process-wide OnceLock<Runtime> + OnceLock<ChanoraSession>.
flutter_rust_bridge.yaml at repo root.
apps/chanora_flutter/lib/main.dart — Alpha UI: server form,
connect button, channel tree, disconnect.
apps/chanora_flutter/lib/l10n/app_{en,zh}.arb expanded with the
Alpha key set; ARB metadata reaffirms ADR-008 for
server-provided content.
Generated Dart bindings under apps/chanora_flutter/lib/src/rust/.
Empirical verification (2026-05-14):
Workspace: cargo check + cargo test clean
(workspace tests: all green).
Bridge cdylib: target/release/libchanora_bridge.so produced
(~15 MB).
Flutter: flutter analyze clean; flutter test runs 3/3 green
including the alpha_e2e_test that drives the full
Dart → FRB → chanora_bridge → chanora_core → chanora_protocol
→ tsclientlib → UDP → cn.teamspeak.app
path. The captured logcat/stdout shows the tsproto resender
transitioning Connected → Disconnecting → Disconnected on
clean teardown.
Architecture changes:
- Removed the chanora_core ↔ chanora_bridge cyclic dependency.
chanora_core no longer knows the bridge exists; the bridge
maps from CoreError.
- chanora_bridge crate's #![forbid(unsafe_code)] lint relaxed
because FRB-generated glue legitimately uses unsafe at the
FFI boundary. Hand-written code remains unsafe-free.
Open follow-ups (NOT in this Alpha):
- Audio capture/playback wiring into chanora_audio
(Beta scope per DEC-001).
- Identity persistence via chanora_storage
(currently regenerated on every connect).
- Per-message diagnostics + redaction
(chanora_diagnostics still scaffold).
- Reconnect / network-loss recovery.
- Mobile (Android) build of the bridge cdylib + UI verification.
This commit is contained in:
+74
-50
@@ -6,61 +6,85 @@ This project is expected to follow a Conventional Commits style workflow.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
### Added — Alpha build (v0.1.0-alpha.1)
|
||||
|
||||
- **Product workspace scaffold (DEC-022).** Created the canonical
|
||||
implementation directory layout per the README sketch + SAD §7.2:
|
||||
- `Cargo.toml` — top-level Cargo workspace declaring seven members
|
||||
and excluding the `poc/` spike trees and `apps/chanora_flutter`.
|
||||
Pins `license = "MIT OR Apache-2.0"` (DEC-020) and
|
||||
`rust-version = "1.95"` workspace-wide.
|
||||
- `core/chanora_core/` — top-level Rust API + orchestration. Owns
|
||||
`ChanoraSession` (placeholder) and the `CoreError` enum that
|
||||
typed-wraps every subsystem error.
|
||||
- `crates/chanora_protocol/` — tsclientlib adapter scaffold,
|
||||
opaque `ChannelId` / `ClientId` types, `ProtocolError`.
|
||||
- `crates/chanora_state/` — `StateError`, `ConnectionState` enum.
|
||||
- `crates/chanora_audio/` — `AudioError`, `AudioEffects` struct
|
||||
whose defaults match DEC-007/008/009/010 (all four effects on).
|
||||
- `crates/chanora_storage/` — `StorageError`, marker traits for
|
||||
the two repositories per the SAD-067 separation.
|
||||
- `crates/chanora_diagnostics/` — `DiagnosticsError`,
|
||||
`REDACTION_MARKER` constant matching the PoC, `Redactor` shell.
|
||||
- `crates/chanora_bridge/` — `BridgeError` DTO (Serialize +
|
||||
Deserialize) for FRB 2.x boundary.
|
||||
Workspace `cargo check --workspace` and `cargo test --workspace`
|
||||
both clean on Rust 1.95.
|
||||
- **Flutter application scaffold.**
|
||||
- `apps/chanora_flutter/` created via `flutter create` with
|
||||
`--org app.chanora --project-name chanora_flutter`. Platforms
|
||||
enabled at scaffold time: Linux + Android.
|
||||
- `apps/chanora_flutter/android/app/build.gradle.kts` overridden
|
||||
to `minSdk = 28` per DEC-004 (raised from Flutter's default
|
||||
minSdk by explicit owner ruling, register v0.9.5).
|
||||
- `flutter_localizations` + `intl` added to `pubspec.yaml`;
|
||||
`flutter.generate: true` enabled.
|
||||
- `l10n.yaml` configured to emit `lib/l10n/generated/AppL10n`
|
||||
(no synthetic package — `synthetic-package` was removed in
|
||||
Flutter 3.41+).
|
||||
- `lib/l10n/app_en.arb` — English template ARB with the MVP key
|
||||
set (`appTitle`, `homeGreeting`, `homeNotProductionReadyBanner`,
|
||||
`connectAction`).
|
||||
- `lib/l10n/app_zh.arb` — Simplified Chinese translations per
|
||||
DEC-015 (MVP language scope expanded from English-only).
|
||||
- `lib/main.dart` and `test/widget_test.dart` rewritten as a
|
||||
minimal scaffold that exercises both locales.
|
||||
- `flutter analyze` reports no issues. `flutter test` runs the
|
||||
English + Simplified Chinese smoke tests and both pass.
|
||||
- **First Alpha build wires the connect → snapshot → disconnect cycle
|
||||
end-to-end from the Flutter UI to a live TeamSpeak-compatible
|
||||
server via the typed Flutter/Rust bridge.** Per DEC-001 this is the
|
||||
Internal Alpha milestone; Audio (voice in/out) is deferred to Beta.
|
||||
- `crates/chanora_protocol/` promoted from a scaffold to a working
|
||||
adapter. Public surface:
|
||||
- `ConnectConfig`, `ProtocolClient`, `ProtocolError`.
|
||||
- DTO module exposing `ChannelId`, `ClientId`, `ChannelInfo`,
|
||||
`ClientInfo`, `ServerSnapshot` — all owned primitives and
|
||||
`String`s; no `tsclientlib::*` types leak (SAD-067).
|
||||
- Tokio task owns the `tsclientlib::Connection`; public handle
|
||||
communicates via `mpsc` requests + `oneshot` replies.
|
||||
- Connect waits for the initial `BookEvents` snapshot, then pumps
|
||||
events for ~2 s so the subscribed channel tree settles before
|
||||
the first snapshot is served.
|
||||
- Promoted from `poc/tsclientlib-connect-spike`.
|
||||
- `core/chanora_core::ChanoraSession` now drives the protocol crate
|
||||
with a typed `connect`/`snapshot`/`is_connected`/`disconnect` API.
|
||||
Enforces the DEC-006 single-connection invariant via an internal
|
||||
`tokio::sync::Mutex<Option<ProtocolClient>>`.
|
||||
- `crates/chanora_bridge/` wired against `flutter_rust_bridge` 2.12.0
|
||||
(DEC-014). Compiled as `cdylib + staticlib + rlib`. Exposes:
|
||||
- `bridge_init()` (FRB lifecycle), `connect()`, `snapshot()`,
|
||||
`disconnect()`, `is_connected()`.
|
||||
- Typed `BridgeChannel`, `BridgeClient`, `BridgeSnapshot` DTOs;
|
||||
`BridgeError` with `From<chanora_core::CoreError>`.
|
||||
- A process-wide `tokio::Runtime` + `ChanoraSession` via
|
||||
`OnceLock`, used by every async command.
|
||||
- The crate's `#![forbid(unsafe_code)]` lint was lifted to
|
||||
`#![warn(missing_docs)]` only, with a doc-comment explanation
|
||||
that the FRB-generated glue legitimately uses unsafe at the FFI
|
||||
boundary; hand-written code in the crate is still expected to
|
||||
avoid `unsafe`.
|
||||
- `flutter_rust_bridge.yaml` at the repo root drives codegen for the
|
||||
bridge.
|
||||
- Generated Dart bindings under
|
||||
`apps/chanora_flutter/lib/src/rust/{api.dart,frb_generated*.dart,lib*.dart}`.
|
||||
- Generated Rust glue under `crates/chanora_bridge/src/frb_generated.rs`.
|
||||
- `apps/chanora_flutter/lib/main.dart` rewritten as the Alpha UI:
|
||||
- Form: server address + nickname, both pre-populated for
|
||||
convenience.
|
||||
- Connect button → calls FRB → enters connecting state → shows
|
||||
snapshot.
|
||||
- Snapshot view: server welcome banner (preserved verbatim per
|
||||
ADR-008), `N channels • M online` count, ordered channel list
|
||||
with clients indented under their channel.
|
||||
- Refresh and Disconnect actions in the app bar.
|
||||
- `apps/chanora_flutter/lib/l10n/app_{en,zh}.arb` expanded with the
|
||||
Alpha key set:
|
||||
`homeNotProductionReadyBanner` (now says "Alpha build"),
|
||||
`fieldServerHost`, `fieldNickname`,
|
||||
`connectAction`, `disconnectAction`, `refreshAction`,
|
||||
`statusIdle`, `statusConnecting`, `statusConnected`, `statusError`,
|
||||
`channelsHeading`, `clientsHeading`, `countChannelsAndClients`.
|
||||
- `flutter_localizations`, `intl`, `flutter_rust_bridge`,
|
||||
`freezed_annotation` added to dependencies;
|
||||
`freezed` and `build_runner` added to dev_dependencies.
|
||||
- `apps/chanora_flutter/test/alpha_e2e_test.dart` runs the full
|
||||
Dart → FRB → Rust → tsclientlib → network → server path against
|
||||
`cn.teamspeak.app`. Verifies the snapshot contains a non-empty
|
||||
server name and a non-empty channel list, that `isConnected()`
|
||||
flips true → false across the disconnect, and that a re-fetched
|
||||
snapshot agrees on the server name. Passes in ~2.5 s.
|
||||
- `core/chanora_core/tests/alpha_smoke.rs` runs the same path from the
|
||||
Rust side; tagged `#[ignore]` so `cargo test --workspace` doesn't
|
||||
hit the network by default. Run with `--ignored alpha_smoke`.
|
||||
|
||||
### Changed
|
||||
|
||||
- `docs/governance/path-migration-map.md` bumped to v0.9.3 with a
|
||||
new §3 *Implementation Path Layout* recording the DEC-022
|
||||
directory adoption (`apps/`, `core/`, `crates/`) and pointing
|
||||
each subsystem at its crate alongside the relevant SAD/SysDes/DEC
|
||||
authority.
|
||||
- `chanora_core::CoreError` no longer wraps `chanora_bridge::BridgeError`;
|
||||
the relationship is the other way around (bridge maps from core).
|
||||
This removes a cyclic `chanora_core` ↔ `chanora_bridge` dependency
|
||||
introduced when the bridge crate gained `chanora_core` as a dep.
|
||||
- `chanora_bridge` lints relaxed from `#![forbid(unsafe_code)]` to
|
||||
`#![warn(missing_docs)]` (documented above).
|
||||
|
||||
### Added (LICENSE files)
|
||||
### LICENSE files (carry-over from earlier in this branch)
|
||||
|
||||
- `LICENSE-APACHE` — Apache License Version 2.0 text (DEC-020).
|
||||
- `LICENSE-MIT` — MIT License text (DEC-020).
|
||||
|
||||
Reference in New Issue
Block a user