Files
chanora/CHANGELOG.md
T
EdisonJwa 50768a8f48 feat(mvp): v1.0.0-rc.1 — keyring-backed DEK, encrypted bookmarks, MVP release-gate docs
Closes the v0.4 dual-file weakness in identity-at-rest and turns the
release into an MVP public release candidate. The remaining work
before `v1.0.0` is DEC-012 legal sign-off — see
`docs/governance/legal-review-readiness.md` — and the staged
platform promotions in `docs/governance/staged-release-plan.md`.
No decision rows in `product-decision-register.md` change; the
register's change-history advances to 0.9.8.

`chanora_storage`
-----------------

* New public `Crypto` trait + `IdentityFileStore::crypto()` give
  callers an encrypt / decrypt pair anchored on the per-install
  32-byte DEK without exposing the key material.
* `IdentityFileStore` keyring-first DEK retrieval (Linux Secret
  Service via D-Bus, macOS Keychain, Windows Credential Manager,
  iOS Keychain via the `keyring` crate). Pre-existing
  `identity.dek` files are opportunistically migrated into the
  keyring on first run; the on-disk DEK copy is removed once the
  keyring acknowledges. `CHANORA_DISABLE_KEYRING=1` forces the
  file-fallback path for tests and headless / CI hosts where a
  real keyring call would prompt the user or block on a missing
  D-Bus session.
* `BookmarkRepository::with_crypto(dir, crypto)` encrypts the
  server password into a new `password_blob` BLOB column under
  the same per-install DEK. Schema v2 migration is idempotent —
  legacy v0.4 rows with a plain `password TEXT` are read
  transparently and lifted into `password_blob` on the next
  `update()`. `BookmarkRepository::new` (no crypto) is preserved
  for tests and as a documented fallback when the DEK is
  unreachable.
* Storage tests rise from 8 to 10: encrypted bookmark password
  round-trip + legacy-plaintext-bookmark upgrade.

`chanora_core`
--------------

* `ChanoraSession::init_storage(dir)` wires the bookmark
  repository with crypto by default. On any crypto-derivation
  failure it falls back to the plain-password repository and
  logs the gap — better than hard-failing init.
* `supervisor_loop` now tracks a 64-bit `snapshot_signature` over
  channels (id + parent + order + name) and clients (id + channel
  + name) instead of the old `(channel_count, client_count)`
  tuple. Any in-channel client move, channel rename, or reorder
  now fires `SessionEvent::SnapshotChanged`. The signature sorts
  by id before hashing so it's stable under input-vector
  reordering.
* Two new unit tests cover the signature behaviour; new
  `tests/mvp_storage.rs` integration test drives
  `ChanoraSession::init_storage` end-to-end and verifies the
  bookmark `password_blob` does not contain the plaintext.
* Re-export `ChannelId` + `ClientId` from `chanora_protocol` so
  downstream callers and tests can construct DTOs directly.

Flutter
-------

* New About dialog (info icon in the AppBar) surfaces DEC-018
  (public name "Chanora"), DEC-019 (non-affiliation statement),
  and DEC-020 (Apache-2.0 OR MIT dual license). New ARB keys in
  `app_en.arb` and `app_zh.arb`: `aboutAction`, `aboutVersion`,
  `aboutNonAffiliation`, `aboutLicenseHeading`, `aboutLicenseBody`,
  `aboutThirdPartyHeading`, `aboutThirdPartyBody`.
* `pubspec.yaml` version bumps to `1.0.0-rc.1+5`.

Governance
----------

* `docs/governance/legal-review-readiness.md` — DEC-012 handoff
  package. Enumerates trademark / non-affiliation / license-text
  / third-party-attribution / `tsclientlib`-posture / crypto-
  export / data-handling items the legal reviewer must confirm,
  and lists the concrete engineering deliverables they block on
  (`cargo about generate`, `cargo deny check licenses`,
  Flutter `LicenseRegistry` dump).
* `docs/governance/staged-release-plan.md` — DEC-002 channel
  schedule. Linux + Android sideload promote to GA on DEC-012
  sign-off; Play Store / Windows / macOS / iOS gate on per-
  platform signed-build availability. Rollback policy included.
* `product-decision-register.md` change-history advances to
  0.9.8 with a single entry summarising v0.3, v0.4, and v1.0-rc.1
  progress against DEC-001. No decision rows mutate.

Build + ops
-----------

* `NOTICE` refreshed for the MVP product-code dependency set:
  adds `chacha20poly1305`, `rand`, `zeroize`, `base64`,
  `keyring`, `connectivity_plus`, `path_provider`,
  `freezed_annotation`; drops PoC-only entries.
* `CHANGELOG.md` restructured: explicit version sections for
  v0.3.0-beta.1, v0.4.0-beta.2, v1.0.0-rc.1. Previous "Unreleased"
  contents migrated into their respective milestone sections.
* `.github/workflows/ci.yml` exports `CHANORA_DISABLE_KEYRING=1`
  for the cargo-test job — CI runners have no D-Bus session and
  the keyring crate would otherwise block.
* `run-chanora.sh` reads `CHANORA_BUNDLE_FLAVOUR` (default
  `release`) and self-copies the latest cdylib into the bundle's
  `lib/` if missing.

Verification
------------

* `cargo test --workspace` with `CHANORA_DISABLE_KEYRING=1`: all
  green (49 unit tests across the workspace; up from 36 at
  v0.4.0-beta.2).
* `cargo test -p chanora_core --release -- --ignored alpha_smoke`
  passes against the live `cn.teamspeak.app` (DNS → connect →
  snapshot → disconnect in ~2.5 s).
* `flutter analyze`: clean.
* `cargo build -p chanora_bridge --release` + `flutter build
  linux --release` produce a working Linux x86_64 bundle.

No Android live test in this commit per the user's note that the
physical device was removed; the Android arm64-v8a build path is
mechanically identical to v0.4.0-beta.2.
2026-05-15 02:24:42 +08:00

427 lines
21 KiB
Markdown

# Changelog
All notable changes to Chanora will be documented in this file.
This project is expected to follow a Conventional Commits style workflow.
## [Unreleased]
## [v1.0.0-rc.1] — MVP Public release candidate
This is the first release candidate for the MVP public release per
DEC-001 sequencing. The remaining work before `v1.0.0` is DEC-012
legal sign-off (see `docs/governance/legal-review-readiness.md`) and
the staged platform promotions in
`docs/governance/staged-release-plan.md`.
### Added
- **Platform keyring backing for the per-install Data Encryption Key.**
`chanora_storage::IdentityFileStore` now stores the 32-byte DEK in
the OS keyring (Linux Secret Service via D-Bus, macOS Keychain,
Windows Credential Manager, iOS Keychain) when one is available,
and transparently falls back to the v0.4 file-fallback at
`<storage_dir>/identity.dek` otherwise. Existing file-fallback
installs are migrated into the keyring on first run when the
bus is reachable; the on-disk DEK copy is removed once the
keyring acknowledges the write.
- **`CHANORA_DISABLE_KEYRING=1`** environment override forces the
file-fallback path. Used by tests and headless / CI hosts where
a real keyring call would prompt the user or block on a missing
D-Bus session.
- **`chanora_storage::Crypto` trait + `IdentityFileStore::crypto()`**
give callers an envelope encrypt / decrypt pair anchored on the
per-install DEK. Used by `BookmarkRepository::with_crypto`.
- **Encrypted server passwords for bookmarks.** New `password_blob`
BLOB column (schema v2). When the bookmark repository is
constructed with a `Crypto` handle, server passwords are
ChaCha20-Poly1305-encrypted under the same per-install DEK as
the identity. Legacy `password TEXT` rows are still read for
backward compatibility and lifted into `password_blob` on the
next `update()`. `chanora_core::ChanoraSession::init_storage`
wires this automatically.
- **`SessionEvent::SnapshotChanged` now triggers on any tree
mutation**, not just count changes. The watchdog probe hashes
channels (id + parent + order + name) and clients (id + channel
+ name); any change to those bits — including in-channel client
moves and channel renames — emits the event.
- **In-app About dialog** with the public product name (DEC-018),
the non-affiliation statement (DEC-019), the dual-license
declaration (DEC-020), and a pointer to the `NOTICE` file for
third-party attribution. New AppBar info icon opens the dialog.
- **`docs/governance/legal-review-readiness.md`** — the
engineering-side handoff package for the DEC-012 legal /
trademark / licensing review.
- **`docs/governance/staged-release-plan.md`** — the staging
schedule for DEC-002's five-platform MVP target. Linux + Android
sideload promote to GA on DEC-012 sign-off; Windows / macOS /
iOS gate on per-platform build host availability.
### Changed
- **`chanora_storage` test count rises from 8 to 10**: encrypted
bookmark password round-trip and legacy-plaintext-bookmark
upgrade scenarios.
- **`chanora_core` test count rises from 5 to 7**: signature
detects in-channel move; signature is stable under input-vector
reordering.
- **`NOTICE` refreshed** for the MVP product-code dependency set
(`chacha20poly1305`, `rand`, `zeroize`, `keyring`,
`connectivity_plus`, `path_provider`, `freezed_annotation`, the
removed `linux-keyutils` / `hound`, etc.). The PoC-era
enumeration is preserved upstream in git history.
- **`chanora_protocol::lib`** now also re-exports `ChannelId` and
`ClientId` for downstream signature / hashing helpers.
### Fixed
- The watchdog signature blind spot from v0.4: same-count
snapshots no longer suppress `SnapshotChanged`.
### Security
- DEK now sits behind the OS session lock on every supported
desktop platform; the v0.4 two-file weakness is closed for those
installs. Android, iOS, and other platforms without keyring
reach fall back to the v0.4 file model; `RISK-PoC-002` remains
open for them.
### Notes (MVP scope honesty)
- DEC-012 legal review has *not* been performed. `v1.0.0-rc.1` is
not the public release; it is the candidate that the review
signs off on (or rejects) before `v1.0.0` is tagged.
- Crash reporting is intentionally disabled (DEC-017). Repository
grep for `sentry|crashlytics|bugsnag` returns zero hits in the
MVP product code.
- Automatic diagnostic upload remains forbidden (DEC-016). The
user-initiated export path is the only way logs leave the
device.
- Android Keystore-backed DEK and iOS-side
`AVAudioSession.Mode.voiceChat` engagement are deferred to
v1.1.
- iOS, Windows, and macOS release binaries are not built into
`v1.0.0-rc.1`. The release page ships Linux x86_64 + Android
arm64-v8a only; the other three platforms are source-buildable
and promote per `staged-release-plan.md`.
## [v0.4.0-beta.2] — External Beta
Server password input, channel join via tap, self mute (mic +
speaker), master output gain slider, SQLite bookmarks with save /
connect / delete, ChaCha20-Poly1305-encrypted identity at rest
with `identity.dek` file, Android `MODE_IN_COMMUNICATION` routing,
default log filter trims `tsproto::resend` chatter, GitHub Actions
CI on every push.
## [v0.3.0-beta.1] — Internal Beta (polish)
A.1 cross-platform DNS, A.2 identity persistence, A.3 redacted
diagnostic export, A.4 SnapshotChanged event, A.5 mobile voice-
preset flag, A.6 reconnect supervisor with watchdog + UI banner,
A.6.1 OS connectivity signal drives reconnect timing.
## [v0.2.0-beta.1] — Internal Beta first build
- **Voice in/out wired end-to-end through the Flutter UI.** Per DEC-001
this reaches the Internal Beta milestone. Build hash: see the
`v0.2.0-beta.1` git tag.
- `crates/chanora_audio/` promoted from scaffold to a working engine:
- cpal-based capture (mic gain, linear resampling to 48 kHz, mono
down-mix) and playback (48 kHz stereo, requested config).
- `audiopus::Encoder` for Opus VoIP encoding (20 ms / 960-sample
mono frames).
- `tsclientlib::audio::AudioHandler` for the decode + per-client
jitter buffer + mix on the playback side.
- Push-to-talk gate: encoder is bypassed entirely when PTT is off,
so no spurious silence frames leak out.
- Graceful playback-only fallback: if the host has no usable mic
(typical for headless CI / users who deny the mic permission),
capture logs a warning and the engine continues with output only.
`AudioEngine::capture_active()` exposes this for the UI.
- Live counters: `frames_sent` / `frames_received` / `ptt()`.
- `crates/chanora_protocol/` extended with voice channels:
- `ProtocolClient::voice_out()` returns a clonable
`mpsc::Sender<OutPacket>` for outbound frames.
- `ProtocolClient::take_voice_in()` returns a one-shot
`mpsc::Receiver<InboundVoice>` of decoded `S2C` / `S2CWhisper`
packets, with the originating `from_client` ID extracted.
- Re-exports the few `tsproto_packets::packets` types (`OutAudio`,
`OutPacket`, `InAudioBuf`, `AudioData`, `CodecType`, `Direction`)
that `chanora_audio` legitimately needs. This is the **only**
deliberate cross-crate type re-export; per SAD-067 the audio
path is performance-sensitive and a parallel type hierarchy
would force a copy per 20 ms frame.
- Connection task interleaves outbound voice (drained first per
loop iteration), event pumping, and control-request handling.
- `core/chanora_core::ChanoraSession` audio API:
- `start_audio(AudioEngineConfig)` — starts the engine attached to
the active connection. Idempotent.
- `set_ptt(bool)` — toggles transmission. No-op without an engine.
- `audio_stats()``(frames_sent, frames_received, ptt_active)`.
- `disconnect()` now stops the engine before disconnecting the
protocol task.
- `crates/chanora_bridge/` audio surface:
- `start_audio()`, `set_ptt(active)`, `audio_stats()` Dart-callable
commands.
- `BridgeAudioStats { frames_sent, frames_received, ptt_active }`
DTO.
- Mapped `CoreError::AudioNotStarted` and `CoreError::Audio(_)`
arms in `BridgeError::From<CoreError>`.
- `apps/chanora_flutter/`:
- Beta UI rewrite of `main.dart`: "Start audio" button after
connect; hold-to-talk button with pressed/released visual state;
live audio-stats line below the PTT (`TX … frames • RX … frames
• PTT on/off`).
- ARB key set expanded with `startAudioAction`, `pttHoldToTalk`,
`pttTransmitting`, `audioStatsLine` in both `en` and `zh-Hans`.
- `test/beta_e2e_test.dart` exercises the full
Dart → FRB → chanora_bridge → chanora_core → chanora_audio
path against `cn.teamspeak.app`. Verifies connect, audio start,
PTT toggle, disconnect.
- `flutter_rust_bridge.yaml` now sets `local: true` so the codegen
resolves the workspace member's library name correctly. Without
this, the generated Dart side fell back to `libUNKNOWN.so` and
failed to load the cdylib.
### Changed
- `flutter_rust_bridge.yaml`: added `local: true`.
- `chanora_bridge::api`: `BridgeError::From<CoreError>` now maps
`CoreError::AudioNotStarted` to `BridgeError::InvalidCommand` and
`CoreError::Audio(_)` to `BridgeError::Connection`.
- `apps/chanora_flutter/test/widget_test.dart`: banner-string
expectations updated from "Alpha build" to "Beta build" and from
"Alpha 版本" to "Beta 版本".
- `docs/governance/product-decision-register.md` bumped to v0.9.7
with a Beta-milestone change-history entry. No decision rows
change.
- `docs/governance/poc-results-summary.md` bumped to v0.6.0 with a
Beta-milestone change-history entry; RISK-PoC-005 updated to
reflect Beta progress.
### Notes (Beta scope honesty)
- DSP chain (AEC / AGC / NS / HPF per DEC-007..010) is **not yet
implemented**. `AudioEffects` exists as a struct but its filters
are no-ops in v0.2.0-beta.1. Real DSP is queued for Beta+ work.
- The capture resampler is a simple linear interpolator. Production
quality requires a proper resampler in Beta+.
- Identity is still ephemeral per connect; persistence via
`chanora_storage` is queued.
- No live event stream into Dart yet — the UI fetches snapshots and
audio stats on a timer instead of subscribing to push events.
- `chanora_diagnostics` is still a scaffold; no redaction wired into
`tracing` yet.
- Audio engine is desktop-only in this Beta. Mobile bundle of the
bridge cdylib + UI verification was proven by the PoC but is not
re-built into product code in this milestone.
### Carry-over from Alpha (v0.1.0-alpha.1)
- **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
- `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).
### 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).
- Initial repository foundation files.
- Documentation-first project structure.
- `justfile` with `format`, `lint`, `test`, `verify-docs`, and
`security-scan` targets, completing `repository-bootstrap-plan` v0.1.0 §3.
- `poc/tsclientlib-connect-spike/` — PoC proving protocol feasibility via
`tsclientlib`. Verified against `cn.teamspeak.app` on 2026-05-13.
- `poc/flutter_rust_bridge_hello/` — PoC proving the Flutter↔Rust command
and event-stream boundary via `flutter_rust_bridge` 2.12.0. Verified on
Linux desktop on 2026-05-13.
- `poc/secure-storage-spike/` — PoC proving platform secure storage via
a typed `SecretStorageRepository` trait and a Linux adapter selecting
between Secret Service (libsecret) and kernel keyutils. Audit checks
SS-AUD-001/002/003/005/006 and SS-TC-003 verified on 2026-05-13.
- `poc/sqlite-storage-spike/` — PoC proving SRS-089's "embedded data
store + migration mechanism" acceptance criteria: forward-only schema
migrator tracked via `PRAGMA user_version`, repository pattern with
`BookmarkRepository` / `SettingsRepository` traits over
`LocalDatabaseRepository`. 11/11 tests verified on 2026-05-13.
- `poc/diagnostics-redaction-spike/` — PoC proving the diagnostic
redaction policy from `diagnostic-redaction-audit-report.md`:
typed policy + regex rules + literal known-secret registry + bundle
redaction. Audit cases REDACT-TC-001..010 verified on 2026-05-13.
- `poc/audio-capture-playback-spike/` — PoC proving platform audio
capture/playback via cpal. Desktop half (Linux + PipeWire)
empirically verified end-to-end on 2026-05-13; mobile half closed
separately by `poc/audio-capture-playback-android-spike`.
- `poc/audio-capture-playback-android-spike/` — PoC closing the
mobile half of the audio capture/playback PoC plan entry. Rust
cdylib + JNI + Kotlin Android app; cpal targets Android's Oboe
backend (AAudio). Verified end-to-end on a physical Motorola
Moto G Stylus 5G (2023) running Android 14 arm64-v8a on
2026-05-13: 500 ms 440 Hz sine wave driven out the device speaker
(22,050 frames at 44.1 kHz) and 1 s captured from the microphone
into a valid 85,292-byte RIFF/WAVE mono 16-bit PCM file pulled via
`adb exec-out run-as`.
- `poc/README.md` summarising PoC status against
`docs/architecture/proof-of-concept-plan.md`.
### Changed
- **DEC-020 license resolved.** Chanora is now dual-licensed under
**Apache-2.0 OR MIT** (recipient's choice), the standard
Rust-ecosystem permissive model. Compatible with every direct
dependency (`tsclientlib`, `flutter_rust_bridge`, `cpal`, `rusqlite`,
`keyring`, etc.) and with the Flutter framework's BSD-3-Clause.
`LICENSE` rewritten as a dual-license aggregator pointing at
`LICENSE-APACHE` and `LICENSE-MIT`. `NOTICE` rewritten with current
direct-dependency attributions. `README.md` §License updated.
- `docs/governance/product-decision-register.md` bumped to v0.9.6:
DEC-020 status promoted from Open to Accepted. §4 license row
updated. §6 collapsed: there is no longer any open decision —
DEC-012 legal review remains as a *work* item, not a pending
decision. Change-history entry added.
- `docs/governance/poc-results-summary.md` bumped to v0.4.0:
RISK-PoC-003 closed. DEC-020 row moved from the "Still open"
section into the closed table.
- `docs/governance/product-decision-register.md` bumped to v0.9.5:
owner confirmation on all 17 previously-Proposed decisions
(DEC-001..010, 012, 015..019, 021). Sixteen were Accepted as
recommended; two were modified by the owner — **DEC-004**
Android minimum raised from API 24 to **API 28**, and
**DEC-015** product language for MVP expanded from English-only
to **English + Chinese (Simplified)**. DEC-020 license remains
Open / Deferred and is now the only public-release-gating
decision outstanding. §4 renamed "Recommended" → "Accepted MVP
Defaults" with MODIFIED rows annotated. §6 collapsed to the
single remaining DEC-020 item. §7 dated and statused for every
decision.
- `docs/governance/poc-results-summary.md` bumped to v0.3.0:
RISK-PoC-004 closed by the owner-confirmation pass; new
RISK-PoC-006 (Android `minSdk` move 24 → 28 for product code)
and RISK-PoC-007 (MVP language expansion to en + zh-Hans) added.
- `docs/architecture/proof-of-concept-plan.md` bumped to v0.3.0 to
promote the audio PoC from PARTIAL PASS to PASS after the Android
mobile half was closed; all six PoC plan entries are now PASS.
- `docs/governance/product-decision-register.md` bumped to v0.9.4 to
promote DEC-011.1 mobile half from Deferred to Accepted (Android),
keeping iOS Deferred.
- `docs/governance/poc-results-summary.md` bumped to v0.2.0:
audio row promoted to PASS, RISK-PoC-001 narrowed from "mobile
audio" to "iOS audio only", Android toolchain added to the
toolchain table.
- `poc/audio-capture-playback-spike/VERIFICATION.md` updated to point
at the Android spike for the mobile half.
- `poc/README.md` updated to list both audio spike directories.
- `docs/architecture/proof-of-concept-plan.md` bumped to v0.2.0 to
record PoC outcomes (5 PASS, 1 PARTIAL) and add a Status column.
- `docs/security/secure-storage-audit-report.md` bumped to v0.9.3:
SS-AUD-001/002/003/005/006 status set to PoC Pass with evidence
pointers; SS-TC-003 (Linux) Actual Result populated and Status set
to PoC Pass; findings SS-FIND-001..003 added; non-Linux test cases
marked Deferred.
- `docs/security/diagnostic-redaction-audit-report.md` bumped to
v0.9.3: REDACT-TC-001..010 status set to PoC Pass with evidence
pointers; export bundle policy §5 populated; findings
REDACT-FIND-001..003 added.
- `docs/governance/product-decision-register.md` bumped to v0.9.3:
owner-confirmed decisions recorded — DEC-014 Accepted
(`flutter_rust_bridge` 2.x pinned), DEC-013.1 Accepted (`rusqlite`
bundled), DEC-013.2 Accepted (Linux Secret Service preferred,
keyutils fallback), DEC-011.1 Accepted (desktop `cpal`) / Deferred
(mobile), DEC-022 Accepted (canonical implementation directory
layout per README sketch + SAD §7.2), DEC-020 explicitly Deferred
and remains a public-release blocker.
### Added (governance)
- `docs/governance/poc-results-summary.md` v0.1.0 — single-page
reviewer-facing summary of the PoC phase, the toolchain exercised,
the owner decisions taken, the audit coverage, and the open risks
RISK-PoC-001..005.
### Fixed
- N/A
### Security
- N/A
## Versioning note
The project implementation has not reached a public release version yet.