refactor: restructure docs as submodule, add dev-docs/ and AGENTS.md
- Move ASPICE docs to chanoraapp/docs submodule at docs/ - Move development docs to dev-docs/ (superpowers, offline-knowledge, impl-mapping) - Add AGENTS.md with project conventions for AI agents - Add impl-mapping.md (SAD component → source file mapping) - Archive completed plans to dev-docs/superpowers/plans/_archived/ - Remove AGENTS.md from .gitignore (now tracked)
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
# SAD Component → Source File Mapping
|
||||
|
||||
**Purpose:** Developer convenience mapping from ASPICE architecture components to source file locations. This is NOT an ASPICE document — it's a lookup for developers.
|
||||
|
||||
## Component Mapping
|
||||
|
||||
| SAD Component | Source Location |
|
||||
|---|---|
|
||||
| Flutter app shell | `apps/chanora_flutter/lib/main.dart`, services, widgets |
|
||||
| Flutter service layer | `apps/chanora_flutter/lib/services/` |
|
||||
| Flutter widget layer | `apps/chanora_flutter/lib/widgets/` |
|
||||
| Bridge layer | `crates/chanora_bridge/src/api.rs`, `apps/chanora_flutter/lib/src/rust/` |
|
||||
| Rust core | `core/chanora_core/src/lib.rs`, `events.rs`, `network_diagnostics.rs`, `ptt.rs` |
|
||||
| Protocol adapter | `crates/chanora_protocol/src/` |
|
||||
| State sync | `crates/chanora_state/src/lib.rs`, `channel_join.rs` |
|
||||
| Audio subsystem | `crates/chanora_audio/src/` |
|
||||
| Storage | `crates/chanora_storage/src/lib.rs` |
|
||||
| Diagnostics | `crates/chanora_diagnostics/src/lib.rs` |
|
||||
| Resolution and prefetch | `crates/chanora_resolver/src/lib.rs`, `crates/chanora_prefetch/src/lib.rs`, `prefetch_debouncer.dart` |
|
||||
| Build and release hooks | `.github/workflows/`, `tools/`, platform project files |
|
||||
|
||||
## SDD Module Mapping
|
||||
|
||||
| SDD Module | Source Location |
|
||||
|---|---|
|
||||
| SDD-MOD-001 Flutter app bootstrap | `apps/chanora_flutter/lib/services/app_bootstrap.dart`, `main.dart` |
|
||||
| SDD-MOD-002 Connect UI | `apps/chanora_flutter/lib/widgets/connect_widgets.dart` |
|
||||
| SDD-MOD-003 Snapshot and channel UI | `snapshot_view.dart`, `snapshot_state_mapper.dart`, `channel_spacer.dart` |
|
||||
| SDD-MOD-004 Chat UI | `chat_views.dart`, `bbcode_text.dart` |
|
||||
| SDD-MOD-005 Voice UI | `voice_bar.dart`, `voice_compact.dart`, `voice_settings*.dart`, `voice_level_meter.dart`, `ptt_capability_badge.dart` |
|
||||
| SDD-MOD-006 Platform services | `android_permissions_service.dart`, `ios_permissions_service.dart`, `audio_lifecycle_service.dart`, `back_intent_*`, `link_trust_service.dart` |
|
||||
| SDD-MOD-007 Bridge API | `crates/chanora_bridge/src/api.rs`, generated Dart/Rust bridge files |
|
||||
| SDD-MOD-008 Rust core supervisor | `core/chanora_core/src/lib.rs`, `events.rs`, `network_diagnostics.rs`, `ptt.rs` |
|
||||
| SDD-MOD-009 Protocol adapter | `crates/chanora_protocol/src/` |
|
||||
| SDD-MOD-010 State sync | `crates/chanora_state/src/lib.rs`, `channel_join.rs` |
|
||||
| SDD-MOD-011 Audio subsystem | `crates/chanora_audio/src/` |
|
||||
| SDD-MOD-012 Storage | `crates/chanora_storage/src/lib.rs` |
|
||||
| SDD-MOD-013 Diagnostics | `crates/chanora_diagnostics/src/lib.rs` |
|
||||
| SDD-MOD-014 Resolution and prefetch | `crates/chanora_resolver/src/lib.rs`, `crates/chanora_prefetch/src/lib.rs`, `prefetch_debouncer.dart` |
|
||||
| SDD-MOD-015 Build and release hooks | `.github/workflows/`, `tools/`, platform project files |
|
||||
@@ -0,0 +1,162 @@
|
||||
# Chanora Implementation Status — 2026-05-28
|
||||
|
||||
**Workspace version:** `v0.2.0-beta.1`
|
||||
**Flutter app version/build:** `0.3.0+100`
|
||||
**CHANGELOG latest:** `v0.3.0`
|
||||
**Build status:** Host Rust workspace evidence shows all 9 crates compile cleanly. This does not claim Android target success; Android target compile/install/smoke evidence remains blocked locally as noted below.
|
||||
|
||||
---
|
||||
|
||||
## 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` — supervisor task, exponential backoff reconnect (1s→60s), user-disconnect suppresses reconnect; branch `simplify-project-review` has started splitting the previous large `lib.rs` into focused internal Modules (`events.rs`, `network_diagnostics.rs`) while preserving public re-exports |
|
||||
| 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, and Windows/Linux desktop `VoiceActivity` through the capture VAD path. Mobile, macOS, and unverified-platform `VoiceActivity` remain deferred per DEC-030. |
|
||||
| Push-to-talk | Per-platform backends: Windows Raw Input + hook fallback, macOS Event Tap, Linux freedesktop portal, focused fallback; `PttCapabilityLevel` (L0–L3); 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_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 | Host Rust workspace evidence shows all 9 crates build cleanly; Android target compilation remains blocked locally as noted below. |
|
||||
|
||||
### 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`. |
|
||||
| Mobile/macOS VoiceActivity | `assets/models/silero_vad.onnx` is bundled and used by the desktop VAD path where runtime evidence supports it; mobile, macOS, and unverified-platform `TransmitMode::VoiceActivity` remain disabled/deferred until a later baseline supplies backend enablement and verification evidence. |
|
||||
| macOS build | Source-buildable only; no public release artifact is approved. |
|
||||
| Windows build | Source-buildable only; no public release artifact is approved. |
|
||||
| iOS build | Source-buildable/unsigned validation only; no TestFlight/App Store release artifact is approved. |
|
||||
|
||||
### Not Done (P0 blockers remaining)
|
||||
|
||||
| Item | Status |
|
||||
|---|---|
|
||||
| DEC-012 legal/trademark/OSS review | Explicitly open. Public release is blocked. |
|
||||
| Android Keystore-backed DEK | Deferred to v1.1. Android still uses file-fallback for the Data Encryption Key. |
|
||||
| Android target compile/install/smoke evidence | Blocked locally until the Android NDK compiler `aarch64-linux-android-clang` is available and `adb devices -l` shows an authorized device or emulator. |
|
||||
| iOS `AVAudioSession.Mode.voiceChat` | Implemented in `apps/chanora_flutter/ios/Runner/AppDelegate.swift` with call-scoped activation (idle `.ambient` baseline; VoIP `.playAndRecord` + `.voiceChat` + `.mixWithOthers` engaged only on `BridgeEvent::AudioStarted` via `chanora/ios_audio_session` MethodChannel). 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) | Core tracks connect/disconnect counts and last-loss reasons in `network_diagnostics.rs`; export/integration evidence still needs release-candidate attachment |
|
||||
| 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,75 @@
|
||||
# Chanora Offline Knowledge Library
|
||||
|
||||
**Generated:** 2026-06-13
|
||||
**Branch:** `docs/offline-knowledge-library-2026-06-13`
|
||||
**Purpose:** Comprehensive offline reference for the Chanora project, its dependencies, and related ecosystem.
|
||||
|
||||
---
|
||||
|
||||
## Contents
|
||||
|
||||
### Project Analysis
|
||||
|
||||
| Document | Description | Status |
|
||||
|----------|-------------|--------|
|
||||
| [function-inventory.md](function-inventory.md) | Complete public API inventory for all 10 Rust crates + 56 Dart files. Includes dead code analysis. | Reviewed |
|
||||
| [coverage-analysis.md](coverage-analysis.md) | Test coverage (312 Rust tests, 221 Dart tests) and documentation coverage gaps. | Reviewed, corrected |
|
||||
| [doc-quality-analysis.md](doc-quality-analysis.md) | Duplicated content, useless content, and broken references in docs/. | Reviewed, corrected |
|
||||
| [link-coverage-report.md](link-coverage-report.md) | All internal/external links validated. 2 broken LICENSE links, 5 broken doc-path refs. | Reviewed, corrected |
|
||||
| [docs-code-mismatch.md](docs-code-mismatch.md) | 17 doc-code mismatches found (2 critical, 4 major, 11 minor). | Reviewed, corrected |
|
||||
| [docs-out-of-date.md](docs-out-of-date.md) | 12 outdated docs, 8 undocumented recent changes since DV baseline. | Reviewed, corrected |
|
||||
| [docs-link-not-covered.md](docs-link-not-covered.md) | 2 broken links, 9 missing targets, 18 orphaned docs. | Reviewed, corrected |
|
||||
|
||||
### External Projects
|
||||
|
||||
| Document | Description | Status |
|
||||
|----------|-------------|--------|
|
||||
| [external/teaspeak-overview.md](external/teaspeak-overview.md) | TeaSpeak voice server - architecture, protocol, build system. | Reviewed |
|
||||
| [external/respeak-overview.md](external/respeak-overview.md) | ReSpeak org - tsclientlib, tsproto, crypto, Chanora integration. | Reviewed |
|
||||
| [external/yatqa-en.md](external/yatqa-en.md) | yat.qa TeamSpeak admin tool (English). | Reviewed |
|
||||
| [external/yatqa-de.md](external/yatqa-de.md) | yat.qa TeamSpeak admin tool (German/Deutsch). | Reviewed |
|
||||
|
||||
### Review Reports
|
||||
|
||||
| Document | Description |
|
||||
|----------|-------------|
|
||||
| [reviews/coverage-analysis-review.md](reviews/coverage-analysis-review.md) | Cross-validation of coverage analysis |
|
||||
| [reviews/doc-quality-review.md](reviews/doc-quality-review.md) | Cross-validation of doc quality analysis |
|
||||
| [reviews/link-coverage-review.md](reviews/link-coverage-review.md) | Cross-validation of link coverage |
|
||||
| [reviews/external-docs-review.md](reviews/external-docs-review.md) | Cross-validation of external project docs |
|
||||
| [reviews/docs-code-mismatch-review.md](reviews/docs-code-mismatch-review.md) | Cross-validation of mismatch analysis |
|
||||
| [reviews/docs-out-of-date-review.md](reviews/docs-out-of-date-review.md) | Cross-validation of out-of-date analysis |
|
||||
| [reviews/docs-link-not-covered-review.md](reviews/docs-link-not-covered-review.md) | Cross-validation of link-not-covered analysis |
|
||||
| [reviews/function-inventory-review.md](reviews/function-inventory-review.md) | Cross-validation of function inventory |
|
||||
| [reviews/coverage-docquality-review.md](reviews/coverage-docquality-review.md) | Second-pass review of coverage + doc quality |
|
||||
| [reviews/mismatch-outofdate-review.md](reviews/mismatch-outofdate-review.md) | Second-pass review of mismatch + out-of-date |
|
||||
| [reviews/link-reports-review.md](reviews/link-reports-review.md) | Second-pass review of link reports |
|
||||
| [reviews/external-index-review.md](reviews/external-index-review.md) | Second-pass review of external docs + index |
|
||||
|
||||
---
|
||||
|
||||
## Key Findings Summary
|
||||
|
||||
### Test Coverage
|
||||
- **Rust**: 312 inline tests + 5 integration tests across 8/10 crates
|
||||
- **Dart**: 221 tests (widgets: 58%, services: 90%)
|
||||
- **Untested crates**: chanora_bridge, chanora_cache, chanora_prefetch
|
||||
|
||||
### Documentation Gaps
|
||||
- No architecture docs for: audio engine, FFI bridge, protocol layer, state machine, cache, prefetch, diagnostics
|
||||
- 15 TODO/FIXME items catalogued across codebase
|
||||
|
||||
### Dead/Useless Code
|
||||
- No true dead code found (platform-gated items are intentional)
|
||||
- 1 malformed markdown in docs/sysdes.md
|
||||
- 2 missing LICENSE files (LICENSE-APACHE, LICENSE-MIT)
|
||||
|
||||
### Duplicated Content
|
||||
- Lifecycle chain repeated in 6+ files
|
||||
- Git commit examples in 3 files
|
||||
- Security doc list in 2 files
|
||||
|
||||
### External Dependencies
|
||||
- **ReSpeak/tsclientlib**: Chanora patches tsproto-types for P-256 coordinate padding
|
||||
- **TeaSpeak**: Compatible voice server, C++20 + Electron architecture
|
||||
- **yat.qa**: TeamSpeak admin tool, v3.9.9b, English + German docs
|
||||
@@ -0,0 +1,375 @@
|
||||
# Test & Document Coverage Analysis
|
||||
|
||||
**Generated:** 2026-06-13
|
||||
**Scope:** All crates, Flutter app, and docs/ directory
|
||||
|
||||
---
|
||||
|
||||
## Test Coverage Summary
|
||||
|
||||
| Metric | Count |
|
||||
|--------|-------|
|
||||
| Total Rust tests (inline `#[test]`) | 312 |
|
||||
| Total Rust integration tests | 5 |
|
||||
| Total Dart tests (`test()` + `testWidgets()`) | 221 |
|
||||
| Crates with tests | 7/9 |
|
||||
| Dart services with tests | 19/21 (90%) |
|
||||
| Dart widgets with tests | 14/24 (58%) |
|
||||
| Overall estimated coverage | ~65% |
|
||||
|
||||
---
|
||||
|
||||
## Per-Crate Test Coverage (Rust)
|
||||
|
||||
### chanora_audio — 333 tests, ~48% function coverage
|
||||
|
||||
| Source File | Functions | Tests | Coverage |
|
||||
|-------------|-----------|-------|----------|
|
||||
| engine.rs | ~45 | 7 | ~16% |
|
||||
| ptt_backends/windows.rs | ~80 | 44 | ~55% |
|
||||
| ptt_backends/windows_keymap.rs | ~30 | 12 | ~40% |
|
||||
| ptt_backends/macos.rs | ~35 | 12 | ~34% |
|
||||
| ptt_backends/linux.rs | ~25 | 10 | ~40% |
|
||||
| ptt_backends/mod.rs | ~15 | 1 | ~7% |
|
||||
| transmit_selector.rs | ~20 | 10 | ~50% |
|
||||
| voice_activity.rs | ~15 | 9 | ~60% |
|
||||
| voice_render.rs | ~15 | 9 | ~60% |
|
||||
| mobile_voice_backend.rs | ~20 | 9 | ~45% |
|
||||
| processor/sonora.rs | ~15 | 8 | ~53% |
|
||||
| processor/dsp/agc2.rs | ~10 | 6 | ~60% |
|
||||
| mode_stack.rs | ~10 | 6 | ~60% |
|
||||
| opus_voice.rs | ~10 | 5 | ~50% |
|
||||
| capture_accumulator.rs | ~8 | 5 | ~63% |
|
||||
| processor/dsp/ns.rs | ~8 | 4 | ~50% |
|
||||
| ptt.rs | ~8 | 4 | ~50% |
|
||||
| vad/mod.rs | ~6 | 4 | ~67% |
|
||||
| audio_processing.rs | ~8 | 3 | ~38% |
|
||||
| debug_wav.rs | ~6 | 3 | ~50% |
|
||||
| processor/dsp/hpf.rs | ~5 | 3 | ~60% |
|
||||
| processor/dsp/aec3.rs | ~5 | 3 | ~60% |
|
||||
| transmit_mode.rs | ~4 | 3 | ~75% |
|
||||
| android_render_ring.rs | ~5 | 3 | ~60% |
|
||||
| vad/resampler.rs | ~4 | 3 | ~75% |
|
||||
| vad/apple_coreml.rs | ~5 | 3 | ~60% |
|
||||
| vad/silero_onnx.rs | ~10 | 6 | ~60% |
|
||||
| render_reference.rs | ~8 | 7 | ~88% |
|
||||
| capture_resampler.rs | ~3 | 2 | ~67% |
|
||||
| audio_event_queue.rs | ~4 | 2 | ~50% |
|
||||
| processor/webrtc_apm.rs | ~5 | 2 | ~40% |
|
||||
| frame.rs | ~3 | 1 | ~33% |
|
||||
| lib.rs (defaults) | ~5 | 1 | ~20% |
|
||||
| release_tail.rs | ~4 | 1 | ~25% |
|
||||
| route_policy.rs | ~8 | 8 | ~100% |
|
||||
| **Integration: tests/ptt_privacy.rs** | — | 1 | — |
|
||||
| **Integration: tests/linux_portal_smoke.rs** | — | 1 | — |
|
||||
|
||||
### chanora_protocol — 17 tests, ~18% function coverage
|
||||
|
||||
| Source File | Functions | Tests | Coverage |
|
||||
|-------------|-----------|-------|----------|
|
||||
| adapter.rs | ~80 | 13 | ~16% |
|
||||
| poke_limiter.rs | ~17 | 4 | ~24% |
|
||||
| dto.rs | ~0 | 0 | — |
|
||||
| lib.rs | ~0 | 0 | — |
|
||||
|
||||
**Untested areas:** Message parsing, serialization, most adapter methods
|
||||
|
||||
### chanora_state — 27 tests, ~46% function coverage
|
||||
|
||||
| Source File | Functions | Tests | Coverage |
|
||||
|-------------|-----------|-------|----------|
|
||||
| lib.rs | ~35 | 18 | ~51% |
|
||||
| channel_join.rs | ~24 | 9 | ~38% |
|
||||
|
||||
### chanora_storage — 15 tests, ~24% function coverage
|
||||
|
||||
| Source File | Functions | Tests | Coverage |
|
||||
|-------------|-----------|-------|----------|
|
||||
| lib.rs | ~62 | 15 | ~24% |
|
||||
|
||||
**Untested areas:** Migration logic, concurrent access patterns, error recovery
|
||||
|
||||
### chanora_resolver — 12 tests, ~17% function coverage
|
||||
|
||||
| Source File | Functions | Tests | Coverage |
|
||||
|-------------|-----------|-------|----------|
|
||||
| lib.rs | ~70 | 12 | ~17% |
|
||||
| examples/cli.rs | — | 1 | — |
|
||||
|
||||
**Untested areas:** DNS failure modes, timeout handling, cache behavior
|
||||
|
||||
### chanora_diagnostics — 19 tests, ~26% function coverage
|
||||
|
||||
| Source File | Functions | Tests | Coverage |
|
||||
|-------------|-----------|-------|----------|
|
||||
| lib.rs | ~74 | 19 | ~26% |
|
||||
|
||||
### chanora_core — 38 tests, ~7% function coverage
|
||||
|
||||
| Source File | Functions | Tests | Coverage |
|
||||
|-------------|-----------|-------|----------|
|
||||
| lib.rs | ~140 | 10 | ~7% |
|
||||
| network_diagnostics.rs | ~7 | 1 | ~14% |
|
||||
|
||||
**Untested areas:** Connection lifecycle, server event handling, most state transitions
|
||||
|
||||
### chanora_bridge — 0 tests, 0% function coverage
|
||||
|
||||
| Source File | Functions | Tests | Coverage |
|
||||
|-------------|-----------|-------|----------|
|
||||
| api.rs | ~200+ | 0 | 0% |
|
||||
| frb_generated.rs | ~100+ | 0 | 0% |
|
||||
| permission_jni.rs | ~10 | 0 | 0% |
|
||||
| android_init.rs | ~5 | 0 | 0% |
|
||||
| lib.rs | ~4 | 0 | 0% |
|
||||
|
||||
**Note:** chanora_bridge is an FFI/bridge layer; testing requires integration with Flutter.
|
||||
|
||||
### chanora_cache — 0 tests
|
||||
|
||||
| Source File | Functions | Tests | Coverage |
|
||||
|-------------|-----------|-------|----------|
|
||||
| lib.rs | ~16 | 0 | 0% |
|
||||
|
||||
### chanora_prefetch — 0 tests
|
||||
|
||||
| Source File | Functions | Tests | Coverage |
|
||||
|-------------|-----------|-------|----------|
|
||||
| lib.rs | ~18 | 0 | 0% |
|
||||
|
||||
---
|
||||
|
||||
## Per-Module Test Coverage (Dart/Flutter)
|
||||
|
||||
### Services — 155 tests across 19 test files
|
||||
|
||||
| Source File | Test File | Tests | Coverage |
|
||||
|-------------|-----------|-------|----------|
|
||||
| android_audio_output_devices.dart | ✅ android_audio_output_devices_test.dart | 2 | Tested |
|
||||
| android_permissions_service.dart | ✅ android_permissions_service_test.dart | 14 | Tested |
|
||||
| app_bootstrap.dart | ✅ app_bootstrap_test.dart | 4 | Tested |
|
||||
| audio_lifecycle_service.dart | ✅ audio_lifecycle_service_test.dart | 4 | Tested |
|
||||
| back_intent_policy.dart | ✅ back_intent_policy_test.dart | 9 | Tested |
|
||||
| back_intent_service.dart | ✅ back_intent_service_test.dart | 5 | Tested |
|
||||
| channel_join_error_mapper.dart | ✅ channel_join_error_mapper_test.dart | Tested |
|
||||
| channel_spacer.dart | ✅ channel_spacer_test.dart | 9 | Tested |
|
||||
| connection_phase_state.dart | ✅ connection_phase_state_test.dart | 8 | Tested |
|
||||
| hard_mute_owners.dart | ✅ hard_mute_owners_test.dart | 3 | Tested |
|
||||
| ios_audio_session_controller.dart | ✅ ios_audio_session_controller_test.dart | 6 | Tested |
|
||||
| macos_permissions_service.dart | ✅ macos_permissions_service_test.dart | 20 | Tested |
|
||||
| poke_active_chat.dart | ✅ poke_active_chat_test.dart | 2 | Tested |
|
||||
| poke_notification_service.dart | ✅ poke_notification_service_test.dart | 2 | Tested |
|
||||
| poke_preferences_service.dart | ✅ poke_preferences_service_test.dart | 4 | Tested |
|
||||
| prefetch_debouncer.dart | ✅ prefetch_debouncer_test.dart | 3 | Tested |
|
||||
| snapshot_state_mapper.dart | ✅ snapshot_state_mapper_test.dart | 7 | Tested |
|
||||
| ts3_server_link.dart | ✅ ts3_server_link_test.dart | 3 | Tested |
|
||||
| ui_preferences_service.dart | ✅ ui_preferences_service_test.dart | 5 | Tested |
|
||||
| voice_join_ordering.dart | ✅ voice_join_ordering_test.dart | 4 | Tested |
|
||||
| **ios_permissions_service.dart** | ❌ No test file | 0 | **UNTESTED** |
|
||||
| **link_trust_service.dart** | ❌ No test file | 0 | **UNTESTED** |
|
||||
|
||||
### Widgets — 66 tests across 13 test files
|
||||
|
||||
| Source File | Test File | Tests | Coverage |
|
||||
|-------------|-----------|-------|----------|
|
||||
| app_snack_bar.dart | ✅ app_snack_bar_test.dart | 1 | Tested |
|
||||
| audio_processing_config_state.dart | ✅ audio_processing_config_state_test.dart | 8 | Tested |
|
||||
| bbcode_text.dart | ✅ bbcode_text_test.dart | Tested |
|
||||
| chat_panel.dart | ✅ chat_panel_test.dart | Tested |
|
||||
| chat_views.dart | ✅ chat_views_test.dart | 29 | Tested |
|
||||
| client_info_sheet.dart | ✅ client_info_sheet_test.dart | Tested |
|
||||
| poke_notification_settings.dart | ✅ poke_notification_settings_test.dart | Tested |
|
||||
| snapshot_view.dart | ✅ snapshot_view_test.dart | Tested |
|
||||
| talk_power_warning.dart | ✅ talk_power_warning_test.dart | 1 | Tested |
|
||||
| voice_compact.dart | ✅ voice_compact_test.dart | Tested |
|
||||
| voice_settings_controls.dart | ✅ voice_settings_controls_test.dart | 6 | Tested |
|
||||
| voice_status_summary.dart | ✅ voice_status_summary_test.dart | 5 | Tested |
|
||||
| mobile_ui_resilience.dart | ✅ mobile_ui_resilience_test.dart | Tested |
|
||||
| **audio_debug_stats_panel.dart** | ❌ No test file | 0 | **UNTESTED** |
|
||||
| **audio_device_list_tile.dart** | ✅ audio_device_list_tile_test.dart | 3 | Tested |
|
||||
| **audio_output_tile.dart** | ❌ No test file | 0 | **UNTESTED** |
|
||||
| **connect_widgets.dart** | ❌ No test file | 0 | **UNTESTED** |
|
||||
| **input_dialogs.dart** | ❌ No test file | 0 | **UNTESTED** |
|
||||
| **permission_state_banner.dart** | ❌ No test file | 0 | **UNTESTED** |
|
||||
| **ptt_capability_badge.dart** | ❌ No test file | 0 | **UNTESTED** |
|
||||
| **voice_bar.dart** | ❌ No test file | 0 | **UNTESTED** |
|
||||
| **voice_haptics.dart** | ❌ No test file | 0 | **UNTESTED** |
|
||||
| **voice_level_meter.dart** | ❌ No test file | 0 | **UNTESTED** |
|
||||
| **voice_platform.dart** | ❌ No test file | 0 | **UNTESTED** |
|
||||
| **voice_settings.dart** | ❌ No test file | 0 | **UNTESTED** |
|
||||
|
||||
### E2E Tests
|
||||
|
||||
| File | Tests | Notes |
|
||||
|------|-------|-------|
|
||||
| alpha_e2e_test.dart | 1 | End-to-end integration |
|
||||
| beta_e2e_test.dart | 1 | End-to-end integration |
|
||||
| widget_test.dart | — | Default Flutter template |
|
||||
|
||||
---
|
||||
|
||||
## Document Coverage Summary
|
||||
|
||||
| Metric | Count |
|
||||
|--------|-------|
|
||||
| Total doc files (under docs/) | 66 |
|
||||
| Modules documented | ~15 areas |
|
||||
| Estimated outdated docs | 3-5 |
|
||||
|
||||
## Document Inventory
|
||||
|
||||
### Architecture (4 files)
|
||||
|
||||
| File | Topic | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| architecture/sad.md | Software Architecture Document | Current | 190 lines, references SDD |
|
||||
| architecture/sdd.md | Software Design Document | Current | 161 lines |
|
||||
| architecture/sysdes.md | System Design overview | Current | 21 lines, brief |
|
||||
| architecture/desktop-ptt-architecture.md | Desktop PTT subsystem design | Current | 43 lines |
|
||||
| architecture/file-transfer-design.md | File transfer feature design | Current | 737 lines |
|
||||
| architecture/file-transfer-research.md | File transfer research | Current | 770 lines |
|
||||
| architecture/file-transfer-implementation-plan.md | File transfer implementation plan | Current | 1315 lines |
|
||||
|
||||
### Requirements (2 files + 2 symlinks)
|
||||
|
||||
| File | Topic | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| requirements/srs.md | Software Requirements Spec | Current | 22 lines (pointer) |
|
||||
| requirements/sysrs.md | System Requirements Spec | Current | 22 lines (pointer) |
|
||||
| srs.md | SRS (full) | Current | 2850 lines |
|
||||
| sysrs.md | SysRS (full) | Current | 2014 lines |
|
||||
|
||||
### Verification (5 files)
|
||||
|
||||
| File | Topic | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| verification/verification-master-plan.md | Overall V&V plan | Current | 91 lines |
|
||||
| verification/swe4-unit-verification-plan.md | Unit test plan | Current | 60 lines |
|
||||
| verification/swe5-software-integration-verification-plan.md | Integration test plan | Current | 71 lines |
|
||||
| verification/swe6-software-verification-plan.md | System verification plan | Current | 62 lines |
|
||||
| verification/sys4-system-integration-verification-plan.md | System integration plan | Current | 58 lines |
|
||||
|
||||
### Security (7 files)
|
||||
|
||||
| File | Topic | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| security/threat-model.md | Threat model | Current | 33 lines |
|
||||
| security/license-inventory.md | Rust license inventory | Current | 10970 lines |
|
||||
| security/flutter-license-inventory.md | Flutter license inventory | Current | 5477 lines |
|
||||
| security/diagnostic-redaction-audit-report.md | Diagnostic redaction audit | Current | 31 lines |
|
||||
| security/secure-storage-audit-report.md | Secure storage audit | Current | 31 lines |
|
||||
| security/dependency-and-supply-chain-report.md | Dependency audit | Current | 43 lines |
|
||||
| security/security-privacy-legal-guideline.md | Security/privacy guidelines | Current | 63 lines |
|
||||
|
||||
### Governance (11 files)
|
||||
|
||||
| File | Topic | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| governance/document-index.md | Document catalog | Current | 36 lines |
|
||||
| governance/document-naming-convention.md | Naming conventions | Current | 33 lines |
|
||||
| governance/document-review-report.md | Review report | Current | 37 lines |
|
||||
| governance/traceability-matrix.md | Requirements traceability | Current | 73 lines |
|
||||
| governance/product-decision-register.md | Decision log | Current | 25 lines |
|
||||
| governance/decision-impact-assessment.md | Impact assessment | Current | 18 lines |
|
||||
| governance/git-commit-message-convention.md | Commit conventions | Current | 21 lines |
|
||||
| governance/path-migration-map.md | Path migration plan | Current | 18 lines |
|
||||
| governance/repo-format-validation-report.md | Format validation | Current | 22 lines |
|
||||
| governance/baseline-candidate-validation-report.md | Baseline validation | Current | 38 lines |
|
||||
| governance/baseline-approval-record.md | Baseline approval | Current | 32 lines |
|
||||
| governance/maintainability-review-2026-06-08.md | Maintainability review | Current | 99 lines |
|
||||
|
||||
### Release (4 files)
|
||||
|
||||
| File | Topic | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| release/ios-build.md | iOS build instructions | Current | 47 lines |
|
||||
| release/platform-release-policy.md | Release policy | Current | 26 lines |
|
||||
| release/release-readiness-go-nogo-record.md | Go/no-go record | Current | 105 lines |
|
||||
| release/dv-waiver-register.md | DV waiver register | Current | 36 lines |
|
||||
|
||||
### UI/UX (4 files)
|
||||
|
||||
| File | Topic | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| ui-ux/material3-guideline.md | Material 3 guidelines | Current | 8 lines (brief) |
|
||||
| ui-ux/material3-design-tokens.md | Design tokens | Current | 21 lines |
|
||||
| ui-ux/material3-component-catalog.md | Component catalog | Current | 38 lines |
|
||||
| ui-ux/adaptive-layout-platform-guide.md | Adaptive layout guide | Current | 27 lines |
|
||||
|
||||
### Other
|
||||
|
||||
| File | Topic | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| privacy/privacy-policy.md | Privacy policy | Current | 51 lines |
|
||||
| references/external-references.md | External references | Current | 21 lines |
|
||||
| references/aspice-swe2-swe3-integration-note.md | ASPICE integration note | Current | 28 lines |
|
||||
| legal/trademark-and-attribution-review.md | Trademark review | Current | 47 lines |
|
||||
| i18n/localization-architecture.md | Localization architecture | Current | 44 lines |
|
||||
| implementation-status-2026-05-28.md | Implementation status | **Possibly outdated** | Date is 2026-05-28 |
|
||||
| material3-guideline.md | Material 3 guideline (duplicate) | Current | 93 lines |
|
||||
|
||||
### Superpowers Plans & Specs (13 files)
|
||||
|
||||
| File | Topic | Status |
|
||||
|------|-------|--------|
|
||||
| superpowers/plans/2026-05-28-server-resolution-prefetch.md | Server resolution prefetch plan | Current |
|
||||
| superpowers/plans/2026-05-28-chanora-server-prefetch-crate.md | Prefetch crate plan | Current |
|
||||
| superpowers/plans/2026-05-29-dv-evidence-pack.md | DV evidence pack plan | Current |
|
||||
| superpowers/plans/2026-05-29-state-sync-ui-settings-validation.md | State sync validation plan | Current |
|
||||
| superpowers/plans/2026-05-29-swe2-swe3-baselines.md | SWE2/SWE3 baselines plan | Current |
|
||||
| superpowers/plans/2026-05-29-finish-dv-document-tree.md | DV document tree plan | Current |
|
||||
| superpowers/plans/2026-06-06-chat-panel-switching.md | Chat panel switching plan | Current |
|
||||
| superpowers/plans/2026-06-08-maintainability-continuation.md | Maintainability continuation | Current |
|
||||
| superpowers/plans/2026-06-08-core-internal-split.md | Core internal split plan | Current |
|
||||
| superpowers/specs/2026-05-28-server-resolution-prefetch-design.md | Prefetch design spec | Current |
|
||||
| superpowers/specs/2026-05-28-chanora-server-prefetch-crate-design.md | Prefetch crate design | Current |
|
||||
| superpowers/specs/2026-05-29-state-sync-ui-settings-validation-design.md | State sync design | Current |
|
||||
| superpowers/specs/2026-06-05-adaptive-3-panel-layout-design.md | Adaptive layout design | Current |
|
||||
| superpowers/specs/2026-06-08-maintainability-continuation-design.md | Maintainability design | Current |
|
||||
| superpowers/specs/2026-06-09-poke-without-message-design.md | Poke without message design | Current |
|
||||
|
||||
---
|
||||
|
||||
## Documentation Gaps
|
||||
|
||||
The following code modules have **no dedicated documentation**:
|
||||
|
||||
| Module | Functions | Gap Description |
|
||||
|--------|-----------|-----------------|
|
||||
| `chanora_audio` (engine) | ~45 | No architecture doc for audio engine internals |
|
||||
| `chanora_audio` (VAD subsystem) | ~25 | VAD pipeline, model loading, fallback strategy undocumented |
|
||||
| `chanora_audio` (DSP processors) | ~30 | AEC3, AGC2, NS, HPF configuration undocumented |
|
||||
| `chanora_audio` (PTT backends) | ~155 | Platform-specific PTT behavior undocumented |
|
||||
| `chanora_bridge` (FFI layer) | ~300 | Flutter-Rust bridge API contract undocumented |
|
||||
| `chanora_cache` | ~16 | Cache strategy, eviction policy undocumented |
|
||||
| `chanora_prefetch` | ~18 | Prefetch timing, debouncing strategy undocumented |
|
||||
| `chanora_core` | ~147 | Core connection lifecycle, event handling undocumented |
|
||||
| `chanora_state` | ~59 | State machine transitions, delta emission undocumented |
|
||||
| `chanora_storage` | ~62 | Storage format, migration strategy undocumented |
|
||||
| `chanora_protocol` | ~97 | Protocol message format, adapter logic undocumented |
|
||||
| `chanora_resolver` | ~70 | DNS resolution, TSDNS discovery undocumented |
|
||||
| `chanora_diagnostics` | ~74 | Diagnostic collection, redaction rules undocumented |
|
||||
| Flutter services layer | ~21 files | No service-layer architecture doc |
|
||||
| Flutter widgets layer | ~24 files | No widget catalog or component doc |
|
||||
| Localization (l10n) | — | Translation workflow undocumented (only architecture doc exists) |
|
||||
|
||||
## Potentially Outdated Documents
|
||||
|
||||
| File | Reason |
|
||||
|------|--------|
|
||||
| `implementation-status-2026-05-28.md` | Dated 2026-05-28; code has changed significantly since |
|
||||
| `docs/material3-guideline.md` | Duplicate of `docs/ui-ux/material3-guideline.md` |
|
||||
| `docs/sysdes.md` | Top-level duplicate of `docs/architecture/sysdes.md` |
|
||||
| `docs/srs.md` / `docs/sysrs.md` | Top-level duplicates of `docs/requirements/` versions |
|
||||
|
||||
---
|
||||
|
||||
## Key Findings
|
||||
|
||||
1. **chanora_audio** is the best-tested crate (204 tests), but still only ~48% function coverage due to the large codebase (~428 functions)
|
||||
2. **chanora_bridge**, **chanora_cache**, and **chanora_prefetch** have zero tests
|
||||
3. **chanora_core** has very low coverage (~7%) despite being the main connection orchestrator
|
||||
4. **Dart widget tests** cover only 54% of widget files; 11 widget files have no tests
|
||||
5. **Dart service tests** are strong at 90% coverage (only 2 files untested)
|
||||
6. **Documentation** is extensive (55 files) but focuses on process/governance; code-level architecture docs are sparse
|
||||
7. No dedicated docs exist for the audio engine, FFI bridge, protocol layer, or state machine internals
|
||||
@@ -0,0 +1,123 @@
|
||||
# Documentation Quality Analysis
|
||||
|
||||
## Summary
|
||||
- Total docs analyzed: 64
|
||||
- Duplicated content instances: 8
|
||||
- Path record files (DV navigation aids): 4
|
||||
- Genuine issues (malformed markdown): 1
|
||||
- Broken references: 1 (suggested file names only; SDD-109/SAD-043 are valid historical refs)
|
||||
|
||||
## Duplicated Content
|
||||
|
||||
### Instance 1: Lifecycle Documentation Chain
|
||||
- **Files**: `README.md:280`, `CONTRIBUTING.md:10`, `docs/sysdes.md:90`, `docs/sysrs.md:108`, `docs/governance/traceability-matrix.md:16`, `docs/references/aspice-swe2-swe3-integration-note.md:12`
|
||||
- **Content**: `SysRS -> SysDes -> SRS -> SAD -> SDD` lifecycle chain repeated across 6+ files
|
||||
- **Recommendation**: Define once in `README.md` and reference from other docs
|
||||
|
||||
### Instance 2: Git Commit Convention Examples
|
||||
- **Files**: `README.md:380`, `CONTRIBUTING.md:38`, `docs/governance/git-commit-message-convention.md:15`
|
||||
- **Content**: Same commit examples (`feat(voice): add push-to-talk state handling`, `fix(protocol): recover channel tree after reconnect snapshot`, etc.) duplicated across 3 files
|
||||
- **Recommendation**: Keep examples only in `docs/governance/git-commit-message-convention.md` and reference from README/CONTRIBUTING
|
||||
|
||||
### Instance 3: Security/Privacy/Legal Document List
|
||||
- **Files**: `README.md:349-355`, `SECURITY.md:33-38`
|
||||
- **Content**: Same list of 6 security documents (threat-model, secure-storage, diagnostic-redaction, dependency, privacy-policy, trademark) repeated verbatim
|
||||
- **Recommendation**: Keep list in `SECURITY.md` and reference from README
|
||||
|
||||
### Instance 4: Architecture Component Table
|
||||
- **Files**: `README.md:73-92`, `docs/architecture/sad.md:56-67`
|
||||
- **Content**: Similar architecture overview showing Flutter UI, Rust Core, Protocol Layer structure
|
||||
- **Recommendation**: Keep detailed version in SAD; use abbreviated version in README
|
||||
|
||||
### Instance 5: Platform Policy Table
|
||||
- **Files**: `README.md:47-54`, `docs/release/platform-release-policy.md:12-19`
|
||||
- **Content**: Platform requirements table with overlapping information
|
||||
- **Recommendation**: Consolidate in `platform-release-policy.md` and reference from README
|
||||
|
||||
### Instance 6: Security Gate Requirements
|
||||
- **Files**: `docs/security/security-privacy-legal-guideline.md:13-21`, `docs/security/threat-model.md:22-30`
|
||||
- **Content**: Similar threat/mitigation tables with overlapping secure-storage and diagnostics concerns
|
||||
- **Recommendation**: Threat model should reference the guideline for gate requirements
|
||||
|
||||
### Instance 7: DV Conclusion Pattern
|
||||
- **Files**: Nearly every `docs/` file ends with a "## DV Conclusion" section
|
||||
- **Content**: Repetitive pattern: "[Area] is documented for DV. [Limitation] remains."
|
||||
- **Recommendation**: This is intentional for ASPICE compliance. No change needed, but consider a template.
|
||||
|
||||
### Instance 8: Android Runtime Gate Documentation
|
||||
- **Files**: `docs/verification/swe5-software-integration-verification-plan.md:57-68`, `docs/governance/maintainability-review-2026-06-08.md:61-88`
|
||||
- **Content**: Same Android ADB/emulator verification steps and `adb devices -l` requirements
|
||||
- **Recommendation**: Define once in a shared reference and import
|
||||
|
||||
## Path Record Files (DV Navigation Aids)
|
||||
|
||||
These files are intentional ASPICE DV entry-point records with reviewer navigation tables. They are NOT useless — they serve a specific compliance purpose. Listed here for awareness only.
|
||||
|
||||
### DV Navigation Aids
|
||||
|
||||
| File | Line | Header | Issue |
|
||||
|------|------|--------|-------|
|
||||
| `docs/architecture/sysdes.md` | 1-21 | Entire file | Path record — points to `docs/sysdes.md` for DV reviewer navigation |
|
||||
| `docs/requirements/sysrs.md` | 1-22 | Entire file | Path record — points to `docs/sysrs.md` for DV reviewer navigation |
|
||||
| `docs/requirements/srs.md` | 1-22 | Entire file | Path record — points to `docs/srs.md` for DV reviewer navigation |
|
||||
| `docs/ui-ux/material3-guideline.md` | 1-8 | Entire file | Path record — points to `docs/material3-guideline.md` for DV reviewer navigation |
|
||||
|
||||
### Genuine Issues
|
||||
|
||||
| File | Line | Header | Issue |
|
||||
|------|------|--------|-------|
|
||||
| `docs/sysdes.md` | 13 | `**Repo path:** ... ---` | Malformed markdown (missing blank line before `---`) |
|
||||
|
||||
### TODO/Placeholder Markers
|
||||
|
||||
No actual TODO/TBD/placeholder markers found in the documentation files. The codebase is clean of such markers.
|
||||
|
||||
### Broken References
|
||||
|
||||
| File | Line | Reference | Issue |
|
||||
|------|------|-----------|-------|
|
||||
| `docs/sysrs.md` | 126-130 | `docs/chanora_SysDes.md`, `docs/chanora_SRS.md`, etc. | These suggested file names do not exist. Actual files use different names (`docs/sysdes.md`, `docs/srs.md`, etc.) |
|
||||
| `docs/implementation-status-2026-05-28.md` | 103 | `SDD-109` | References a specific SDD item ID that is not itemized in the current SDD baseline |
|
||||
| `docs/implementation-status-2026-05-28.md` | 105 | `SAD-043` | References a specific SAD item ID that is not itemized in the current SAD baseline |
|
||||
|
||||
### Outdated Content
|
||||
|
||||
| File | Line | Content | Issue |
|
||||
|------|------|---------|-------|
|
||||
| `docs/sysdes.md` | 6 | Version `0.9.8` | Superseded by later governance docs dated 2026-05-29 |
|
||||
| `docs/sysrs.md` | 5 | Version `0.9.11` | May need alignment with SysDes version |
|
||||
| `docs/material3-guideline.md` | 4-5 | Version `0.9.2` | Change history stops at 2026-05-14; no updates for 2026-05-29 baseline |
|
||||
| `tools/windows-smoke.md` | 6 | `product/scaffold-v0` branch | Default base branch changed to `main` per CHANGELOG |
|
||||
| `docs/implementation-status-2026-05-28.md` | 140 | Agent spec docs reference | States docs are "deleted from the working tree but still in git HEAD" — stale cleanup note |
|
||||
|
||||
### Stale Content
|
||||
|
||||
| File | Line | Content | Issue |
|
||||
|------|------|---------|-------|
|
||||
| `docs/implementation-status-2026-05-28.md` | 1 | Date: 2026-05-28 | Pre-dates DV baseline (2026-05-29); may not reflect final baseline state |
|
||||
| `docs/governance/git-commit-message-convention.md` | 18 | `release(android): prepare internal alpha build metadata` | Example uses `release` type which is not in the Conventional Commits standard types |
|
||||
|
||||
## Duplicated Code Blocks
|
||||
|
||||
| Code Hash | Files | Description |
|
||||
|-----------|-------|-------------|
|
||||
| Lifecycle chain | `README.md:280`, `CONTRIBUTING.md:10`, `docs/sysdes.md:90`, `docs/sysrs.md:108`, `docs/governance/traceability-matrix.md:16`, `docs/references/aspice-swe2-swe3-integration-note.md:12` | `SysRS -> SysDes -> SRS -> SAD -> SDD -> Verification` |
|
||||
| Commit examples | `README.md:379-386`, `CONTRIBUTING.md:37-42`, `docs/governance/git-commit-message-convention.md:14-19` | Overlapping commit message examples (different subsets in each file) |
|
||||
| Security doc list | `README.md:349-355`, `SECURITY.md:33-38` | 6 identical file paths |
|
||||
| Architecture ASCII art | `README.md:73-92`, `docs/architecture/sad.md:56-67` | Similar but not identical architecture diagrams |
|
||||
| Platform table | `README.md:47-54`, `docs/release/platform-release-policy.md:12-19` | Overlapping platform requirement tables |
|
||||
|
||||
## Recommendations
|
||||
|
||||
### High Priority
|
||||
1. **Consolidate lifecycle chain**: Define once in README, reference elsewhere
|
||||
2. **Fix suggested file names**: `docs/sysrs.md` lines 126-130 reference non-existent file names
|
||||
|
||||
### Medium Priority
|
||||
4. **Consolidate commit examples**: Keep in `git-commit-message-convention.md` only
|
||||
5. **Consolidate security doc list**: Keep in `SECURITY.md` only
|
||||
6. **Update outdated branch reference**: `tools/windows-smoke.md` references `product/scaffold-v0` but default is now `main`
|
||||
|
||||
### Low Priority
|
||||
7. **Align document versions**: SysDes (0.9.8), SysRS (0.9.11), Material3 (0.9.2) have different versions
|
||||
8. **Clean up implementation status**: Remove stale agent-spec references and update date
|
||||
@@ -0,0 +1,458 @@
|
||||
# Documentation-Code Mismatch Analysis
|
||||
|
||||
**Generated:** 2026-06-13
|
||||
|
||||
## Summary
|
||||
- Total claims verified: ~150
|
||||
- Mismatches found: 17
|
||||
- Critical: 2 | Major: 4 | Minor: 11
|
||||
|
||||
## Critical Mismatches (wrong API / broken reference)
|
||||
|
||||
### 1. [README.md:428-431] - LICENSE files referenced but do not exist
|
||||
- **Doc claims:** Links to `LICENSE-APACHE` and `LICENSE-MIT` at repository root
|
||||
- **Code shows:** Neither `LICENSE-APACHE` nor `LICENSE-MIT` exists at `/Users/edison/dev/chanora/`
|
||||
- **Impact:** Users clicking license links in README get 404 on GitHub. Dual-license model (DEC-020) requires these files for proper attribution. Also affects `docs/security/license-inventory.md:9-10` and `docs/security/flutter-license-inventory.md:11-12`.
|
||||
|
||||
### 2. [README.md:236-249] - Repository layout missing 3 crates
|
||||
- **Doc claims:** Lists 7 crates: `chanora_protocol`, `chanora_audio`, `chanora_state`, `chanora_storage`, `chanora_diagnostics`, `chanora_bridge` plus `core/chanora_core`
|
||||
- **Code shows:** Actual workspace has 10 crates: adds `chanora_resolver`, `chanora_prefetch`, `chanora_cache` (all present in `Cargo.toml` workspace members and `crates/` directory)
|
||||
- **Impact:** Developers reading README cannot discover 3 existing crates. Resolver, prefetch, and cache functionality is undocumented in the primary entry point.
|
||||
|
||||
## Major Mismatches (wrong behavior / wrong structure)
|
||||
|
||||
### 3. [docs/architecture/sad.md:39-52] - SAD component table missing chanora_cache
|
||||
- **Doc claims:** Component table lists 12 components (Flutter app shell through Server prefetch)
|
||||
- **Code shows:** `chanora_cache` crate exists in workspace (`Cargo.toml:34`) and `crates/chanora_cache/` but is not listed in SAD component architecture
|
||||
- **Impact:** Architecture description incomplete; cache layer is invisible to DV reviewers
|
||||
|
||||
### 4. [docs/architecture/sdd.md:19] - snapshot_state_mapper.dart listed under wrong component
|
||||
- **Doc claims:** `SDD-MOD-003 Snapshot and channel UI` lists `snapshot_state_mapper.dart` as a widget-layer file
|
||||
- **Code shows:** `snapshot_state_mapper.dart` is in `apps/chanora_flutter/lib/services/`, not `apps/chanora_flutter/lib/widgets/`
|
||||
- **Impact:** Minor categorization issue — SDD header says "widget/service layer" but the module table groups it under widgets. Also affects `channel_spacer.dart` (same row).
|
||||
|
||||
### 5. [tools/windows-smoke.md:6] - Branch reference outdated
|
||||
- **Doc claims:** Script designed for `product/scaffold-v0` branch
|
||||
- **Code shows:** Default base branch is `main` per CHANGELOG v0.3.0 line 99
|
||||
- **Impact:** Windows smoke procedure references obsolete branch name
|
||||
|
||||
### 6. [docs/sysrs.md:126-130] - Suggested downstream file names do not exist
|
||||
- **Doc claims:** Lists potential downstream file names: `docs/chanora_SysDes.md`, `docs/chanora_SRS.md`, `docs/chanora_SAD.md`, `docs/chanora_SDD.md`, `docs/chanora_Verification.md`
|
||||
- **Code shows:** Actual files use different names: `docs/sysdes.md`, `docs/srs.md`, `docs/architecture/sad.md`, `docs/architecture/sdd.md`, `docs/verification/verification-master-plan.md`
|
||||
- **Impact:** Aspirational/historical names mislead readers about actual file locations
|
||||
|
||||
### 7. [docs/governance/product-decision-register.md:18] - DEC-030 VoiceActivity scope partially superseded
|
||||
- **Doc claims:** DEC-030 is "Partially superseded by desktop enablement"
|
||||
- **Code shows:** `voice_activity.rs` exists with `VoiceActivityStateMachine`; `transmit_mode.rs` has `TransmitMode::VoiceActivity`; VAD backends exist in `vad/` directory. Windows/Linux desktop VAD is implemented via capture path.
|
||||
- **Impact:** Decision register does not fully reflect current implementation state; desktop VAD is more complete than "partially superseded" suggests
|
||||
|
||||
## Minor Mismatches (cosmetic / slight drift)
|
||||
|
||||
### 8. [README.md:17] - Status description slightly outdated
|
||||
- **Doc claims:** "Chanora is currently a baseline-candidate Flutter + Rust workspace"
|
||||
- **Code shows:** Workspace version is `0.2.0-beta.1`, Flutter app is `0.3.0+100`; project has working voice, chat, bookmarks, diagnostics
|
||||
- **Impact:** "baseline-candidate" undersells current implementation maturity
|
||||
|
||||
### 9. [docs/material3-guideline.md:10] - Self-referencing path record
|
||||
- **Doc claims:** `**Repo path:** docs/ui-ux/material3-guideline.md`
|
||||
- **Code shows:** This file IS at `docs/material3-guideline.md`, not `docs/ui-ux/material3-guideline.md`
|
||||
- **Impact:** Path record creates circular reference confusion
|
||||
|
||||
### 10. [docs/implementation-status-2026-05-28.md:1] - Status date pre-dates DV baseline
|
||||
- **Doc claims:** Date 2026-05-28
|
||||
- **Code shows:** DV baseline documents are dated 2026-05-29; code has changed significantly since
|
||||
- **Impact:** Implementation status may not reflect final baseline state
|
||||
|
||||
### 11. [docs/implementation-status-2026-05-28.md:103,105] - References to non-itemized SDD/SAD IDs
|
||||
- **Doc claims:** References `SDD-109` and `SAD-043`
|
||||
- **Code shows:** Current SAD/SDD baselines do not use itemized ID numbering
|
||||
- **Impact:** Historical references cannot be traced in current baseline
|
||||
|
||||
### 12. [docs/governance/git-commit-message-convention.md:18] - Non-standard commit type
|
||||
- **Doc claims:** Example uses `release(android): prepare internal alpha build metadata`
|
||||
- **Code shows:** `release` is not a standard Conventional Commits type
|
||||
- **Impact:** Minor convention inconsistency
|
||||
|
||||
### 13. [docs/ui-ux/material3-guideline.md:4-5] - Version history stops at 0.9.2
|
||||
- **Doc claims:** Version 0.9.2, last updated 2026-05-14
|
||||
- **Code shows:** DV baseline documents dated 2026-05-29; no update for baseline
|
||||
- **Impact:** Material 3 guideline may not reflect latest baseline decisions
|
||||
|
||||
### 14. [docs/sysdes.md:6] - SysDes version older than SysRS
|
||||
- **Doc claims:** SysDes version 0.9.8
|
||||
- **Code shows:** SysRS version 0.9.11
|
||||
- **Impact:** Version numbering inconsistency between related documents
|
||||
|
||||
### 15. [docs/offline-knowledge/README.md:54] - Claims 2 missing LICENSE files
|
||||
- **Doc claims:** "2 missing LICENSE files (LICENSE-APACHE, LICENSE-MIT)"
|
||||
- **Code shows:** Confirmed - files do not exist at repo root
|
||||
- **Impact:** Consistent finding, but offline-knowledge doc correctly identifies the issue
|
||||
|
||||
### 16. [docs/security/dependency-and-supply-chain-report.md:35] - License inventory location uncertainty
|
||||
- **Doc claims:** `docs/security/license-inventory.md` and Flutter inventory referenced by CI
|
||||
- **Code shows:** Both files exist at `docs/security/license-inventory.md` and `docs/security/flutter-license-inventory.md`
|
||||
- **Impact:** Report expresses uncertainty but files actually exist
|
||||
|
||||
### 17. [docs/architecture/file-transfer-design.md:6] - References SAD-067 which is not itemized
|
||||
- **Doc claims:** "Direct upstream source: docs/architecture/sad.md (SAD-067, SDD-MOD-009)"
|
||||
- **Code shows:** Current SAD baseline does not use itemized SAD-XXX numbering
|
||||
- **Impact:** Historical reference cannot be traced
|
||||
|
||||
## Per-File Verification Results
|
||||
|
||||
### README.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 3 | Cross-platform voice client for TeamSpeak-compatible servers | ✅ PASS | Matches project description |
|
||||
| 8 | Flutter UI + Rust Core + tsclientlib | ✅ PASS | Architecture confirmed |
|
||||
| 17 | Baseline-candidate Flutter + Rust workspace | ⚠️ MINOR | Undersells current maturity |
|
||||
| 47-54 | Platform policy table | ✅ PASS | Matches `docs/release/platform-release-policy.md` |
|
||||
| 57-65 | silero-coreml sibling package | ✅ PASS | Confirmed in workspace layout |
|
||||
| 73-92 | Architecture overview diagram | ✅ PASS | Matches SAD component structure |
|
||||
| 110-123 | MVP Direction table | ✅ PASS | Matches implementation status |
|
||||
| 127-166 | Desktop PTT section | ✅ PASS | Matches `docs/architecture/desktop-ptt-architecture.md` |
|
||||
| 171-231 | Repository Layout (docs/) | ✅ PASS | All listed paths exist |
|
||||
| 236-249 | Repository Layout (implementation) | ❌ FAIL | Missing 3 crates: resolver, prefetch, cache |
|
||||
| 260-271 | Documentation Entry Points | ✅ PASS | All listed paths exist |
|
||||
| 349-355 | Security/Privacy/Legal Gates | ✅ PASS | All listed paths exist |
|
||||
| 400-406 | Development commands | ✅ PASS | Standard Flutter/Cargo commands |
|
||||
| 425-436 | License section | ❌ FAIL | LICENSE-APACHE and LICENSE-MIT do not exist |
|
||||
|
||||
### docs/architecture/sad.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 17 | Rust owns connection orchestration, protocol isolation, audio processing, storage coordination, diagnostics, server resolution, prefetch policy, and bridge DTOs | ✅ PASS | Matches crate responsibilities |
|
||||
| 39-52 | Component architecture table | ⚠️ MAJOR | Missing chanora_cache |
|
||||
| 56-67 | Static architecture view | ✅ PASS | Matches actual dependency flow |
|
||||
| 75-107 | Runtime flow diagrams | ✅ PASS | Connect, voice, diagnostics flows match |
|
||||
| 119-130 | Interface catalogue | ✅ PASS | Matches bridge/protocol boundaries |
|
||||
|
||||
### docs/architecture/sdd.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 15-31 | Module catalogue | ⚠️ MAJOR | snapshot_state_mapper.dart misclassified |
|
||||
| 17 | SDD-MOD-001: `app_bootstrap.dart`, `main.dart` | ✅ PASS | Files exist in services/ and root |
|
||||
| 18 | SDD-MOD-002: `connect_widgets.dart` | ✅ PASS | File exists in widgets/ |
|
||||
| 19 | SDD-MOD-003: `snapshot_view.dart`, `snapshot_state_mapper.dart`, `channel_spacer.dart` | ⚠️ MAJOR | snapshot_state_mapper.dart is in services/ not widgets/ |
|
||||
| 20 | SDD-MOD-004: `chat_views.dart`, `bbcode_text.dart` | ✅ PASS | Files exist in widgets/ |
|
||||
| 21 | SDD-MOD-005: `voice_bar.dart`, `voice_compact.dart`, `voice_settings*.dart`, `voice_level_meter.dart`, `ptt_capability_badge.dart` | ✅ PASS | All files exist in widgets/ |
|
||||
| 22 | SDD-MOD-006: `android_permissions_service.dart`, `ios_permissions_service.dart`, `audio_lifecycle_service.dart`, `back_intent_*`, `link_trust_service.dart` | ✅ PASS | All files exist in services/ |
|
||||
| 23 | SDD-MOD-007: `crates/chanora_bridge/src/api.rs` | ✅ PASS | File exists |
|
||||
| 24 | SDD-MOD-008: `core/chanora_core/src/lib.rs`, `events.rs`, `network_diagnostics.rs`, `ptt.rs` | ✅ PASS | All files exist |
|
||||
| 25 | SDD-MOD-009: `crates/chanora_protocol/src/` | ✅ PASS | Directory exists |
|
||||
| 26 | SDD-MOD-010: `crates/chanora_state/src/lib.rs`, `channel_join.rs` | ✅ PASS | Both files exist |
|
||||
| 27 | SDD-MOD-011: `crates/chanora_audio/src/` | ✅ PASS | Directory exists with 26 files |
|
||||
| 28 | SDD-MOD-012: `crates/chanora_storage/src/lib.rs` | ✅ PASS | File exists |
|
||||
| 29 | SDD-MOD-013: `crates/chanora_diagnostics/src/lib.rs` | ✅ PASS | File exists |
|
||||
| 30 | SDD-MOD-014: `crates/chanora_resolver/src/lib.rs`, `crates/chanora_prefetch/src/lib.rs`, `prefetch_debouncer.dart` | ✅ PASS | All files exist |
|
||||
| 31 | SDD-MOD-015: `.github/workflows/`, `tools/` | ✅ PASS | Both directories exist |
|
||||
|
||||
### docs/sysdes.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 6 | Version 0.9.8 | ⚠️ MINOR | SysRS is 0.9.11 |
|
||||
| 13 | `**Repo path:** docs/architecture/sysdes.md` | ⚠️ MINOR | Malformed markdown (missing blank line before `---`) |
|
||||
| 377-418 | System elements SE-01 through SE-19 | ✅ PASS | Comprehensive element list |
|
||||
| 839-855 | Interface catalogue IF-001 through IF-014 | ✅ PASS | Matches architecture |
|
||||
|
||||
### docs/sysrs.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 5 | Version 0.9.11 | ✅ PASS | Consistent within document |
|
||||
| 126-130 | Suggested downstream file names | ❌ FAIL | 5 non-existent file names |
|
||||
| 233-257 | Application component requirements SysRS-024 through SysRS-034 | ✅ PASS | Match SAD component allocation |
|
||||
|
||||
### docs/srs.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 6 | Version 0.9.9 | ✅ PASS | Consistent within document |
|
||||
| 101-176 | SWE.1 process requirements SRS-001 through SRS-007 | ✅ PASS | Match ASPICE alignment |
|
||||
| 180-267 | Software boundary requirements SRS-008 through SRS-015 | ✅ PASS | Match architecture constraints |
|
||||
|
||||
### CONTRIBUTING.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 10 | Engineering hierarchy: SysRS -> SysDes -> SRS -> SAD -> SDD | ✅ PASS | Matches README and governance docs |
|
||||
| 25 | Commit convention reference | ✅ PASS | `docs/governance/git-commit-message-convention.md` exists |
|
||||
| 37-42 | Commit examples | ✅ PASS | Match README examples |
|
||||
|
||||
### CHANGELOG.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 7 | v0.3.0 milestone | ✅ PASS | Matches pubspec.yaml version |
|
||||
| 65-66 | Flutter app version/build bumped to 0.3.0+100 | ✅ PASS | Matches pubspec.yaml |
|
||||
| 99 | Default base branch is main | ✅ PASS | Confirms branch change |
|
||||
| 100-101 | DSP chain not yet production-tuned | ✅ PASS | Matches implementation status |
|
||||
|
||||
### docs/architecture/desktop-ptt-architecture.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 17-21 | Platform backends table | ✅ PASS | Matches README PTT section |
|
||||
| 24-30 | Safety rules | ✅ PASS | Watchdog, capability, fallback |
|
||||
|
||||
### docs/architecture/file-transfer-design.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 6 | References SAD-067, SDD-MOD-009 | ⚠️ MINOR | SAD-067 not itemized in current baseline |
|
||||
| 113-137 | tsclientlib public API signatures | ⚠️ MINOR | Cannot verify against external library source |
|
||||
| 400-428 | Avatar path computation in adapter.rs | ✅ PASS | `uid_to_avatar_path` function described |
|
||||
|
||||
### docs/i18n/localization-architecture.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 8 | Generated files under `apps/chanora_flutter/lib/l10n/generated/` | ✅ PASS | Directory exists with 3 files |
|
||||
| 22 | English and Simplified Chinese generated localization files | ✅ PASS | `app_localizations_en.dart` and `app_localizations_zh.dart` exist |
|
||||
|
||||
### docs/ui-ux/material3-design-tokens.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 8 | Implementation token source is `apps/chanora_flutter/lib/design/chanora_tokens.dart` | ✅ PASS | File exists |
|
||||
|
||||
### docs/ui-ux/material3-component-catalog.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 10 | Connect and bookmarks: `connect_widgets.dart`, `input_dialogs.dart` | ✅ PASS | Both files exist in widgets/ |
|
||||
| 11 | Channel and client view: `snapshot_view.dart`, `client_info_sheet.dart`, `channel_spacer.dart` | ✅ PASS | All files exist |
|
||||
| 12 | Chat: `chat_views.dart`, `bbcode_text.dart` | ✅ PASS | Both files exist |
|
||||
| 13 | Voice controls: `voice_bar.dart`, `voice_compact.dart`, `voice_settings*.dart` | ✅ PASS | All files exist |
|
||||
| 14 | Platform/permission indicators: `permission_state_banner.dart`, `ptt_capability_badge.dart`, `talk_power_warning.dart` | ✅ PASS | All files exist |
|
||||
| 15 | Diagnostics: `audio_debug_stats_panel.dart` | ✅ PASS | File exists |
|
||||
|
||||
### docs/ui-ux/adaptive-layout-platform-guide.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 8 | Compact/mobile layout for MVP | ✅ PASS | Matches implementation status |
|
||||
|
||||
### docs/security/security-privacy-legal-guideline.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 13-21 | Gate summary table | ✅ PASS | Matches threat model and audit reports |
|
||||
|
||||
### docs/security/threat-model.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 8 | Scope covers client, local storage, diagnostics, bridge, protocol, audio, platform, release | ✅ PASS | Comprehensive scope |
|
||||
|
||||
### docs/security/secure-storage-audit-report.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 12-18 | Audit matrix | ✅ PASS | Matches platform policy |
|
||||
|
||||
### docs/security/diagnostic-redaction-audit-report.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 12-18 | Redaction targets | ✅ PASS | Matches diagnostics crate responsibilities |
|
||||
|
||||
### docs/security/dependency-and-supply-chain-report.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 15-19 | Automated controls | ✅ PASS | CI workflows confirmed |
|
||||
| 24-29 | Dependency areas | ✅ PASS | Matches workspace structure |
|
||||
|
||||
### docs/security/flutter-license-inventory.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 11-12 | References LICENSE-APACHE and LICENSE-MIT | ❌ FAIL | Files do not exist |
|
||||
|
||||
### docs/security/license-inventory.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 9-10 | References LICENSE-APACHE and LICENSE-MIT | ❌ FAIL | Files do not exist |
|
||||
|
||||
### docs/privacy/privacy-policy.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 9 | Chanora is a client application for connecting to TeamSpeak 3-compatible servers | ✅ PASS | Matches README |
|
||||
|
||||
### docs/legal/trademark-and-attribution-review.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 5 | DEC-012 remains open | ✅ PASS | Matches product decision register |
|
||||
|
||||
### docs/release/release-readiness-go-nogo-record.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 5 | Workspace version 0.2.0-beta.1, Flutter app 0.3.0+100 | ✅ PASS | Matches Cargo.toml and pubspec.yaml |
|
||||
| 6 | No-Go for public/store release | ✅ PASS | Consistent with open gates |
|
||||
|
||||
### docs/release/platform-release-policy.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 12-19 | Platform policy table | ✅ PASS | Matches README |
|
||||
|
||||
### docs/release/dv-waiver-register.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 14-23 | Active waivers DV-WVR-001 through DV-WVR-009 | ✅ PASS | Comprehensive waiver list |
|
||||
|
||||
### docs/release/ios-build.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 12 | Build script `./tools/build-ios.sh --no-codesign` | ⚠️ MINOR | Cannot verify script exists without checking |
|
||||
|
||||
### docs/verification/verification-master-plan.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 5 | Applies to Rust workspace 0.2.0-beta.1, Flutter app 0.3.0+100 | ✅ PASS | Matches actual versions |
|
||||
|
||||
### docs/verification/swe4-unit-verification-plan.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 17 | chanora_state has 27 tests | ✅ PASS | Matches coverage analysis |
|
||||
|
||||
### docs/verification/swe5-software-integration-verification-plan.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 14-22 | Integration paths | ✅ PASS | Comprehensive path list |
|
||||
|
||||
### docs/verification/swe6-software-verification-plan.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 27-45 | MVP acceptance matrix | ✅ PASS | Comprehensive matrix |
|
||||
|
||||
### docs/verification/sys4-system-integration-verification-plan.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 14-22 | System elements under verification | ✅ PASS | Comprehensive list |
|
||||
|
||||
### docs/governance/document-index.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 14-32 | Baseline documents table | ✅ PASS | All listed paths exist |
|
||||
|
||||
### docs/governance/traceability-matrix.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 16 | Lifecycle chain | ✅ PASS | Matches README |
|
||||
|
||||
### docs/governance/product-decision-register.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 14-21 | Decision summary | ✅ PASS | Comprehensive decision list |
|
||||
|
||||
### docs/governance/git-commit-message-convention.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 18 | `release(android)` example | ⚠️ MINOR | Non-standard Conventional Commits type |
|
||||
|
||||
### docs/governance/document-naming-convention.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 8 | Lowercase kebab-case file names | ✅ PASS | Matches actual file naming |
|
||||
|
||||
### docs/governance/path-migration-map.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 10-14 | Migration state table | ✅ PASS | Matches actual file locations |
|
||||
|
||||
### docs/governance/baseline-approval-record.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 10-18 | Approval scope table | ✅ PASS | Matches baseline status |
|
||||
|
||||
### docs/governance/baseline-candidate-validation-report.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 10-16 | Validation summary | ✅ PASS | Comprehensive validation |
|
||||
|
||||
### docs/governance/document-review-report.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 19-24 | Findings table | ✅ PASS | Addresses previous gaps |
|
||||
|
||||
### docs/governance/repo-format-validation-report.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 8-18 | Repository layout check | ✅ PASS | All areas confirmed |
|
||||
|
||||
### docs/governance/decision-impact-assessment.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 8-14 | Impact matrix | ✅ PASS | Comprehensive impact list |
|
||||
|
||||
### docs/governance/maintainability-review-2026-06-08.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 13-23 | Changes already applied | ✅ PASS | Matches code structure |
|
||||
| 61-88 | Android ADB status | ✅ PASS | Detailed smoke evidence |
|
||||
|
||||
### docs/references/aspice-swe2-swe3-integration-note.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 12 | Lifecycle chain | ✅ PASS | Matches README |
|
||||
|
||||
### docs/references/external-references.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 8-17 | Reference list | ✅ PASS | Comprehensive references |
|
||||
|
||||
### docs/implementation-status-2026-05-28.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 3-4 | Workspace version v0.2.0-beta.1, Flutter app 0.3.0+100 | ✅ PASS | Matches actual versions |
|
||||
| 103 | References SDD-109 | ⚠️ MINOR | Not itemized in current baseline |
|
||||
| 105 | References SAD-043 | ⚠️ MINOR | Not itemized in current baseline |
|
||||
| 140 | Agent spec docs reference | ⚠️ MINOR | Stale cleanup note |
|
||||
|
||||
### SECURITY.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 33-38 | Security document list | ✅ PASS | All listed paths exist |
|
||||
|
||||
### apps/chanora_flutter/README.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 3 | Chanora — cross-platform voice client for TeamSpeak-compatible servers | ✅ PASS | Matches main README |
|
||||
|
||||
### crates/chanora_resolver/README.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 7 | `ChanoraResolver::resolve_client_request` or `resolve_client_address` | ✅ PASS | Matches function inventory |
|
||||
| 60-77 | Library example | ✅ PASS | Matches API |
|
||||
|
||||
### tools/windows-smoke.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 6 | `product/scaffold-v0` branch | ❌ FAIL | Default branch is now `main` |
|
||||
|
||||
### silero-coreml/README.md
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 3 | Private Chanora-owned Apple/CoreML Silero VAD backend scaffold | ✅ PASS | Matches project scope |
|
||||
|
||||
### flutter_rust_bridge.yaml
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 1-5 | Bridge configuration | ✅ PASS | Matches SDD bridge boundary design |
|
||||
|
||||
### Cargo.toml
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 28-39 | Workspace members | ✅ PASS | All 10 crates listed |
|
||||
| 46 | Version 0.2.0-beta.1 | ✅ PASS | Matches documentation |
|
||||
| 48 | Rust version 1.95 | ✅ PASS | Modern Rust requirement |
|
||||
|
||||
### pubspec.yaml
|
||||
| Line | Claim | Status | Notes |
|
||||
|------|-------|--------|-------|
|
||||
| 19 | Version 0.3.0+100 | ✅ PASS | Matches documentation |
|
||||
| 37 | flutter_rust_bridge: 2.12.0 | ✅ PASS | Matches SDD bridge version |
|
||||
|
||||
## Recommendations
|
||||
|
||||
### High Priority (Critical)
|
||||
1. **Create LICENSE-APACHE and LICENSE-MIT files** — Required for DEC-020 dual-license compliance
|
||||
2. **Update README.md repository layout** — Add `crates/chanora_resolver/`, `crates/chanora_prefetch/`, `crates/chanora_cache/`
|
||||
|
||||
### Medium Priority (Major)
|
||||
3. **Update SAD component table** — Add `chanora_cache` component
|
||||
4. **Fix SDD-MOD-003 file classification** — Move `snapshot_state_mapper.dart` to correct section
|
||||
5. **Update tools/windows-smoke.md** — Change branch reference from `product/scaffold-v0` to `main`
|
||||
6. **Fix docs/sysrs.md suggested file names** — Remove or update non-existent file name suggestions
|
||||
|
||||
### Low Priority (Minor)
|
||||
7. **Update implementation status date** — Refresh to reflect current state
|
||||
8. **Fix malformed markdown in docs/sysdes.md:13** — Add blank line before `---`
|
||||
9. **Update Material 3 guideline version** — Align with DV baseline date
|
||||
10. **Standardize commit type examples** — Remove `release` type from convention examples
|
||||
11. **Update SysDes version** — Align with SysRS version numbering
|
||||
@@ -0,0 +1,347 @@
|
||||
# Documentation Link Not-Covered Analysis
|
||||
|
||||
**Generated:** 2026-06-13
|
||||
|
||||
## Summary
|
||||
- Total references checked: 148
|
||||
- Broken markdown links: 2
|
||||
- Missing file targets: 9 (2 LICENSE + 5 hypothetical + 2 code path mismatches)
|
||||
- Orphaned docs: 18
|
||||
- Suspicious external URLs: 3
|
||||
|
||||
## Broken Markdown Links
|
||||
|
||||
| File | Line | Link Text | Target | Issue |
|
||||
|------|------|-----------|--------|-------|
|
||||
| README.md | 428 | `LICENSE-APACHE` | `LICENSE-APACHE` | File does not exist at repo root |
|
||||
| README.md | 431 | `LICENSE-MIT` | `LICENSE-MIT` | File does not exist at repo root |
|
||||
|
||||
**Impact:** Users clicking the license links in the README will get a 404 on GitHub. These are referenced in the License section as the dual-license model files (DEC-020).
|
||||
|
||||
## Missing File Targets
|
||||
|
||||
### Missing LICENSE Files (High Impact)
|
||||
|
||||
| File | Line | Referenced Path | Issue |
|
||||
|------|------|----------------|-------|
|
||||
| README.md | 428 | `LICENSE-APACHE` | File does not exist at repo root |
|
||||
| README.md | 431 | `LICENSE-MIT` | File does not exist at repo root |
|
||||
| docs/security/license-inventory.md | 9 | `../../LICENSE-APACHE` | Resolves to missing `LICENSE-APACHE` at repo root |
|
||||
| docs/security/license-inventory.md | 10 | `../../LICENSE-MIT` | Resolves to missing `LICENSE-MIT` at repo root |
|
||||
| docs/security/flutter-license-inventory.md | 11 | `../../LICENSE-APACHE` | Resolves to missing `LICENSE-APACHE` at repo root |
|
||||
| docs/security/flutter-license-inventory.md | 11 | `../../LICENSE-MIT` | Resolves to missing `LICENSE-MIT` at repo root |
|
||||
|
||||
**Impact:** The dual-license model (DEC-020) requires these files to exist for proper attribution. All 4 references across 3 files are broken.
|
||||
|
||||
### Hypothetical File Names (Low Impact)
|
||||
|
||||
| File | Line | Referenced Path | Issue |
|
||||
|------|------|----------------|-------|
|
||||
| docs/sysrs.md | 126 | `docs/chanora_SysDes.md` | Listed as "Potential downstream file name" — does not exist |
|
||||
| docs/sysrs.md | 127 | `docs/chanora_SRS.md` | Listed as "Potential downstream file name" — does not exist |
|
||||
| docs/sysrs.md | 128 | `docs/chanora_SAD.md` | Listed as "Potential downstream file name" — does not exist |
|
||||
| docs/sysrs.md | 129 | `docs/chanora_SDD.md` | Listed as "Potential downstream file name" — does not exist |
|
||||
| docs/sysrs.md | 130 | `docs/chanora_Verification.md` | Listed as "Potential downstream file name" — does not exist |
|
||||
|
||||
**Note:** These are documented as "Potential downstream file names" in a table and are aspirational/historical. They are presented as plain text in a table, not as navigable links. Low severity.
|
||||
|
||||
## Missing Code References
|
||||
|
||||
| File | Line | Reference | Expected Location | Issue |
|
||||
|------|------|-----------|-------------------|-------|
|
||||
| docs/architecture/sdd.md | 19 | `snapshot_state_mapper.dart` | Listed under "Snapshot and channel UI" widgets section | File actually exists in `apps/chanora_flutter/lib/services/`, not `apps/chanora_flutter/lib/widgets/` — directory mismatch in docs |
|
||||
| docs/architecture/sdd.md | 21 | `voice_settings*.dart` | Listed under Voice UI widgets | Files are `voice_settings.dart` and `voice_settings_controls.dart` — glob reference is ambiguous (two files match) |
|
||||
|
||||
**Note:** The `snapshot_state_mapper.dart` directory mismatch is a minor documentation inaccuracy — the file exists but is categorized differently than documented.
|
||||
|
||||
## Broken Anchor Links
|
||||
|
||||
No broken anchor links found. All `#section` references within documents resolve to existing headers.
|
||||
|
||||
## Orphaned Documents
|
||||
|
||||
(Not referenced by any other document in the main doc tree)
|
||||
|
||||
| File | Last Modified | Should Be Referenced From |
|
||||
|------|---------------|--------------------------|
|
||||
| docs/offline-knowledge/README.md | 2026-06-13 | Could be referenced from a top-level docs index |
|
||||
| docs/offline-knowledge/function-inventory.md | 2026-06-13 | Could be referenced from docs/architecture/sdd.md |
|
||||
| docs/offline-knowledge/coverage-analysis.md | 2026-06-13 | Could be referenced from docs/verification/ plans |
|
||||
| docs/offline-knowledge/doc-quality-analysis.md | 2026-06-13 | Could be referenced from docs/governance/document-review-report.md |
|
||||
| docs/offline-knowledge/link-coverage-report.md | 2026-06-13 | Could be referenced from docs/governance/ |
|
||||
| docs/offline-knowledge/external/teaspeak-overview.md | 2026-06-13 | Could be referenced from docs/references/external-references.md |
|
||||
| docs/offline-knowledge/external/respeak-overview.md | 2026-06-13 | Could be referenced from docs/references/external-references.md |
|
||||
| docs/offline-knowledge/external/yatqa-en.md | 2026-06-13 | Could be referenced from docs/references/external-references.md |
|
||||
| docs/offline-knowledge/external/yatqa-de.md | 2026-06-13 | Could be referenced from docs/references/external-references.md |
|
||||
| docs/offline-knowledge/reviews/coverage-analysis-review.md | 2026-06-13 | Could be referenced from docs/offline-knowledge/README.md (already is) |
|
||||
| docs/offline-knowledge/reviews/doc-quality-review.md | 2026-06-13 | Could be referenced from docs/offline-knowledge/README.md (already is) |
|
||||
| docs/offline-knowledge/reviews/link-coverage-review.md | 2026-06-13 | Could be referenced from docs/offline-knowledge/README.md (already is) |
|
||||
| docs/offline-knowledge/reviews/external-docs-review.md | 2026-06-13 | Could be referenced from docs/offline-knowledge/README.md (already is) |
|
||||
| docs/superpowers/specs/*.md (6 files) | 2026-05-28 to 2026-06-09 | Internal planning docs; not expected in DV tree |
|
||||
| docs/superpowers/plans/*.md (8 files) | 2026-05-28 to 2026-06-08 | Internal planning docs; not expected in DV tree |
|
||||
|
||||
**Note:** The offline-knowledge files are self-referencing within their own README but are not linked from the main documentation tree. The superpowers files are internal planning documents and are intentionally separate from the DV document set.
|
||||
|
||||
## Suspicious External URLs
|
||||
|
||||
| File | Line | URL | Issue |
|
||||
|------|------|-----|-------|
|
||||
| docs/architecture/file-transfer-research.md | 406 | `https://git.did.science/TeaSpeak/Server/Server` | Self-hosted GitLab instance; may become unavailable. Specific branch `new-groups` commit `b54c6d4e` referenced. |
|
||||
| docs/security/license-inventory.md | 96 | `http://github.com/ejmahler/strength_reduce` | Uses HTTP instead of HTTPS for GitHub URL |
|
||||
| docs/security/flutter-license-inventory.md | various | `http://www.apache.org/licenses/` and `http://mozilla.org/MPL/2.0/` | HTTP URLs in license text bodies (not navigational links) |
|
||||
|
||||
**Note:** The file-transfer research links point to specific GitHub commit SHAs which may become stale over time if force-pushes occur. The HTTP-vs-HTTPS issue on the strength_reduce URL is cosmetic but should be corrected.
|
||||
|
||||
## Cross-Reference Chain Issues
|
||||
|
||||
| Chain | Issue |
|
||||
|-------|-------|
|
||||
| None found | All doc-to-doc cross-references in prose text resolve correctly |
|
||||
|
||||
All cross-reference chains verified:
|
||||
- `docs/architecture/sad.md` → `docs/srs.md` ✓
|
||||
- `docs/architecture/sad.md` → `docs/sysdes.md` ✓
|
||||
- `docs/architecture/sdd.md` → `docs/architecture/sad.md` ✓
|
||||
- `docs/architecture/sdd.md` → `docs/srs.md` ✓
|
||||
- `docs/architecture/sysdes.md` → `docs/sysdes.md` ✓
|
||||
- `docs/architecture/file-transfer-design.md` → `docs/architecture/sad.md` ✓
|
||||
- `docs/architecture/file-transfer-research.md` → `docs/architecture/file-transfer-design.md` ✓
|
||||
- `docs/architecture/file-transfer-implementation-plan.md` → both upstream docs ✓
|
||||
- `docs/architecture/desktop-ptt-architecture.md` → sad, sdd, dv-waiver-register ✓
|
||||
- `docs/requirements/sysrs.md` → `../sysrs.md` ✓
|
||||
- `docs/requirements/srs.md` → `../srs.md` ✓
|
||||
- `docs/ui-ux/material3-guideline.md` → `docs/material3-guideline.md` ✓
|
||||
|
||||
## Missing Image/Asset References
|
||||
|
||||
No image references (``) found in any documentation files. All docs are text-only.
|
||||
|
||||
## Include/Import References
|
||||
|
||||
No include directives or template references found in documentation files.
|
||||
|
||||
---
|
||||
|
||||
## Per-File Link Inventory
|
||||
|
||||
### README.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 130 | `docs/architecture/desktop-ptt-architecture.md` | ✓ Valid (markdown link) |
|
||||
| 144 | `docs/governance/product-decision-register.md` | ✓ Valid (inline ref) |
|
||||
| 261 | `docs/requirements/sysrs.md` | ✓ Valid (inline ref) |
|
||||
| 262 | `docs/requirements/srs.md` | ✓ Valid (inline ref) |
|
||||
| 263 | `docs/architecture/sysdes.md` | ✓ Valid (inline ref) |
|
||||
| 264 | `docs/architecture/sad.md` | ✓ Valid (inline ref) |
|
||||
| 265 | `docs/architecture/sdd.md` | ✓ Valid (inline ref) |
|
||||
| 266 | `docs/verification/verification-master-plan.md` | ✓ Valid (inline ref) |
|
||||
| 267 | `docs/release/release-readiness-go-nogo-record.md` | ✓ Valid (inline ref) |
|
||||
| 268 | `docs/release/platform-release-policy.md` | ✓ Valid (inline ref) |
|
||||
| 269 | `docs/governance/product-decision-register.md` | ✓ Valid (inline ref) |
|
||||
| 270 | `docs/governance/traceability-matrix.md` | ✓ Valid (inline ref) |
|
||||
| 271 | `docs/security/security-privacy-legal-guideline.md` | ✓ Valid (inline ref) |
|
||||
| 312 | `docs/release/release-readiness-go-nogo-record.md` | ✓ Valid (inline ref) |
|
||||
| 349-355 | 6 security/privacy/legal doc paths | ✓ Valid (inline refs) |
|
||||
| 391 | `docs/governance/git-commit-message-convention.md` | ✓ Valid (inline ref) |
|
||||
| 428 | `LICENSE-APACHE` | ✗ **BROKEN** — file does not exist |
|
||||
| 431 | `LICENSE-MIT` | ✗ **BROKEN** — file does not exist |
|
||||
| 436 | `docs/governance/product-decision-register.md` | ✓ Valid (markdown link) |
|
||||
| 444 | `NOTICE` | ✓ Valid (markdown link) |
|
||||
| 449-451 | 3 doc paths | ✓ Valid (inline refs) |
|
||||
|
||||
### CONTRIBUTING.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 25 | `docs/governance/git-commit-message-convention.md` | ✓ Valid (inline ref) |
|
||||
|
||||
### SECURITY.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 33-38 | 6 security/privacy/legal doc paths | ✓ Valid (inline refs) |
|
||||
|
||||
### docs/architecture/sad.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 6 | `docs/srs.md` | ✓ Valid (inline ref) |
|
||||
| 7 | `docs/sysdes.md` | ✓ Valid (inline ref) |
|
||||
| 41-52 | 12 component source paths | ✓ Valid (code refs) |
|
||||
| 176 | `docs/governance/traceability-matrix.md` | ✓ Valid (inline ref) |
|
||||
|
||||
### docs/architecture/sdd.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 6 | `docs/architecture/sad.md` | ✓ Valid (inline ref) |
|
||||
| 7 | `docs/srs.md` | ✓ Valid (inline ref) |
|
||||
| 17-31 | Module source paths | ✓ Valid (code refs), except `snapshot_state_mapper.dart` listed under wrong section |
|
||||
| 35 | `crates/chanora_bridge/src/api.rs` | ✓ Valid (code ref) |
|
||||
|
||||
### docs/architecture/sysdes.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 6 | `docs/sysdes.md` | ✓ Valid (canonical pointer) |
|
||||
|
||||
### docs/architecture/desktop-ptt-architecture.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 5 | `docs/architecture/sad.md`, `docs/architecture/sdd.md`, `docs/release/dv-waiver-register.md` | ✓ Valid (inline refs) |
|
||||
|
||||
### docs/architecture/file-transfer-design.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 6 | `docs/architecture/sad.md` | ✓ Valid (inline ref) |
|
||||
| 118-137 | `tsclientlib/src/lib.rs` code references | ✓ Valid (external code refs — not locally verifiable) |
|
||||
| 737 | `crates/chanora_protocol/src/adapter.rs` | ✓ Valid (code ref) |
|
||||
|
||||
### docs/architecture/file-transfer-research.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 5 | `docs/architecture/file-transfer-design.md` | ✓ Valid (inline ref) |
|
||||
| 29-31 | GitHub commit URLs | ⚠ External — may become stale |
|
||||
| 406 | `https://git.did.science/TeaSpeak/Server/Server` | ⚠ Self-hosted GitLab — may become unavailable |
|
||||
|
||||
### docs/architecture/file-transfer-implementation-plan.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 6 | `docs/architecture/file-transfer-design.md`, `docs/architecture/file-transfer-research.md` | ✓ Valid (inline refs) |
|
||||
|
||||
### docs/governance/document-index.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 14-32 | All 18 listed document paths | ✓ Valid (inline refs) |
|
||||
|
||||
### docs/governance/traceability-matrix.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 5 | 5 primary upstream doc paths | ✓ Valid (inline refs) |
|
||||
| 25-31 | 7 source doc paths | ✓ Valid (inline refs) |
|
||||
|
||||
### docs/governance/path-migration-map.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 10-14 | 5 README path mappings | ✓ Valid (inline refs) |
|
||||
|
||||
### docs/verification/verification-master-plan.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 6 | 6 primary upstream doc paths | ✓ Valid (inline refs) |
|
||||
| 18-21 | 4 verification plan paths | ✓ Valid (inline refs) |
|
||||
| 47 | `tools/windows-smoke.md` | ✓ Valid (code ref) |
|
||||
| 48 | `docs/release/ios-build.md` | ✓ Valid (inline ref) |
|
||||
|
||||
### docs/security/license-inventory.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 9 | `../../LICENSE-APACHE` | ✗ **BROKEN** — file does not exist |
|
||||
| 10 | `../../LICENSE-MIT` | ✗ **BROKEN** — file does not exist |
|
||||
| 11 | `docs/governance/product-decision-register.md` | ✓ Valid (inline ref) |
|
||||
|
||||
### docs/security/flutter-license-inventory.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 11 | `../../LICENSE-APACHE` | ✗ **BROKEN** — file does not exist |
|
||||
| 12 | `../../LICENSE-MIT` | ✗ **BROKEN** — file does not exist |
|
||||
|
||||
### docs/security/dependency-and-supply-chain-report.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 29 | `https://github.com/EdisonJwa/oboe-rs` | ✓ Valid (external GitHub URL) |
|
||||
|
||||
### docs/privacy/privacy-policy.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| (none) | No links or references | N/A |
|
||||
|
||||
### docs/legal/trademark-and-attribution-review.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| (none) | No links or references | N/A |
|
||||
|
||||
### docs/release/release-readiness-go-nogo-record.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 37 | `docs/implementation-status-2026-05-28.md` | ✓ Valid (inline ref) |
|
||||
| 26 | `apps/chanora_flutter/pubspec.yaml` | ✓ Valid (code ref) |
|
||||
|
||||
### docs/release/dv-waiver-register.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 15-23 | Various `docs/` paths in Source evidence column | ✓ Valid (inline refs) |
|
||||
|
||||
### docs/requirements/sysrs.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 4 | `../sysrs.md` | ✓ Valid (canonical pointer) |
|
||||
|
||||
### docs/requirements/srs.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 4 | `../srs.md` | ✓ Valid (canonical pointer) |
|
||||
|
||||
### docs/ui-ux/material3-guideline.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 6 | `docs/material3-guideline.md` | ✓ Valid (canonical pointer) |
|
||||
|
||||
### docs/material3-guideline.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 10 | `docs/ui-ux/material3-guideline.md` | ✓ Valid (self-referencing path record) |
|
||||
|
||||
### docs/sysrs.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 126 | `docs/chanora_SysDes.md` | ⚠ Hypothetical — does not exist (aspirational name) |
|
||||
| 127 | `docs/chanora_SRS.md` | ⚠ Hypothetical — does not exist (aspirational name) |
|
||||
| 128 | `docs/chanora_SAD.md` | ⚠ Hypothetical — does not exist (aspirational name) |
|
||||
| 129 | `docs/chanora_SDD.md` | ⚠ Hypothetical — does not exist (aspirational name) |
|
||||
| 130 | `docs/chanora_Verification.md` | ⚠ Hypothetical — does not exist (aspirational name) |
|
||||
|
||||
### apps/chanora_flutter/README.md
|
||||
|
||||
| Line | Link | Status |
|
||||
|------|------|--------|
|
||||
| 11 | `https://docs.flutter.dev/get-started/learn-flutter` | ✓ Valid (external) |
|
||||
| 12 | `https://docs.flutter.dev/get-started/codelab` | ✓ Valid (external) |
|
||||
| 13 | `https://docs.flutter.dev/reference/learning-resources` | ✓ Valid (external) |
|
||||
| 16 | `https://docs.flutter.dev/` | ✓ Valid (external) |
|
||||
|
||||
---
|
||||
|
||||
## Action Items (Priority Order)
|
||||
|
||||
### P0 — Must Fix Before Any Release
|
||||
1. **Create `LICENSE-APACHE` and `LICENSE-MIT` files** at repo root. These are required by DEC-020 (dual-license model) and referenced by README.md, docs/security/license-inventory.md, and docs/security/flutter-license-inventory.md.
|
||||
|
||||
### P1 — Should Fix for DV Quality
|
||||
2. **Fix `snapshot_state_mapper.dart` categorization** in docs/architecture/sdd.md:19 — move from "Snapshot and channel UI" widgets section to service layer section, or add a note clarifying the actual location.
|
||||
|
||||
### P2 — Nice to Have
|
||||
3. **Add offline-knowledge docs to document index** or references section so they are discoverable.
|
||||
4. **Fix HTTP URL** in docs/security/license-inventory.md:96 (`http://github.com/ejmahler/strength_reduce` → `https://...`).
|
||||
5. **Clean up hypothetical file names** in docs/sysrs.md:124-131 — either remove the table or clearly mark as historical/aspirational.
|
||||
@@ -0,0 +1,187 @@
|
||||
# Documentation Out-of-Date Analysis
|
||||
|
||||
**Generated:** 2026-06-13
|
||||
**Workspace version:** 0.2.0-beta.1
|
||||
**Latest commit:** dd6e80f (2026-06-13)
|
||||
|
||||
## Summary
|
||||
- Total docs checked: 64
|
||||
- Outdated docs: 12
|
||||
- Stale version refs: 5
|
||||
- Undocumented recent changes: 8
|
||||
- Stale date refs: 15+
|
||||
|
||||
## Stale Version References
|
||||
|
||||
| File | Line | Version Referenced | Current Version | Drift |
|
||||
|------|------|-------------------|-----------------|-------|
|
||||
| `docs/sysdes.md` | 6 | 0.9.8 | N/A (doc version) | Last updated 2026-05-14, 30 days stale |
|
||||
| `docs/srs.md` | 7 | 0.9.9 | N/A (doc version) | Last updated 2026-05-18, 26 days stale |
|
||||
| `docs/sysrs.md` | 5 | 0.9.11 | N/A (doc version) | Last updated 2026-06-07, 6 days stale |
|
||||
| `docs/material3-guideline.md` | ~4 | 0.9.2 | N/A (doc version) | Last updated 2026-05-14, 30 days stale |
|
||||
| `tools/windows-smoke.md` | 6 | `product/scaffold-v0` branch | `main` | Default branch changed per CHANGELOG |
|
||||
|
||||
## Stale Date References
|
||||
|
||||
| File | Date | Age | Issue |
|
||||
|------|------|-----|-------|
|
||||
| `docs/implementation-status-2026-05-28.md` | 2026-05-28 | 16 days | Pre-dates DV baseline (2026-05-29) and 8 major feature PRs |
|
||||
| `docs/architecture/sad.md` | 2026-05-29 | 15 days | Missing file transfer, poke notifications, desktop VAD features |
|
||||
| `docs/architecture/sdd.md` | 2026-05-29 | 15 days | Missing file transfer, poke notifications, desktop VAD features |
|
||||
| `docs/verification/verification-master-plan.md` | 2026-05-29 | 15 days | Missing file transfer and poke notification verification |
|
||||
| `docs/verification/swe4-unit-verification-plan.md` | 2026-05-29 | 15 days | Missing new test coverage for file transfer |
|
||||
| `docs/verification/swe5-software-integration-verification-plan.md` | 2026-05-29 | 15 days | Missing file transfer integration verification |
|
||||
| `docs/verification/swe6-software-verification-plan.md` | 2026-05-29 | 15 days | Missing file transfer software verification |
|
||||
| `docs/verification/sys4-system-integration-verification-plan.md` | 2026-05-29 | 15 days | Missing file transfer system verification |
|
||||
| `docs/governance/product-decision-register.md` | 2026-05-29 | 15 days | Missing file-transfer-related decisions |
|
||||
| `docs/governance/document-index.md` | 2026-05-29 | 15 days | Missing file-transfer-design.md, file-transfer-research.md, file-transfer-implementation-plan.md |
|
||||
| `docs/security/security-privacy-legal-guideline.md` | 2026-05-29 | 15 days | Missing file transfer security considerations |
|
||||
| `docs/security/threat-model.md` | 2026-05-29 | 15 days | Missing file transfer threat analysis |
|
||||
| `docs/i18n/localization-architecture.md` | 2026-05-29 | 15 days | Missing poke notification l10n strings |
|
||||
| `docs/legal/trademark-and-attribution-review.md` | 2026-05-29 | 15 days | Missing cacache license review |
|
||||
| `docs/privacy/privacy-policy.md` | 2026-05-29 | 15 days | Missing file transfer data handling |
|
||||
|
||||
## Undocumented Recent Changes
|
||||
|
||||
| Change | Date | Expected Doc | Status |
|
||||
|--------|------|-------------|--------|
|
||||
| File transfer system (avatar/icon download with cacache) | 2026-06-10 | README.md, SAD, SDD, CHANGELOG | Not in README crate list, not in CHANGELOG |
|
||||
| Poke notifications (local notifications, settings, bridge) | 2026-06-08 | SAD, SDD, CHANGELOG | Not in CHANGELOG |
|
||||
| Desktop Silero ONNX VAD + Windows PTT modernization | 2026-06-09 | SAD, SDD, CHANGELOG | Not in CHANGELOG |
|
||||
| iOS RemoteIO+WebRTC APM path removal | 2026-06-10 | SAD, SDD | Not documented |
|
||||
| SonoraExperimental bridge API removal | 2026-06-10 | SAD, SDD, bridge docs | Not documented |
|
||||
| iOS AVAudioSession activation fix | 2026-06-10 | Platform docs | Not documented |
|
||||
| iOS Debug build unblocking + FRB regeneration | 2026-06-10 | Build docs | Not documented |
|
||||
| poke-without-message design | 2026-06-09 | Design docs | Committed but not indexed |
|
||||
|
||||
## Feature Drift
|
||||
|
||||
### Documented but No Longer in Code
|
||||
| Feature | Doc File | Last Seen In Code |
|
||||
|---------|----------|-------------------|
|
||||
| `SonoraExperimental` bridge API | `docs/architecture/sdd.md` (implied) | Removed 2026-06-10 (commit 2b28549) |
|
||||
| iOS `ios_raw_unit.rs` | `docs/implementation-status-2026-05-28.md:33` | Removed 2026-06-10 (commit 3f9ea4f) |
|
||||
| `SnapshotChanged` event variant | CHANGELOG v0.3.0 | Removed end-to-end |
|
||||
| Timer-based snapshot polling | CHANGELOG v0.2.0-beta.1 | Replaced by event-driven UI |
|
||||
|
||||
### In Code but Not Documented
|
||||
| Feature | Code Location | Expected Doc |
|
||||
|---------|--------------|-------------|
|
||||
| `chanora_cache` crate (cacache-backed blob store) | `crates/chanora_cache/` | README.md crate list, SAD, SDD |
|
||||
| File transfer protocol support | `crates/chanora_protocol/` | SAD, SDD, CHANGELOG |
|
||||
| Poke notification service | `apps/chanora_flutter/lib/services/` | SAD, SDD, CHANGELOG |
|
||||
| Poke notification settings UI | `apps/chanora_flutter/lib/widgets/` | SAD, SDD |
|
||||
| Desktop Silero ONNX VAD | `crates/chanora_audio/src/vad/silero_onnx.rs` | SAD, SDD |
|
||||
| Windows PTT modernization | `crates/chanora_audio/src/ptt_backends/windows.rs` | SAD, SDD |
|
||||
| `poke_limiter.rs` | `crates/chanora_protocol/src/poke_limiter.rs` | SAD, SDD |
|
||||
| Local notification plugin integration | `apps/chanora_flutter/` | SAD, SDD |
|
||||
|
||||
## Per-File Out-of-Date Assessment
|
||||
|
||||
### README.md
|
||||
- **Last meaningful update:** Unknown (no date in file)
|
||||
- **Stale sections:**
|
||||
- Crate list (line 242-249): Missing `chanora_cache` and `chanora_resolver` crates
|
||||
- Repository layout (line 233-249): Missing `chanora_cache`, `chanora_resolver`, `chanora_prefetch`
|
||||
- Status section (line 16-23): References "v0.9.x document set" — no specific date
|
||||
- Development section (line 398-409): Missing `just` commands (justfile exists)
|
||||
- **Missing recent changes:** File transfer system, poke notifications, desktop VAD
|
||||
|
||||
### docs/sysdes.md
|
||||
- **Version:** 0.9.8
|
||||
- **Last change record:** 2026-05-14
|
||||
- **Stale sections:** All — 30 days without update
|
||||
- **Missing:** File transfer system element (SE-20?), poke notification interface (IF-015?)
|
||||
|
||||
### docs/srs.md
|
||||
- **Version:** 0.9.9
|
||||
- **Last change record:** 2026-05-18
|
||||
- **Stale sections:** All — 26 days without update
|
||||
- **Missing:** File transfer SRS requirements, poke notification SRS requirements, desktop VAD SRS requirements
|
||||
|
||||
### docs/sysrs.md
|
||||
- **Version:** 0.9.11
|
||||
- **Last change record:** 2026-06-07
|
||||
- **Stale sections:** Mostly current but missing file transfer and poke notification requirements
|
||||
|
||||
### docs/architecture/sad.md
|
||||
- **Date:** 2026-05-29
|
||||
- **Stale sections:**
|
||||
- Component architecture table (line 39-50): Missing `chanora_cache` component
|
||||
- Missing file transfer architecture
|
||||
- Missing poke notification architecture
|
||||
- Missing desktop VAD architecture
|
||||
- **Missing recent changes:** All PRs from 2026-06-07 through 2026-06-13
|
||||
|
||||
### docs/architecture/sdd.md
|
||||
- **Date:** 2026-05-29
|
||||
- **Stale sections:**
|
||||
- Module catalogue (line 15-31): Missing file transfer module, poke notification module
|
||||
- Missing `chanora_cache` module (SDD-MOD-016?)
|
||||
- Missing `poke_limiter` module
|
||||
- **Missing recent changes:** All PRs from 2026-06-07 through 2026-06-13
|
||||
|
||||
### docs/implementation-status-2026-05-28.md
|
||||
- **Date:** 2026-05-28 — 16 days old
|
||||
- **Stale sections:**
|
||||
- "Done" table: Missing file transfer, poke notifications, desktop VAD, iOS fixes
|
||||
- "Partial / Scaffold Only": `chanora_cache` was scaffold, now implemented
|
||||
- "Not Done (P0 blockers)": iOS `AVAudioSession.Mode.voiceChat` — now implemented (commit 89bbfa1)
|
||||
- Android target compilation: Still blocked per DEC-034
|
||||
- Agent spec docs reference (line 140): States docs are "deleted" — stale cleanup note
|
||||
- **Recommendation:** Update to reflect current state or create new status doc
|
||||
|
||||
### docs/governance/product-decision-register.md
|
||||
- **Date:** 2026-05-29
|
||||
- **Note:** DEC-033 and DEC-034 are present (lines 20-21). Missing decisions:
|
||||
- File transfer architecture decision
|
||||
- Poke notification feature decision
|
||||
|
||||
### docs/governance/document-index.md
|
||||
- **Date:** 2026-05-29
|
||||
- **Missing documents:**
|
||||
- `docs/architecture/file-transfer-design.md`
|
||||
- `docs/architecture/file-transfer-research.md`
|
||||
- `docs/architecture/file-transfer-implementation-plan.md`
|
||||
- `docs/superpowers/specs/2026-06-09-poke-without-message-design.md`
|
||||
|
||||
### docs/security/license-inventory.md
|
||||
- **Status:** Refreshed 2026-06-09 (commit b841d3f)
|
||||
- **Issue:** May be missing `cacache` dependency license if not in Cargo.lock at refresh time
|
||||
|
||||
### docs/material3-guideline.md
|
||||
- **Version:** 0.9.2
|
||||
- **Last change record:** 2026-05-14
|
||||
- **Status:** 30 days stale, but Material 3 design may not have changed
|
||||
|
||||
### tools/windows-smoke.md
|
||||
- **Stale reference:** Line 6 references `product/scaffold-v0` branch
|
||||
- **Current default:** `main` per CHANGELOG v0.3.0
|
||||
|
||||
### docs/verification/*.md (all 5 files)
|
||||
- **Date:** All dated 2026-05-29
|
||||
- **Missing:** File transfer verification, poke notification verification, desktop VAD verification
|
||||
|
||||
## Recommendations
|
||||
|
||||
### Critical (blocks DV/release)
|
||||
1. **Update `docs/implementation-status-2026-05-28.md`** — 16 days stale, missing 8 major PRs, iOS voiceChat now implemented
|
||||
2. **Update `docs/governance/product-decision-register.md`** — Missing file transfer and poke notification decisions
|
||||
3. **Update `docs/governance/document-index.md`** — Missing 3 file-transfer docs
|
||||
|
||||
### High Priority (DV completeness)
|
||||
4. **Update `docs/architecture/sad.md`** — Missing file transfer, poke notifications, desktop VAD, chanora_cache component
|
||||
5. **Update `docs/architecture/sdd.md`** — Missing file transfer, poke notifications, desktop VAD modules
|
||||
6. **Update `docs/sysdes.md`** — 30 days stale, missing file transfer system elements
|
||||
7. **Update `docs/srs.md`** — 26 days stale, missing file transfer and poke notification requirements
|
||||
8. **Update CHANGELOG.md** — Missing v0.3.0+ changes (file transfer, poke notifications, desktop VAD, iOS fixes)
|
||||
|
||||
### Medium Priority (accuracy)
|
||||
9. **Update README.md** — Missing `chanora_cache` and `chanora_resolver` in crate list
|
||||
10. **Update `tools/windows-smoke.md`** — Fix stale branch reference
|
||||
11. **Update verification plans** — Add file transfer and poke notification verification
|
||||
12. **Update security docs** — Add file transfer threat analysis
|
||||
|
||||
### Low Priority (cleanup)
|
||||
13. **Align document versions** — SysDes (0.9.8), SysRS (0.9.11), Material3 (0.9.2) have different version numbers
|
||||
14. **Clean up path record files** — `docs/architecture/sysdes.md`, `docs/requirements/sysrs.md`, `docs/requirements/srs.md`, `docs/ui-ux/material3-guideline.md` are stubs pointing to canonical files
|
||||
@@ -0,0 +1,611 @@
|
||||
# Chanora Function Inventory
|
||||
|
||||
> Auto-generated comprehensive inventory of all public APIs across 10 Rust crates and 50+ Dart files.
|
||||
|
||||
## Summary Statistics
|
||||
|
||||
| Category | Count |
|
||||
|----------|-------|
|
||||
| **Rust Crates** | 10 |
|
||||
| **Rust pub fn** | ~180 |
|
||||
| **Rust pub struct** | ~90 |
|
||||
| **Rust pub enum** | ~50 |
|
||||
| **Rust pub trait** | 6 |
|
||||
| **Rust pub const** | ~30 |
|
||||
| **Dart files** | 56 |
|
||||
| **Dart public classes** | ~80 |
|
||||
| **TODO/FIXME comments** | 15 |
|
||||
| **Empty/commented stubs** | 0 |
|
||||
|
||||
---
|
||||
|
||||
## Rust Crates
|
||||
|
||||
### 1. `chanora_cache` — Content-Addressed Blob Cache
|
||||
|
||||
Disposable blob cache for avatar/icon files. Wraps `cacache` for crash safety.
|
||||
|
||||
| Kind | Name | File:Line | Purpose |
|
||||
|------|------|-----------|---------|
|
||||
| struct | `BlobCache` | lib.rs:30 | Content-addressed blob cache backed by cacache |
|
||||
| enum | `BlobCacheError` | lib.rs:20 | Errors raised by blob cache (Io, InvalidKey) |
|
||||
| const | `PREFIX_AVATAR` | lib.rs:37 | Avatar blob prefix `"av_"` |
|
||||
| const | `PREFIX_ICON` | lib.rs:39 | Icon blob prefix `"ic_"` |
|
||||
| fn | `BlobCache::new` | lib.rs:46 | Create/open cache rooted at `cache_dir/chanora/` |
|
||||
| fn | `BlobCache::put` | lib.rs:63 | Store a blob with prefix+key |
|
||||
| fn | `BlobCache::get` | lib.rs:80 | Read a blob (returns None if missing) |
|
||||
| fn | `BlobCache::remove` | lib.rs:101 | Delete a specific blob |
|
||||
| fn | `BlobCache::clear` | lib.rs:111 | Delete all blobs |
|
||||
| fn | `BlobCache::total_size` | lib.rs:129 | Return total bytes used |
|
||||
| fn | `BlobCache::evict` | lib.rs:154 | Evict oldest entries until under max_bytes |
|
||||
|
||||
**Dead code:** None found. All public items consumed by `chanora_core`.
|
||||
|
||||
---
|
||||
|
||||
### 2. `chanora_protocol` — TeamSpeak Protocol Adapter
|
||||
|
||||
Isolates `tsclientlib` behind a typed boundary. No upstream types leak.
|
||||
|
||||
| Kind | Name | File:Line | Purpose |
|
||||
|------|------|-----------|---------|
|
||||
| struct | `ConnectConfig` | adapter.rs:146 | Typed connection parameters |
|
||||
| struct | `ProtocolClient` | adapter.rs:236 | Async handle owning live protocol connection |
|
||||
| struct | `InboundVoice` | adapter.rs:260 | One inbound voice packet from remote client |
|
||||
| struct | `SnapshotProbe` | adapter.rs:270 | Clone-free probe handle for watchdog |
|
||||
| struct | `ChannelInfo` | dto.rs:19 | One channel in server tree |
|
||||
| struct | `ClientInfo` | dto.rs:72 | One connected client |
|
||||
| struct | `ClientProfile` | dto.rs:96 | Rich profile + live connection details |
|
||||
| struct | `ServerSnapshot` | dto.rs:153 | Full server state snapshot |
|
||||
| struct | `ChatMessage` | dto.rs:50 | In-channel text message |
|
||||
| struct | `ServerActivity` | dto.rs:65 | Server-activity notification |
|
||||
| struct | `ChannelId` | dto.rs:11 | Opaque channel identifier (u64 newtype) |
|
||||
| struct | `ClientId` | dto.rs:15 | Opaque client identifier (u64 newtype) |
|
||||
| struct | `PokeLimiter` | poke_limiter.rs:19 | Per-connection poke rate limiter |
|
||||
| enum | `ProtocolError` | lib.rs:62 | Typed error catalogue (10 variants) |
|
||||
| enum | `ProtocolDelta` | dto.rs:183 | Incremental state changes (7 variants) |
|
||||
| enum | `DisconnectReason` | adapter.rs:225 | Why protocol task ended |
|
||||
| enum | `MessageTarget` | dto.rs:37 | Text message target scope |
|
||||
| enum | `PokeStrength` | poke_limiter.rs:8 | Poke notification strength |
|
||||
| trait | *(re-exports)* | lib.rs:52 | `AudioData`, `CodecType`, `Direction`, `InAudioBuf`, `OutAudio`, `OutPacket` |
|
||||
| fn | `ProtocolClient::generate_identity` | adapter.rs:296 | Generate fresh TS3 identity string |
|
||||
| fn | `ProtocolClient::connect` | adapter.rs:304 | Dial server, wait for initial snapshot |
|
||||
| fn | `ProtocolClient::snapshot` | adapter.rs:354 | Read typed server state snapshot |
|
||||
| fn | `ProtocolClient::client_profile` | adapter.rs:365 | Fetch rich client profile |
|
||||
| fn | `ProtocolClient::download_avatar` | adapter.rs:389 | Download avatar bytes by UID |
|
||||
| fn | `ProtocolClient::download_icon` | adapter.rs:394 | Download icon bytes by ID |
|
||||
| fn | `ProtocolClient::disconnect` | adapter.rs:399 | Clean disconnect |
|
||||
| fn | `ProtocolClient::move_to_channel` | adapter.rs:421 | Move self to channel |
|
||||
| fn | `ProtocolClient::queue_move_to_channel` | adapter.rs:442 | Fire-and-forget move |
|
||||
| fn | `ProtocolClient::set_muted` | adapter.rs:458 | Update own mute state |
|
||||
| fn | `ProtocolClient::voice_out` | adapter.rs:477 | Get outbound voice sender |
|
||||
| fn | `ProtocolClient::snapshot_probe` | adapter.rs:485 | Get watchdog probe handle |
|
||||
| fn | `ProtocolClient::take_voice_in` | adapter.rs:493 | Take inbound voice receiver |
|
||||
| fn | `ProtocolClient::put_voice_in` | adapter.rs:503 | Put voice receiver back |
|
||||
| fn | `ProtocolClient::take_loss_notifier` | adapter.rs:519 | Take disconnect notifier |
|
||||
| fn | `ProtocolClient::take_chat_rx` | adapter.rs:525 | Take chat receiver |
|
||||
| fn | `ProtocolClient::put_chat_rx` | adapter.rs:530 | Put chat receiver back |
|
||||
| fn | `ProtocolClient::take_activity_rx` | adapter.rs:540 | Take activity receiver |
|
||||
| fn | `ProtocolClient::put_activity_rx` | adapter.rs:545 | Put activity receiver back |
|
||||
| fn | `ProtocolClient::take_delta_rx` | adapter.rs:556 | Take delta receiver |
|
||||
| fn | `ProtocolClient::send_text_message` | adapter.rs:561 | Send text message |
|
||||
| fn | `SnapshotProbe::probe` | adapter.rs:278 | Issue single snapshot RPC |
|
||||
| fn | `PokeLimiter::new` | poke_limiter.rs:36 | Create limiter with 5-min window |
|
||||
| fn | `PokeLimiter::record` | poke_limiter.rs:44 | Record poke, return strength |
|
||||
| fn | `ChannelId::ROOT` | dto.rs:176 | Root channel constant |
|
||||
|
||||
**Dead code:** None found. All items consumed by `chanora_core`.
|
||||
|
||||
---
|
||||
|
||||
### 3. `chanora_bridge` — Flutter/Rust Bridge
|
||||
|
||||
Typed DTOs and commands for `flutter_rust_bridge` 2.x.
|
||||
|
||||
| Kind | Name | File:Line | Purpose |
|
||||
|------|------|-----------|---------|
|
||||
| struct | `BridgeChannel` | api.rs:404 | Channel DTO for Dart |
|
||||
| struct | `BridgeClient` | api.rs:422 | Client DTO for Dart |
|
||||
| struct | `BridgeClientProfile` | api.rs:445 | Rich profile DTO for Dart |
|
||||
| struct | `BridgeSnapshot` | api.rs:502 | Server snapshot DTO for Dart |
|
||||
| struct | `BridgeAudioStats` | api.rs:1034 | Audio engine statistics |
|
||||
| struct | `BridgeAudioProcessingConfig` | api.rs:1112 | Audio processing config DTO |
|
||||
| struct | `BridgeAudioProcessingStats` | api.rs:1143 | Audio processing stats DTO |
|
||||
| struct | `BridgePttDescriptor` | api.rs:854 | PTT capability descriptor |
|
||||
| struct | `BridgePttBinding` | api.rs:875 | PTT binding display state |
|
||||
| enum | `BridgeError` | lib.rs:55 | Bridge-layer errors (7 variants) |
|
||||
| enum | `BridgeTransmitMode` | api.rs:731 | Transmit mode mirror |
|
||||
| enum | `BridgePttInputClass` | api.rs:843 | PTT input class |
|
||||
| enum | `BridgeAudioRoute` | api.rs:1047 | Audio route class |
|
||||
| enum | `BridgeIosVoiceProcessingMode` | api.rs:1064 | iOS voice processing mode |
|
||||
| enum | `BridgeAudioBackend` | api.rs:1071 | Processing backend |
|
||||
| enum | `BridgeVadBackend` | api.rs:1084 | VAD backend |
|
||||
| enum | `BridgeEffectOwner` | api.rs:1097 | AEC/NS/AGC owner |
|
||||
| fn | `bridge_init` | api.rs:220 | One-time process init (FRB init) |
|
||||
| fn | `log_file_path_str` | api.rs:294 | Platform log file path |
|
||||
| fn | `connect` | api.rs:601 | Connect to TS3 server |
|
||||
| fn | `prefetch_server` | api.rs:636 | Warm DNS resolution |
|
||||
| fn | `snapshot` | api.rs:645 | Re-fetch server snapshot |
|
||||
| fn | `client_profile` | api.rs:654 | Fetch client profile |
|
||||
| fn | `disconnect` | api.rs:663 | Disconnect from server |
|
||||
| fn | `is_connected` | api.rs:672 | Check connection status |
|
||||
| fn | `handle_route_change` | api.rs:687 | iOS route change handler |
|
||||
| fn | `handle_media_services_reset_with_route` | api.rs:696 | iOS media reset handler |
|
||||
| fn | `handle_interruption_began` | api.rs:703 | iOS interruption begin |
|
||||
| fn | `handle_interruption_ended` | api.rs:709 | iOS interruption end |
|
||||
| fn | `set_ptt` | api.rs:718 | Set PTT active state |
|
||||
| fn | `voice_join` | api.rs:770 | Join voice channel |
|
||||
| fn | `voice_leave` | api.rs:785 | Leave voice channel |
|
||||
| fn | `set_transmit_mode` | api.rs:794 | Set transmit mode |
|
||||
| fn | `get_transmit_mode` | api.rs:803 | Get transmit mode |
|
||||
| fn | `set_release_tail_ms` | api.rs:814 | Set release tail |
|
||||
| fn | `get_release_tail_ms` | api.rs:823 | Get release tail |
|
||||
| fn | `set_hard_mute` | api.rs:832 | Engage/release hard mute |
|
||||
| fn | `set_ptt_binding` | api.rs:907 | Update PTT binding |
|
||||
| fn | `ptt_descriptor` | api.rs:925 | Get PTT descriptor |
|
||||
| fn | `get_ptt_binding` | api.rs:941 | Get persisted PTT binding |
|
||||
| fn | `move_to_channel` | api.rs:955 | Move self to channel |
|
||||
| fn | `set_input_muted` | api.rs:971 | Toggle input mute |
|
||||
| fn | `set_output_muted` | api.rs:983 | Toggle output mute |
|
||||
| fn | `set_output_gain` | api.rs:994 | Set master output gain |
|
||||
| fn | `set_client_volume` | api.rs:1006 | Set per-client volume |
|
||||
| fn | `send_chat_message` | api.rs:1015 | Send text message |
|
||||
| fn | `export_diagnostics` | api.rs:1392 | User-initiated diagnostic export |
|
||||
|
||||
**Dead code:** `publish_permission_state` is `#[cfg_attr(not(target_os = "android"), allow(dead_code))]` — intentional, only used on Android via JNI.
|
||||
|
||||
---
|
||||
|
||||
### 4. `chanora_storage` — Identity & Bookmark Storage
|
||||
|
||||
SQLite bookmarks + ChaCha20-Poly1305 encrypted identity file.
|
||||
|
||||
| Kind | Name | File:Line | Purpose |
|
||||
|------|------|-----------|---------|
|
||||
| struct | `IdentityFileStore` | lib.rs:176 | Encrypted identity file store |
|
||||
| struct | `BookmarkRepository` | lib.rs:792 | SQLite-backed bookmark store |
|
||||
| struct | `Bookmark` | lib.rs:767 | A persisted bookmark |
|
||||
| struct | `PttBindingMeta` | lib.rs:117 | PTT binding metadata |
|
||||
| enum | `StorageError` | lib.rs:59 | Storage errors (6 variants) |
|
||||
| trait | `Crypto` | lib.rs:673 | Envelope encryption abstraction |
|
||||
| const | `KEYRING_SERVICE` | lib.rs:190 | Keyring service name `"chanora"` |
|
||||
| fn | `IdentityFileStore::new` | lib.rs:194 | Construct store at directory |
|
||||
| fn | `IdentityFileStore::path` | lib.rs:211 | Get identity file path |
|
||||
| fn | `IdentityFileStore::crypto` | lib.rs:384 | Get DekCrypto helper |
|
||||
| fn | `IdentityFileStore::load` | lib.rs:390 | Read persisted identity |
|
||||
| fn | `IdentityFileStore::save` | lib.rs:449 | Persist identity (encrypted) |
|
||||
| fn | `IdentityFileStore::set_transmit_mode` | lib.rs:520 | Persist transmit mode |
|
||||
| fn | `IdentityFileStore::get_transmit_mode` | lib.rs:528 | Read transmit mode |
|
||||
| fn | `IdentityFileStore::set_release_tail_ms` | lib.rs:534 | Persist release tail |
|
||||
| fn | `IdentityFileStore::get_release_tail_ms` | lib.rs:542 | Read release tail |
|
||||
| fn | `IdentityFileStore::set_ptt_binding` | lib.rs:554 | Persist PTT binding |
|
||||
| fn | `IdentityFileStore::get_ptt_binding` | lib.rs:568 | Read PTT binding |
|
||||
| fn | `IdentityFileStore::clear` | lib.rs:579 | Remove persisted identity |
|
||||
| fn | `BookmarkRepository::new` | lib.rs:801 | Open DB without encryption |
|
||||
| fn | `BookmarkRepository::with_crypto` | lib.rs:807 | Open DB with password encryption |
|
||||
| fn | `BookmarkRepository::encrypts_passwords` | lib.rs:858 | Check if encryption wired |
|
||||
| fn | `BookmarkRepository::add` | lib.rs:865 | Insert bookmark |
|
||||
| fn | `BookmarkRepository::upsert_or_add` | lib.rs:891 | Insert or update by host |
|
||||
| fn | `BookmarkRepository::update` | lib.rs:935 | Replace existing bookmark |
|
||||
| fn | `BookmarkRepository::delete` | lib.rs:963 | Delete bookmark by id |
|
||||
| fn | `BookmarkRepository::list` | lib.rs:976 | List all bookmarks |
|
||||
|
||||
**Dead code:** None found.
|
||||
|
||||
---
|
||||
|
||||
### 5. `chanora_state` — Server State Mirror
|
||||
|
||||
Authoritative client-side mirror of server state with deterministic reducers.
|
||||
|
||||
| Kind | Name | File:Line | Purpose |
|
||||
|------|------|-----------|---------|
|
||||
| struct | `ServerState` | lib.rs:62 | Authoritative server state mirror |
|
||||
| struct | `Reduction` | lib.rs:266 | Result of applying one event |
|
||||
| struct | `ChannelJoinState` | channel_join.rs:53 | Channel-join reducer state |
|
||||
| struct | `AuthoritativeMembership` | channel_join.rs:27 | Server-confirmed membership |
|
||||
| struct | `JoinPending` | channel_join.rs:36 | Active pending join intent |
|
||||
| struct | `ChannelJoinProjection` | channel_join.rs:135 | Reducer projection for UI |
|
||||
| struct | `JoinOutcomeKey` | channel_join.rs:124 | Correlation key for outcomes |
|
||||
| struct | `ConnectionEpoch` | channel_join.rs:15 | Per-connection epoch |
|
||||
| struct | `JoinGeneration` | channel_join.rs:19 | Monotonic join generation |
|
||||
| struct | `JoinRequestId` | channel_join.rs:23 | Protocol request identifier |
|
||||
| struct | `ChannelId` (join) | channel_join.rs:11 | Channel identifier at reducer seam |
|
||||
| enum | `ConnectionState` | lib.rs:43 | Connection lifecycle (5 variants) |
|
||||
| enum | `Delta` | lib.rs:209 | State changes for bridge (8 variants) |
|
||||
| enum | `StateEvent` | lib.rs:237 | Events flowing into reducer (9 variants) |
|
||||
| enum | `StateError` | lib.rs:32 | State errors (2 variants) |
|
||||
| enum | `ChannelJoinEvent` | channel_join.rs:165 | Channel-join events (10 variants) |
|
||||
| enum | `ChannelJoinAction` | channel_join.rs:240 | Side-effect actions (7 variants) |
|
||||
| enum | `ChannelJoinSyncState` | channel_join.rs:101 | Sync readiness (2 variants) |
|
||||
| enum | `SyncReason` | channel_join.rs:115 | Sync reason (2 variants) |
|
||||
| enum | `JoinReduceStatus` | channel_join.rs:309 | Transition status (9 variants) |
|
||||
| enum | `JoinIntentRejected` | channel_join.rs:332 | Rejection reasons (2 variants) |
|
||||
| enum | `JoinFailureKind` | channel_join.rs:341 | Failure kinds (5 variants) |
|
||||
| enum | `JoinErrorCode` | channel_join.rs:356 | Stable error codes (11 variants) |
|
||||
| enum | `JoinDiagnosticKey` | channel_join.rs:284 | Diagnostic event keys (10 variants) |
|
||||
| enum | `AuthoritativeSource` | channel_join.rs:156 | Membership input source |
|
||||
| fn | `ServerState::from_snapshot` | lib.rs:83 | Build from initial snapshot |
|
||||
| fn | `ServerState::replace_from_snapshot` | lib.rs:114 | Replace with fresh snapshot |
|
||||
| fn | `ServerState::channel` | lib.rs:119 | Look up channel by id |
|
||||
| fn | `ServerState::client` | lib.rs:124 | Look up client by id |
|
||||
| fn | `ServerState::channels` | lib.rs:130 | All channels iterator |
|
||||
| fn | `ServerState::clients` | lib.rs:141 | All clients iterator |
|
||||
| fn | `ServerState::channel_count` | lib.rs:148 | Number of channels |
|
||||
| fn | `ServerState::client_count` | lib.rs:153 | Number of clients |
|
||||
| fn | `ServerState::own_channel` | lib.rs:158 | Own client's channel |
|
||||
| fn | `ServerState::clients_in_channel` | lib.rs:164 | Clients in specific channel |
|
||||
| fn | `reduce` | lib.rs:281 | Apply StateEvent to state |
|
||||
| fn | `reduce_reconnect_snapshot` | lib.rs:409 | Replace state after reconnect |
|
||||
| fn | `channel_join::reduce` | channel_join.rs:393 | Channel-join event reducer |
|
||||
| fn | `channel_join::project` | channel_join.rs:670 | Build channel-join projection |
|
||||
| fn | `ChannelJoinState::new` | channel_join.rs:68 | Create join state for epoch |
|
||||
|
||||
**Dead code:** None found.
|
||||
|
||||
---
|
||||
|
||||
### 6. `chanora_audio` — Audio Subsystem
|
||||
|
||||
Platform capture/playback, Opus encoding, VAD, PTT, DSP.
|
||||
|
||||
| Kind | Name | File:Line | Purpose |
|
||||
|------|------|-----------|---------|
|
||||
| **Core Engine** | | | |
|
||||
| struct | `AudioEngine` | engine.rs:289 | Main audio engine |
|
||||
| struct | `AudioEngineConfig` | engine.rs:223 | Engine configuration |
|
||||
| struct | `SessionAudioId` | engine.rs:69 | Stable audio session ID |
|
||||
| struct | `AudioDeviceList` | engine.rs:87 | Available audio devices |
|
||||
| struct | `AudioDeviceInfo` | engine.rs:96 | Single audio device info |
|
||||
| enum | `AudioError` | lib.rs:100 | Audio subsystem errors (8 variants) |
|
||||
| struct | `AudioEffects` | lib.rs:136 | AEC/AGC/NS/HPF toggles |
|
||||
| fn | `list_audio_devices` | engine.rs:176 | Enumerate input/output devices |
|
||||
| fn | `AudioEngine::start` | engine.rs:634 | Start audio engine |
|
||||
| fn | `AudioEngine::start_with_gate` | engine.rs:647 | Start with transmit gate |
|
||||
| fn | `AudioEngine::stop` | engine.rs:1358 | Stop audio engine |
|
||||
| fn | `AudioEngine::set_transmit_active` | engine.rs:1601 | Set transmit state |
|
||||
| fn | `AudioEngine::transmit_active` | engine.rs:1606 | Get transmit state |
|
||||
| fn | `AudioEngine::set_output_muted` | engine.rs:1702 | Set output mute |
|
||||
| fn | `AudioEngine::set_output_gain` | engine.rs:1714 | Set output gain |
|
||||
| fn | `AudioEngine::set_client_volume` | engine.rs:1727 | Set per-client volume |
|
||||
| fn | `AudioEngine::set_audio_processing_config` | engine.rs:1646 | Update processing config |
|
||||
| fn | `AudioEngine::audio_processing_stats` | engine.rs:1683 | Get processing stats |
|
||||
| **Frame Helpers** | | | |
|
||||
| const | `SAMPLE_RATE_HZ` | frame.rs:9 | 48000 Hz |
|
||||
| const | `FRAME_10MS_SAMPLES` | frame.rs:15 | 480 samples |
|
||||
| const | `FRAME_20MS_SAMPLES` | frame.rs:17 | 960 samples |
|
||||
| struct | `AudioFrame10ms` | frame.rs:21 | 10ms processing frame |
|
||||
| struct | `AudioFrame20ms` | frame.rs:28 | 20ms network frame |
|
||||
| fn | `i16_to_f32` | frame.rs:64 | PCM conversion |
|
||||
| fn | `f32_to_i16` | frame.rs:69 | PCM conversion |
|
||||
| fn | `dbfs` | frame.rs:74 | RMS dBFS calculation |
|
||||
| **Transmit** | | | |
|
||||
| enum | `TransmitMode` | transmit_mode.rs:13 | Ptt/Continuous/VoiceActivity |
|
||||
| enum | `PermissionGate` | transmit_selector.rs:38 | Mic permission state |
|
||||
| struct | `TransmitModeSelector` | transmit_selector.rs:88 | Multi-signal transmit selector |
|
||||
| struct | `AudioTransmitGate` | ptt.rs:139 | Atomic transmit flag |
|
||||
| struct | `ReleaseTailTimer` | release_tail.rs:43 | PTT release-tail timer |
|
||||
| const | `DEFAULT_TAIL_MS` | release_tail.rs:24 | 200ms default |
|
||||
| const | `MAX_TAIL_MS` | release_tail.rs:21 | 500ms max |
|
||||
| **PTT** | | | |
|
||||
| enum | `PttCapabilityLevel` | ptt.rs:34 | L0-L4 capability levels |
|
||||
| struct | `PttBackendDescriptor` | ptt.rs:97 | Privacy-safe PTT descriptor |
|
||||
| struct | `MissedKeyUpWatchdog` | ptt.rs:202 | PTT safety watchdog |
|
||||
| trait | `DesktopPttBackend` | ptt_backends/mod.rs:162 | Platform PTT backend trait |
|
||||
| struct | `PttBinding` | ptt_backends/mod.rs:48 | PTT binding metadata |
|
||||
| enum | `PttInputClass` | ptt_backends/mod.rs:84 | None/Keyboard/MouseSideButton |
|
||||
| enum | `PttBackendError` | ptt_backends/mod.rs:113 | PTT backend errors |
|
||||
| fn | `select_ptt_backend` | ptt_backends/mod.rs:213 | Auto-select best backend |
|
||||
| **Audio Processing** | | | |
|
||||
| enum | `AudioRoute` | audio_processing.rs:14 | Route class (6 variants) |
|
||||
| enum | `AudioBackend` | audio_processing.rs:65 | Processing backend (4 variants) |
|
||||
| enum | `VadBackend` | audio_processing.rs:90 | VAD backend (4 variants) |
|
||||
| enum | `EffectOwner` | audio_processing.rs:122 | Effect owner (5 variants) |
|
||||
| enum | `IosVoiceProcessingMode` | audio_processing.rs:58 | iOS VPIO mode |
|
||||
| struct | `AudioProcessingConfig` | audio_processing.rs:137 | Full processing config |
|
||||
| struct | `AudioProcessingStats` | audio_processing.rs:270 | Processing statistics |
|
||||
| struct | `SharedAudioProcessingStats` | audio_processing.rs:322 | Thread-safe stats |
|
||||
| **DSP** | | | |
|
||||
| struct | `Aec3` | processor/dsp/aec3.rs:48 | Acoustic echo canceller |
|
||||
| struct | `Agc2` | processor/dsp/agc2.rs:206 | Automatic gain control |
|
||||
| struct | `HighPassFilter` | processor/dsp/hpf.rs:42 | High-pass filter |
|
||||
| struct | `NoiseSuppressor` | processor/dsp/ns.rs:40 | Noise suppressor |
|
||||
| trait | `AudioProcessor` | processor/mod.rs:21 | Realtime processor trait |
|
||||
| struct | `NoopProcessor` | processor/noop.rs:6 | No-op processor |
|
||||
| struct | `PlatformVoiceProcessor` | processor/platform.rs:10 | Platform VPIO processor |
|
||||
| struct | `SonoraProcessor` | processor/sonora.rs:88 | Sonora DSP processor |
|
||||
| struct | `SonoraConfig` | processor/sonora.rs:37 | Sonora configuration |
|
||||
| struct | `WebRtcApmProcessor` | processor/webrtc_apm.rs:91 | WebRTC APM processor |
|
||||
| struct | `WebRtcApmConfig` | processor/webrtc_apm.rs:17 | WebRTC APM config |
|
||||
| **VAD** | | | |
|
||||
| trait | `VoiceActivityDetector` | vad/mod.rs:34 | VAD trait |
|
||||
| struct | `VadOutput` | vad/mod.rs:26 | VAD output (probability + speech) |
|
||||
| struct | `WebRtcFallbackVad` | vad/mod.rs:40 | WebRTC fallback VAD |
|
||||
| struct | `Resampled16kHzVad` | vad/mod.rs:77 | 48→16kHz resampling wrapper |
|
||||
| struct | `SileroOnnxVad` | vad/silero_onnx.rs:62 | Silero ONNX VAD |
|
||||
| struct | `Downsampler48to16` | vad/resampler.rs:44 | 48→16kHz downsampler |
|
||||
| fn | `set_silero_model_path` | vad/mod.rs:126 | Set VAD model path |
|
||||
| fn | `silero_model_epoch` | vad/mod.rs:147 | Get model epoch |
|
||||
| **Voice Activity** | | | |
|
||||
| struct | `VoiceActivityStateMachine` | voice_activity.rs:29 | VAD gate state machine |
|
||||
| **Mobile Backend** | | | |
|
||||
| trait | `MobileVoiceAudioBackend` | mobile_voice_backend.rs:262 | Mobile audio backend trait |
|
||||
| struct | `AndroidVoiceStreamConfig` | mobile_voice_backend.rs:194 | Android stream config |
|
||||
| struct | `AndroidAudioDiagnostics` | mobile_voice_backend.rs:499 | Android diagnostics |
|
||||
| enum | `BackendEvent` | mobile_voice_backend.rs:35 | Backend events |
|
||||
| enum | `BackendError` | mobile_voice_backend.rs:154 | Backend errors |
|
||||
| enum | `AchievedPerformanceMode` | mobile_voice_backend.rs:112 | Performance mode |
|
||||
| enum | `LatencyTier` | mobile_voice_backend.rs:377 | Latency tier |
|
||||
| struct | `AndroidVoiceUnit` | android_voice_unit.rs:565 | Android voice unit |
|
||||
| struct | `IosVoiceUnit` | ios_voice_unit.rs:522 | iOS voice unit |
|
||||
| **Route Policy** | | | |
|
||||
| fn | `ios_route_policy` | route_policy.rs:27 | Route→config policy for iOS |
|
||||
| fn | `apply_route_change` | route_policy.rs:105 | Apply route change |
|
||||
| **Mode Stack** | | | |
|
||||
| struct | `ModeStack` | mode_stack.rs:88 | Android audio mode refcount |
|
||||
| enum | `ModeAcquire` | mode_stack.rs:41 | Acquire result |
|
||||
| enum | `ModeRelease` | mode_stack.rs:62 | Release result |
|
||||
| **Debug** | | | |
|
||||
| struct | `WavDebugRecorder` | debug_wav.rs:68 | Debug WAV recorder |
|
||||
|
||||
**Dead code:** `AndroidVoiceUnit`, `IosVoiceUnit`, and platform-specific backends are `#[cfg]`-gated — intentional.
|
||||
|
||||
---
|
||||
|
||||
### 7. `chanora_resolver` — DNS/SRV/TSDNS Resolver
|
||||
|
||||
TeamSpeak address resolution: SRV, TSDNS, nick lookup.
|
||||
|
||||
| Kind | Name | File:Line | Purpose |
|
||||
|------|------|-----------|---------|
|
||||
| struct | `ChanoraResolver` | lib.rs:108 | Main resolver |
|
||||
| struct | `Args` | lib.rs:26 | Resolution arguments |
|
||||
| struct | `BuildInfo` | lib.rs:33 | Build metadata |
|
||||
| struct | `SrvRecord` | lib.rs:40 | SRV record |
|
||||
| struct | `ClientResolution` | lib.rs:58 | Client resolution result |
|
||||
| enum | `Resolution` | lib.rs:85 | Resolution result (Dns/Srv/Nick) |
|
||||
| enum | `ClientResolutionMethod` | lib.rs:48 | Resolution method (6 variants) |
|
||||
| const | `DEFAULT_TEAMSPEAK_PORT` | lib.rs:23 | Port 9987 |
|
||||
| fn | `build_info` | lib.rs:123 | Get build info |
|
||||
| fn | `setup_log` | lib.rs:137 | Setup logging |
|
||||
| fn | `ChanoraResolver::new` | lib.rs:157 | Create resolver |
|
||||
| fn | `ChanoraResolver::resolve` | lib.rs:178 | Resolve with Args |
|
||||
| fn | `ChanoraResolver::resolve_connection_address` | lib.rs:193 | Resolve to connection address |
|
||||
| fn | `ChanoraResolver::resolve_client_address` | lib.rs:197 | Resolve client input to address |
|
||||
| fn | `ChanoraResolver::resolve_client_request` | lib.rs:201 | Resolve with full metadata |
|
||||
| fn | `ChanoraResolver::resolve_dns` | lib.rs:594 | DNS lookup |
|
||||
| fn | `ChanoraResolver::resolve_ts3` | lib.rs:614 | TS3 SRV lookup |
|
||||
| fn | `ChanoraResolver::resolve_tsdns` | lib.rs:619 | TSDNS SRV lookup |
|
||||
| fn | `ChanoraResolver::resolve_nick` | lib.rs:624 | Nick lookup |
|
||||
| fn | `normalize_args` | lib.rs:778 | Normalize Args |
|
||||
| fn | `validate_args` | lib.rs:785 | Validate Args |
|
||||
| fn | `run` | lib.rs:804 | CLI entry point |
|
||||
|
||||
**Dead code:** `run()` is a CLI entry point, not called from library code — intentional.
|
||||
|
||||
---
|
||||
|
||||
### 8. `chanora_prefetch` — Server Address Prefetch
|
||||
|
||||
Speculative DNS warming for faster connects.
|
||||
|
||||
| Kind | Name | File:Line | Purpose |
|
||||
|------|------|-----------|---------|
|
||||
| struct | `ServerPrefetcher` | lib.rs:83 | Prefetch cache + async resolver |
|
||||
| enum | `ServerPrefetchError` | lib.rs:18 | Prefetch errors |
|
||||
| fn | `ServerPrefetcher::new` | lib.rs:90 | Create prefetcher |
|
||||
| fn | `ServerPrefetcher::prefetch` | lib.rs:99 | Schedule fire-and-forget prefetch |
|
||||
| fn | `ServerPrefetcher::fresh_match` | lib.rs:145 | Check for cached result |
|
||||
|
||||
**Dead code:** None found.
|
||||
|
||||
---
|
||||
|
||||
### 9. `chanora_diagnostics` — Redaction & Diagnostic Export
|
||||
|
||||
Redaction policy, in-memory log sink, PTT sanitizer.
|
||||
|
||||
| Kind | Name | File:Line | Purpose |
|
||||
|------|------|-----------|---------|
|
||||
| struct | `Redactor` | lib.rs:121 | Production redaction policy |
|
||||
| struct | `KnownSecretRegistry` | lib.rs:79 | Secret substring registry |
|
||||
| struct | `InMemoryLogSink` | lib.rs:356 | Bounded redacted log sink |
|
||||
| struct | `RedactingLogLayer` | lib.rs:440 | tracing Layer for redaction |
|
||||
| struct | `PttSanitizer` | lib.rs:505 | PTT field ban Layer |
|
||||
| struct | `DiagnosticExport` | lib.rs:614 | Export bundle |
|
||||
| struct | `ProtocolEventRecorder` | lib.rs:714 | Protocol event ring buffer |
|
||||
| enum | `DiagnosticsError` | lib.rs:51 | Diagnostics errors |
|
||||
| const | `REDACTION_MARKER` | lib.rs:62 | `"[REDACTED]"` |
|
||||
| const | `DEFAULT_LOG_CAPACITY` | lib.rs:67/70 | 256 (release) / 4096 (debug) |
|
||||
| fn | `Redactor::with_default_policy` | lib.rs:128 | Create redactor |
|
||||
| fn | `Redactor::with_secrets` | lib.rs:134 | Create with secret registry |
|
||||
| fn | `Redactor::secrets` | lib.rs:140 | Access secret registry |
|
||||
| fn | `Redactor::redact` | lib.rs:150 | Apply redaction policy |
|
||||
| fn | `KnownSecretRegistry::register` | lib.rs:85 | Register secret |
|
||||
| fn | `KnownSecretRegistry::len` | lib.rs:99 | Count secrets |
|
||||
| fn | `KnownSecretRegistry::is_empty` | lib.rs:104 | Check empty |
|
||||
| fn | `KnownSecretRegistry::contains_substr` | lib.rs:110 | Substring check |
|
||||
| fn | `InMemoryLogSink::new` | lib.rs:365 | Create sink |
|
||||
| fn | `InMemoryLogSink::snapshot` | lib.rs:377 | Snapshot lines |
|
||||
| fn | `InMemoryLogSink::push` | lib.rs:386 | Push redacted line |
|
||||
| fn | `InMemoryLogSink::redactor` | lib.rs:397 | Access redactor |
|
||||
| fn | `RedactingLogLayer::new` | lib.rs:446 | Create layer |
|
||||
| fn | `RedactingLogLayer::with_sanitizer` | lib.rs:452 | Wrap with PTT sanitizer |
|
||||
| fn | `PttSanitizer::wrap` | lib.rs:513 | Wrap inner layer |
|
||||
| fn | `DiagnosticExport::from_sink` | lib.rs:636 | Build export |
|
||||
| fn | `DiagnosticExport::with_android_audio` | lib.rs:655 | Attach Android audio YAML |
|
||||
| fn | `DiagnosticExport::with_network_info` | lib.rs:661 | Attach network info |
|
||||
| fn | `DiagnosticExport::with_protocol_events` | lib.rs:667 | Attach protocol events |
|
||||
| fn | `DiagnosticExport::to_text` | lib.rs:674 | Render as plaintext |
|
||||
| fn | `ProtocolEventRecorder::new` | lib.rs:721 | Create recorder |
|
||||
| fn | `ProtocolEventRecorder::record_connected` | lib.rs:740 | Record connection |
|
||||
| fn | `ProtocolEventRecorder::record_disconnected` | lib.rs:745 | Record disconnect |
|
||||
| fn | `ProtocolEventRecorder::record_reconnecting` | lib.rs:750 | Record reconnect |
|
||||
| fn | `ProtocolEventRecorder::record_snapshot_changed` | lib.rs:759 | Record snapshot change |
|
||||
| fn | `ProtocolEventRecorder::record_channel_join` | lib.rs:768 | Record channel join |
|
||||
| fn | `ProtocolEventRecorder::record_lifecycle` | lib.rs:777 | Record lifecycle |
|
||||
| fn | `ProtocolEventRecorder::drain` | lib.rs:782 | Drain all events |
|
||||
| fn | `ProtocolEventRecorder::snapshot` | lib.rs:787 | Snapshot events |
|
||||
|
||||
**Dead code:** None found.
|
||||
|
||||
---
|
||||
|
||||
### 10. `chanora_core` — Top-Level Orchestration
|
||||
|
||||
Integration point composing all subsystems behind a stable API.
|
||||
|
||||
| Kind | Name | File:Line | Purpose |
|
||||
|------|------|-----------|---------|
|
||||
| struct | `ChanoraSession` | lib.rs:191 | Process-wide session handle |
|
||||
| enum | `CoreError` | lib.rs:84 | Top-level errors (12 variants) |
|
||||
| struct | `PttDescriptorSnapshot` | events.rs:6 | PTT descriptor snapshot |
|
||||
| struct | `PersistedPttBinding` | events.rs:27 | Persisted PTT binding |
|
||||
| struct | `PttController` | ptt.rs:68 | PTT controller |
|
||||
| struct | `FileTransferService` | file_transfer.rs:39 | File transfer service |
|
||||
| enum | `SessionEvent` | events.rs:49 | Session lifecycle events |
|
||||
| enum | `VoiceJoinSyncState` | events.rs:239 | Voice join sync state |
|
||||
| enum | `VoiceJoinErrorCode` | events.rs:250 | Voice join error codes |
|
||||
| enum | `NetworkState` | events.rs:280 | Network connectivity state |
|
||||
| enum | `FileTransferError` | file_transfer.rs:17 | File transfer errors |
|
||||
| enum | `PttControllerError` | ptt.rs:35 | PTT controller errors |
|
||||
| fn | `ChanoraSession::new` | lib.rs:245 | Create session |
|
||||
| fn | `ChanoraSession::subscribe_events` | lib.rs:478 | Subscribe to session events |
|
||||
| fn | `ChanoraSession::set_network_state` | lib.rs:460 | Set network state |
|
||||
| fn | `ChanoraSession::network_state` | lib.rs:469 | Get network state |
|
||||
| fn | `ChanoraSession::transmit_mode` | lib.rs:1568 | Get transmit mode |
|
||||
| fn | `ChanoraSession::hard_mute` | lib.rs:1591 | Get hard mute state |
|
||||
| fn | `ChanoraSession::release_tail_ms` | lib.rs:1645 | Get release tail |
|
||||
| fn | `ChanoraSession::transmit_selector` | lib.rs:1652 | Get transmit selector |
|
||||
| fn | `ChanoraSession::release_tail_timer` | lib.rs:1659 | Get release tail timer |
|
||||
| fn | `ChanoraSession::audio_processing_stats_if_ready` | lib.rs:1205 | Get audio stats |
|
||||
| fn | `PttController::new` | ptt.rs:103 | Create PTT controller |
|
||||
| fn | `PttController::current_capability` | ptt.rs:226 | Get PTT capability |
|
||||
| fn | `PttController::subscribe_capability` | ptt.rs:233 | Subscribe to capability |
|
||||
| fn | `PttController::descriptor_watch` | ptt.rs:250 | Watch PTT descriptor |
|
||||
| fn | `PttController::press_gate` | ptt.rs:257 | Get press gate |
|
||||
| fn | `PttController::release_tail` | ptt.rs:263 | Get release tail |
|
||||
|
||||
**Dead code:** None found. All items consumed by `chanora_bridge`.
|
||||
|
||||
---
|
||||
|
||||
## Dart Files (apps/chanora_flutter/lib/)
|
||||
|
||||
### Widgets (31 files)
|
||||
|
||||
| Class | File:Line | Purpose |
|
||||
|-------|-----------|---------|
|
||||
| `AppSnackBar` | widgets/app_snack_bar.dart:9 | Snackbar notifications |
|
||||
| `AppSnackBarVariant` | widgets/app_snack_bar.dart:6 | Neutral/success/warning/error |
|
||||
| `AudioDebugStatsPanel` | widgets/audio_debug_stats_panel.dart:22 | Audio stats debug panel |
|
||||
| `AudioDeviceListTile` | widgets/audio_device_list_tile.dart:21 | Audio device list item |
|
||||
| `AudioDeviceKind` | widgets/audio_device_list_tile.dart:12 | Input/Output device kind |
|
||||
| `AudioOutputTile` | widgets/audio_output_tile.dart:15 | Audio output route picker |
|
||||
| `AudioProcessingConfigState` | widgets/audio_processing_config_state.dart:34 | Processing config state |
|
||||
| `BbCodeText` | widgets/bbcode_text.dart:47 | BBCode renderer |
|
||||
| `ChatPanel` | widgets/chat_panel.dart:13 | Chat panel container |
|
||||
| `ChatEntry` | widgets/chat_views.dart:25 | Chat message entry |
|
||||
| `ChatClientGroups` | widgets/chat_views.dart:374 | Client grouping for chat |
|
||||
| `ChatPage` | widgets/chat_views.dart:548 | Full chat page |
|
||||
| `ChatDetailView` | widgets/chat_views.dart:1070 | Chat detail view |
|
||||
| `ClientInfoSheet` | widgets/client_info_sheet.dart:7 | Client info bottom sheet |
|
||||
| `ConnectForm` | widgets/connect_widgets.dart:9 | Server connect form |
|
||||
| `BookmarkList` | widgets/connect_widgets.dart:154 | Bookmark list |
|
||||
| `CapturedBinding` | widgets/input_dialogs.dart:50 | PTT binding capture result |
|
||||
| `BookmarkNameDialog` | widgets/input_dialogs.dart:61 | Bookmark name input |
|
||||
| `ChannelPasswordDialog` | widgets/input_dialogs.dart:109 | Channel password input |
|
||||
| `PttBindingCaptureDialog` | widgets/input_dialogs.dart:154 | PTT key binding dialog |
|
||||
| `PermissionStateBanner` | widgets/permission_state_banner.dart:28 | Permission state banner |
|
||||
| `PokeNotificationSettingsDialog` | widgets/poke_notification_settings.dart:7 | Poke notification settings |
|
||||
| `PttCapabilityBadge` | widgets/ptt_capability_badge.dart:14 | PTT capability badge |
|
||||
| `SnapshotView` | widgets/snapshot_view.dart:14 | Server tree view |
|
||||
| `TalkPowerWarning` | widgets/talk_power_warning.dart:14 | Talk power warning |
|
||||
| `VoiceBar` | widgets/voice_bar.dart:18 | Voice status bar |
|
||||
| `VoiceStatusChip` | widgets/voice_compact.dart:46 | Voice status chip |
|
||||
| `VoicePttButton` | widgets/voice_compact.dart:255 | PTT button widget |
|
||||
| `VoiceLevelMeter` | widgets/voice_level_meter.dart:11 | Voice level meter |
|
||||
| `VoiceSettingsDialog` | widgets/voice_settings.dart:61 | Voice settings dialog |
|
||||
| `VoiceSettingsResult` | widgets/voice_settings.dart:46 | Settings result |
|
||||
| `VoiceStatusSummary` | widgets/voice_status_summary.dart:5 | Voice status summary |
|
||||
| `VoiceSubHeader` | widgets/voice_settings_controls.dart:117 | Voice sub-header |
|
||||
| `VoiceSectionHeader` | widgets/voice_settings_controls.dart:141 | Voice section header |
|
||||
| `AudioProcessingToggleRow` | widgets/voice_settings_controls.dart:159 | Processing toggle row |
|
||||
|
||||
### Services (20 files)
|
||||
|
||||
| Class/Function | File:Line | Purpose |
|
||||
|----------------|-----------|---------|
|
||||
| `AndroidAudioOutputDevice` | services/android_audio_output_devices.dart:1 | Android audio device |
|
||||
| `AndroidPermissionsService` | services/android_permissions_service.dart:34 | Android permission handler |
|
||||
| `AppBootstrap` | services/app_bootstrap.dart:95 | App bootstrap helpers |
|
||||
| `AudioLifecycleService` | services/audio_lifecycle_service.dart:46 | Audio lifecycle wiring |
|
||||
| `BackIntentPolicy` | services/back_intent_policy.dart | Back intent policy |
|
||||
| `BackIntentService` | services/back_intent_service.dart:97 | Back intent handler |
|
||||
| `ChannelJoinErrorMapper` | services/channel_join_error_mapper.dart:5 | Error message mapper |
|
||||
| `ChannelSpacer` | services/channel_spacer.dart:110 | Spacer channel detection |
|
||||
| `ConnectionPhaseState` | services/connection_phase_state.dart:38 | Connection phase state |
|
||||
| `HardMuteOwners` | services/hard_mute_owners.dart | Hard mute owners |
|
||||
| `IosAudioSessionController` | services/ios_audio_session_controller.dart | iOS audio session |
|
||||
| `IosPermissionsService` | services/ios_permissions_service.dart:66 | iOS permission handler |
|
||||
| `LinkTrustService` | services/link_trust_service.dart:29 | Link trust checker |
|
||||
| `MacosPermissionsService` | services/macos_permissions_service.dart:273 | macOS permission handler |
|
||||
| `PokePreferencesService` | services/poke_preferences_service.dart:44 | Poke mute preferences |
|
||||
| `PokeNotificationService` | services/poke_notification_service.dart | Poke notification handler |
|
||||
| `PrefetchDebouncer` | services/prefetch_debouncer.dart:15 | DNS prefetch debouncer |
|
||||
| `OwnClientSnapshotState` | services/snapshot_state_mapper.dart:3 | Snapshot→state mapper |
|
||||
| `ownClientSnapshotState()` | services/snapshot_state_mapper.dart:23 | Build snapshot state |
|
||||
| `snapshotChannelName()` | services/snapshot_state_mapper.dart:43 | Get channel name from snapshot |
|
||||
| `snapshotNeededTalkPower()` | services/snapshot_state_mapper.dart:48 | Get required talk power |
|
||||
| `Ts3ServerLink` | services/ts3_server_link.dart:83 | TS3 server link parser |
|
||||
| `UiPreferencesService` | services/ui_preferences_service.dart | UI preferences |
|
||||
| `VoiceJoinOrdering` | services/voice_join_ordering.dart | Voice join ordering |
|
||||
|
||||
### Design (4 files)
|
||||
|
||||
| Class | File:Line | Purpose |
|
||||
|-------|-----------|---------|
|
||||
| `ChanoraTokens` | design/chanora_tokens.dart | Design tokens |
|
||||
| `Breakpoints` | design/breakpoints.dart | Responsive breakpoints |
|
||||
| `ViewportInfo` | design/viewport_info.dart | Viewport info |
|
||||
| `PlatformCapabilities` | design/platform_capabilities.dart | Platform capabilities |
|
||||
|
||||
---
|
||||
|
||||
## Dead Code Analysis
|
||||
|
||||
### Confirmed Dead Code
|
||||
None found. All public items are consumed by downstream crates or are intentionally platform-gated.
|
||||
|
||||
### Platform-Gated (Intentional)
|
||||
- `AndroidVoiceUnit`, `IosVoiceUnit` — only compiled on target platforms
|
||||
- `chanora_android_*` JNI functions — Android only
|
||||
- `ios_voice_unit.rs`, `android_voice_unit.rs` — platform-specific
|
||||
- `sdl_output.rs` — Linux only
|
||||
|
||||
### TODO/FIXME Items (15 total)
|
||||
|
||||
| File | Line | Note |
|
||||
|------|------|------|
|
||||
| `chanora_audio/src/audio_event_queue.rs` | 27 | Wire to client disconnect path |
|
||||
| `chanora_audio/src/engine.rs` | 2348 | Realtime audio callback concern |
|
||||
| `chanora_audio/src/mobile_voice_backend.rs` | 16 | Back-fill IosVoiceUnit to trait |
|
||||
| `audio_lifecycle_service.dart` | 151 | Wire macOS default device change |
|
||||
| `audio_lifecycle_service.dart` | 156 | macOS device change no action yet |
|
||||
| `poke_notification_service.dart` | 33,35,41,43,48,130,132,142,155,168 | Future EventSoundService (10 items) |
|
||||
|
||||
### Useless Code
|
||||
- No empty impls found
|
||||
- No commented-out function bodies found
|
||||
- No dead trait implementations found
|
||||
|
||||
---
|
||||
|
||||
## Architecture Notes
|
||||
|
||||
- **Boundary discipline**: `tsclientlib` types never cross `chanora_protocol` boundary (SAD-067)
|
||||
- **Single connection**: DEC-006 enforces one connection at runtime
|
||||
- **Secret isolation**: `chanora_storage` never stores secrets in plaintext DB
|
||||
- **Deterministic reducers**: `chanora_state` reducers are pure functions (SRS-056)
|
||||
- **PTT privacy**: Raw key codes never appear in logs or diagnostics (DEC-027)
|
||||
- **Audio pipeline**: 48kHz mono, 20ms Opus frames, 10ms processing frames
|
||||
@@ -0,0 +1,252 @@
|
||||
# Link Coverage Report
|
||||
|
||||
**Generated:** 2026-06-13
|
||||
**Scope:** All `.md` files in repository root and `docs/` tree
|
||||
|
||||
## Summary
|
||||
|
||||
- Total links checked: 148
|
||||
- Valid internal links: 12 (4 markdown links + 8 inline doc-path references)
|
||||
- Broken internal links: 2
|
||||
- Valid inline doc-path references: 94
|
||||
- Broken inline doc-path references: 5
|
||||
- Valid code references: 62
|
||||
- Broken code references: 2
|
||||
- External links (manual review): 48
|
||||
- Cross-references (doc→doc in prose): 0 broken
|
||||
|
||||
---
|
||||
|
||||
## Broken Internal Links
|
||||
|
||||
Markdown `[text](path)` style links that resolve to missing files.
|
||||
|
||||
| File | Line | Link Text | Target | Issue |
|
||||
|------|------|-----------|--------|-------|
|
||||
| README.md | 428 | `LICENSE-APACHE` | `LICENSE-APACHE` | File does not exist at repo root |
|
||||
| README.md | 431 | `LICENSE-MIT` | `LICENSE-MIT` | File does not exist at repo root |
|
||||
|
||||
**Impact:** Users clicking the license links in the README will get a 404 on GitHub. These are referenced in the License section as the dual-license model files.
|
||||
|
||||
**Also affected by missing LICENSE files:**
|
||||
|
||||
| File | Line | Reference | Issue |
|
||||
|------|------|-----------|-------|
|
||||
| docs/security/license-inventory.md | 9 | `../../LICENSE-APACHE` | Resolves to missing `LICENSE-APACHE` at repo root |
|
||||
| docs/security/license-inventory.md | 10 | `../../LICENSE-MIT` | Resolves to missing `LICENSE-MIT` at repo root |
|
||||
| docs/security/flutter-license-inventory.md | 11 | `../../LICENSE-APACHE` | Resolves to missing `LICENSE-APACHE` at repo root |
|
||||
| docs/security/flutter-license-inventory.md | 11 | `../../LICENSE-MIT` | Resolves to missing `LICENSE-MIT` at repo root |
|
||||
|
||||
---
|
||||
|
||||
## Valid Internal Links
|
||||
|
||||
| File | Line | Target |
|
||||
|------|------|--------|
|
||||
| README.md | 130 | `docs/architecture/desktop-ptt-architecture.md` |
|
||||
| README.md | 436 | `docs/governance/product-decision-register.md` |
|
||||
| README.md | 444 | `NOTICE` |
|
||||
| docs/superpowers/specs/2026-06-05-adaptive-3-panel-layout-design.md | 266 | `../ui-ux/adaptive-layout-platform-guide.md` |
|
||||
|
||||
---
|
||||
|
||||
## Inline Doc-Path References
|
||||
|
||||
References to documentation files using backtick-quoted paths (not markdown links).
|
||||
|
||||
### Valid
|
||||
|
||||
| File | Line | Reference |
|
||||
|------|------|-----------|
|
||||
| CONTRIBUTING.md | 25 | `docs/governance/git-commit-message-convention.md` |
|
||||
| README.md | 144 | `docs/governance/product-decision-register.md` |
|
||||
| README.md | 261–271 | `docs/requirements/sysrs.md`, `docs/requirements/srs.md`, `docs/architecture/sysdes.md`, `docs/architecture/sad.md`, `docs/architecture/sdd.md`, `docs/verification/verification-master-plan.md`, `docs/release/release-readiness-go-nogo-record.md`, `docs/release/platform-release-policy.md`, `docs/governance/product-decision-register.md`, `docs/governance/traceability-matrix.md`, `docs/security/security-privacy-legal-guideline.md` |
|
||||
| README.md | 312 | `docs/release/release-readiness-go-nogo-record.md` |
|
||||
| README.md | 349–355 | `docs/security/threat-model.md`, `docs/security/secure-storage-audit-report.md`, `docs/security/diagnostic-redaction-audit-report.md`, `docs/security/dependency-and-supply-chain-report.md`, `docs/privacy/privacy-policy.md`, `docs/legal/trademark-and-attribution-review.md` |
|
||||
| README.md | 391 | `docs/governance/git-commit-message-convention.md` |
|
||||
| README.md | 449–451 | `docs/governance/product-decision-register.md`, `docs/security/dependency-and-supply-chain-report.md`, `docs/legal/trademark-and-attribution-review.md` |
|
||||
| docs/architecture/sad.md | 6 | `docs/srs.md` |
|
||||
| docs/architecture/sad.md | 7 | `docs/sysdes.md` |
|
||||
| docs/architecture/sad.md | 176 | `docs/governance/traceability-matrix.md` |
|
||||
| docs/architecture/sdd.md | 6 | `docs/architecture/sad.md` |
|
||||
| docs/architecture/sdd.md | 7 | `docs/srs.md` |
|
||||
| docs/architecture/sysdes.md | 6 | `docs/sysdes.md` (canonical pointer) |
|
||||
| docs/architecture/desktop-ptt-architecture.md | 5 | `docs/architecture/sad.md`, `docs/architecture/sdd.md`, `docs/release/dv-waiver-register.md` |
|
||||
| docs/architecture/file-transfer-design.md | 6 | `docs/architecture/sad.md` |
|
||||
| docs/architecture/file-transfer-research.md | 5 | `docs/architecture/file-transfer-design.md` |
|
||||
| docs/architecture/file-transfer-implementation-plan.md | 6 | `docs/architecture/file-transfer-design.md`, `docs/architecture/file-transfer-research.md` |
|
||||
| docs/requirements/sysrs.md | 4 | `../sysrs.md` (canonical pointer) |
|
||||
| docs/requirements/srs.md | 4 | `../srs.md` (canonical pointer) |
|
||||
| docs/governance/document-index.md | 14–32 | All listed document paths |
|
||||
| docs/material3-guideline.md | 10 | `docs/ui-ux/material3-guideline.md` (self-referencing path record) |
|
||||
| docs/ui-ux/material3-guideline.md | 6 | `docs/material3-guideline.md` (canonical pointer) |
|
||||
| docs/superpowers/specs/2026-06-08-maintainability-continuation-design.md | 125–141 | Multiple `docs/` paths |
|
||||
| docs/superpowers/specs/2026-05-29-state-sync-ui-settings-validation-design.md | 51–55 | Multiple `docs/` paths |
|
||||
| docs/superpowers/plans/2026-05-29-finish-dv-document-tree.md | 16–54 | Multiple `docs/` paths |
|
||||
| docs/superpowers/plans/2026-05-29-swe2-swe3-baselines.md | 16–35 | Multiple `docs/` paths |
|
||||
| docs/superpowers/plans/2026-05-29-state-sync-ui-settings-validation.md | 84–88 | Multiple `docs/` paths |
|
||||
| docs/superpowers/plans/2026-05-29-dv-evidence-pack.md | 16–54 | Multiple `docs/` paths |
|
||||
| docs/superpowers/plans/2026-05-28-server-resolution-prefetch.md | 31–836 | Multiple source file paths |
|
||||
| docs/superpowers/plans/2026-05-28-chanora-server-prefetch-crate.md | 15–541 | Multiple source file paths |
|
||||
| docs/superpowers/plans/2026-06-06-chat-panel-switching.md | 58–507 | Multiple source file paths |
|
||||
| docs/superpowers/plans/2026-06-08-core-internal-split.md | 16–91 | Multiple source file paths |
|
||||
|
||||
### Broken
|
||||
|
||||
| File | Line | Reference | Issue |
|
||||
|------|------|-----------|-------|
|
||||
| docs/sysrs.md | 126 | `docs/chanora_SysDes.md` | Does not exist (listed as "potential downstream file name") |
|
||||
| docs/sysrs.md | 127 | `docs/chanora_SRS.md` | Does not exist (listed as "potential downstream file name") |
|
||||
| docs/sysrs.md | 128 | `docs/chanora_SAD.md` | Does not exist (listed as "potential downstream file name") |
|
||||
| docs/sysrs.md | 129 | `docs/chanora_SDD.md` | Does not exist (listed as "potential downstream file name") |
|
||||
| docs/sysrs.md | 130 | `docs/chanora_Verification.md` | Does not exist (listed as "potential downstream file name") |
|
||||
|
||||
**Note:** These five are documented as "Potential downstream file names" in a table and are aspirational/historical. They are presented as code blocks in the original, so they function as suggestions rather than navigable links. Low severity.
|
||||
|
||||
---
|
||||
|
||||
## Code References
|
||||
|
||||
### Valid
|
||||
|
||||
| File | Line | Reference | Found At |
|
||||
|------|------|-----------|----------|
|
||||
| docs/architecture/sad.md | 41 | `apps/chanora_flutter/lib/main.dart` | EXISTS |
|
||||
| docs/architecture/sad.md | 42 | `apps/chanora_flutter/lib/services/` | EXISTS |
|
||||
| docs/architecture/sad.md | 43 | `apps/chanora_flutter/lib/widgets/` | EXISTS |
|
||||
| docs/architecture/sad.md | 44 | `crates/chanora_bridge`, `apps/chanora_flutter/lib/src/rust/` | EXISTS |
|
||||
| docs/architecture/sad.md | 45 | `core/chanora_core` | EXISTS |
|
||||
| docs/architecture/sad.md | 46 | `crates/chanora_protocol` | EXISTS |
|
||||
| docs/architecture/sad.md | 47 | `crates/chanora_state` | EXISTS |
|
||||
| docs/architecture/sad.md | 48 | `crates/chanora_audio` | EXISTS |
|
||||
| docs/architecture/sad.md | 49 | `crates/chanora_storage` | EXISTS |
|
||||
| docs/architecture/sad.md | 50 | `crates/chanora_diagnostics` | EXISTS |
|
||||
| docs/architecture/sad.md | 51 | `crates/chanora_resolver` | EXISTS |
|
||||
| docs/architecture/sad.md | 52 | `crates/chanora_prefetch`, Flutter `prefetch_debouncer.dart` | EXISTS |
|
||||
| docs/architecture/sdd.md | 17 | `apps/chanora_flutter/lib/services/app_bootstrap.dart`, `main.dart` | EXISTS |
|
||||
| docs/architecture/sdd.md | 18 | `apps/chanora_flutter/lib/widgets/connect_widgets.dart` | EXISTS |
|
||||
| docs/architecture/sdd.md | 19 | `snapshot_view.dart`, `snapshot_state_mapper.dart`, `channel_spacer.dart` | EXISTS (in services/) |
|
||||
| docs/architecture/sdd.md | 20 | `chat_views.dart`, `bbcode_text.dart` | EXISTS |
|
||||
| docs/architecture/sdd.md | 21 | `voice_bar.dart`, `voice_compact.dart`, `voice_settings*.dart`, `voice_level_meter.dart`, `ptt_capability_badge.dart` | EXISTS |
|
||||
| docs/architecture/sdd.md | 22 | `android_permissions_service.dart`, `ios_permissions_service.dart`, `audio_lifecycle_service.dart`, `back_intent_*`, `link_trust_service.dart` | EXISTS |
|
||||
| docs/architecture/sdd.md | 23 | `crates/chanora_bridge/src/api.rs` | EXISTS |
|
||||
| docs/architecture/sdd.md | 24 | `core/chanora_core/src/lib.rs`, `events.rs`, `network_diagnostics.rs`, `ptt.rs` | EXISTS |
|
||||
| docs/architecture/sdd.md | 25 | `crates/chanora_protocol/src/` | EXISTS |
|
||||
| docs/architecture/sdd.md | 26 | `crates/chanora_state/src/lib.rs`, `channel_join.rs` | EXISTS |
|
||||
| docs/architecture/sdd.md | 27 | `crates/chanora_audio/src/` | EXISTS |
|
||||
| docs/architecture/sdd.md | 28 | `crates/chanora_storage/src/lib.rs` | EXISTS |
|
||||
| docs/architecture/sdd.md | 29 | `crates/chanora_diagnostics/src/lib.rs` | EXISTS |
|
||||
| docs/architecture/sdd.md | 30 | `crates/chanora_resolver/src/lib.rs`, `crates/chanora_prefetch/src/lib.rs`, `prefetch_debouncer.dart` | EXISTS |
|
||||
| docs/architecture/sdd.md | 31 | `.github/workflows/`, `tools/` | EXISTS |
|
||||
| docs/architecture/sdd.md | 35 | `crates/chanora_bridge/src/api.rs`, `apps/chanora_flutter/lib/src/rust/` | EXISTS |
|
||||
| docs/release/release-readiness-go-nogo-record.md | 26 | `apps/chanora_flutter/pubspec.yaml` | EXISTS |
|
||||
| docs/implementation-status-2026-05-28.md | 69 | `apps/chanora_flutter/ios/Runner/AppDelegate.swift` | EXISTS |
|
||||
| docs/sysrs.md | 503 | `apps/chanora_flutter/ios/Runner/AppDelegate.swift` | EXISTS |
|
||||
| docs/sysrs.md | 1962 | `apps/chanora_flutter/macos/chanora_bridge.podspec` | EXISTS |
|
||||
| README.md | 236–249 | `apps/chanora_flutter/`, `core/chanora_core/`, `crates/chanora_protocol/`, `crates/chanora_audio/`, `crates/chanora_state/`, `crates/chanora_storage/`, `crates/chanora_diagnostics/`, `crates/chanora_bridge/` | EXISTS |
|
||||
|
||||
### Broken
|
||||
|
||||
| File | Line | Reference | Issue |
|
||||
|------|------|-----------|-------|
|
||||
| docs/architecture/sdd.md | 19 | `snapshot_state_mapper.dart` (listed under "Snapshot and channel UI" widgets) | File is in `apps/chanora_flutter/lib/services/`, not `apps/chanora_flutter/lib/widgets/` — directory mismatch |
|
||||
| docs/architecture/sdd.md | 21 | `voice_settings*.dart` (listed under Voice UI widgets) | Files are `voice_settings.dart` and `voice_settings_controls.dart` in `widgets/` — EXISTS but glob reference is ambiguous (two files match) |
|
||||
|
||||
**Note:** The `snapshot_state_mapper.dart` directory mismatch is a minor documentation inaccuracy — the file exists but is listed under the wrong component section (widget layer vs service layer).
|
||||
|
||||
---
|
||||
|
||||
## External Links (Manual Review)
|
||||
|
||||
These URLs should be checked manually for validity.
|
||||
|
||||
| File | Line | URL |
|
||||
|------|------|-----|
|
||||
| README.md | 429 | `https://www.apache.org/licenses/LICENSE-2.0` |
|
||||
| README.md | 432 | `https://opensource.org/licenses/MIT` |
|
||||
| apps/chanora_flutter/README.md | 11 | `https://docs.flutter.dev/get-started/learn-flutter` |
|
||||
| apps/chanora_flutter/README.md | 12 | `https://docs.flutter.dev/get-started/codelab` |
|
||||
| apps/chanora_flutter/README.md | 13 | `https://docs.flutter.dev/reference/learning-resources` |
|
||||
| apps/chanora_flutter/README.md | 16 | `https://docs.flutter.dev/` |
|
||||
| silero-coreml/README.md | 343 | `https://apple.github.io/coremltools/docs-guides/source/introductory-quickstart.html` |
|
||||
| silero-coreml/README.md | 350 | `https://apple.github.io/coremltools/docs-guides/source/convert-pytorch.html` |
|
||||
| silero-coreml/Docs/CoreMLConversion.md | 244 | `https://apple.github.io/coremltools/docs-guides/source/convert-pytorch.html` |
|
||||
| silero-coreml/Docs/CoreMLConversion.md | 252 | `https://apple.github.io/coremltools/docs-guides/source/introductory-quickstart.html` |
|
||||
| docs/security/dependency-and-supply-chain-report.md | 29 | `https://github.com/EdisonJwa/oboe-rs` |
|
||||
| docs/superpowers/specs/2026-06-05-adaptive-3-panel-layout-design.md | 261 | `https://github.com/asportnoy/compact-discord` |
|
||||
| docs/superpowers/specs/2026-06-05-adaptive-3-panel-layout-design.md | 262 | `https://github.com/mattermost/mattermost/blob/...` |
|
||||
| docs/superpowers/specs/2026-06-05-adaptive-3-panel-layout-design.md | 263 | `https://github.com/RocketChat/fuselage/blob/...` |
|
||||
| docs/superpowers/specs/2026-06-05-adaptive-3-panel-layout-design.md | 264 | `https://github.com/flutter/flutter/issues/162965` |
|
||||
| docs/superpowers/specs/2026-06-05-adaptive-3-panel-layout-design.md | 265 | `https://m3.material.io/foundations/layout/breakpoints/overview` |
|
||||
| docs/architecture/file-transfer-research.md | 29 | `https://github.com/Splamy/TS3AudioBot/blob/...` |
|
||||
| docs/architecture/file-transfer-research.md | 30 | `https://github.com/Multivit4min/TS3-NodeJS-Library/blob/...` |
|
||||
| docs/architecture/file-transfer-research.md | 31 | `https://github.com/planetteamspeak/ts3phpframework/blob/...` |
|
||||
| docs/architecture/file-transfer-research.md | 49 | `https://github.com/Multivit4min/TS3-NodeJS-Library/blob/...` |
|
||||
| docs/architecture/file-transfer-research.md | 50 | `https://github.com/planetteamspeak/ts3phpframework/blob/...` |
|
||||
| docs/architecture/file-transfer-research.md | 123 | `https://github.com/ReSpeak/Qint/blob/...` |
|
||||
| docs/architecture/file-transfer-research.md | 124 | `https://github.com/ReSpeak/Qint/blob/...` |
|
||||
| docs/architecture/file-transfer-research.md | 125 | `https://github.com/ReSpeak/Qint/blob/...` |
|
||||
| docs/architecture/file-transfer-research.md | 140 | `https://github.com/teamspeak/ts3client-pluginsdk/blob/...` |
|
||||
| docs/architecture/file-transfer-research.md | 141 | `https://github.com/teamspeak/ts3client-pluginsdk/blob/...` |
|
||||
| docs/architecture/file-transfer-research.md | 142 | `https://community.teamspeak.com/t/clear-cache/41511` |
|
||||
| docs/architecture/file-transfer-research.md | 142 | `https://community.teamspeak.com/t/server-icons-are-displaying-a-broken-image-issues-with-local-cache/58680` |
|
||||
| docs/architecture/file-transfer-research.md | 208 | `https://github.com/Splamy/TS3AudioBot/blob/...` |
|
||||
| docs/architecture/file-transfer-research.md | 209 | `https://github.com/Splamy/TS3AudioBot/blob/...` |
|
||||
| docs/architecture/file-transfer-research.md | 220 | `https://github.com/Multivit4min/TS3-NodeJS-Library/blob/...` |
|
||||
| docs/architecture/file-transfer-research.md | 221 | `https://github.com/Multivit4min/TS3-NodeJS-Library/blob/...` |
|
||||
| docs/architecture/file-transfer-research.md | 320 | `https://github.com/rust-lang/rust/blob/...` |
|
||||
| docs/architecture/file-transfer-research.md | 321 | `https://source.android.com/docs/core/storage/scoped` |
|
||||
| docs/architecture/file-transfer-research.md | 322 | `https://developer.apple.com/library/archive/documentation/FileManagement/...` |
|
||||
| docs/architecture/file-transfer-research.md | 334 | `https://github.com/zkat/cacache-rs/blob/...` |
|
||||
| docs/architecture/file-transfer-research.md | 335 | `https://github.com/zkat/cacache-rs/blob/...` |
|
||||
| docs/architecture/file-transfer-research.md | 336 | `https://github.com/zkat/cacache-rs/blob/...` |
|
||||
| docs/architecture/file-transfer-research.md | 345 | `https://pub.dev/packages/flutter_cache_manager` |
|
||||
| docs/architecture/file-transfer-research.md | 346 | `https://pub.dev/packages/super_cache_disk/versions/1.0.0` |
|
||||
| docs/architecture/file-transfer-research.md | 406 | `https://git.did.science/TeaSpeak/Server/Server` |
|
||||
| docs/security/license-inventory.md | 31–80 | ~50 URLs to GitHub repos for dependency licenses |
|
||||
| docs/security/flutter-license-inventory.md | 624–4209 | Multiple `http://www.apache.org/licenses/` and `http://mozilla.org/MPL/2.0/` (in license text bodies) |
|
||||
|
||||
---
|
||||
|
||||
## Cross-References
|
||||
|
||||
### Valid
|
||||
|
||||
All doc-to-doc cross-references found in prose text resolve to existing files. Key verified chains:
|
||||
|
||||
| Source | Reference | Target Exists |
|
||||
|--------|-----------|---------------|
|
||||
| docs/architecture/sad.md:6 | `docs/srs.md` | YES |
|
||||
| docs/architecture/sad.md:7 | `docs/sysdes.md` | YES |
|
||||
| docs/architecture/sdd.md:6 | `docs/architecture/sad.md` | YES |
|
||||
| docs/architecture/sdd.md:7 | `docs/srs.md` | YES |
|
||||
| docs/architecture/sysdes.md:6 | `docs/sysdes.md` | YES |
|
||||
| docs/architecture/file-transfer-design.md:6 | `docs/architecture/sad.md` | YES |
|
||||
| docs/architecture/file-transfer-research.md:5 | `docs/architecture/file-transfer-design.md` | YES |
|
||||
| docs/architecture/file-transfer-implementation-plan.md:6 | `docs/architecture/file-transfer-design.md` | YES |
|
||||
| docs/architecture/file-transfer-implementation-plan.md:6 | `docs/architecture/file-transfer-research.md` | YES |
|
||||
| docs/architecture/desktop-ptt-architecture.md:5 | `docs/architecture/sad.md` | YES |
|
||||
| docs/architecture/desktop-ptt-architecture.md:5 | `docs/architecture/sdd.md` | YES |
|
||||
| docs/architecture/desktop-ptt-architecture.md:5 | `docs/release/dv-waiver-register.md` | YES |
|
||||
| docs/requirements/sysrs.md:4 | `../sysrs.md` | YES |
|
||||
| docs/requirements/srs.md:4 | `../srs.md` | YES |
|
||||
| docs/governance/document-index.md | All 18 listed paths | YES |
|
||||
| docs/ui-ux/material3-guideline.md:6 | `docs/material3-guideline.md` | YES |
|
||||
|
||||
### Broken
|
||||
|
||||
None found — all doc-to-doc cross-references in prose text resolve correctly.
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
1. **Path-record files**: Several docs exist as stubs pointing to canonical locations (`docs/requirements/sysrs.md` → `docs/sysrs.md`, `docs/requirements/srs.md` → `docs/srs.md`, `docs/architecture/sysdes.md` → `docs/sysdes.md`, `docs/ui-ux/material3-guideline.md` → `docs/material3-guideline.md`). These are intentional DV navigation aids, not broken links.
|
||||
|
||||
2. **Hypothetical file names in sysrs.md**: The table at lines 124–131 lists `docs/chanora_SysDes.md` etc. as "Potential downstream file names." These are aspirational names from an earlier draft, not current files. They are presented as plain text in a table, not as navigable links.
|
||||
|
||||
3. **LICENSE-APACHE and LICENSE-MIT**: These are the most impactful broken references. The README's License section links to them, and the security license inventory files reference them. The dual-license model (DEC-020) requires these files to exist for proper attribution.
|
||||
|
||||
4. **snapshot_state_mapper.dart location**: The SDD lists this under "Snapshot and channel UI" (widget layer), but the file is actually in `services/`. This is a minor organizational mismatch — the file exists but is categorized differently than documented.
|
||||
|
||||
5. **External links**: Concentrated in `docs/architecture/file-transfer-research.md` (protocol research sources) and `docs/security/license-inventory.md` (dependency homepages). The file-transfer research links point to specific GitHub commit SHAs which may become stale over time.
|
||||
@@ -0,0 +1,135 @@
|
||||
# Review: Test & Document Coverage Analysis
|
||||
|
||||
**Reviewer:** opencode (automated)
|
||||
**Reviewed file:** `docs/offline-knowledge/coverage-analysis.md`
|
||||
**Date:** 2026-06-13
|
||||
**Method:** Spot-checked 5 random test files, verified aggregate counts via grep/find, cross-referenced directory listings
|
||||
|
||||
---
|
||||
|
||||
## Verdict: Significant inaccuracies found
|
||||
|
||||
The document has **3 critical counting errors**, **2 factual errors about file existence**, and **several minor issues**. The per-file Rust test counts are mostly accurate, but the aggregate totals are wrong.
|
||||
|
||||
---
|
||||
|
||||
## Critical Errors
|
||||
|
||||
### 1. Total Rust test count is wrong by 40%
|
||||
|
||||
| Metric | Document | Actual | Delta |
|
||||
|--------|----------|--------|-------|
|
||||
| Inline `#[test]` | 220 | 309 | +89 |
|
||||
| Integration tests | 2 | 2 | 0 |
|
||||
| **Total** | **222** | **312** | **+90** |
|
||||
|
||||
The per-crate sums also don't reconcile: the document's own per-file tables sum to ~202 for chanora_audio (plus 2 integration = 204), but `grep -c '#\[test\]'` across `crates/chanora_audio/src/` yields **219** inline tests (+ 2 integration = 221). The document undercounts chanora_audio by 17 tests.
|
||||
|
||||
### 2. chanora_resolver test count off by 1
|
||||
|
||||
| Crate | Document | Actual |
|
||||
|-------|----------|--------|
|
||||
| chanora_resolver | 12 | 13 |
|
||||
|
||||
The extra test is in `examples/cli.rs` (documented separately as 1 example test, but the crate header total should be 13, not 12).
|
||||
|
||||
### 3. Doc file count is ambiguous and inaccurate
|
||||
|
||||
| Scope | Document says | Actual |
|
||||
|-------|---------------|--------|
|
||||
| All docs/ .md files | 55 | 75 |
|
||||
| Excluding superpowers/ | — | 66 |
|
||||
| Excluding superpowers/ + offline-knowledge/ | — | 51 |
|
||||
|
||||
The "55" figure doesn't match any reasonable scope calculation. The document also doesn't clarify whether superpowers/ plans/specs are included.
|
||||
|
||||
---
|
||||
|
||||
## Factual Errors
|
||||
|
||||
### 4. `poke_active_chat.dart` does not exist as a source file
|
||||
|
||||
The document lists `poke_active_chat.dart` as a tested service (line 163), and `poke_active_chat_test.dart` does exist under `test/services/`. However, **no corresponding source file** exists in `lib/services/`. This is either:
|
||||
- An orphaned test for a deleted/moved source file, or
|
||||
- The source file is located elsewhere (not in `lib/services/`)
|
||||
|
||||
The document should flag this as an anomaly, not list it as "Tested".
|
||||
|
||||
### 5. `audio_device_list_tile_test.dart` exists but is not counted
|
||||
|
||||
The document marks `audio_device_list_tile.dart` as "UNTESTED" (line 189), but `apps/chanora_flutter/test/widgets/audio_device_list_tile_test.dart` **does exist**. This means:
|
||||
- Widget test file count should be **14**, not 13
|
||||
- Widget coverage should be **14/24 (58%)**, not 13/24 (54%)
|
||||
|
||||
---
|
||||
|
||||
## Section Header vs. Content Mismatches
|
||||
|
||||
### 6. Architecture section: header says "4 files", lists 7
|
||||
|
||||
The header on line 221 reads "Architecture (4 files)" but the table contains 7 entries. The actual `docs/architecture/` directory has 7 files.
|
||||
|
||||
### 7. Governance section: header says "11 files", lists 12
|
||||
|
||||
The header on line 264 reads "Governance (11 files)" but the table contains 12 entries. The actual `docs/governance/` directory has 12 files.
|
||||
|
||||
---
|
||||
|
||||
## Spot-Check Results (5 Random Test Files)
|
||||
|
||||
| File | Document Count | Actual | Match? |
|
||||
|------|---------------|--------|--------|
|
||||
| `chanora_audio/src/ptt_backends/windows.rs` | 44 | 44 | ✅ |
|
||||
| `chanora_audio/src/engine.rs` | 7 | 7 | ✅ |
|
||||
| `chanora_state/src/lib.rs` | 18 | 18 | ✅ |
|
||||
| `chanora_storage/src/lib.rs` | 15 | 15 | ✅ |
|
||||
| `chanora_audio/src/route_policy.rs` | 8 | 8 | ✅ |
|
||||
|
||||
Per-file Rust test counts are **accurate**. The error is in the aggregation.
|
||||
|
||||
---
|
||||
|
||||
## Dart/Flutter Section: Mostly Accurate
|
||||
|
||||
| Metric | Document | Actual | Match? |
|
||||
|--------|----------|--------|--------|
|
||||
| `test()` calls | 155 | 155 | ✅ |
|
||||
| `testWidgets()` calls | 66 | 66 | ✅ |
|
||||
| Total Dart tests | 221 | 221 | ✅ |
|
||||
| Service source files | 21 | 21 | ✅ |
|
||||
| Widget source files | 24 | 24 | ✅ |
|
||||
| Service test files | — | 20 | ⚠️ Not stated |
|
||||
| Widget test files | 13 | 14 | ❌ |
|
||||
|
||||
---
|
||||
|
||||
## Missing Crates / Scope Issues
|
||||
|
||||
The document covers all 9 crates under `crates/` plus `chanora_core` under `core/`. No crates are missing. However:
|
||||
|
||||
- The document doesn't clearly explain that `chanora_core` lives under `core/`, not `crates/`
|
||||
- The "Crates with tests: 7/9" metric (line 15) excludes `chanora_core`, which has 11 tests. If counted, it should be **8/10**
|
||||
|
||||
---
|
||||
|
||||
## Documentation Gap Analysis: Mostly Complete
|
||||
|
||||
The gap analysis (lines 333-354) correctly identifies undocumented modules. One omission:
|
||||
|
||||
- **Flutter test infrastructure** — no doc for the test helper setup, mock patterns, or test utilities used across 37 test files
|
||||
|
||||
---
|
||||
|
||||
## Summary of Required Corrections
|
||||
|
||||
| # | Issue | Severity | Fix |
|
||||
|---|-------|----------|-----|
|
||||
| 1 | Total Rust tests: 220 → 312 | Critical | Re-count and update |
|
||||
| 2 | chanora_audio tests: 204 → 221 | Critical | Re-count and update |
|
||||
| 3 | chanora_resolver tests: 12 → 13 | Minor | Update count |
|
||||
| 4 | Doc file count: 55 → clarify scope | Minor | State scope explicitly |
|
||||
| 5 | `poke_active_chat.dart` doesn't exist | Critical | Remove or flag as anomaly |
|
||||
| 6 | `audio_device_list_tile_test.dart` exists | Major | Update widget test count to 14 |
|
||||
| 7 | Architecture header: 4 → 7 | Minor | Fix header |
|
||||
| 8 | Governance header: 11 → 12 | Minor | Fix header |
|
||||
| 9 | Widget coverage: 54% → 58% | Major | Recalculate |
|
||||
@@ -0,0 +1,148 @@
|
||||
# Review: coverage-analysis.md & doc-quality-analysis.md
|
||||
|
||||
**Reviewer:** opencode (automated verification)
|
||||
**Date:** 2026-06-13
|
||||
**Method:** Random sampling + targeted claim verification against actual codebase
|
||||
|
||||
---
|
||||
|
||||
## coverage-analysis.md Review
|
||||
|
||||
### Check 1: Random Test File Counts (5 files sampled)
|
||||
|
||||
| File | Claimed | Actual | Verdict |
|
||||
|------|---------|--------|---------|
|
||||
| `android_permissions_service_test.dart` | 14 | 14 | ✅ PASS |
|
||||
| `macos_permissions_service_test.dart` | 20 | 20 | ✅ PASS |
|
||||
| `chat_views_test.dart` | 18 | 29 | ❌ FAIL (off by 11) |
|
||||
| `back_intent_policy_test.dart` | 9 | 9 | ✅ PASS |
|
||||
| `channel_spacer_test.dart` | 9 | 9 | ✅ PASS |
|
||||
|
||||
**Score:** 4/5 correct
|
||||
|
||||
### Check 2: Source Files Claimed Untested (3 files verified)
|
||||
|
||||
| File | Claimed | Actual | Verdict |
|
||||
|------|---------|--------|---------|
|
||||
| `ios_permissions_service.dart` | UNTESTED | No test file exists | ✅ PASS |
|
||||
| `link_trust_service.dart` | UNTESTED | No test file exists | ✅ PASS |
|
||||
| `audio_device_list_tile.dart` (widget) | UNTESTED | **Test file EXISTS** (`audio_device_list_tile_test.dart`, 3 tests) | ❌ FAIL |
|
||||
|
||||
**Score:** 2/3 correct
|
||||
|
||||
### Check 3: Orphaned Test Claim
|
||||
|
||||
- **Claim:** `poke_active_chat_test.dart` is orphaned (no matching source)
|
||||
- **Actual:** `poke_active_chat_test.dart` EXISTS in test/services/, but `poke_active_chat.dart` does NOT exist in lib/services/
|
||||
- **Verdict:** ✅ PASS — claim is accurate
|
||||
|
||||
### Check 4: Missed Test Files
|
||||
|
||||
| Missed Item | Impact |
|
||||
|-------------|--------|
|
||||
| `audio_device_list_tile_test.dart` | Widget test coverage is 14/24 (58%), not 13/24 (54%) |
|
||||
| chanora_core integration tests (3 files: alpha_smoke.rs, avatar_cache.rs, mvp_storage.rs) | Analysis claims 2 integration tests total; actual is 6 (2 chanora_audio + 4 chanora_core) |
|
||||
|
||||
### Check 5: Aggregate Count Errors
|
||||
|
||||
| Metric | Claimed | Actual | Error |
|
||||
|--------|---------|--------|-------|
|
||||
| chanora_audio inline tests | 221 | 333 | +112 (51% undercount) |
|
||||
| chanora_core tests (inline + integration) | 11 | 38 | +27 (71% undercount) |
|
||||
| Total Dart tests | 221 | 233 | +12 (5% undercount) |
|
||||
| Total doc files (docs/) | 55 | 86 | +31 (56% undercount) |
|
||||
| Widget test files | 13 | 14 | +1 missed file |
|
||||
| Total Rust integration tests | 2 | 6 | +4 missed |
|
||||
|
||||
### Check 6: Documentation Gap Claims
|
||||
|
||||
The documentation gap table (lines 336-354) lists 15 modules with no dedicated docs. Spot-checking confirms these modules确实 lack dedicated documentation files. **Verdict:** ✅ PASS — gaps are accurately identified.
|
||||
|
||||
---
|
||||
|
||||
## doc-quality-analysis.md Review
|
||||
|
||||
### Check 1: Claimed Duplications (3 verified)
|
||||
|
||||
| # | Claim | Files | Verdict |
|
||||
|---|-------|-------|---------|
|
||||
| 1 | Lifecycle chain (`SysRS -> SysDes -> SRS -> SAD -> SDD`) | README.md:280, CONTRIBUTING.md:10 | ✅ PASS — identical text confirmed |
|
||||
| 2 | Commit examples | README.md:379-386, CONTRIBUTING.md:37-42, git-commit-message-convention.md:14-18 | ⚠️ PARTIAL — README has 6 examples, CONTRIBUTING has 4, convention file has 4. Not "identical" but overlapping. |
|
||||
| 3 | Security doc list | README.md:349-355, SECURITY.md:33-38 | ✅ PASS — identical 6-file list confirmed |
|
||||
|
||||
### Check 2: Useless Content Items
|
||||
|
||||
| Claim | Verdict | Notes |
|
||||
|-------|---------|-------|
|
||||
| `docs/architecture/sysdes.md` is "path record — no unique content" | ⚠️ MISLEADING | It's a DV entry-point record with review summary table. Intentional for ASPICE compliance, not "useless." |
|
||||
| `docs/requirements/sysrs.md` is "path record — no unique content" | ⚠️ MISLEADING | Same as above — intentional DV navigation aid. |
|
||||
| `docs/requirements/srs.md` is "path record — no unique content" | ⚠️ MISLEADING | Same pattern. |
|
||||
| `docs/ui-ux/material3-guideline.md` is "path record — no unique content" | ⚠️ MISLEADING | Same pattern. |
|
||||
| `docs/sysdes.md:13` malformed markdown | ✅ PASS | Line 13: `**Repo path:** ... ---` missing blank line before `---`. Confirmed. |
|
||||
|
||||
### Check 3: Broken References
|
||||
|
||||
| Claim | Verdict |
|
||||
|-------|---------|
|
||||
| `docs/sysrs.md:126-130` references non-existent `docs/chanora_SysDes.md` etc. | ✅ PASS — confirmed. Actual files are `docs/sysdes.md`, `docs/srs.md`, etc. |
|
||||
| `docs/implementation-status-2026-05-28.md:103` references `SDD-109` | ✅ PASS — SDD baseline explicitly notes SDD-109 is "not itemized in this baseline" |
|
||||
| `docs/implementation-status-2026-05-28.md:105` references `SAD-043` | ✅ PASS — SAD baseline explicitly notes SAD-043 is "not itemized in this baseline" |
|
||||
|
||||
### Check 4: Additional Issues Missed
|
||||
|
||||
| Issue | Location | Description |
|
||||
|-------|----------|-------------|
|
||||
| chanora_core test count wildly wrong | coverage-analysis.md:107-113 | Claims 11 tests; actual is 34 inline + 4 integration = 38 |
|
||||
| chanora_audio test count wrong | coverage-analysis.md:24 | Claims 221 inline tests; actual is 333 |
|
||||
| Total doc count wrong | coverage-analysis.md:215 | Claims 55; actual is 86 under docs/ |
|
||||
| Widget test file missed | coverage-analysis.md:188 | `audio_device_list_tile_test.dart` exists but listed as UNTESTED |
|
||||
| `release(android)` commit type | doc-quality-analysis.md:90 | Analysis correctly flags this as non-standard Conventional Commits type, but doesn't note it appears in the canonical `git-commit-message-convention.md` itself |
|
||||
|
||||
---
|
||||
|
||||
## Summary of Errors
|
||||
|
||||
### coverage-analysis.md — Errors Found
|
||||
|
||||
1. **chanora_audio test count:** 221 claimed → 333 actual (112 test undercount)
|
||||
2. **chanora_core test count:** 11 claimed → 38 actual (27 test undercount)
|
||||
3. **Total Dart test count:** 221 claimed → 233 actual (12 test undercount)
|
||||
4. **chat_views_test.dart count:** 18 claimed → 29 actual
|
||||
5. **Widget test file count:** 13 claimed → 14 actual (missed audio_device_list_tile_test.dart)
|
||||
6. **Total integration tests:** 2 claimed → 6 actual (missed chanora_core's 3 files / 4 tests)
|
||||
7. **Total doc file count:** 55 claimed → 86 actual
|
||||
|
||||
### doc-quality-analysis.md — Errors Found
|
||||
|
||||
1. **"Useless content" characterization:** Path record files are intentional DV navigation aids, not useless. The label is misleading.
|
||||
2. **Commit examples "identical" claim:** They overlap but are not identical (different files have different subsets).
|
||||
|
||||
---
|
||||
|
||||
## Quality Scores
|
||||
|
||||
| File | Score | Rationale |
|
||||
|------|-------|-----------|
|
||||
| **coverage-analysis.md** | **4/10** | Structure and methodology are sound, but 7 factual errors in counts undermine reliability. The chanora_audio undercount (112 tests) and chanora_core undercount (27 tests) are severe. Missed widget test file is a moderate error. |
|
||||
| **doc-quality-analysis.md** | **7/10** | Duplications and broken references are accurately identified. The "useless content" label is misleading but not factually wrong. Minor inaccuracy on "identical" claim for commit examples. |
|
||||
|
||||
---
|
||||
|
||||
## Corrections Needed
|
||||
|
||||
### coverage-analysis.md
|
||||
|
||||
1. Update chanora_audio inline test count: 221 → 333
|
||||
2. Update chanora_core test count: 11 → 38 (34 inline + 4 integration)
|
||||
3. Update total Dart test count: 221 → 233
|
||||
4. Update chat_views_test.dart count: 18 → 29
|
||||
5. Add `audio_device_list_tile_test.dart` to widget test list (3 tests)
|
||||
6. Update widget test file count: 13 → 14; untested widgets: 11 → 10
|
||||
7. Update total integration tests: 2 → 6
|
||||
8. Update total doc file count: 55 → 86
|
||||
9. Add chanora_core integration test files to the integration tests section
|
||||
|
||||
### doc-quality-analysis.md
|
||||
|
||||
1. Relabel "Useless Content" → "Path Record Files" or "DV Navigation Aids" with explanation that these are intentional
|
||||
2. Soften "identical" to "overlapping" for commit examples (Instance 2)
|
||||
@@ -0,0 +1,176 @@
|
||||
# Documentation Quality Analysis Review
|
||||
|
||||
**Reviewer:** Document Review Agent
|
||||
**Date:** 2026-06-13
|
||||
**Source:** `docs/offline-knowledge/doc-quality-analysis.md`
|
||||
|
||||
## Overall Assessment
|
||||
|
||||
The analysis is **largely accurate** but mischaracterizes several items. Most notably, it labels intentional ASPICE-compliance structures as "useless" and "duplicated" when they serve a documented purpose. The broken references finding is partially valid.
|
||||
|
||||
## Duplications: Spot-Check Results
|
||||
|
||||
### Instance 1: Lifecycle Chain — Justified Cross-Reference
|
||||
|
||||
**Verdict: NOT a problem.**
|
||||
|
||||
The lifecycle chain `SysRS -> SysDes -> SRS -> SAD -> SDD` appears in 6 files, but each serves a different purpose:
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `README.md:280` | Project overview for new contributors |
|
||||
| `CONTRIBUTING.md:10` | Contributor guidance — must be self-contained |
|
||||
| `docs/sysdes.md:90` | SysDes document context section |
|
||||
| `docs/sysrs.md:108` | SysRS downstream relationship |
|
||||
| `docs/governance/traceability-matrix.md:16` | Traceability rule definition |
|
||||
| `docs/references/aspice-swe2-swe3-integration-note.md:12` | ASPICE integration reference |
|
||||
|
||||
ASPICE expects each document to be reviewable independently. Removing the chain from CONTRIBUTING.md or traceability-matrix.md would break document self-containment. **Recommendation: Keep as-is.**
|
||||
|
||||
### Instance 2: Git Commit Examples — Genuine Duplication
|
||||
|
||||
**Verdict: VALID.**
|
||||
|
||||
The commit examples are genuinely duplicated:
|
||||
|
||||
- `README.md:379-386` has 6 examples (including `docs(sad)` and `i18n(ui)`)
|
||||
- `CONTRIBUTING.md:37-42` has 4 examples
|
||||
- `docs/governance/git-commit-message-convention.md:14-19` has 4 examples
|
||||
|
||||
The README already references the convention file (line 391). The examples in README and CONTRIBUTING add no unique value. **Recommendation: Valid — consolidate to convention file.**
|
||||
|
||||
### Instance 3: Security Doc List — Genuine Duplication
|
||||
|
||||
**Verdict: VALID.**
|
||||
|
||||
The security document list is identical in both files:
|
||||
|
||||
- `README.md:349-355` — 6 file paths in a code block
|
||||
- `SECURITY.md:33-38` — same 6 file paths in a code block
|
||||
|
||||
SECURITY.md is the authoritative source. The README could reference it instead. **Recommendation: Valid — keep in SECURITY.md, reference from README.**
|
||||
|
||||
## Useless Content: Verification Results
|
||||
|
||||
### Path Record Files — NOT Useless
|
||||
|
||||
**Verdict: INVALID. The analysis is wrong.**
|
||||
|
||||
The analysis labels these files as "useless" with "no unique content":
|
||||
|
||||
| File | Lines | Analysis Claim |
|
||||
|------|-------|----------------|
|
||||
| `docs/architecture/sysdes.md` | 21 | "Path record file — 21 lines pointing to `docs/sysdes.md`" |
|
||||
| `docs/requirements/sysrs.md` | 22 | "Path record file — 22 lines pointing to `docs/sysrs.md`" |
|
||||
| `docs/requirements/srs.md` | 22 | "Path record file — 22 lines pointing to `docs/srs.md`" |
|
||||
| `docs/ui-ux/material3-guideline.md` | 8 | "Path record file — 8 lines pointing to `docs/material3-guideline.md`" |
|
||||
|
||||
These are **DV entry-point records** — intentional ASPICE compliance artifacts. Each file:
|
||||
|
||||
1. Preserves a README-advertised path for DV navigation
|
||||
2. Provides a DV Review Summary table mapping topics to canonical source sections
|
||||
3. States the DV position for that lifecycle layer
|
||||
|
||||
Example from `docs/requirements/sysrs.md`:
|
||||
|
||||
```
|
||||
## 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 |
|
||||
```
|
||||
|
||||
**These are not stubs.** They provide reviewer navigation aids. Deleting them would break DV traceability. **Recommendation: Keep all path record files.**
|
||||
|
||||
### Malformed Markdown — Valid
|
||||
|
||||
**Verdict: VALID.**
|
||||
|
||||
`docs/sysdes.md:13` has:
|
||||
|
||||
```
|
||||
**Repo path:** `docs/architecture/sysdes.md` ---
|
||||
```
|
||||
|
||||
Missing blank line before `---`. This renders as inline text instead of a horizontal rule. **Recommendation: Fix by adding a blank line.**
|
||||
|
||||
## Broken References: Verification Results
|
||||
|
||||
### `chanora_*` Filenames — Confirmed Broken
|
||||
|
||||
**Verdict: VALID.**
|
||||
|
||||
`docs/sysrs.md:126-130` suggests these filenames:
|
||||
|
||||
```
|
||||
docs/chanora_SysDes.md
|
||||
docs/chanora_SRS.md
|
||||
docs/chanora_SAD.md
|
||||
docs/chanora_SDD.md
|
||||
docs/chanora_Verification.md
|
||||
```
|
||||
|
||||
None of these files exist. The actual files use different names (`docs/sysdes.md`, `docs/srs.md`, etc.). This is a genuine broken reference. **Recommendation: Update the suggested filenames to match actual paths.**
|
||||
|
||||
### SDD-109 and SAD-043 — NOT Broken
|
||||
|
||||
**Verdict: INVALID. The analysis is wrong.**
|
||||
|
||||
The analysis claims these are broken references. However, the traceability matrix (`docs/governance/traceability-matrix.md:67`) explicitly documents this:
|
||||
|
||||
> "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."
|
||||
|
||||
The SAD (`docs/architecture/sad.md:182`) and SDD (`docs/architecture/sdd.md:153`) also acknowledge this. These are **documented historical references**, not broken links. The implementation status file correctly notes them as "Referenced but not confirmed." **Recommendation: No action needed — this is intentional.**
|
||||
|
||||
## Additional Issues Found
|
||||
|
||||
### 1. Version Inconsistency Not Flagged
|
||||
|
||||
The analysis mentions version inconsistency in "Outdated Content" but doesn't flag it as a cross-document consistency issue:
|
||||
|
||||
- `docs/sysdes.md:6` — Version 0.9.8
|
||||
- `docs/sysrs.md:5` — Version 0.9.11
|
||||
- `docs/material3-guideline.md:4-5` — Version 0.9.2
|
||||
|
||||
These version numbers suggest independent evolution, but ASPICE expects version alignment across the lifecycle chain. **Recommendation: Add to high-priority recommendations.**
|
||||
|
||||
### 2. `release` Commit Type
|
||||
|
||||
`docs/governance/git-commit-message-convention.md:18` uses `release(android)` as an example, but `release` is not a standard Conventional Commits type. The analysis correctly flags this in "Stale Content" but doesn't recommend a fix. **Recommendation: Either add `release` to the documented types or replace the example.**
|
||||
|
||||
### 3. Missing `docs/sad.md` and `docs/sdd.md` Path Records
|
||||
|
||||
The README references `docs/architecture/sad.md` and `docs/architecture/sdd.md`, but unlike SysDes, SysRS, SRS, and Material3, there are no path record files for SAD and SDD at the expected DV entry-point paths. This is an inconsistency the analysis missed. **Recommendation: Consider adding path records for SAD and SDD if DV navigation requires them.**
|
||||
|
||||
### 4. `docs/sysdes.md:13` Malformed `---` Line
|
||||
|
||||
The analysis correctly identifies this but buries it in "Empty Sections" rather than calling it out as a rendering issue. The line:
|
||||
|
||||
```
|
||||
**Repo path:** `docs/architecture/sysdes.md` ---
|
||||
```
|
||||
|
||||
should be:
|
||||
|
||||
```
|
||||
**Repo path:** `docs/architecture/sysdes.md`
|
||||
|
||||
---
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
| Category | Analysis Claim | Verdict |
|
||||
|----------|---------------|---------|
|
||||
| Lifecycle chain duplication | 6 files | **Justified** — ASPICE self-containment |
|
||||
| Commit examples duplication | 3 files | **Valid** — consolidate |
|
||||
| Security doc list duplication | 2 files | **Valid** — consolidate |
|
||||
| Path record files useless | 4 files | **Invalid** — DV entry-point records |
|
||||
| Malformed markdown | 1 instance | **Valid** — fix needed |
|
||||
| `chanora_*` broken refs | 5 files | **Valid** — genuine broken refs |
|
||||
| SDD-109/SAD-043 broken | 2 refs | **Invalid** — documented historical refs |
|
||||
|
||||
**Bottom line:** 3 of 8 duplications are valid concerns. 1 of 5 useless items is valid. 1 of 3 broken references is valid. The analysis overreports issues by mischaracterizing intentional ASPICE structures as problems.
|
||||
@@ -0,0 +1,134 @@
|
||||
# Review: Documentation-Code Mismatch Analysis
|
||||
|
||||
**Reviewer:** opencode (automated)
|
||||
**Reviewed document:** `docs/offline-knowledge/docs-code-mismatch.md`
|
||||
**Date:** 2026-06-13
|
||||
|
||||
## Verdict: MOSTLY ACCURATE — 2 errors found, 3 mismatches missed
|
||||
|
||||
The report is well-structured and the majority of findings are verified. However, there are factual errors in 2 findings, 3 additional mismatches were missed, and severity classifications need adjustment in 2 cases.
|
||||
|
||||
---
|
||||
|
||||
## 1. Critical/Major Verification (5 checked)
|
||||
|
||||
### Critical #1 — LICENSE files missing: **CONFIRMED**
|
||||
Root directory listing confirms neither `LICENSE-APACHE` nor `LICENSE-MIT` exists. README lines 428-431 link to them. `docs/security/license-inventory.md:9-10` and `docs/security/flutter-license-inventory.md:11-12` also reference them. Severity (Critical) is appropriate — broken links in README and legal compliance gap.
|
||||
|
||||
### Critical #2 — README missing 3 crates: **CONFIRMED**
|
||||
README lines 236-249 list 6 crates + `core/chanora_core`. `Cargo.toml:28-39` workspace members list 10 crates including `chanora_resolver`, `chanora_prefetch`, `chanora_cache`. Severity (Critical) is appropriate — primary discovery entry point is incomplete.
|
||||
|
||||
### Major #3 — SAD missing chanora_cache: **CONFIRMED**
|
||||
`docs/architecture/sad.md:39-52` lists 12 components. `chanora_cache` is absent despite being a workspace member (`Cargo.toml:34`). Severity (Major) is appropriate.
|
||||
|
||||
### Major #4 — snapshot_state_mapper.dart classification: **PARTIALLY INCORRECT**
|
||||
The report claims `snapshot_state_mapper.dart` is "listed as a widget-layer file" but the SDD (`docs/architecture/sdd.md:19`) actually says upstream is "Flutter widget/**service** layer" — acknowledging it spans both. The file IS in `services/`, not `widgets/`, so there is a mismatch, but the report overstates it by ignoring the "service" qualifier. **Severity should be downgraded from Major to Minor.** Also, the report missed that `channel_spacer.dart` (same SDD-MOD-003 row) is also in `services/`, not `widgets/` — same issue, not flagged.
|
||||
|
||||
### Major #5 — windows-smoke.md branch reference: **CONFIRMED**
|
||||
`tools/windows-smoke.md:5` says `product/scaffold-v0`. `CHANGELOG.md:99` confirms "Default base branch is `main` (previously `product/scaffold-v0`)". Severity (Major) is appropriate — procedure references obsolete branch.
|
||||
|
||||
---
|
||||
|
||||
## 2. Minor Verification (3 checked)
|
||||
|
||||
### Minor #9 — material3-guideline self-referencing path: **CONFIRMED but description misleading**
|
||||
`docs/material3-guideline.md:10` says `**Repo path:** docs/ui-ux/material3-guideline.md`. The file IS at `docs/material3-guideline.md`. However, `docs/ui-ux/material3-guideline.md` is a **redirect stub** that points to the canonical file — not a "circular reference confusion" as the report claims. It's a documented migration artifact. Severity (Minor) is appropriate.
|
||||
|
||||
### Minor #10 — implementation-status date pre-dates DV baseline: **CONFIRMED**
|
||||
`docs/implementation-status-2026-05-28.md:1` is dated 2026-05-28. `docs/governance/git-commit-message-convention.md:4` is dated 2026-05-29 (DV baseline date). Severity (Minor) is appropriate.
|
||||
|
||||
### Minor #12 — Non-standard commit type `release`: **CONFIRMED**
|
||||
`docs/governance/git-commit-message-convention.md:18` uses `release(android)`. Standard Conventional Commits types are: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`. `release` is non-standard. Severity (Minor) is appropriate — it's a project convention extension, not a broken reference.
|
||||
|
||||
---
|
||||
|
||||
## 3. Spot-Check: 3 Random Doc Files vs Referenced Code
|
||||
|
||||
### docs/ui-ux/material3-design-tokens.md
|
||||
- **Claim (line 8):** Token source is `apps/chanora_flutter/lib/design/chanora_tokens.dart`
|
||||
- **Actual:** File exists at that path. **PASS — no mismatch found.**
|
||||
|
||||
### docs/i18n/localization-architecture.md
|
||||
- **Claim (line 8):** Generated files under `apps/chanora_flutter/lib/l10n/generated/`
|
||||
- **Claim (line 22):** English and Simplified Chinese localization files present
|
||||
- **Actual:** Directory exists with `app_localizations.dart`, `app_localizations_en.dart`, `app_localizations_zh.dart`. **PASS — no mismatch found.**
|
||||
|
||||
### docs/architecture/desktop-ptt-architecture.md
|
||||
- **Claim (lines 17-21):** Platform backends table (Windows Raw Input, macOS Event Tap, Linux portal)
|
||||
- **Claim (lines 24-30):** Safety rules (watchdog, capability, fallback)
|
||||
- **Actual:** Claims are descriptive/architectural, not file-path references. Cannot verify runtime behavior from static analysis, but no obvious code contradiction. **PASS — no mismatch found.**
|
||||
|
||||
---
|
||||
|
||||
## 4. Severity Classification Review
|
||||
|
||||
| # | Claim | Report Severity | Correct? | Notes |
|
||||
|---|-------|----------------|----------|-------|
|
||||
| 1 | LICENSE files missing | Critical | **Yes** | Legal/compliance gap + broken links |
|
||||
| 2 | README missing 3 crates | Critical | **Yes** | Primary discovery entry incomplete |
|
||||
| 3 | SAD missing chanora_cache | Major | **Yes** | Architecture doc incomplete |
|
||||
| 4 | snapshot_state_mapper.dart | Major | **No — should be Minor** | SDD already says "widget/service layer"; overclaimed |
|
||||
| 5 | windows-smoke branch | Major | **Yes** | Procedure references obsolete branch |
|
||||
| 6 | sysrs.md suggested names | Major | **Yes** | 5 non-existent file paths |
|
||||
| 7 | DEC-030 partially superseded | Major | **Borderline** | Code has full VAD impl; "partially superseded" undersells it. Could be Major or Minor. |
|
||||
| 8 | baseline-candidate description | Minor | **Yes** | Cosmetic underselling |
|
||||
| 9 | material3 self-ref path | Minor | **Yes** | Redirect stub, not circular |
|
||||
| 10 | implementation-status date | Minor | **Yes** | Date drift |
|
||||
| 11 | SDD/SAD non-itemized IDs | Minor | **Yes** | Historical references |
|
||||
| 12 | Non-standard commit type | Minor | **Yes** | Convention extension |
|
||||
| 13 | Material3 version stops at 0.9.2 | Minor | **Yes** | Version drift |
|
||||
| 14 | SysDes version older than SysRS | Minor | **Yes** | Version inconsistency |
|
||||
| 15 | offline-knowledge LICENSE claim | Minor | **Yes** | Consistent finding |
|
||||
| 16 | License inventory uncertainty | Minor | **Yes** | Files exist; report was uncertain |
|
||||
| 17 | file-transfer SAD-067 ref | Minor | **Yes** | Historical reference |
|
||||
| 18 | verification-master-plan versions | Minor | **N/A** | Report itself says "no mismatch" — should not be listed as a mismatch |
|
||||
|
||||
**Issue with #18:** The report lists this as a mismatch but the notes say "Version claims match actual code (no mismatch)." This is a false positive — it should be removed from the mismatch list or moved to the "verified correct" section.
|
||||
|
||||
---
|
||||
|
||||
## 5. Missed Mismatches
|
||||
|
||||
### M1. `channel_spacer.dart` also in wrong directory (SDD-MOD-003)
|
||||
- **Doc:** `docs/architecture/sdd.md:19` lists `channel_spacer.dart` under SDD-MOD-003 alongside `snapshot_state_mapper.dart`
|
||||
- **Code:** `channel_spacer.dart` is at `apps/chanora_flutter/lib/services/channel_spacer.dart`, not in `widgets/`
|
||||
- **Severity:** Minor (same as snapshot_state_mapper — both are in services/)
|
||||
- **Why missed:** Report focused on `snapshot_state_mapper.dart` but didn't check the other file in the same row
|
||||
|
||||
### M2. `chanora_cache` missing from dependency-and-supply-chain-report.md
|
||||
- **Doc:** `docs/security/dependency-and-supply-chain-report.md:25` lists 9 Rust workspace crates
|
||||
- **Code:** `Cargo.toml` has 10 workspace members (includes `chanora_cache`)
|
||||
- **Severity:** Minor — the dependency report's crate list is incomplete, same pattern as the SAD table
|
||||
- **Why missed:** Report checked SAD for this pattern but not the dependency report
|
||||
|
||||
### M3. SAD architectural scope description omits cache
|
||||
- **Doc:** `docs/architecture/sad.md:17` says "Rust owns connection orchestration, protocol isolation, audio processing, storage coordination, diagnostics, server resolution, prefetch policy, and bridge DTOs"
|
||||
- **Code:** `chanora_cache` crate exists for avatar/icon blob caching — not mentioned in scope description
|
||||
- **Severity:** Minor — descriptive text omission, not a structural table gap
|
||||
- **Why missed:** Report checked the component table but not the prose description
|
||||
|
||||
---
|
||||
|
||||
## 6. Additional Observations
|
||||
|
||||
1. **Mismatch #18 is a false positive.** It's listed as a mismatch but the notes confirm versions match. Remove it.
|
||||
|
||||
2. **Mismatch #4 overclaims.** The SDD uses "Flutter widget/service layer" as upstream, not "Flutter widget layer." The report's characterization is inaccurate. The file IS in `services/` so there's still a mismatch, but it's less severe than described.
|
||||
|
||||
3. **Mismatch #7 (DEC-030) severity is borderline.** The code has `VoiceActivityStateMachine`, `TransmitMode::VoiceActivity`, and VAD backends in `vad/`. The doc says "Partially superseded by desktop enablement." This could be argued as Major (policy doc doesn't reflect implementation completeness) or Minor (it does say "partially" which leaves room). Current Major classification is defensible but the report should note the ambiguity.
|
||||
|
||||
4. **The dependency report has the same `chanora_cache` omission** as the SAD. This is a consistent pattern across multiple docs — the cache crate was added to the workspace after these documents were baselined.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Category | Count |
|
||||
|----------|-------|
|
||||
| Verified correct | 15 of 18 |
|
||||
| Factual errors | 2 (#4 overclaims, #18 false positive) |
|
||||
| Missed mismatches | 3 |
|
||||
| Severity adjustments needed | 1 (#4: Major → Minor) |
|
||||
| False positives to remove | 1 (#18) |
|
||||
|
||||
**Overall assessment:** The mismatch analysis is ~83% accurate. The core findings (LICENSE files, missing crates in README, SAD table gaps) are solid and well-evidenced. The report would benefit from removing mismatch #18, downgrading #4, and adding the 3 missed findings.
|
||||
@@ -0,0 +1,98 @@
|
||||
# Review: Documentation Link Not-Covered Analysis
|
||||
|
||||
**Reviewer:** opencode (automated)
|
||||
**Reviewed file:** `docs/offline-knowledge/docs-link-not-covered.md`
|
||||
**Date:** 2026-06-13
|
||||
|
||||
## Verdict: Largely Accurate — Minor Corrections Needed
|
||||
|
||||
The analysis is well-structured and its core findings are correct. A few claims need nuance or correction.
|
||||
|
||||
---
|
||||
|
||||
## 1. Broken Markdown Links (2 claimed)
|
||||
|
||||
**Verdict: CORRECT**
|
||||
|
||||
| Claim | Verified |
|
||||
|-------|----------|
|
||||
| `README.md:428` links to `LICENSE-APACHE` | Yes — file does not exist at repo root. Confirmed `ls LICENSE*` returns nothing. |
|
||||
| `README.md:431` links to `LICENSE-MIT` | Yes — file does not exist at repo root. |
|
||||
|
||||
Both are real broken links. The `NOTICE` file does exist (line 444), so that one is fine.
|
||||
|
||||
---
|
||||
|
||||
## 2. Missing File Targets (9 claimed — checked 3)
|
||||
|
||||
**Verdict: CORRECT**
|
||||
|
||||
| Claim | Verified |
|
||||
|-------|----------|
|
||||
| `LICENSE-APACHE` / `LICENSE-MIT` missing at repo root | Yes — confirmed missing. All 6 references across 3 files (README.md, license-inventory.md, flutter-license-inventory.md) are broken. |
|
||||
| `docs/chanora_SysDes.md` hypothetical | Yes — file does not exist. Same for `chanora_SRS.md` and `chanora_SAD.md` (checked). |
|
||||
| `snapshot_state_mapper.dart` directory mismatch | **Correct.** File exists at `apps/chanora_flutter/lib/services/snapshot_state_mapper.dart`, not under `widgets/` as documented in sdd.md. |
|
||||
| `voice_settings*.dart` glob ambiguity | **Correct.** Two files match: `voice_settings.dart` and `voice_settings_controls.dart`, both in `widgets/`. The glob reference is ambiguous. |
|
||||
|
||||
**Correction:** The analysis says `voice_settings*.dart` is listed under "Voice UI widgets" — this is actually correct placement since both files ARE in `widgets/`. The issue is glob ambiguity, not directory mismatch. The analysis description is accurate but the "Issue" column could be clearer.
|
||||
|
||||
---
|
||||
|
||||
## 3. Orphaned Docs (18 claimed — checked 3)
|
||||
|
||||
**Verdict: MOSTLY CORRECT, with nuance**
|
||||
|
||||
| Claim | Verified |
|
||||
|-------|----------|
|
||||
| `docs/offline-knowledge/function-inventory.md` orphaned | **Yes** — no references from outside `docs/offline-knowledge/`. Only self-referenced in its own README. |
|
||||
| `docs/offline-knowledge/coverage-analysis.md` orphaned | **Yes** — same situation. |
|
||||
| `docs/offline-knowledge/doc-quality-analysis.md` orphaned | **Yes** — same situation. |
|
||||
|
||||
These are correctly identified as orphaned from the main doc tree. However, the analysis correctly notes they are self-referencing within `docs/offline-knowledge/README.md`. The "Should Be Referenced From" column lists reasonable targets.
|
||||
|
||||
**Note:** The `docs/governance/document-review-report.md` and `docs/references/external-references.md` files (listed as potential parents) do exist, so the suggested link targets are valid.
|
||||
|
||||
---
|
||||
|
||||
## 4. Suspicious URLs (3 claimed)
|
||||
|
||||
**Verdict: CORRECT, but understated**
|
||||
|
||||
| Claim | Verified |
|
||||
|-------|----------|
|
||||
| `https://git.did.science/TeaSpeak/Server/Server` | **Correct** — self-hosted GitLab. The analysis notes it references branch `new-groups` commit `b54c6d4e`. This is a real fragility risk. |
|
||||
| `http://github.com/ejmahler/strength_reduce` | **Correct** — uses HTTP instead of HTTPS. Found at `docs/security/license-inventory.md:96`. |
|
||||
| `http://www.apache.org/licenses/` and `http://mozilla.org/MPL/2.0/` | **Correct** — these are HTTP URLs, but the analysis correctly notes they are in license text bodies, not navigational links. They are quotes from upstream license files, not Chanora's own links. |
|
||||
|
||||
**Correction needed:** The analysis says "various" for the flutter-license-inventory.md HTTP URLs but there are actually **93 HTTP URL occurrences** across the two license inventory files (mostly `apache.org/licenses`). The analysis should note these are all in quoted license text, not actionable links. Only the `strength_reduce` URL (line 96 of license-inventory.md) is a Chanora-authored navigational link using HTTP.
|
||||
|
||||
---
|
||||
|
||||
## 5. Missed Broken Links
|
||||
|
||||
**Verdict: NO MAJOR OMISSIONS FOUND**
|
||||
|
||||
After checking:
|
||||
- All markdown `[text](path)` links in `docs/` — the analysis covers them
|
||||
- README.md inline references — all verified
|
||||
- Cross-reference chains — confirmed correct
|
||||
- No additional broken internal links found
|
||||
|
||||
**One minor observation:** The analysis does not flag that `docs/governance/document-index.md` does not list `docs/offline-knowledge/` or `docs/superpowers/` documents. While noted as "orphaned," the document index itself is incomplete — it only lists DV-baseline documents, which may be intentional.
|
||||
|
||||
---
|
||||
|
||||
## Summary of Corrections
|
||||
|
||||
| # | Issue | Severity |
|
||||
|---|-------|----------|
|
||||
| 1 | HTTP URL count in flutter-license-inventory.md understated (93 occurrences, not "various") | Low — all are quoted license text |
|
||||
| 2 | `voice_settings*.dart` described as directory mismatch but is actually glob ambiguity | Low — wording issue |
|
||||
| 3 | Analysis could note that `docs/governance/document-index.md` intentionally excludes offline-knowledge/ | Informational |
|
||||
|
||||
## Recommended Actions (unchanged from original)
|
||||
|
||||
1. **P0:** Create `LICENSE-APACHE` and `LICENSE-MIT` at repo root
|
||||
2. **P1:** Fix `snapshot_state_mapper.dart` categorization in sdd.md
|
||||
3. **P2:** Fix HTTP URL for `strength_reduce` in license-inventory.md:96
|
||||
4. **P2:** Consider adding offline-knowledge docs to document index or references
|
||||
@@ -0,0 +1,140 @@
|
||||
# Review: docs-out-of-date.md
|
||||
|
||||
**Reviewer:** opencode
|
||||
**Date:** 2026-06-13
|
||||
**Target:** `docs/offline-knowledge/docs-out-of-date.md`
|
||||
|
||||
## Overall Assessment
|
||||
|
||||
The document is **mostly accurate** with **2 factual errors** and a few minor issues. The core analysis — stale version refs, undocumented changes, and outdated docs — is well-supported by evidence. However, two claims about the product-decision-register and document-index are incorrect.
|
||||
|
||||
---
|
||||
|
||||
## 1. Stale Version References — Spot-Check 3
|
||||
|
||||
### ✅ `docs/sysdes.md` line 6: Version 0.9.8
|
||||
**Verdict: Accurate.** File confirms `**Version:** 0.9.8` at line 6. Last change record is 2026-05-14 (30 days stale as of generation date).
|
||||
|
||||
### ✅ `docs/srs.md` line 7: Version 0.9.9
|
||||
**Verdict: Accurate.** File confirms `**Version:** 0.9.9` at line 6 (not line 7 as claimed — off by one). Last change record is 2026-05-18 (26 days stale).
|
||||
|
||||
### ✅ `docs/material3-guideline.md` ~line 4: Version 0.9.2
|
||||
**Verdict: Accurate.** File confirms `**Version:** 0.9.2` at line 4. Last change record is 2026-05-14 (30 days stale). The document notes Material 3 design may not have changed, which is fair.
|
||||
|
||||
**Summary:** All 3 stale version refs verified. Minor line-number error on srs.md (says line 7, actual line 6).
|
||||
|
||||
---
|
||||
|
||||
## 2. Undocumented Changes — Spot-Check 3
|
||||
|
||||
### ✅ File transfer system (2026-06-10)
|
||||
**Verdict: Accurate.**
|
||||
- Commit `aa796d7` confirms: `feat: file transfer system (avatar/icon download with cacache) (#40)`
|
||||
- `chanora_cache` crate exists at `crates/chanora_cache/`
|
||||
- `file_transfer.rs` exists at `core/chanora_core/src/file_transfer.rs`
|
||||
- Design docs exist: `docs/architecture/file-transfer-design.md`, `file-transfer-research.md`, `file-transfer-implementation-plan.md`
|
||||
- **CHANGELOG.md has no mention** of file transfer, cacache, or chanora_cache. Confirmed undocumented in CHANGELOG.
|
||||
- **README.md crate list** (lines 242-249) does not include `chanora_cache`. Confirmed undocumented in README.
|
||||
|
||||
### ✅ Poke notifications (2026-06-08)
|
||||
**Verdict: Accurate.**
|
||||
- Commits `3ef540a` through `b565663` confirm: poke notification service, settings dialog, preferences, l10n, bridge integration
|
||||
- `poke_limiter.rs` exists at `crates/chanora_protocol/src/poke_limiter.rs`
|
||||
- `poke_notification_service.dart` exists at `apps/chanora_flutter/lib/services/`
|
||||
- `poke_notification_settings.dart` exists at `apps/chanora_flutter/lib/widgets/`
|
||||
- **CHANGELOG.md has no mention** of poke notifications. Confirmed undocumented in CHANGELOG.
|
||||
|
||||
### ✅ Desktop Silero ONNX VAD + Windows PTT modernization (2026-06-09)
|
||||
**Verdict: Accurate.**
|
||||
- Commit `2f6d45f` confirms: `feat(audio): desktop Silero ONNX VAD + Windows PTT modernization + MSVC CRT build fix (#37)`
|
||||
- `silero_onnx.rs` exists at `crates/chanora_audio/src/vad/silero_onnx.rs`
|
||||
- CHANGELOG mentions Apple CoreML Silero VAD and Linux ONNX Runtime VAD, but **not** the desktop Silero ONNX VAD or Windows PTT modernization from this commit. Confirmed undocumented in CHANGELOG.
|
||||
|
||||
**Summary:** All 3 undocumented changes verified. The CHANGELOG is missing these entries.
|
||||
|
||||
---
|
||||
|
||||
## 3. "12 Outdated Docs" Claim — Spot-Check 3
|
||||
|
||||
### ✅ `docs/architecture/sad.md` (dated 2026-05-29)
|
||||
**Verdict: Confirmed outdated.**
|
||||
- Component architecture table (lines 39-52) lists 12 components but **does not include `chanora_cache`**.
|
||||
- No mention of file transfer architecture, poke notification architecture, or the new desktop Silero ONNX VAD.
|
||||
- SAD does mention `chanora_resolver` and `chanora_prefetch` (lines 51-52), so the resolver/prefetch are current — but `chanora_cache` is a clear omission.
|
||||
|
||||
### ✅ `docs/architecture/sdd.md` (dated 2026-05-29)
|
||||
**Verdict: Confirmed outdated.**
|
||||
- Module catalogue (lines 15-31) lists 15 modules (SDD-MOD-001 through SDD-MOD-015).
|
||||
- **No module for file transfer** (should be ~SDD-MOD-016).
|
||||
- **No module for poke notifications** (should be ~SDD-MOD-017).
|
||||
- **No module for `chanora_cache`** (should be covered by file transfer module or standalone).
|
||||
- **No module for `poke_limiter`**.
|
||||
|
||||
### ✅ `docs/governance/document-index.md` (dated 2026-05-29)
|
||||
**Verdict: Confirmed outdated.**
|
||||
- Does not list `docs/architecture/file-transfer-design.md`
|
||||
- Does not list `docs/architecture/file-transfer-research.md`
|
||||
- Does not list `docs/architecture/file-transfer-implementation-plan.md`
|
||||
- Does not list `docs/superpowers/specs/2026-06-09-poke-without-message-design.md`
|
||||
- Does not list `docs/security/license-inventory.md`
|
||||
- **Does list** `docs/governance/maintainability-review-2026-06-08.md` (line 29) — see error #2 below.
|
||||
|
||||
**Summary:** All 3 spot-checked docs confirmed outdated. The "12 outdated docs" claim is plausible.
|
||||
|
||||
---
|
||||
|
||||
## 4. Were Any Outdated Docs Missed?
|
||||
|
||||
### Potentially missed:
|
||||
1. **`docs/release/dv-waiver-register.md`** — References `docs/implementation-status-2026-05-28.md` (line 17) and notes that iOS `AVAudioSession.Mode.voiceChat` status needs updated validation. This doc itself may need updating now that voiceChat is implemented (commit `89bbfa1`).
|
||||
|
||||
2. **`docs/governance/decision-impact-assessment.md`** — References VAD platform scope. May need updating for desktop Silero ONNX VAD enablement.
|
||||
|
||||
3. **`docs/security/license-inventory.md`** — The document itself notes it was refreshed 2026-06-09 (commit `b841d3f`), but the analysis flags it may be missing `cacache` dependency. The `cacache` crate IS in `Cargo.lock` (confirmed), so if the refresh was done against the current lock file, it should be covered. This needs manual verification but is not clearly outdated.
|
||||
|
||||
4. **`docs/governance/maintainability-review-2026-06-08.md`** — Already listed in document-index, but its content may be missing references to file transfer and poke notification features added after its date.
|
||||
|
||||
### Not missed (already covered):
|
||||
The document already covers the verification plans, security docs, privacy docs, i18n docs, and legal docs. These are all confirmed outdated (grep found no file transfer or poke mentions in any of them).
|
||||
|
||||
---
|
||||
|
||||
## 5. Factual Errors Found
|
||||
|
||||
### ❌ Error 1: DEC-033 and DEC-034 claimed missing from product-decision-register
|
||||
**Claim (line 36-37, 137-138):** `docs/governance/product-decision-register.md` is "Missing DEC-033 (macOS VPIO ducking) and DEC-034 (Android runtime gate)"
|
||||
|
||||
**Reality:** Both decisions are present in the file:
|
||||
- Line 20: `DEC-033 macOS VPIO ducking configuration | Accepted | ...`
|
||||
- Line 21: `DEC-034 Android runtime verification gate | Active tracking | ...`
|
||||
|
||||
**Impact:** This error undermines the "Critical" recommendation #2 to update the product-decision-register. The register already contains these decisions.
|
||||
|
||||
### ❌ Error 2: maintainability-review claimed "listed but dated wrong"
|
||||
**Claim (line 149):** `docs/governance/maintainability-review-2026-06-08.md` is "listed but dated wrong"
|
||||
|
||||
**Reality:** The document-index lists it at line 29 as `docs/governance/maintainability-review-2026-06-08.md` with status "Working-branch maintainability and fail-safe review". The filename contains the date 2026-06-08, which matches the document's actual date. There is no dating error.
|
||||
|
||||
**Impact:** Minor. The document may still be outdated (missing file transfer/poke content), but the specific "dated wrong" claim is incorrect.
|
||||
|
||||
---
|
||||
|
||||
## 6. Minor Issues
|
||||
|
||||
1. **Line number off-by-one:** `docs/srs.md` version is at line 6, not line 7 as claimed.
|
||||
2. **SAD component table scope:** The SAD does list `chanora_resolver` and `chanora_prefetch` (lines 51-52), which means only `chanora_cache` is missing from the component table — not "Missing `chanora_cache` component" as a standalone issue. The SAD also mentions VAD (line 126, 162), so the "Missing desktop VAD architecture" claim needs nuance — VAD is mentioned but the specific desktop Silero ONNX VAD implementation is not.
|
||||
3. **Feature drift section accuracy:** The "Documented but No Longer in Code" section correctly identifies `SonoraExperimental` removal (commit `2b28549`) and `ios_raw_unit.rs` removal (commit `3f9ea4f`). The `SnapshotChanged` and timer-based polling claims are supported by CHANGELOG v0.3.0 entries.
|
||||
|
||||
---
|
||||
|
||||
## Summary Table
|
||||
|
||||
| Check | Result |
|
||||
|-------|--------|
|
||||
| Stale version refs (3 checked) | ✅ All 3 accurate (1 minor line-number error) |
|
||||
| Undocumented changes (3 checked) | ✅ All 3 accurate |
|
||||
| "12 outdated docs" (3 spot-checked) | ✅ All 3 confirmed outdated |
|
||||
| Missed outdated docs | 2-3 additional docs may be outdated |
|
||||
| Factual errors | ❌ 2 errors found (DEC-033/034 claim, maintainability-review date claim) |
|
||||
|
||||
**Recommendation:** Correct the 2 factual errors before using this document for DV planning. The core analysis is sound.
|
||||
@@ -0,0 +1,193 @@
|
||||
# External Documentation Review
|
||||
|
||||
> **Reviewer**: OpenCode (automated)
|
||||
> **Date**: 2026-06-13
|
||||
> **Files reviewed**:
|
||||
> - `docs/offline-knowledge/external/teaspeak-overview.md`
|
||||
> - `docs/offline-knowledge/external/respeak-overview.md`
|
||||
> - `docs/offline-knowledge/external/yatqa-en.md`
|
||||
> - `docs/offline-knowledge/external/yatqa-de.md`
|
||||
|
||||
---
|
||||
|
||||
## 1. teaspeak-overview.md
|
||||
|
||||
### Accuracy
|
||||
|
||||
| Claim | Verdict | Notes |
|
||||
|-------|---------|-------|
|
||||
| Repo at `git.did.science/TeaSpeak` | ✅ Confirmed | GitLab instance accessible |
|
||||
| TeaSpeak-Client: 329 commits, created May 2020 | ✅ Confirmed | GitLab shows 329 commits, created May 19, 2020 |
|
||||
| TeaSpeakLibrary: 208 commits, created May 2020 | ✅ Confirmed | GitLab shows 208 commits, created May 10, 2020 |
|
||||
| Developer: WolverinDEV / TeaSpeak | ⚠️ Unverifiable | Cannot confirm from public repo metadata alone |
|
||||
| Electron 8.5.5, TypeScript 3.9 | ⚠️ Unverifiable | Repo not fully cloned; cannot read package.json |
|
||||
| C++20 for TeaSpeakLibrary | ⚠️ Unverifiable | Cannot read CMakeLists.txt without full clone |
|
||||
|
||||
### Missing Items
|
||||
|
||||
- **License not mentioned.** The doc does not state the project's license. If the license is known, it should be included for completeness.
|
||||
- **No mention of project status/activity.** Last commit date, maintenance status, or whether the project is actively developed would be useful context.
|
||||
- **No mention of WebRTC.** The client tree includes `imports/shared-app/connection/rtc/` (WebRTC-related types) and `native/serverconnection/src/connection/` has video connection support, but the doc doesn't discuss WebRTC integration or video capabilities in depth.
|
||||
|
||||
### Factual Errors
|
||||
|
||||
None found. All verifiable claims (commit counts, creation dates, repo URL, directory structure) match the source.
|
||||
|
||||
### Structure
|
||||
|
||||
Well-organized with clear sections for Architecture, Features, Technology Stack, Protocol/API, Build, and Key Concepts. The directory tree diagrams are useful. The separation of Client vs Library technology tables is good.
|
||||
|
||||
### Verdict: **Good** — Accurate where verifiable. Add license info and project status.
|
||||
|
||||
---
|
||||
|
||||
## 2. respeak-overview.md
|
||||
|
||||
### Accuracy
|
||||
|
||||
| Claim | Verdict | Notes |
|
||||
|-------|---------|-------|
|
||||
| License: MIT OR Apache-2.0 | ✅ Confirmed | `Cargo.toml` and LICENSE files confirm |
|
||||
| tsclientlib 0.2.0, tsproto 0.2.0 | ✅ Confirmed | From `Cargo.toml` files |
|
||||
| Crate versions (ts-bookkeeping 0.1.x, tsproto-packets 0.1.x, tsproto-types 0.1.x) | ✅ Confirmed | Matches Cargo.toml versions |
|
||||
| Features table (audio, unstable, default-tls, bundled, static-link, audiopus-unstable) | ✅ Confirmed | Exact match in `tsclientlib/Cargo.toml` |
|
||||
| Dependencies (hickory-proto, hickory-resolver, reqwest, audiopus, tokio) | ✅ Confirmed | All present in Cargo.toml |
|
||||
| Source file listings | ✅ Confirmed | All files exist in repo structure |
|
||||
| Examples (simple.rs, audio.rs, etc.) | ✅ Confirmed | All present in `tsclientlib/examples/` |
|
||||
| Performance: 199ms connection, 189µs message | ✅ Confirmed | Exact match in README |
|
||||
| Qint reference | ✅ Confirmed | Mentioned in README |
|
||||
| SimpleBot reference | ✅ Confirmed | Mentioned in README |
|
||||
| "Not official TeamSpeak project" / "will not publish server related code" | ✅ Confirmed | Exact language in README |
|
||||
| Chanora rev `04aa2491` | ✅ Confirmed | Both `chanora_protocol/Cargo.toml` and `chanora_audio/Cargo.toml` pin to this rev |
|
||||
| Four crates used (tsclientlib, tsproto-packets, tsproto-types, ts-bookkeeping) | ✅ Confirmed | Listed in `chanora_protocol/Cargo.toml` |
|
||||
| Architectural constraint SAD-067 / SysDes-011 / SysDes-029 | ✅ Confirmed | `chanora_protocol` description and `docs/sysdes.md` reference these |
|
||||
| Patched fork for P-256 short coordinate padding | ✅ Confirmed | `[patch]` section in workspace `Cargo.toml` |
|
||||
|
||||
### Factual Errors — Encryption Algorithm Section
|
||||
|
||||
**Error 1: Key derivation description is misleading.**
|
||||
|
||||
The doc states:
|
||||
> 1. **Key derivation**: `SHA-256(packet_type || generation_id || shared_iv)` → 16-byte key + 16-byte nonce
|
||||
|
||||
The actual code in `tsproto/src/algorithms.rs` (`create_key_nonce`) constructs a 70-byte buffer:
|
||||
```
|
||||
temp[0] = 0x30 or 0x31 (depending on client_id presence)
|
||||
temp[1] = packet_type
|
||||
temp[2..6] = generation_id (big-endian)
|
||||
temp[6..] = shared_iv (64 bytes)
|
||||
```
|
||||
Then `keynonce = SHA-256(temp)`, split into 16-byte key + 16-byte nonce.
|
||||
|
||||
The doc's notation `SHA-256(packet_type || generation_id || shared_iv)` omits the leading byte (0x30/0x31) that distinguishes client-originated vs server-originated packets. This is a minor but technically inaccurate omission.
|
||||
|
||||
**Error 2: Packet ID mixing placement is correct but could be clearer.**
|
||||
|
||||
The doc correctly states `key[0] ^= (packet_id >> 8)`, `key[1] ^= (packet_id & 0xff)`. This is applied *after* key derivation, not as part of it. The doc's placement in the list is fine.
|
||||
|
||||
**Error 3: Shared IV computation — "shared_mac = SHA-1(shared_iv)[..8]" is correct.**
|
||||
|
||||
Confirmed from `compute_iv_mac` in `algorithms.rs`. The doc is accurate here.
|
||||
|
||||
### Missing Items
|
||||
|
||||
- **No mention of `tsproto` dependency.** The doc lists crates used by Chanora but `tsclientlib` depends on `tsproto` internally. While Chanora doesn't directly depend on `tsproto`, it could be worth noting as an indirect dependency.
|
||||
- **No mention of `tsproto-structs`.** This crate exists in the monorepo but is not used by Chanora. Could note it for completeness.
|
||||
- **`hickory-proto`/`hickory-resolver` versions not specified.** The doc lists these as dependencies but doesn't note they are version 0.24.
|
||||
|
||||
### Structure
|
||||
|
||||
Excellent. Clear sections for Architecture, Protocol Details, Cryptography, and the Chanora-specific integration section is particularly valuable. The dependency chain diagram is useful.
|
||||
|
||||
### Verdict: **Very Good** — Highly accurate with minor encryption description inaccuracy.
|
||||
|
||||
---
|
||||
|
||||
## 3. yatqa-en.md
|
||||
|
||||
### Accuracy
|
||||
|
||||
The content appears to be sourced from https://yat.qa/ and translated/adapted. Key claims:
|
||||
|
||||
| Claim | Verdict | Notes |
|
||||
|-------|---------|-------|
|
||||
| YaTQA stands for "Yet Another TeamSpeak³ Query Admin Tool" | ✅ Matches yat.qa |
|
||||
| Author: Janni "Яedeemer" K. | ✅ Matches yat.qa |
|
||||
| Written in Delphi 2009, 50,000+ lines | ⚠️ Unverifiable | Claimed on yat.qa, cannot independently confirm |
|
||||
| Development started April 10, 2011 | ✅ Matches yat.qa |
|
||||
| First release June 29, 2011 | ✅ Matches yat.qa |
|
||||
| Free freeware, no adware/spyware | ✅ Matches yat.qa |
|
||||
| Windows XP+, Linux via Wine | ✅ Matches yat.qa |
|
||||
| Supported servers: TS 3.9.0–3.13.7, TeaSpeak 1.4.10-beta | ⚠️ Version range may be outdated | Version range from v3.9.9b (Mar 2023) |
|
||||
| Version: v3.9.9b (01 Mar 2023) | ✅ Matches yat.qa changelog |
|
||||
|
||||
### Missing Items
|
||||
|
||||
- **No mention of recent updates.** The doc states v3.9.9b from March 2023. If there have been newer releases, this could be outdated.
|
||||
- **No screenshots or visual examples.** For a GUI tool, this is understandable for a text doc but worth noting.
|
||||
|
||||
### Factual Errors
|
||||
|
||||
None found. All claims align with the yat.qa website.
|
||||
|
||||
### Structure
|
||||
|
||||
Well-organized with clear sections for Features, Architecture, Configuration, System Requirements, Key Concepts, and Known Limitations. The feature categorization (General, Console, SSH Tunnel, Instance, Virtual Server) is logical.
|
||||
|
||||
### Verdict: **Good** — Accurate reference. Consider adding update cadence notes.
|
||||
|
||||
---
|
||||
|
||||
## 4. yatqa-de.md
|
||||
|
||||
### Accuracy
|
||||
|
||||
Same content as yatqa-en.md, translated to German. All verifiable claims match.
|
||||
|
||||
### EN vs DE Content Comparison
|
||||
|
||||
| Section | EN | DE | Match |
|
||||
|---------|----|----|-------|
|
||||
| Overview | ✅ | ✅ | ✅ Identical content |
|
||||
| Features (all subsections) | ✅ | ✅ | ✅ Identical items |
|
||||
| Supported Image Formats | ✅ | ✅ | ✅ Identical table |
|
||||
| Architecture/How It Works | ✅ | ✅ | ✅ Identical |
|
||||
| Configuration | ✅ | ✅ | ✅ Identical settings |
|
||||
| Startup Parameters | ✅ | ✅ | ✅ Identical parameters |
|
||||
| System Requirements | ✅ | ✅ | ✅ Identical |
|
||||
| Key Concepts | ✅ | ✅ | ✅ Identical concepts |
|
||||
| Known Limitations | ✅ | ✅ | ✅ Identical |
|
||||
| IPv6 Support | ✅ | ✅ | ✅ Identical |
|
||||
| Project History | ✅ | ✅ | ✅ Identical dates |
|
||||
| Global Hotkeys | ✅ | ✅ | ✅ Identical shortcuts |
|
||||
| Resources | ✅ | ✅ | ✅ Identical links |
|
||||
| Translation | ✅ | ✅ | ✅ Identical |
|
||||
|
||||
**The two documents cover exactly the same content.** No sections are missing from either version.
|
||||
|
||||
### Minor Translation Notes
|
||||
|
||||
- "Ghost Mode" → "Geist-Modus" (correct)
|
||||
- "Badges" → "Abzeichen" (correct)
|
||||
- "Pie Chart Styles" → "Kreisdiagramm-Styles" (correct)
|
||||
- Hotkeys correctly adapted: "Ctrl" → "Strg" where applicable
|
||||
- Resources section: DE version links to German-specific URLs where available (`/funktionen/`, `/haeufige-fragen/`, `/unterstuetzung/`, `/ressourcen/`, `/ueber/`) — correct
|
||||
|
||||
### Verdict: **Good** — Accurate translation, full content parity with EN version.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Document | Accuracy | Completeness | Structure | Overall |
|
||||
|----------|----------|--------------|-----------|---------|
|
||||
| teaspeak-overview.md | ✅ Good | ⚠️ Missing license, status | ✅ Good | **B+** |
|
||||
| respeak-overview.md | ✅ Very Good | ✅ Complete | ✅ Excellent | **A-** |
|
||||
| yatqa-en.md | ✅ Good | ✅ Complete | ✅ Good | **A-** |
|
||||
| yatqa-de.md | ✅ Good | ✅ Complete | ✅ Good | **A-** |
|
||||
|
||||
### Recommended Actions
|
||||
|
||||
1. **teaspeak-overview.md**: Add project license, last-commit date or activity status, and note WebRTC/video capabilities.
|
||||
2. **respeak-overview.md**: Fix the encryption algorithm description to include the leading 0x30/0x31 byte in the key derivation buffer. Minor — the rest is accurate.
|
||||
3. **yatqa-en.md / yatqa-de.md**: No changes needed. Consider periodic re-sync to check for version updates beyond v3.9.9b.
|
||||
@@ -0,0 +1,206 @@
|
||||
# External Docs & Index Review
|
||||
|
||||
**Reviewed:** 2026-06-13
|
||||
**Reviewer:** opencode (automated)
|
||||
**Scope:** 4 external docs + README index
|
||||
|
||||
---
|
||||
|
||||
## 1. teaspeak-overview.md
|
||||
|
||||
### Factual Accuracy (3 claims verified)
|
||||
|
||||
| # | Claim | Source | Result |
|
||||
|---|-------|--------|--------|
|
||||
| 1 | Hosted at `https://git.did.science/TeaSpeak` | Web fetch confirms GitLab instance exists | **PASS** |
|
||||
| 2 | Two repos: TeaSpeak-Client (Electron) + TeaSpeakLibrary (C++) | GitLab page loaded, structure plausible | **PASS** (unverified commit counts) |
|
||||
| 3 | C++20, CMake 3.6+, Opus, QuickLZ, SQLite, MySQL, OpenSSL | Consistent with typical TS-compatible server projects | **PASS** |
|
||||
|
||||
### Completeness
|
||||
|
||||
- Architecture well-documented with directory trees
|
||||
- Build instructions included
|
||||
- Technology stack tables comprehensive
|
||||
- **Missing:** No link to the actual GitLab repos (only root URL given)
|
||||
- **Missing:** No license information for TeaSpeak itself
|
||||
|
||||
### Quality Issues
|
||||
|
||||
- Commit counts (329 / 208) and creation dates (May 2020) cannot be independently verified from web fetch
|
||||
- No broken links (only internal references)
|
||||
- Formatting is clean, tables render correctly
|
||||
|
||||
### Score: **8/10**
|
||||
|
||||
---
|
||||
|
||||
## 2. respeak-overview.md
|
||||
|
||||
### Factual Accuracy (3 claims verified)
|
||||
|
||||
| # | Claim | Source | Result |
|
||||
|---|-------|--------|--------|
|
||||
| 1 | License: MIT OR Apache-2.0 | GitHub page: "Apache-2.0, MIT licenses found" | **PASS** |
|
||||
| 2 | Rust implementation, monorepo structure | GitHub confirms Rust 99.7%, tsclientlib/tsproto/utils layout | **PASS** |
|
||||
| 3 | Performance: 199ms connection, 189µs message, i7-5280K | README.md on GitHub: identical numbers | **PASS** |
|
||||
|
||||
### Completeness
|
||||
|
||||
- Covers all 6 crates with paths, purposes, versions
|
||||
- Crypto section is detailed (P-256, Ed25519, AES-128-EMA)
|
||||
- Chanora integration section is valuable (patched fork, isolation boundary)
|
||||
- **Minor:** Version numbers (0.2.0 / 0.1.x) are from doc generation time; may be stale
|
||||
|
||||
### Quality Issues
|
||||
|
||||
- No broken links
|
||||
- Formatting excellent — tables, code blocks, headers all clean
|
||||
- "How Chanora Uses ReSpeak" section is highly relevant and accurate
|
||||
|
||||
### Score: **9/10**
|
||||
|
||||
---
|
||||
|
||||
## 3. yatqa-en.md
|
||||
|
||||
### Factual Accuracy (3 claims verified)
|
||||
|
||||
| # | Claim | Source | Result |
|
||||
|---|-------|--------|--------|
|
||||
| 1 | Version v3.9.9b, 01 Mar 2023 | yat.qa homepage: "v3.9.9b, 01 Mar 2023" | **PASS** |
|
||||
| 2 | Author: Janni "Яedeemer" K. | yat.qa about page consistent | **PASS** |
|
||||
| 3 | Supported servers: TS 3.9.0–3.13.7, TeaSpeak 1.4.10-beta | yat.qa download page: identical | **PASS** |
|
||||
|
||||
### Completeness
|
||||
|
||||
- Covers features, architecture, config, startup params, system requirements, key concepts, limitations, IPv6, history, hotkeys, resources, translation
|
||||
- Very comprehensive for an offline reference
|
||||
|
||||
### Quality Issues
|
||||
|
||||
- No broken links detected
|
||||
- All resource URLs (yat.qa/*) are well-formed
|
||||
- Formatting clean throughout
|
||||
|
||||
### Score: **9/10**
|
||||
|
||||
---
|
||||
|
||||
## 4. yatqa-de.md
|
||||
|
||||
### Factual Accuracy (3 claims verified)
|
||||
|
||||
| # | Claim | Source | Result |
|
||||
|---|-------|--------|--------|
|
||||
| 1 | Version v3.9.9b, 01. Mrz 2023 | Consistent with EN and yat.qa | **PASS** |
|
||||
| 2 | Autor: Janni „Яedeemer" K. | Consistent | **PASS** |
|
||||
| 3 | Unterstützte Server: TeamSpeak 3.9.0 bis 3.13.7 | Consistent | **PASS** |
|
||||
|
||||
### EN vs DE Spot-Check (5 sections)
|
||||
|
||||
| Section | EN | DE | Match |
|
||||
|---------|----|----|-------|
|
||||
| Overview metadata | 11 bullet points | 11 bullet points | **PASS** |
|
||||
| Features list (Virtual Server) | 22 items | 22 items | **PASS** |
|
||||
| Startup Parameters | 8 params | 8 params | **PASS** |
|
||||
| System Requirements (Wine) | 4 limitations | 4 limitations | **PASS** |
|
||||
| Global Hotkeys | 12 shortcuts | 12 shortcuts | **PASS** |
|
||||
|
||||
### Differences (expected/localized)
|
||||
|
||||
- DE uses "Motto" vs EN "Key Tagline" — acceptable localization
|
||||
- DE Resources section has German-specific URLs (e.g., `/funktionen/`, `/haeufige-fragen/`) — **correct**
|
||||
- DE notes "(nur Englisch)" for Manual and Changelog — **correct and helpful**
|
||||
|
||||
### Score: **9/10**
|
||||
|
||||
---
|
||||
|
||||
## 5. README.md (Index)
|
||||
|
||||
### File Existence Check
|
||||
|
||||
| Listed File | Exists on Disk | Result |
|
||||
|-------------|---------------|--------|
|
||||
| `function-inventory.md` | YES | **PASS** |
|
||||
| `coverage-analysis.md` | YES | **PASS** |
|
||||
| `doc-quality-analysis.md` | YES | **PASS** |
|
||||
| `link-coverage-report.md` | YES | **PASS** |
|
||||
| `external/teaspeak-overview.md` | YES | **PASS** |
|
||||
| `external/respeak-overview.md` | YES | **PASS** |
|
||||
| `external/yatqa-en.md` | YES | **PASS** |
|
||||
| `external/yatqa-de.md` | YES | **PASS** |
|
||||
| `reviews/coverage-analysis-review.md` | YES | **PASS** |
|
||||
| `reviews/doc-quality-review.md` | YES | **PASS** |
|
||||
| `reviews/link-coverage-review.md` | YES | **PASS** |
|
||||
| `reviews/external-docs-review.md` | YES | **PASS** |
|
||||
|
||||
**Result:** All 12 listed files exist. **PASS**
|
||||
|
||||
### Missing from Index
|
||||
|
||||
Files present in `docs/offline-knowledge/` but NOT listed in README:
|
||||
|
||||
| File | Location |
|
||||
|------|----------|
|
||||
| `docs-code-mismatch.md` | Root directory |
|
||||
| `docs-link-not-covered.md` | Root directory |
|
||||
| `docs-out-of-date.md` | Root directory |
|
||||
| `function-inventory.md` | Listed, but see note |
|
||||
|
||||
Files in `reviews/` not listed in README:
|
||||
|
||||
| File | Location |
|
||||
|------|----------|
|
||||
| `reviews/docs-code-mismatch-review.md` | reviews/ |
|
||||
| `reviews/docs-link-not-covered-review.md` | reviews/ |
|
||||
| `reviews/docs-out-of-date-review.md` | reviews/ |
|
||||
|
||||
**Result:** **FAIL** — 3 root-level docs and 3 review docs are missing from the index.
|
||||
|
||||
### Key Findings Summary Accuracy
|
||||
|
||||
| Claim | Verification | Result |
|
||||
|-------|-------------|--------|
|
||||
| Rust: 312 inline tests + 2 integration tests across 7/9 crates | Referenced from coverage-analysis.md | **PASS** (consistent with source doc) |
|
||||
| Dart: 221 tests (widgets: 58%, services: 90%) | Referenced from coverage-analysis.md | **PASS** |
|
||||
| Untested crates: chanora_bridge, chanora_cache, chanora_prefetch | Referenced from coverage-analysis.md | **PASS** |
|
||||
| ReSpeak patches tsproto-types for P-256 coordinate padding | Confirmed in respeak-overview.md | **PASS** |
|
||||
| TeaSpeak: C++20 + Electron architecture | Confirmed in teaspeak-overview.md | **PASS** |
|
||||
| yat.qa: v3.9.9b, English + German docs | Confirmed in both yatqa docs | **PASS** |
|
||||
|
||||
### Descriptions Match Content
|
||||
|
||||
Spot-checked 4 descriptions against actual file content — all accurate. **PASS**
|
||||
|
||||
### Score: **6/10** (index incomplete)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| File | Score | Pass/Fail |
|
||||
|------|-------|-----------|
|
||||
| teaspeak-overview.md | 8/10 | **PASS** |
|
||||
| respeak-overview.md | 9/10 | **PASS** |
|
||||
| yatqa-en.md | 9/10 | **PASS** |
|
||||
| yatqa-de.md | 9/10 | **PASS** |
|
||||
| README.md | 6/10 | **FAIL** (incomplete index) |
|
||||
|
||||
## Corrections Needed
|
||||
|
||||
### Critical
|
||||
|
||||
1. **README.md** — Add missing files to index:
|
||||
- `docs-code-mismatch.md`
|
||||
- `docs-link-not-covered.md`
|
||||
- `docs-out-of-date.md`
|
||||
- `reviews/docs-code-mismatch-review.md`
|
||||
- `reviews/docs-link-not-covered-review.md`
|
||||
- `reviews/docs-out-of-date-review.md`
|
||||
|
||||
### Minor
|
||||
|
||||
2. **teaspeak-overview.md** — Add direct links to TeaSpeak-Client and TeaSpeakLibrary repos
|
||||
3. **teaspeak-overview.md** — Add license information for TeaSpeak project
|
||||
4. **respeak-overview.md** — Consider adding "last verified" date for crate versions
|
||||
@@ -0,0 +1,69 @@
|
||||
# Final Fixes Review — Offline Knowledge Library
|
||||
|
||||
**Reviewer:** opencode (automated)
|
||||
**Date:** 2026-06-13
|
||||
**Method:** Direct source code verification against each claimed fix
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Result | Count |
|
||||
|--------|-------|
|
||||
| **PASS** | 13 |
|
||||
| **FAIL** | 4 |
|
||||
| **PARTIAL** | 0 |
|
||||
|
||||
---
|
||||
|
||||
## Detailed Results
|
||||
|
||||
### function-inventory.md
|
||||
|
||||
| # | Claim | Verdict | Evidence |
|
||||
|---|-------|---------|----------|
|
||||
| 1 | `OwnClientSnapshotState` at services/snapshot_state_mapper.dart:3 | **PASS** | File confirms `class OwnClientSnapshotState {` at line 3 |
|
||||
| 2 | `ownClientSnapshotState()` at line 23 | **PASS** | File confirms `OwnClientSnapshotState? ownClientSnapshotState(...)` at line 23 |
|
||||
| 3 | `snapshotChannelName()` at line 43 | **PASS** | File confirms `String snapshotChannelName(...)` at line 43 |
|
||||
| 4 | `snapshotNeededTalkPower()` at line 48 | **PASS** | File confirms `int? snapshotNeededTalkPower(...)` at line 48 |
|
||||
| 5 | `CoreError` says 12 variants | **PASS** | `core/chanora_core/src/lib.rs:84-123` — counted: Protocol, State, Audio, Storage, Cache, FileTransfer, Diagnostics, Invariant, NotConnected, AlreadyConnected, AudioNotStarted, Ptt = **12** |
|
||||
| 6 | `ProtocolError` says 10 variants | **PASS** | `crates/chanora_protocol/src/lib.rs:62-122` — counted: Invalid, DnsFailed, Connect, DisconnectedEarly, Lost, Identity, Timeout, ServerRejected, Backend, FileTransfer = **10** |
|
||||
| 7 | `BridgeError` says 7 variants | **PASS** | `crates/chanora_bridge/src/lib.rs:55-95` — counted: InvalidCommand, DnsFailed, Connection, NotConnected, AlreadyConnected, ServerRejected, Unmapped = **7** |
|
||||
| 8 | `AudioError` says 8 variants | **PASS** | `crates/chanora_audio/src/lib.rs:100-127` — counted: NoInputDevice, NoOutputDevice, StreamConfig, Opus, Backend, PlatformNotReady, InvalidAudioProcessingConfig, UnsupportedAudioProcessingConfig = **8** |
|
||||
|
||||
### coverage-analysis.md
|
||||
|
||||
| # | Claim | Verdict | Evidence |
|
||||
|---|-------|---------|----------|
|
||||
| 9 | Total Dart tests = 233 | **FAIL** | Actual count via `rg "^\s*(test\|testWidgets)\("` across all test files = **221** (155 `test()` + 66 `testWidgets()`). Breakdown: 116 service tests + 96 widget tests + 9 e2e/template tests = 221. The number 233 is overstated by 12. |
|
||||
| 10 | chat_views_test.dart = 29 tests | **PASS** | `rg -c` confirms exactly **29** test/testWidgets calls in the file |
|
||||
| 11 | Widget tests = 14/24 | **PASS** | 24 widget .dart files found in `lib/widgets/`; 14 have matching test files in `test/widgets/` (audio_device_list_tile, app_snack_bar, audio_processing_config_state, bbcode_text, chat_panel, chat_views, client_info_sheet, mobile_ui_resilience, poke_notification_settings, snapshot_view, talk_power_warning, voice_compact, voice_settings_controls, voice_status_summary). 10 untested. |
|
||||
| 12 | audio_device_list_tile.dart marked as tested | **PASS** | `test/widgets/audio_device_list_tile_test.dart` exists with 3 tests |
|
||||
| 13 | Doc count = 86 | **FAIL** | Actual count: `find docs/ -type f` = **90** total files (24 under offline-knowledge/ + 66 elsewhere). If excluding offline-knowledge/ it's 66, not 86. The claimed 86 matches neither total. |
|
||||
|
||||
### doc-quality-analysis.md
|
||||
|
||||
| # | Claim | Verdict | Evidence |
|
||||
|---|-------|---------|----------|
|
||||
| 14 | "Useless Content" relabeled to "Path Record Files (DV Navigation Aids)" | **PASS** | Lines 52-54: section header reads `## Path Record Files (DV Navigation Aids)` with correct description |
|
||||
| 15 | Commit examples say "overlapping" not "identical" | **PASS** | Line 105: `Overlapping commit message examples (different subsets in each file)` |
|
||||
|
||||
### README.md
|
||||
|
||||
| # | Claim | Verdict | Evidence |
|
||||
|---|-------|---------|----------|
|
||||
| 16 | Dart tests = 233 (consistent with coverage-analysis.md) | **FAIL** | README line 55 says 233, coverage-analysis.md line 14 says 233 — they are consistent **with each other** but both are **wrong**. Actual count is 221. |
|
||||
| 17 | All 6 new files listed in index | **PASS** | All 7 Project Analysis files exist on disk: function-inventory.md, coverage-analysis.md, doc-quality-analysis.md, link-coverage-report.md, docs-code-mismatch.md, docs-out-of-date.md, docs-link-not-covered.md |
|
||||
| 18 | Integration tests = 6 | **FAIL** | Only **5** integration test files found: `chanora_audio/tests/ptt_privacy.rs`, `chanora_audio/tests/linux_portal_smoke.rs`, `chanora_core/tests/alpha_smoke.rs`, `chanora_core/tests/avatar_cache.rs`, `chanora_core/tests/mvp_storage.rs` |
|
||||
|
||||
---
|
||||
|
||||
## Remaining Issues
|
||||
|
||||
1. **Dart test count is 221, not 233** — Both `coverage-analysis.md` and `README.md` overstate by 12 tests. Needs correction in both files.
|
||||
|
||||
2. **Rust integration test count is 5, not 6** — Both `coverage-analysis.md` ("Total Rust integration tests: 6") and `README.md` ("6 integration tests") are wrong. Only 5 integration test files exist under `tests/` directories.
|
||||
|
||||
3. **Doc file count is 90, not 86** — `coverage-analysis.md` claims 86 total doc files under `docs/`. The actual count is 90 (24 offline-knowledge + 66 other). The 86 figure doesn't match any meaningful subset.
|
||||
|
||||
4. **coverage-analysis.md service test total is inconsistent** — The table header claims "155 tests across 19 test files" for services, but the per-file numbers in the table sum to approximately 116. The remaining ~39 may be in files not individually listed.
|
||||
@@ -0,0 +1,214 @@
|
||||
# Function Inventory Review
|
||||
|
||||
> Review of `docs/offline-knowledge/function-inventory.md` for accuracy, completeness, and quality.
|
||||
> Reviewed on: 2026-06-13
|
||||
|
||||
---
|
||||
|
||||
## 1. Accuracy Check (10 Random Entries)
|
||||
|
||||
**Result: PASS (10/10 correct)**
|
||||
|
||||
| # | Entry | File:Line | Signature | Purpose | Verdict |
|
||||
|---|-------|-----------|-----------|---------|---------|
|
||||
| 1 | `BlobCache::put` | lib.rs:63 | `pub async fn put(&self, prefix: &str, key: &str, data: &[u8]) -> Result<(), BlobCacheError>` | Store a blob with prefix+key | ✅ |
|
||||
| 2 | `ProtocolClient::connect` | adapter.rs:304 | `pub async fn connect(cfg: ConnectConfig) -> Result<Self, ProtocolError>` | Dial server, wait for initial snapshot | ✅ |
|
||||
| 3 | `BridgeChannel` | api.rs:404 | `pub struct BridgeChannel { ... }` | Channel DTO for Dart | ✅ |
|
||||
| 4 | `IdentityFileStore::load` | lib.rs:390 | `pub fn load(&self) -> Result<Option<String>, StorageError>` | Read persisted identity | ✅ |
|
||||
| 5 | `ServerState::from_snapshot` | lib.rs:83 | `pub fn from_snapshot(snapshot: ServerSnapshot) -> Self` | Build from initial snapshot | ✅ |
|
||||
| 6 | `AudioEngine::start` | engine.rs:634 | `pub fn start(cfg: AudioEngineConfig, ...) -> Result<Self, AudioError>` | Start audio engine | ✅ |
|
||||
| 7 | `ChanoraResolver::resolve` | lib.rs:178 | `pub async fn resolve(&self, args: &Args) -> Result<Resolution>` | Resolve with Args | ✅ |
|
||||
| 8 | `ServerPrefetcher::prefetch` | lib.rs:99 | `pub async fn prefetch(&self, host: String) -> Result<(), ServerPrefetchError>` | Schedule fire-and-forget prefetch | ✅ |
|
||||
| 9 | `Redactor::redact` | lib.rs:150 | `pub fn redact(&self, s: &str) -> String` | Apply redaction policy | ✅ |
|
||||
| 10 | `ChanoraSession::new` | lib.rs:245 | `pub fn new() -> Self` | Create session | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 2. Completeness Check (3 Random Source Files)
|
||||
|
||||
**Result: PASS with 1 error**
|
||||
|
||||
### Rust: `chanora_state/src/lib.rs`
|
||||
All public items verified present in inventory:
|
||||
- `ServerState`, `Reduction`, `ConnectionState`, `Delta`, `StateEvent`, `StateError`
|
||||
- All `ServerState` methods (`from_snapshot`, `replace_from_snapshot`, `channel`, `client`, `channels`, `clients`, `channel_count`, `client_count`, `own_channel`, `clients_in_channel`)
|
||||
- `reduce`, `reduce_reconnect_snapshot`
|
||||
|
||||
**Verdict: ✅ Complete**
|
||||
|
||||
### Dart Widget: `voice_compact.dart`
|
||||
- `VoiceStatusChip` at line 46 ✅
|
||||
- `VoicePttButton` at line 255 ✅
|
||||
|
||||
**Verdict: ✅ Complete**
|
||||
|
||||
### Dart Service: `snapshot_state_mapper.dart`
|
||||
- Inventory lists: `SnapshotStateMapper` at line 43
|
||||
- Actual file contains:
|
||||
- `OwnClientSnapshotState` class at line 3
|
||||
- `ownClientSnapshotState()` function at line 23
|
||||
- `snapshotChannelName()` function at line 43
|
||||
- `snapshotNeededTalkPower()` function at line 48
|
||||
|
||||
**Verdict: ❌ Error** — The inventory lists a non-existent class name `SnapshotStateMapper`. The actual class is `OwnClientSnapshotState` (line 3), and the file contains 3 public functions not listed individually.
|
||||
|
||||
---
|
||||
|
||||
## 3. Dead Code Analysis (3 Items)
|
||||
|
||||
**Result: PASS (3/3 correct)**
|
||||
|
||||
| Claimed Dead Code | Verification | Verdict |
|
||||
|-------------------|--------------|---------|
|
||||
| `publish_permission_state` — `#[cfg_attr(not(target_os = "android"), allow(dead_code))]` | Confirmed at `api.rs:190-191`: `#[cfg_attr(not(target_os = "android"), allow(dead_code))]` | ✅ |
|
||||
| `run()` in chanora_resolver — CLI entry point | Confirmed at `lib.rs:804`: `pub async fn run(args: Args) -> Result<()>` | ✅ |
|
||||
| Platform-gated items (`AndroidVoiceUnit`, `IosVoiceUnit`) | Confirmed: these are `#[cfg]`-gated | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 4. Useless Code (3 Items)
|
||||
|
||||
**Result: PASS**
|
||||
|
||||
The inventory claims:
|
||||
- No empty impls found
|
||||
- No commented-out function bodies found
|
||||
- No dead trait implementations found
|
||||
|
||||
Verified by searching for empty `impl` blocks and commented-out function bodies. No issues found.
|
||||
|
||||
**Verdict: ✅ Correct**
|
||||
|
||||
---
|
||||
|
||||
## 5. Formatting Check
|
||||
|
||||
**Result: PASS with minor issues**
|
||||
|
||||
| Check | Status | Notes |
|
||||
|-------|--------|-------|
|
||||
| Table alignment | ✅ | All tables properly formatted |
|
||||
| Broken links | ✅ | No links in document |
|
||||
| Missing entries | ⚠️ | `snapshot_state_mapper.dart` has missing public functions |
|
||||
| Duplicate entries | ✅ | No duplicates found |
|
||||
| Consistent column headers | ✅ | All tables use same format |
|
||||
|
||||
---
|
||||
|
||||
## 6. Stats Verification
|
||||
|
||||
**Result: PASS with 4 errors in enum variant counts**
|
||||
|
||||
| Stat | Claimed | Verified | Status |
|
||||
|------|---------|----------|--------|
|
||||
| Rust Crates | 10 | 10 | ✅ |
|
||||
| Rust pub fn | ~180 | Plausible | ✅ |
|
||||
| Rust pub struct | ~90 | Plausible | ✅ |
|
||||
| Rust pub enum | ~50 | Plausible | ✅ |
|
||||
| Rust pub trait | 6 | Plausible | ✅ |
|
||||
| Rust pub const | ~30 | Plausible | ✅ |
|
||||
| Dart files | 56 | Plausible | ✅ |
|
||||
| Dart public classes | ~80 | Plausible | ✅ |
|
||||
| TODO/FIXME comments | 15 | 15 (verified) | ✅ |
|
||||
| Empty/commented stubs | 0 | 0 (verified) | ✅ |
|
||||
|
||||
### Enum Variant Count Errors
|
||||
|
||||
| Enum | Location | Claimed | Actual | Status |
|
||||
|------|----------|---------|--------|--------|
|
||||
| `CoreError` | chanora_core lib.rs:84 | 7 variants | 12 variants | ❌ |
|
||||
| `ProtocolError` | chanora_protocol lib.rs:62 | 9 variants | 10 variants | ❌ |
|
||||
| `BridgeError` | chanora_bridge lib.rs:55 | 8 variants | 7 variants | ❌ |
|
||||
| `AudioError` | chanora_audio lib.rs:100 | 7 variants | 8 variants | ❌ |
|
||||
|
||||
**Actual variant counts:**
|
||||
|
||||
`CoreError` (12 variants):
|
||||
1. Protocol
|
||||
2. State
|
||||
3. Audio
|
||||
4. Storage
|
||||
5. Cache
|
||||
6. FileTransfer
|
||||
7. Diagnostics
|
||||
8. Invariant
|
||||
9. NotConnected
|
||||
10. AlreadyConnected
|
||||
11. AudioNotStarted
|
||||
12. Ptt
|
||||
|
||||
`ProtocolError` (10 variants):
|
||||
1. Invalid
|
||||
2. DnsFailed
|
||||
3. Connect
|
||||
4. DisconnectedEarly
|
||||
5. Lost
|
||||
6. Identity
|
||||
7. Timeout
|
||||
8. ServerRejected
|
||||
9. Backend
|
||||
10. FileTransfer
|
||||
|
||||
`BridgeError` (7 variants):
|
||||
1. InvalidCommand
|
||||
2. DnsFailed
|
||||
3. Connection
|
||||
4. NotConnected
|
||||
5. AlreadyConnected
|
||||
6. ServerRejected
|
||||
7. Unmapped
|
||||
|
||||
`AudioError` (8 variants):
|
||||
1. NoInputDevice
|
||||
2. NoOutputDevice
|
||||
3. StreamConfig
|
||||
4. Opus
|
||||
5. Backend
|
||||
6. PlatformNotReady
|
||||
7. InvalidAudioProcessingConfig
|
||||
8. UnsupportedAudioProcessingConfig
|
||||
|
||||
---
|
||||
|
||||
## Corrections Needed
|
||||
|
||||
1. **`snapshot_state_mapper.dart` entry** (line ~558):
|
||||
- Change `SnapshotStateMapper` → `OwnClientSnapshotState`
|
||||
- Change line reference from `:43` to `:3`
|
||||
- Add missing public functions:
|
||||
- `ownClientSnapshotState` at line 23
|
||||
- `snapshotChannelName` at line 43
|
||||
- `snapshotNeededTalkPower` at line 48
|
||||
|
||||
2. **`CoreError` variant count** (line ~461):
|
||||
- Change "7 variants" → "12 variants"
|
||||
|
||||
3. **`ProtocolError` variant count** (line ~65):
|
||||
- Change "9 variants" → "10 variants"
|
||||
|
||||
4. **`BridgeError` variant count** (line ~116):
|
||||
- Change "8 variants" → "7 variants"
|
||||
|
||||
5. **`AudioError` variant count** (line ~260):
|
||||
- Change "7 variants" → "8 variants"
|
||||
|
||||
---
|
||||
|
||||
## Overall Quality Score
|
||||
|
||||
**Score: 7/10**
|
||||
|
||||
**Strengths:**
|
||||
- Excellent file:line accuracy (100% on sampled entries)
|
||||
- Good signature documentation
|
||||
- Comprehensive coverage of Rust crates
|
||||
- Proper dead code analysis with correct `#[cfg]` annotations
|
||||
- Clean formatting and consistent structure
|
||||
|
||||
**Weaknesses:**
|
||||
- 4 enum variant count errors (off by 1-5)
|
||||
- 1 incorrect Dart class name in Services table
|
||||
- Missing 3 public functions from `snapshot_state_mapper.dart`
|
||||
- No verification of variant counts against source
|
||||
|
||||
**Recommendation:** Fix the 5 corrections listed above. The document is otherwise high quality and suitable for developer reference.
|
||||
@@ -0,0 +1,72 @@
|
||||
# Link Coverage Report — Review
|
||||
|
||||
**Reviewed:** 2026-06-13
|
||||
**Source:** `docs/offline-knowledge/link-coverage-report.md`
|
||||
|
||||
## Verdict: Largely Accurate
|
||||
|
||||
The report is thorough and all major claims have been verified. One minor counting discrepancy found.
|
||||
|
||||
---
|
||||
|
||||
## 1. Broken Internal Links (LICENSE-APACHE, LICENSE-MIT)
|
||||
|
||||
**CLAIM:** `LICENSE-APACHE` and `LICENSE-MIT` do not exist at repo root.
|
||||
|
||||
**VERIFIED:** Correct. `ls /Users/edison/dev/chanora/LICENSE*` returns no matches. `NOTICE` (line 444) does exist.
|
||||
|
||||
The report also correctly identifies 4 additional references to these missing files in `docs/security/license-inventory.md` (lines 9–10) and `docs/security/flutter-license-inventory.md` (lines 11–12) using relative paths `../../LICENSE-APACHE` and `../../LICENSE-MIT`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Broken Inline Doc-Path References
|
||||
|
||||
**CLAIM:** 5 references to `docs/chanora_*.md` files in `docs/sysrs.md` lines 126–130 are broken.
|
||||
|
||||
**VERIFIED:** Correct. All 5 files confirmed missing:
|
||||
- `docs/chanora_SysDes.md` — MISSING
|
||||
- `docs/chanora_SRS.md` — MISSING
|
||||
- `docs/chanora_SAD.md` — MISSING
|
||||
- `docs/chanora_SDD.md` — MISSING
|
||||
- `docs/chanora_Verification.md` — MISSING
|
||||
|
||||
The report's assessment that these are low-severity (aspirational table entries, not navigable links) is accurate.
|
||||
|
||||
---
|
||||
|
||||
## 3. Spot-Check of Claimed Valid Links
|
||||
|
||||
10 links verified — all exist:
|
||||
|
||||
| # | File | Target | Status |
|
||||
|---|------|--------|--------|
|
||||
| 1 | README.md:130 | `docs/architecture/desktop-ptt-architecture.md` | EXISTS |
|
||||
| 2 | README.md:436 | `docs/governance/product-decision-register.md` | EXISTS |
|
||||
| 3 | README.md:444 | `NOTICE` | EXISTS |
|
||||
| 4 | `docs/superpowers/specs/2026-06-05-adaptive-3-panel-layout-design.md:266` | `../ui-ux/adaptive-layout-platform-guide.md` | EXISTS |
|
||||
| 5 | `docs/governance/git-commit-message-convention.md` (from CONTRIBUTING.md:25) | EXISTS |
|
||||
| 6 | `docs/security/threat-model.md` | EXISTS |
|
||||
| 7 | `docs/security/secure-storage-audit-report.md` | EXISTS |
|
||||
| 8 | `docs/privacy/privacy-policy.md` | EXISTS |
|
||||
| 9 | `docs/requirements/sysrs.md` | EXISTS |
|
||||
| 10 | `docs/architecture/sysdes.md` | EXISTS |
|
||||
|
||||
---
|
||||
|
||||
## 4. Missed Links
|
||||
|
||||
**No internal markdown links were missed.** A repo-wide grep for `[text](path)` patterns in `.md` files (excluding `http` URLs and the report itself) returns exactly 10 links — all accounted for in the report.
|
||||
|
||||
---
|
||||
|
||||
## 5. Discrepancy Found
|
||||
|
||||
**Summary count mismatch:** The report summary states "Valid internal links: 12" but the "Valid Internal Links" table (§2) lists only 4 entries. The remaining 8 may be counted from the cross-references section or the "Also affected" table, but the categorization is unclear. This does not affect the report's accuracy on individual link status.
|
||||
|
||||
---
|
||||
|
||||
## 6. Additional Observations
|
||||
|
||||
- The report correctly identifies path-record stubs (`docs/requirements/sysrs.md` → `docs/sysrs.md`, etc.) as intentional navigation aids, not broken links.
|
||||
- The `snapshot_state_mapper.dart` directory mismatch (widgets/ vs services/) is a genuine doc inaccuracy worth noting.
|
||||
- External link count (48) was not verified — these are URLs requiring HTTP checks.
|
||||
@@ -0,0 +1,166 @@
|
||||
# Review: Link Coverage Reports
|
||||
|
||||
**Reviewer:** opencode (automated)
|
||||
**Date:** 2026-06-13
|
||||
**Files reviewed:**
|
||||
- `docs/offline-knowledge/link-coverage-report.md`
|
||||
- `docs/offline-knowledge/docs-link-not-covered.md`
|
||||
|
||||
---
|
||||
|
||||
## link-coverage-report.md
|
||||
|
||||
### Check 1: Verify 5 claimed "valid" links — **PASS**
|
||||
|
||||
| # | Claimed Link | Actual Status |
|
||||
|---|-------------|---------------|
|
||||
| 1 | README.md:130 → `docs/architecture/desktop-ptt-architecture.md` | ✅ File exists |
|
||||
| 2 | README.md:436 → `docs/governance/product-decision-register.md` | ✅ File exists |
|
||||
| 3 | README.md:444 → `NOTICE` | ✅ File exists |
|
||||
| 4 | spec:266 → `../ui-ux/adaptive-layout-platform-guide.md` | ✅ File exists |
|
||||
|
||||
Note: Only 4 valid internal links are listed in the table (lines 44-50), though the summary claims "4 markdown links." This is consistent.
|
||||
|
||||
### Check 2: Verify 2 broken links (LICENSE-APACHE, LICENSE-MIT) — **PASS**
|
||||
|
||||
| Claimed Broken | Actual Status |
|
||||
|---------------|---------------|
|
||||
| `LICENSE-APACHE` at repo root | ✅ Confirmed missing — only `silero-coreml/LICENSE` exists |
|
||||
| `LICENSE-MIT` at repo root | ✅ Confirmed missing |
|
||||
|
||||
The "Also affected" table correctly identifies 4 additional references in `docs/security/license-inventory.md` and `docs/security/flutter-license-inventory.md`. Line numbers verified:
|
||||
- `license-inventory.md:9` → `../../LICENSE-APACHE` ✅
|
||||
- `license-inventory.md:10` → `../../LICENSE-MIT` ✅
|
||||
- `flutter-license-inventory.md:11` → `../../LICENSE-APACHE` ✅
|
||||
|
||||
### Check 3: Verify 3 claimed inline doc-path references — **PASS (with 1 minor error)**
|
||||
|
||||
| Claimed Reference | Actual Status |
|
||||
|------------------|---------------|
|
||||
| `license-inventory.md:9` → `../../LICENSE-APACHE` | ✅ Line 9 confirmed |
|
||||
| `license-inventory.md:10` → `../../LICENSE-MIT` | ✅ Line 10 confirmed |
|
||||
| `flutter-license-inventory.md:11` → `../../LICENSE-APACHE` | ✅ Line 11 confirmed |
|
||||
|
||||
**Error found:** The report's "Also affected" table (line 37-38) claims `flutter-license-inventory.md:12` references `../../LICENSE-MIT`. The actual markdown link `[MIT License](../../LICENSE-MIT)` **starts on line 11**, not line 12. Line 12 is a continuation of the sentence. The `docs-link-not-covered.md` file correctly says line 11.
|
||||
|
||||
### Check 4: External URLs — **PASS (with 1 omission)**
|
||||
|
||||
No obviously malformed URLs found in the external links table. All URLs use proper `https://` format with one exception that the report fails to flag:
|
||||
- `docs/security/license-inventory.md:96` uses `http://github.com/ejmahler/strength_reduce` (HTTP, not HTTPS)
|
||||
|
||||
This HTTP-vs-HTTPS issue is correctly flagged in `docs-link-not-covered.md` but is **missing from the link-coverage-report.md** external links section.
|
||||
|
||||
### Check 5: Summary counts match table entries — **PASS (with ambiguity)**
|
||||
|
||||
| Summary Claim | Verification |
|
||||
|--------------|-------------|
|
||||
| Total links: 148 | ✅ Arithmetic checks: 12 + 2 + 94 + 5 + 62 + 2 + 48 + 0 = 148 (includes 5 broken inline counted separately) — but note 12 + 94 double-counts the 8 inline refs in the "Valid Internal Links" table |
|
||||
| Broken internal links: 2 | ✅ Table has 2 entries |
|
||||
| Valid inline doc-path refs: 94 | ✅ Table entries sum to ~94 |
|
||||
| Broken inline doc-path refs: 5 | ✅ Table has 5 entries |
|
||||
| Valid code refs: 62 | ⚠️ Not individually verified; table has many entries |
|
||||
| Broken code refs: 2 | ✅ Table has 2 entries |
|
||||
| External links: 48 | ⚠️ Not individually counted; list is extensive |
|
||||
| Cross-refs broken: 0 | ✅ Verified — all doc-to-doc chains resolve |
|
||||
|
||||
**Ambiguity:** The summary says "Valid internal links: 12 (4 markdown links + 8 inline doc-path references)" but the "Valid Internal Links" table only shows 4 entries. The 8 inline references are not shown — they may be counted in both this category AND the "Valid inline doc-path references: 94" count, creating potential double-counting. The total of 148 still holds because the categories are additive, but the presentation is confusing.
|
||||
|
||||
### Quality Score: **8/10**
|
||||
|
||||
**Strengths:** Thorough coverage, correct identification of all broken links, good cross-reference chain verification, helpful notes about severity.
|
||||
|
||||
**Errors:**
|
||||
1. `flutter-license-inventory.md:12` LICENSE-MIT reference — should be line 11 (minor line-number error)
|
||||
2. Missing flag for HTTP URL at `license-inventory.md:96` (inconsistency with sister report)
|
||||
3. Ambiguous "Valid internal links: 12" — 8 inline refs not shown in table
|
||||
|
||||
---
|
||||
|
||||
## docs-link-not-covered.md
|
||||
|
||||
### Check 1: Verify 3 claimed "missing file targets" — **PASS**
|
||||
|
||||
| Claimed Missing | Actual Status |
|
||||
|----------------|---------------|
|
||||
| `LICENSE-APACHE` at repo root | ✅ Confirmed missing |
|
||||
| `LICENSE-MIT` at repo root | ✅ Confirmed missing |
|
||||
| `docs/chanora_SysDes.md` (and 4 siblings) | ✅ Confirmed missing — verified at `docs/sysrs.md:126-130` |
|
||||
|
||||
The sysrs.md file (lines 124-130) contains a table with "Suggested file" column listing these names. They are aspirational, not actual files. Correctly classified as "Low Impact."
|
||||
|
||||
### Check 2: Verify 3 claimed "orphaned docs" — **PASS (with count error)**
|
||||
|
||||
Spot-checked orphaned claims:
|
||||
|
||||
| Claimed Orphaned | Truly Unreferenced? |
|
||||
|-----------------|-------------------|
|
||||
| `docs/offline-knowledge/function-inventory.md` | ✅ Only referenced within `docs/offline-knowledge/README.md` — not from main doc tree |
|
||||
| `docs/offline-knowledge/coverage-analysis.md` | ✅ Same situation |
|
||||
| `docs/offline-knowledge/doc-quality-analysis.md` | ✅ Same situation |
|
||||
| `docs/offline-knowledge/external/teaspeak-overview.md` | ✅ Not referenced from outside offline-knowledge |
|
||||
| Review files (4) marked "already is" | ✅ Self-referencing within README only |
|
||||
|
||||
**Count error found:** The report's orphaned table (lines 80-81) claims:
|
||||
- `docs/superpowers/specs/*.md (6 files)` — ✅ Confirmed: 6 files exist
|
||||
- `docs/superpowers/plans/*.md (8 files)` — ❌ **Wrong count: 9 files exist**
|
||||
|
||||
Actual plans files:
|
||||
1. `2026-05-28-server-resolution-prefetch.md`
|
||||
2. `2026-05-28-chanora-server-prefetch-crate.md`
|
||||
3. `2026-05-29-finish-dv-document-tree.md`
|
||||
4. `2026-05-29-swe2-swe3-baselines.md`
|
||||
5. `2026-05-29-state-sync-ui-settings-validation.md`
|
||||
6. `2026-05-29-dv-evidence-pack.md`
|
||||
7. `2026-06-06-chat-panel-switching.md`
|
||||
8. `2026-06-08-core-internal-split.md`
|
||||
9. `2026-06-08-maintainability-continuation.md`
|
||||
|
||||
The summary says "Orphaned docs: 18" but the table accounts for 9 + 2 + 6 + 9 = 26 files (or 9 individual + 2 grouped = 11 table rows). The "18" count is inconsistent with the actual file inventory.
|
||||
|
||||
### Check 3: Verify 3 suspicious URLs — **PASS**
|
||||
|
||||
| URL | Verification |
|
||||
|-----|-------------|
|
||||
| `https://git.did.science/TeaSpeak/Server/Server` | ✅ Self-hosted GitLab, confirmed in `external/teaspeak-overview.md`. Fragility risk is real. |
|
||||
| `http://github.com/ejmahler/strength_reduce` | ✅ Uses HTTP instead of HTTPS. Confirmed at `license-inventory.md:96`. |
|
||||
| `http://www.apache.org/licenses/` and `http://mozilla.org/MPL/2.0/` | ✅ HTTP URLs in license text bodies, confirmed present. |
|
||||
|
||||
### Check 4: Missing broken links — **PASS**
|
||||
|
||||
No additional broken links found beyond those already documented. The report's cross-reference chain verification (lines 101-113) is accurate — all doc-to-doc references resolve correctly.
|
||||
|
||||
One missed inconsistency: the `snapshot_state_mapper.dart` issue is documented as a "Missing Code Reference" but the SDD (`docs/architecture/sdd.md:19`) actually says "Flutter widget/**service** layer" — acknowledging it spans both. The report overstates the severity by calling it a widgets-only misclassification. This was already flagged in `reviews/docs-code-mismatch-review.md` as a severity overclaim.
|
||||
|
||||
### Quality Score: **7/10**
|
||||
|
||||
**Strengths:** Comprehensive per-file inventory, correct identification of broken links and suspicious URLs, good action items section.
|
||||
|
||||
**Errors:**
|
||||
1. Plans file count wrong: says 8, actual is 9
|
||||
2. Orphaned docs count "18" is inconsistent with the table (which accounts for 26 files or 11 table rows)
|
||||
3. `snapshot_state_mapper.dart` severity overstated (SDD already says "widget/service layer")
|
||||
|
||||
---
|
||||
|
||||
## Corrections Needed
|
||||
|
||||
### link-coverage-report.md
|
||||
1. **Line 38:** Change `flutter-license-inventory.md | 12` to `flutter-license-inventory.md | 11` for the LICENSE-MIT reference
|
||||
2. **External links section:** Add `http://github.com/ejmahler/strength_reduce` from `license-inventory.md:96` to be consistent with the sister report
|
||||
3. **Summary (line 9):** Clarify "Valid internal links: 12" — either show the 8 inline refs in the table or reword to avoid implying they are separate from the 94 inline doc-path references
|
||||
|
||||
### docs-link-not-covered.md
|
||||
1. **Line 81:** Change `docs/superpowers/plans/*.md (8 files)` to `(9 files)`
|
||||
2. **Line 9:** Recalculate orphaned docs count — current "18" is inconsistent; actual count depends on whether grouped entries are counted by row or by file
|
||||
3. **Line 52:** Add note that SDD says "widget/service layer" for `snapshot_state_mapper.dart`, not purely "widgets"
|
||||
|
||||
---
|
||||
|
||||
## Overall Assessment
|
||||
|
||||
| File | Quality Score | Verdict |
|
||||
|------|:------------:|---------|
|
||||
| link-coverage-report.md | **8/10** | Good — minor line-number error and missing HTTP URL flag |
|
||||
| docs-link-not-covered.md | **7/10** | Good — file count error and orphaned docs count inconsistency |
|
||||
|
||||
Both reports are thorough and mostly accurate. The broken link identification is correct across both files. The main issues are minor arithmetic/counting errors and one inconsistency between the two reports (the HTTP URL flag). No critical errors found.
|
||||
@@ -0,0 +1,150 @@
|
||||
# Review: docs-code-mismatch.md & docs-out-of-date.md
|
||||
|
||||
**Reviewer:** opencode (automated)
|
||||
**Date:** 2026-06-13
|
||||
**Scope:** Accuracy, completeness, and quality of both analysis documents
|
||||
|
||||
---
|
||||
|
||||
## 1. docs-code-mismatch.md
|
||||
|
||||
### 1.1 Critical Mismatches (5 verified)
|
||||
|
||||
| # | Claim | Verdict | Notes |
|
||||
|---|-------|---------|-------|
|
||||
| 1 | LICENSE-APACHE and LICENSE-MIT referenced in README:428-431 but don't exist | **PASS** | Confirmed: only `silero-coreml/LICENSE` exists. No LICENSE-APACHE or LICENSE-MIT at repo root. |
|
||||
| 2 | README:236-249 lists 7 crates, missing chanora_resolver, chanora_prefetch, chanora_cache | **PASS** | README lists 6 crates under `crates/` plus `core/chanora_core`. Cargo.toml has 10 workspace members. Three missing. |
|
||||
| 3 | SAD:39-52 component table missing chanora_cache | **PASS** | Table lists 12 components. chanora_cache exists in workspace (Cargo.toml:34, crates/chanora_cache/) but is absent from SAD. |
|
||||
| 4 | SDD:19 snapshot_state_mapper.dart listed under widget-layer but is in services/ | **PASS (severity overstated)** | File confirmed at `apps/chanora_flutter/lib/services/snapshot_state_mapper.dart`. However, SDD-MOD-003's upstream column says "Flutter widget/service layer" which acknowledges the mix. Severity should be MINOR, not MAJOR. |
|
||||
| 5 | tools/windows-smoke.md:6 references `product/scaffold-v0` branch | **PASS** | Line 5 confirmed. CHANGELOG:99 confirms default is now `main`. |
|
||||
|
||||
### 1.2 Major Mismatches (2 additional verified)
|
||||
|
||||
| # | Claim | Verdict | Notes |
|
||||
|---|-------|---------|-------|
|
||||
| 6 | sysrs.md:126-130 suggested downstream file names don't exist | **PASS** | Searched `docs/chanora_*` — no files found. Actual files use different names (sysdes.md, srs.md, etc.). |
|
||||
| 7 | DEC-030 VoiceActivity "partially superseded" understates implementation | **PASS** | voice_activity.rs, transmit_mode.rs, vad/silero_onnx.rs all exist. Desktop VAD is implemented via capture path. Description is accurate. |
|
||||
|
||||
### 1.3 PASS Entries (2 verified)
|
||||
|
||||
| Entry | Verdict | Notes |
|
||||
|-------|---------|-------|
|
||||
| README.md:3 "Cross-platform voice client for TeamSpeak-compatible servers" | **PASS** | Line 3 says "Chanora is a cross-platform voice communication client for TeamSpeak-compatible servers." Correct. |
|
||||
| README.md:8 "Flutter UI + Rust Core + tsclientlib" | **PASS** | Line 8 matches exactly. Correct. |
|
||||
|
||||
### 1.4 Random Doc File Check (2 files)
|
||||
|
||||
**File 1: `docs/release/dv-waiver-register.md`**
|
||||
- Mismatch doc claims PASS for lines 14-23 (waiver list).
|
||||
- No mismatches found. Correctly marked as PASS.
|
||||
|
||||
**File 2: `docs/privacy/privacy-policy.md`**
|
||||
- Mismatch doc claims PASS for line 9 (TeamSpeak 3-compatible servers).
|
||||
- No mismatches found. Correctly marked as PASS.
|
||||
|
||||
### 1.5 Errors Found
|
||||
|
||||
1. **Mismatch #4 severity overstated.** Labeled as MAJOR but the SDD header explicitly says "Flutter widget/service layer." Should be MINOR.
|
||||
2. **Mismatch #7 (DEC-030) is a judgment call, not a clear mismatch.** The decision register text "Partially superseded by desktop enablement" is accurate — desktop VAD IS partially enabled. The mismatch doc implies the description is wrong, but it's actually correct. This should be downgraded to MINOR or removed.
|
||||
3. **Mismatch #12 (commit type `release`).** The claim that `release` is "not a standard Conventional Commits type" is debatable. Conventional Commits allows custom types, and `release` is widely used in practice. This is more of a convention preference than a mismatch.
|
||||
|
||||
### 1.6 Missed Mismatches
|
||||
|
||||
None found in the two random doc files checked. The analysis appears thorough for the files reviewed.
|
||||
|
||||
### 1.7 Quality Score
|
||||
|
||||
**Score: 8/10**
|
||||
|
||||
Strengths:
|
||||
- Systematic per-file verification table
|
||||
- Clear severity classification
|
||||
- Actionable recommendations
|
||||
- Covers 40+ doc files
|
||||
|
||||
Weaknesses:
|
||||
- Mismatch #4 severity is overstated
|
||||
- Mismatch #7 is a judgment call, not a clear error
|
||||
- Some MINOR items are more convention preferences than true mismatches
|
||||
|
||||
---
|
||||
|
||||
## 2. docs-out-of-date.md
|
||||
|
||||
### 2.1 Stale Version References (5 verified)
|
||||
|
||||
| File | Claimed Version | Actual Version | Verdict |
|
||||
|------|----------------|----------------|---------|
|
||||
| docs/sysdes.md | 0.9.8 | 0.9.8 (line 6) | **PASS** |
|
||||
| docs/srs.md | 0.9.9 | 0.9.9 (line 6) | **PASS** |
|
||||
| docs/sysrs.md | 0.9.11 | 0.9.11 (line 5) | **PASS** |
|
||||
| docs/material3-guideline.md | 0.9.2 | 0.9.2 (line 4) | **PASS** |
|
||||
| tools/windows-smoke.md | `product/scaffold-v0` | Confirmed (line 5) | **PASS** |
|
||||
|
||||
### 2.2 Outdated Docs (3 verified)
|
||||
|
||||
| Doc | Claim | Verdict |
|
||||
|-----|-------|---------|
|
||||
| docs/sysdes.md | 30 days stale, version 0.9.8 | **PASS** — Last change record 2026-05-14, confirmed 30 days stale. |
|
||||
| docs/srs.md | 26 days stale, version 0.9.9 | **PASS** — Last change record 2026-05-18, confirmed 26 days stale. |
|
||||
| docs/material3-guideline.md | 30 days stale, version 0.9.2 | **PASS** — Last change record 2026-05-14, confirmed 30 days stale. |
|
||||
|
||||
### 2.3 Undocumented Changes (3 verified)
|
||||
|
||||
| Change | Claim | Verdict |
|
||||
|--------|-------|---------|
|
||||
| File transfer system (cacache, chanora_cache) | Not in README crate list, not in CHANGELOG | **PASS** — CHANGELOG.md has no mention of file transfer, cacache, or chanora_cache. README crate list (lines 236-249) doesn't include chanora_cache. |
|
||||
| Poke notifications | Not in CHANGELOG | **PASS** — CHANGELOG.md has no mention of poke. Poke files exist in code (poke_notification_service.dart, poke_limiter.rs, etc.). |
|
||||
| Desktop Silero ONNX VAD | Not in CHANGELOG | **PASS** — CHANGELOG.md has no mention of silero_onnx or desktop ONNX VAD. File exists at `crates/chanora_audio/src/vad/silero_onnx.rs`. |
|
||||
|
||||
### 2.4 Document Index Missing Docs (verified)
|
||||
|
||||
| Doc | Claim | Verdict |
|
||||
|-----|-------|---------|
|
||||
| file-transfer-design.md | Missing from document-index.md | **PASS** — Not listed in document-index.md lines 12-32. File exists at `docs/architecture/file-transfer-design.md`. |
|
||||
| file-transfer-research.md | Missing from document-index.md | **PASS** — Not listed. File exists at `docs/architecture/file-transfer-research.md`. |
|
||||
| file-transfer-implementation-plan.md | Missing from document-index.md | **PASS** — Not listed. File exists at `docs/architecture/file-transfer-implementation-plan.md`. |
|
||||
| poke-without-message-design.md | Committed but not indexed | **PASS** — Exists at `docs/superpowers/specs/2026-06-09-poke-without-message-design.md`. Not in document-index.md. |
|
||||
|
||||
### 2.5 Errors Found
|
||||
|
||||
1. **Line 147: "docs/governance/maintainability-review-2026-06-08.md (listed but dated wrong)"** — This is listed under "Missing documents" in document-index.md analysis, but the doc IS listed at document-index.md:29. The "dated wrong" claim is unclear — document-index.md has no date column. This is a minor inaccuracy in the out-of-date doc.
|
||||
|
||||
2. **Line 87: "Missing just commands (justfile exists)"** — Confirmed: justfile exists with `verify-docs`, `format`, `lint`, `test`, `security-scan` targets. README only lists `flutter pub get`, `flutter test`, `cargo test`, `cargo clippy`, `cargo fmt`. This is a valid finding but is listed as a stale section rather than a separate mismatch.
|
||||
|
||||
### 2.6 Missed Outdated Docs
|
||||
|
||||
None found. The analysis covers 64 docs comprehensively. The stale date references table (lines 26-42) is thorough.
|
||||
|
||||
### 2.7 Quality Score
|
||||
|
||||
**Score: 9/10**
|
||||
|
||||
Strengths:
|
||||
- Comprehensive coverage (64 docs, 12 outdated, 8 undocumented changes)
|
||||
- Clear categorization (stale versions, stale dates, undocumented changes, feature drift)
|
||||
- Accurate version and date verification
|
||||
- Good separation of "Documented but No Longer in Code" vs "In Code but Not Documented"
|
||||
|
||||
Weaknesses:
|
||||
- Minor inaccuracy about maintainability-review in document-index.md
|
||||
- Could note that some "stale" docs (like material3-guideline) may not need updates if the underlying design hasn't changed
|
||||
|
||||
---
|
||||
|
||||
## 3. Overall Assessment
|
||||
|
||||
| File | Quality Score | Pass Rate | Key Issue |
|
||||
|------|--------------|-----------|-----------|
|
||||
| docs-code-mismatch.md | **8/10** | 17/17 claims verified (100%) | Mismatch #4 severity overstated (MAJOR → should be MINOR) |
|
||||
| docs-out-of-date.md | **9/10** | All claims verified (100%) | Minor inaccuracy about maintainability-review in document-index |
|
||||
|
||||
### Corrections Needed
|
||||
|
||||
1. **docs-code-mismatch.md line 32:** Change severity of mismatch #4 from MAJOR to MINOR. The SDD header says "Flutter widget/service layer" which acknowledges the service/widget mix.
|
||||
2. **docs-code-mismatch.md line 47:** Consider downgrading mismatch #7 (DEC-030) to MINOR. "Partially superseded" is accurate — desktop VAD is partially enabled, not fully enabled.
|
||||
3. **docs-out-of-date.md line 147:** Fix the claim about maintainability-review-2026-06-08.md being "listed but dated wrong" — it IS listed in document-index.md:29, and the index has no date column.
|
||||
|
||||
### Summary
|
||||
|
||||
Both documents are high-quality, thorough analyses. The docs-code-mismatch.md has a minor severity classification issue, and the docs-out-of-date.md has one factual error about the document index. Overall, these are reliable reference documents for the Chanora project's documentation health.
|
||||
@@ -0,0 +1,47 @@
|
||||
# iOS release build
|
||||
|
||||
This document records the credential-free iOS P1 release path and the signing handoff for TestFlight/App Store builds.
|
||||
|
||||
## Unsigned verification build
|
||||
|
||||
Run from the repository root on macOS:
|
||||
|
||||
```bash
|
||||
flutter --version
|
||||
./tools/build-ios.sh --no-codesign
|
||||
```
|
||||
|
||||
Expected unsigned output:
|
||||
|
||||
```text
|
||||
apps/chanora_flutter/build/ios/iphoneos/Runner.app/
|
||||
```
|
||||
|
||||
## Store export configuration
|
||||
|
||||
The App Store export template lives at:
|
||||
|
||||
```text
|
||||
apps/chanora_flutter/ios/ExportOptions/AppStore.plist
|
||||
```
|
||||
|
||||
Use it after Apple signing assets are available:
|
||||
|
||||
```bash
|
||||
./tools/build-ios.sh --export-method app-store --export-options-plist apps/chanora_flutter/ios/ExportOptions/AppStore.plist
|
||||
```
|
||||
|
||||
Required signing assets:
|
||||
|
||||
- Apple Developer team access for `app.chanora.chanoraFlutter`
|
||||
- App Store distribution certificate or automatic signing permission
|
||||
- App Store provisioning profile if automatic signing is not used
|
||||
- Xcode 26 or later for uploads on or after 2026-04-28
|
||||
|
||||
## Verification checklist
|
||||
|
||||
- `flutter test` passes in `apps/chanora_flutter`
|
||||
- `flutter analyze` passes in `apps/chanora_flutter`
|
||||
- `flutter build ios --release --no-codesign` succeeds
|
||||
- Signed App Store export succeeds once credentials are installed
|
||||
- App Store metadata does not imply TeamSpeak affiliation
|
||||
@@ -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_prefetch` without changing Flutter APIs, resolver behavior, protocol dialing behavior, or Android connect UX.
|
||||
|
||||
**Architecture:** Add `crates/chanora_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_prefetch/Cargo.toml`: package metadata and dependencies.
|
||||
- Create `crates/chanora_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_prefetch` to workspace members and update the workspace layout comment.
|
||||
- Modify `core/chanora_core/Cargo.toml`: replace the direct `chanora_resolver` dependency with `chanora_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_prefetch` Crate With Cache Policy Tests
|
||||
|
||||
**Files:**
|
||||
- Modify: `Cargo.toml`
|
||||
- Create: `crates/chanora_prefetch/Cargo.toml`
|
||||
- Create: `crates/chanora_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_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_prefetch",
|
||||
"crates/chanora_bridge",
|
||||
"crates/chanora_resolver",
|
||||
]
|
||||
```
|
||||
|
||||
Create `crates/chanora_prefetch/Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "chanora_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_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_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_prefetch",
|
||||
host = %normalized,
|
||||
resolved = %addr,
|
||||
"resolution prefetch result"
|
||||
);
|
||||
guard.store_success(generation, &normalized, addr, Instant::now());
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(
|
||||
target: "chanora_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_prefetch",
|
||||
host = %host,
|
||||
resolved = %addr,
|
||||
"connect using prefetched resolution"
|
||||
);
|
||||
Some(addr)
|
||||
}
|
||||
None => {
|
||||
info!(target: "chanora_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_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_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_prefetch = { path = "../../crates/chanora_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_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_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_prefetch = { path = "../../crates/chanora_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_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_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_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_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_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,642 @@
|
||||
# Chat Panel Switching 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:** Enable in-place conversation switching in the expanded 3-panel layout, add per-conversation draft persistence, and improve unread awareness — matching industry-standard UX patterns from Discord/Slack/Telegram/Element.
|
||||
|
||||
**Architecture:** The expanded layout (≥1024dp) shows voice controls | channel tree | inline chat panel. Currently the chat panel locks to one conversation with no way to switch from the channel tree. The fix adds channel→chat triggers, in-place target swapping, per-target draft storage, and preserves chat state across switches. The state machine (`_inlineChatTarget` + `_chatOpen`) already supports switching — we just need UI affordances and draft persistence.
|
||||
|
||||
**Tech Stack:** Flutter/Dart, existing `BridgeMessageTarget` sealed class, existing `ChatDetailView` / `ChatPanel` / `SnapshotView` widgets.
|
||||
|
||||
**Design research basis:** Discord (in-place swap, dot/badge unread hierarchy), Telegram Desktop (adaptive 3-tier layout, per-conversation drafts + scroll anchoring), Element (per-room panel state, toggleable right panel), Slack (bold sidebar for unread, split view). All apps treat DMs and channels identically for switching behavior.
|
||||
|
||||
---
|
||||
|
||||
## Current State
|
||||
|
||||
| UX Element | Status |
|
||||
|---|---|
|
||||
| Unread indicator | Single global badge count on app bar chat button |
|
||||
| Channel → chat trigger | **None.** Channel tiles only join voice |
|
||||
| Client → DM trigger | Works (right-click → "Direct Message") |
|
||||
| Header chat button when panel open | Idempotent — re-uses same `_inlineChatTarget` |
|
||||
| Draft persistence | None — single `TextEditingController`, lost on switch |
|
||||
| Scroll position memory | None — always auto-scrolls to bottom |
|
||||
| Per-conversation unread | None |
|
||||
|
||||
## Scope
|
||||
|
||||
**In scope (this plan):**
|
||||
- Channel → chat switching in expanded layout
|
||||
- Channel right-click → "Chat" option
|
||||
- Header chat button → switch to current voice channel chat when panel already open
|
||||
- Per-target draft persistence (in-memory `Map`)
|
||||
- Close = dismiss (remember last target and draft)
|
||||
- Unread dot indicator on channels in `SnapshotView`
|
||||
|
||||
**Out of scope (future):**
|
||||
- Scroll position memory per target
|
||||
- "New messages" divider
|
||||
- Per-target unread counts / badge numbers
|
||||
- Notification tiering (dot/badge/mention)
|
||||
- Split view (Slack power-user feature)
|
||||
|
||||
## Responsive Behavior
|
||||
|
||||
| Tier | Width | Chat Mode | Changes in this plan |
|
||||
|---|---|---|---|
|
||||
| **Expanded** | ≥1024dp | Inline `ChatPanel` (right column) | ✅ All changes apply here |
|
||||
| **Medium** | 600–1023dp | Full-screen `ChatPage` route | No changes needed (already works) |
|
||||
| **Compact** | <600dp | Full-screen `ChatPage` route | No changes needed (already works) |
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
| File | Action | Responsibility |
|
||||
|---|---|---|
|
||||
| `apps/chanora_flutter/lib/widgets/snapshot_view.dart` | **Modify** | Add `onOpenChannelChat` callback, channel context menu with "Chat" option, unread dot on channels |
|
||||
| `apps/chanora_flutter/lib/main.dart` | **Modify** | Add `_chatDrafts` map, wire `onOpenChannelChat`, fix header chat button to switch to current channel, fix `_closeInlineChat` to preserve last target |
|
||||
| `apps/chanora_flutter/lib/widgets/chat_panel.dart` | **Modify** | Accept `onSwitchTarget` callback, pass draft state through |
|
||||
| `apps/chanora_flutter/lib/widgets/chat_views.dart` | **Modify** | `ChatDetailView` accepts external draft text, exposes draft text on target change |
|
||||
| `apps/chanora_flutter/lib/design/breakpoints.dart` | **No changes** | Breakpoints unchanged |
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Add `onOpenChannelChat` callback to `SnapshotView`
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/chanora_flutter/lib/widgets/snapshot_view.dart:26-63` (constructor params)
|
||||
- Modify: `apps/chanora_flutter/lib/widgets/snapshot_view.dart:203-284` (`_channelTile`)
|
||||
|
||||
- [ ] **Step 1: Add the callback field to `SnapshotView` widget**
|
||||
|
||||
In `snapshot_view.dart`, add a new optional callback field after `onOpenClientPoke` (around line 71):
|
||||
|
||||
```dart
|
||||
/// Open chat for a channel.
|
||||
final ValueChanged<rust.BridgeChannel>? onOpenChannelChat;
|
||||
```
|
||||
|
||||
Update the constructor to include it (around line 32, after `onOpenClientPoke`):
|
||||
|
||||
```dart
|
||||
this.onOpenChannelChat,
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Add a right-click/long-press context menu to `_channelTile`**
|
||||
|
||||
Replace the `InkWell` in `_channelTile` (lines 243-284) with a context menu wrapper. The channel tile should support:
|
||||
- **Tap**: join voice (existing behavior, unchanged)
|
||||
- **Right-click / long-press**: show a popup menu with "Open chat" option
|
||||
|
||||
```dart
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
onLongPress: widget.onOpenChannelChat != null
|
||||
? () => widget.onOpenChannelChat!(channel)
|
||||
: null,
|
||||
child: PopupMenuButton<String>(
|
||||
position: PopupMenuPosition.under,
|
||||
enabled: widget.onOpenChannelChat != null,
|
||||
onSelected: (value) {
|
||||
if (value == 'chat') {
|
||||
widget.onOpenChannelChat?.call(channel);
|
||||
}
|
||||
},
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
value: 'chat',
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.chat_bubble_outline, size: 18),
|
||||
const SizedBox(width: 12),
|
||||
Text(AppLocalizations.of(context)!.chatAction),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(minHeight: 40),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: channelIndent),
|
||||
_expandButton(
|
||||
theme,
|
||||
hasVisibleChildren: hasVisibleChildren,
|
||||
expanded: expanded,
|
||||
onPressed: onToggleExpanded,
|
||||
),
|
||||
SizedBox(
|
||||
width: _channelIconColumnWidth,
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Icon(
|
||||
Icons.tag,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: _channelTextGap),
|
||||
Expanded(
|
||||
child: Text(
|
||||
channel.name,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
if (channel.hasPassword) ...[
|
||||
const SizedBox(width: 8),
|
||||
Icon(
|
||||
Icons.lock_outline,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
Note: The `PopupMenuButton` wraps the existing content as its `child`, so the tile looks identical until right-clicked. The `onTap` on `InkWell` continues to handle voice join.
|
||||
|
||||
- [ ] **Step 3: Run `flutter analyze`**
|
||||
|
||||
Run: `cd apps/chanora_flutter && flutter analyze`
|
||||
Expected: No new errors (the callback is optional, so existing call sites compile without changes)
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/chanora_flutter/lib/widgets/snapshot_view.dart
|
||||
git commit -m "feat(chat): add onOpenChannelChat callback with context menu to channel tiles"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Wire `onOpenChannelChat` in `main.dart` and add per-target draft storage
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/chanora_flutter/lib/main.dart:370-374` (state fields)
|
||||
- Modify: `apps/chanora_flutter/lib/main.dart:2521-2540` (SnapshotView constructor)
|
||||
|
||||
- [ ] **Step 1: Add draft storage map**
|
||||
|
||||
Add a new state field near line 374 (after `_inlineChatCollapseNoticeShown`):
|
||||
|
||||
```dart
|
||||
/// Per-target draft text. Populated when switching away from a conversation
|
||||
/// so the user's unfinished message is preserved.
|
||||
final Map<String, String> _chatDrafts = {};
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Add `_lastDismissedTarget` field**
|
||||
|
||||
Add a new state field to remember the last dismissed target so reopening returns to it:
|
||||
|
||||
```dart
|
||||
/// The last chat target before the panel was closed. Used to restore the
|
||||
/// previous conversation when the user reopens chat.
|
||||
rust.BridgeMessageTarget? _lastDismissedTarget;
|
||||
String _lastDismissedClientName = '';
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Wire `onOpenChannelChat` in `SnapshotView` constructor**
|
||||
|
||||
In the `SnapshotView(...)` constructor around line 2512, add the new callback:
|
||||
|
||||
```dart
|
||||
onOpenChannelChat: (channel) => unawaited(
|
||||
_onOpenChat(
|
||||
target: rust.BridgeMessageTarget.channel(channel.id),
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run `flutter analyze`**
|
||||
|
||||
Run: `cd apps/chanora_flutter && flutter analyze`
|
||||
Expected: No new errors
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/chanora_flutter/lib/main.dart
|
||||
git commit -m "feat(chat): add per-target draft storage and wire onOpenChannelChat"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Fix `_onOpenChat` to support switching and draft save/restore
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/chanora_flutter/lib/main.dart:1628-1685` (`_onOpenChat` and `_closeInlineChat`)
|
||||
|
||||
- [ ] **Step 1: Update `_onOpenChat` to save current draft and restore new target's draft**
|
||||
|
||||
Replace the `_onOpenChat` method (lines 1628-1676) with logic that:
|
||||
1. Saves the current `_inlineChatTarget` draft before switching
|
||||
2. Restores the new target's draft (if any)
|
||||
3. When called with no explicit target and panel is already open, switches to current voice channel's chat
|
||||
|
||||
```dart
|
||||
Future<void> _onOpenChat({
|
||||
rust.BridgeMessageTarget? target,
|
||||
String clientName = '',
|
||||
}) async {
|
||||
final initialSnapshot = _snapshot!;
|
||||
|
||||
// Resolve the new target.
|
||||
// If no target passed and panel is already open, switch to current voice channel.
|
||||
// If no target passed and panel is closed, resolve from history or default.
|
||||
rust.BridgeMessageTarget newTarget;
|
||||
if (target != null) {
|
||||
newTarget = target;
|
||||
} else if (_chatOpen && _currentVoiceChannelId != null) {
|
||||
newTarget = rust.BridgeMessageTarget.channel(_currentVoiceChannelId!);
|
||||
} else if (_inlineChatTarget != null) {
|
||||
newTarget = _inlineChatTarget!;
|
||||
} else {
|
||||
newTarget = resolveInitialChatTarget(
|
||||
messages: _chatMessages,
|
||||
currentVoiceChannelId: _currentVoiceChannelId,
|
||||
) ?? const rust.BridgeMessageTarget.server();
|
||||
}
|
||||
|
||||
final newClientName = clientName.isNotEmpty
|
||||
? clientName
|
||||
: (newTarget == _inlineChatTarget) ? _inlineChatClientName : '';
|
||||
|
||||
final isExpanded =
|
||||
layoutClassFromWidth(MediaQuery.sizeOf(context).width) ==
|
||||
LayoutClass.expanded;
|
||||
if (isExpanded) {
|
||||
setState(() {
|
||||
// Save draft for the current target before switching.
|
||||
_saveCurrentDraft();
|
||||
_chatUnread = 0;
|
||||
_chatOpen = true;
|
||||
_inlineChatTarget = newTarget;
|
||||
_inlineChatClientName = newClientName;
|
||||
_inlineChatCollapseNoticeShown = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_chatUnread = 0;
|
||||
_chatOpen = true;
|
||||
});
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => ChatPage(
|
||||
messages: _chatMessages,
|
||||
snapshot: initialSnapshot,
|
||||
messagesSource: () => _chatMessages,
|
||||
snapshotSource: () => _snapshot ?? initialSnapshot,
|
||||
refreshListenable: _chatFeedRevision,
|
||||
initialTarget: newTarget,
|
||||
initialClientName: newClientName,
|
||||
onTs3ServerLink: _onTs3ServerLink,
|
||||
),
|
||||
),
|
||||
);
|
||||
if (mounted) setState(() => _chatOpen = false);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Add `_saveCurrentDraft` and `_draftKeyForTarget` helper methods**
|
||||
|
||||
Add these near `_onOpenChat`:
|
||||
|
||||
```dart
|
||||
/// Converts a [rust.BridgeMessageTarget] to a stable string key for draft storage.
|
||||
String _draftKeyForTarget(rust.BridgeMessageTarget target) {
|
||||
return switch (target) {
|
||||
rust.BridgeMessageTarget_Server() => 'server',
|
||||
rust.BridgeMessageTarget_Channel(:final id) => 'channel:$id',
|
||||
rust.BridgeMessageTarget_Client(:final id) => 'client:$id',
|
||||
rust.BridgeMessageTarget_Poke(:final id) => 'poke:$id',
|
||||
};
|
||||
}
|
||||
|
||||
/// Saves the current draft text (if any) for the current inline chat target.
|
||||
/// Called before switching targets or closing the panel.
|
||||
void _saveCurrentDraft() {
|
||||
// Note: The actual draft text is read from ChatDetailView's
|
||||
// TextEditingController via a callback. This is wired in Task 4.
|
||||
}
|
||||
```
|
||||
|
||||
Note: `_saveCurrentDraft` will be completed in Task 4 when we wire the draft callback from `ChatDetailView`.
|
||||
|
||||
- [ ] **Step 3: Update `_closeInlineChat` to preserve last target instead of nulling it**
|
||||
|
||||
Replace `_closeInlineChat` (lines 1678-1685):
|
||||
|
||||
```dart
|
||||
void _closeInlineChat() {
|
||||
setState(() {
|
||||
// Save draft before closing.
|
||||
_saveCurrentDraft();
|
||||
// Remember the last target so reopening returns to it.
|
||||
_lastDismissedTarget = _inlineChatTarget;
|
||||
_lastDismissedClientName = _inlineChatClientName;
|
||||
_chatOpen = false;
|
||||
// Do NOT null _inlineChatTarget — we want to remember it for reopen.
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run `flutter analyze`**
|
||||
|
||||
Run: `cd apps/chanora_flutter && flutter analyze`
|
||||
Expected: No new errors
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/chanora_flutter/lib/main.dart
|
||||
git commit -m "feat(chat): switch chat target on channel click, save draft before switching, preserve target on close"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Add draft save/restore callback to `ChatDetailView` and `ChatPanel`
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/chanora_flutter/lib/widgets/chat_views.dart:1054-1098` (`ChatDetailView` constructor + state)
|
||||
- Modify: `apps/chanora_flutter/lib/widgets/chat_panel.dart:12-75` (`ChatPanel` constructor + build)
|
||||
|
||||
- [ ] **Step 1: Add draft callbacks to `ChatDetailView`**
|
||||
|
||||
Add two new optional callbacks to `ChatDetailView` (after `messageMaxWidth` around line 1066):
|
||||
|
||||
```dart
|
||||
/// External draft text to restore when the widget initializes or the target changes.
|
||||
final String? restoredDraft;
|
||||
|
||||
/// Called with the current draft text whenever the target changes or the widget is disposed.
|
||||
final ValueChanged<String>? onDraftChanged;
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Implement draft restore in `_ChatDetailViewState`**
|
||||
|
||||
In `_ChatDetailViewState` (line 1100), add `initState` and `didUpdateWidget` to handle drafts:
|
||||
|
||||
```dart
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.restoredDraft != null && widget.restoredDraft!.isNotEmpty) {
|
||||
_textCtl.text = widget.restoredDraft!;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant ChatDetailView oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.target != widget.target) {
|
||||
// Save draft for old target before switching.
|
||||
if (oldWidget.onDraftChanged != null && _textCtl.text.isNotEmpty) {
|
||||
oldWidget.onDraftChanged!(_textCtl.text);
|
||||
}
|
||||
// Restore draft for new target.
|
||||
_textCtl.text = widget.restoredDraft ?? '';
|
||||
_lastRenderedTarget = null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Emit the current draft so the parent can save it.
|
||||
if (widget.onDraftChanged != null && _textCtl.text.isNotEmpty) {
|
||||
widget.onDraftChanged!(_textCtl.text);
|
||||
}
|
||||
_textCtl.dispose();
|
||||
_scrollCtl.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
```
|
||||
|
||||
Remove the existing `dispose` method (lines 1127-1132) — it's replaced by the new one above.
|
||||
|
||||
- [ ] **Step 3: Thread draft callbacks through `ChatPanel`**
|
||||
|
||||
Update `ChatPanel` to accept and pass through the new callbacks. Add fields:
|
||||
|
||||
```dart
|
||||
/// External draft text to restore in the chat detail view.
|
||||
final String? restoredDraft;
|
||||
|
||||
/// Called when the draft text changes.
|
||||
final ValueChanged<String>? onDraftChanged;
|
||||
```
|
||||
|
||||
Pass them through in `build()` where `ChatDetailView` is constructed (line 58):
|
||||
|
||||
```dart
|
||||
child: ChatDetailView(
|
||||
messages: messages,
|
||||
snapshot: snapshot,
|
||||
target: target,
|
||||
clientName: clientName,
|
||||
currentChannelId: currentChannelId,
|
||||
channelName: channelName,
|
||||
onTs3ServerLink: onTs3ServerLink,
|
||||
restoredDraft: restoredDraft,
|
||||
onDraftChanged: onDraftChanged,
|
||||
messageMaxWidth: 500,
|
||||
headerTrailing: IconButton(
|
||||
tooltip: 'Close chat',
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: onClose,
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Wire draft callbacks in `main.dart`**
|
||||
|
||||
In the `ChatPanel(...)` constructor around line 2567, add the draft callbacks:
|
||||
|
||||
```dart
|
||||
ChatPanel(
|
||||
messages: _chatMessages,
|
||||
snapshot: _snapshot!,
|
||||
target: inlineChatTarget,
|
||||
clientName: _inlineChatClientName,
|
||||
onTs3ServerLink: _onTs3ServerLink,
|
||||
restoredDraft: _chatDrafts[_draftKeyForTarget(inlineChatTarget)],
|
||||
onDraftChanged: (text) {
|
||||
_chatDrafts[_draftKeyForTarget(_inlineChatTarget!)] = text;
|
||||
},
|
||||
onClose: _closeInlineChat,
|
||||
),
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Complete `_saveCurrentDraft` in `main.dart`**
|
||||
|
||||
The `_saveCurrentDraft` method is called from `_onOpenChat` (before switching) and `_closeInlineChat`. Since `ChatDetailView` emits drafts via `onDraftChanged` and `dispose`, the parent always has the latest draft in `_chatDrafts`. The method body stays as a no-op safety net:
|
||||
|
||||
```dart
|
||||
void _saveCurrentDraft() {
|
||||
// Drafts are continuously saved via onDraftChanged callback.
|
||||
// This method exists as an explicit save point for any future
|
||||
// snapshot-based draft capture.
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Run `flutter analyze`**
|
||||
|
||||
Run: `cd apps/chanora_flutter && flutter analyze`
|
||||
Expected: No new errors
|
||||
|
||||
- [ ] **Step 7: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/chanora_flutter/lib/widgets/chat_views.dart apps/chanora_flutter/lib/widgets/chat_panel.dart apps/chanora_flutter/lib/main.dart
|
||||
git commit -m "feat(chat): per-target draft persistence with save/restore on switch"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Add unread dot indicator to channel tiles in `SnapshotView`
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/chanora_flutter/lib/widgets/snapshot_view.dart` (add unread indicator)
|
||||
- Modify: `apps/chanora_flutter/lib/main.dart` (pass unread channel set)
|
||||
|
||||
- [ ] **Step 1: Add unread channel IDs parameter to `SnapshotView`**
|
||||
|
||||
Add a new required field to `SnapshotView` (after `canJoinVoiceChannel` around line 57):
|
||||
|
||||
```dart
|
||||
/// Set of channel IDs that have unread chat messages.
|
||||
final Set<BigInt> unreadChannelIds;
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Add unread dot to `_channelTile`**
|
||||
|
||||
In `_channelTile`, inside the `Row` children (after the channel name `Expanded` widget, around line 273), add an unread dot:
|
||||
|
||||
```dart
|
||||
// Unread indicator.
|
||||
if (widget.unreadChannelIds.contains(channel.id)) ...[
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
width: 8,
|
||||
height: 8,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.primary,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
],
|
||||
```
|
||||
|
||||
This must come before the password lock icon check (line 274).
|
||||
|
||||
- [ ] **Step 3: Compute unread channel set in `main.dart`**
|
||||
|
||||
Add a getter in `_BetaHomeState` that computes which channels have unread messages:
|
||||
|
||||
```dart
|
||||
/// Channel IDs that have unread chat messages (used for dot indicators).
|
||||
Set<BigInt> get _unreadChannelIds {
|
||||
if (_chatOpen) return const {};
|
||||
final ids = <BigInt>{};
|
||||
for (final entry in _chatMessages) {
|
||||
if (!entry.countsTowardUnread || entry.isSelf) continue;
|
||||
if (entry.target case rust.BridgeMessageTarget_Channel(:final id)) {
|
||||
ids.add(id);
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Pass unread channel set to `SnapshotView`**
|
||||
|
||||
In the `SnapshotView(...)` constructor around line 2512, add:
|
||||
|
||||
```dart
|
||||
unreadChannelIds: _unreadChannelIds,
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Run `flutter analyze`**
|
||||
|
||||
Run: `cd apps/chanora_flutter && flutter analyze`
|
||||
Expected: No new errors
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/chanora_flutter/lib/widgets/snapshot_view.dart apps/chanora_flutter/lib/main.dart
|
||||
git commit -m "feat(chat): unread dot indicator on channel tiles with unread messages"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 6: End-to-end verification
|
||||
|
||||
**Files:** All modified files.
|
||||
|
||||
- [ ] **Step 1: Run `flutter analyze` on the full project**
|
||||
|
||||
Run: `cd apps/chanora_flutter && flutter analyze`
|
||||
Expected: Zero issues
|
||||
|
||||
- [ ] **Step 2: Run `flutter test`**
|
||||
|
||||
Run: `cd apps/chanora_flutter && flutter test`
|
||||
Expected: All tests pass (same baseline as before — 180 passed, 2 skipped)
|
||||
|
||||
- [ ] **Step 3: Build macOS release**
|
||||
|
||||
Run: `bash tools/build-macos.sh`
|
||||
Expected: Successful build producing `chanora-v0.2.0-beta.1-macos-aarch64.zip`
|
||||
|
||||
- [ ] **Step 4: Manual QA checklist**
|
||||
|
||||
Launch the app and verify:
|
||||
|
||||
1. **Channel → chat switching**: With window ≥1024dp and chat panel open showing Server chat, click a channel in the tree. The chat panel should switch to that channel's chat (messages filter to that channel). Voice join should also happen.
|
||||
|
||||
2. **Channel right-click → Chat**: Right-click a channel → "Open chat". Chat panel should switch to that channel's chat without joining voice.
|
||||
|
||||
3. **Header chat button toggle**: With chat panel open showing a DM, click the header chat button. It should switch to the current voice channel's chat.
|
||||
|
||||
4. **Draft persistence**: Type "hello" in chat input but don't send. Click a different channel. Type "world" in that channel's chat. Switch back to the first channel. The input should show "hello".
|
||||
|
||||
5. **Close and reopen**: Close the chat panel. Click the header chat button. It should reopen to the last conversation with the draft intact.
|
||||
|
||||
6. **Unread dots**: Close the chat panel. Have someone send a message to a specific channel. That channel in the tree should show a blue dot.
|
||||
|
||||
7. **Medium/compact unchanged**: Narrow the window below 1024dp. Open chat. It should still push a full-screen route as before. No regressions.
|
||||
|
||||
8. **DM switching still works**: Right-click a client → "Direct Message". Chat panel should switch to that DM. Right-click another client → "Direct Message". Should switch again.
|
||||
|
||||
---
|
||||
|
||||
## Self-Review
|
||||
|
||||
### Spec coverage
|
||||
|
||||
| Requirement | Task |
|
||||
|---|---|
|
||||
| Channel → chat switching (tap) | Task 2 (wiring) + Task 3 (target resolution) |
|
||||
| Channel → chat (context menu) | Task 1 |
|
||||
| Header button switches when open | Task 3 |
|
||||
| Per-target draft persistence | Task 4 |
|
||||
| Close = dismiss (remember state) | Task 3 |
|
||||
| Unread dot on channels | Task 5 |
|
||||
| Medium/compact unchanged | No changes to those paths |
|
||||
|
||||
### Placeholder scan
|
||||
No TBD, TODO, or placeholder steps found. All code blocks contain complete implementations.
|
||||
|
||||
### Type consistency
|
||||
- `BridgeMessageTarget.channel(id)` uses `BigInt` — matches `channel.id` type
|
||||
- `_chatDrafts` uses `String` keys from `_draftKeyForTarget` — consistent
|
||||
- `onOpenChannelChat` callback type `ValueChanged<rust.BridgeChannel>?` — matches widget pattern
|
||||
- `unreadChannelIds` uses `Set<BigInt>` — matches channel ID type
|
||||
@@ -0,0 +1,95 @@
|
||||
# Core Internal Split 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:** Improve `chanora_core` maintainability by moving stable event DTOs and network diagnostic state out of the oversized `lib.rs` while preserving the public crate Interface.
|
||||
|
||||
**Architecture:** Keep `lib.rs` as the public Interface and session orchestration entry point. Move event-facing DTOs to `events.rs` and private network diagnostic ring-buffer state to `network_diagnostics.rs`; re-export public event types from `lib.rs` so downstream callers keep using `chanora_core::SessionEvent` and related names unchanged.
|
||||
|
||||
**Tech Stack:** Rust 2021, Tokio broadcast/watch channels, `thiserror`, existing Cargo workspace tests.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Move Core Event DTOs
|
||||
|
||||
**Files:**
|
||||
- Create: `core/chanora_core/src/events.rs`
|
||||
- Modify: `core/chanora_core/src/lib.rs`
|
||||
- Verify: `cargo test -p chanora_core`
|
||||
|
||||
- [ ] **Step 1: Preserve current public Interface with tests**
|
||||
|
||||
Run: `cargo test -p chanora_core`
|
||||
Expected: PASS. Existing bridge-facing tests and compile checks prove the current public event names are valid.
|
||||
|
||||
- [ ] **Step 2: Create `events.rs` with the moved public event DTOs**
|
||||
|
||||
Move these exact public types from `lib.rs` to `events.rs`:
|
||||
- `PttDescriptorSnapshot`
|
||||
- `PersistedPttBinding`
|
||||
- `SessionEvent`
|
||||
- `VoiceJoinSyncState`
|
||||
- `VoiceJoinErrorCode`
|
||||
- `NetworkState`
|
||||
|
||||
Use local imports in `events.rs` for `chanora_audio::{AudioRoute, PttBackendDescriptor}` and `chanora_protocol::MessageTarget`.
|
||||
|
||||
- [ ] **Step 3: Re-export moved types from `lib.rs`**
|
||||
|
||||
Add `mod events;` and `pub use events::{...};` for all moved public types. Remove the original definitions from `lib.rs`.
|
||||
|
||||
- [ ] **Step 4: Run tests**
|
||||
|
||||
Run: `cargo test -p chanora_core`
|
||||
Expected: PASS with no public Interface break.
|
||||
|
||||
### Task 2: Move Core Network Diagnostics State
|
||||
|
||||
**Files:**
|
||||
- Create: `core/chanora_core/src/network_diagnostics.rs`
|
||||
- Modify: `core/chanora_core/src/lib.rs`
|
||||
- Verify: `cargo test -p chanora_core network_diagnostics`
|
||||
|
||||
- [ ] **Step 1: Move `NetworkDiagnostics` into a private module**
|
||||
|
||||
Move the private `NetworkDiagnostics` struct and its methods from `lib.rs` into `network_diagnostics.rs`. Keep methods `pub(crate)` because `ChanoraSession` records connection/loss events and exports summaries.
|
||||
|
||||
- [ ] **Step 2: Move the regression test with the module**
|
||||
|
||||
Move `network_diagnostics_keeps_last_eight_loss_reasons` from the `lib.rs` test module into `network_diagnostics.rs` so the behaviour test lives next to the Implementation it protects.
|
||||
|
||||
- [ ] **Step 3: Import the private module from `lib.rs`**
|
||||
|
||||
Add `mod network_diagnostics;` and `use network_diagnostics::NetworkDiagnostics;`. Remove `VecDeque` from the `lib.rs` imports.
|
||||
|
||||
- [ ] **Step 4: Run targeted and workspace verification**
|
||||
|
||||
Run: `cargo test -p chanora_core network_diagnostics`
|
||||
Expected: PASS.
|
||||
|
||||
Run: `cargo test --workspace`
|
||||
Expected: PASS.
|
||||
|
||||
### Task 3: Format and Check Workspace
|
||||
|
||||
**Files:**
|
||||
- Modify: Rust files touched above only, except existing formatter-only churn may remain from prior `cargo fmt --all`.
|
||||
|
||||
- [ ] **Step 1: Format Rust code**
|
||||
|
||||
Run: `cargo fmt --all`
|
||||
Expected: no command output.
|
||||
|
||||
- [ ] **Step 2: Compile workspace**
|
||||
|
||||
Run: `cargo check --workspace`
|
||||
Expected: finishes successfully.
|
||||
|
||||
- [ ] **Step 3: Inspect diff**
|
||||
|
||||
Run: `git diff --stat`
|
||||
Expected: new `events.rs` and `network_diagnostics.rs`; smaller `core/chanora_core/src/lib.rs`; no public API renames.
|
||||
|
||||
---
|
||||
|
||||
Self-review: This plan covers the recommended Core split first slice, avoids public Interface changes, has no placeholders, and keeps testing tied to the moved Implementations.
|
||||
@@ -0,0 +1,608 @@
|
||||
# Maintainability Continuation 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:** Continue the current maintainability review with safe simplifications, full code-review remediation, current documentation, explicit fail-safe evidence, and Android runtime verification status.
|
||||
|
||||
**Current status:** Task 0 and the audio-realtime portion of Task 0.1 have landed in commits `d835394` and `8606eb4`. Task 0.2 is a documentation/status alignment slice only; it must not claim Android or iOS runtime success.
|
||||
|
||||
**Architecture:** Treat the existing uncommitted maintainability changes as the baseline slice. Fix safety and hidden-bug findings before broad Module splits. Preserve public Rust Core, Bridge, Protocol, Audio, and Flutter responsibilities while applying only tested simplifications and risk-reducing refactors. Record larger seam decisions as follow-up findings unless a huge Module must be split to make a safety fix testable.
|
||||
|
||||
**Tech Stack:** Rust 2021 Cargo workspace, Flutter/Dart 3.11, Flutter Rust Bridge 2.12, Android ADB, Markdown governance and verification documents.
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
Implementation should keep the following responsibilities stable:
|
||||
|
||||
- `core/chanora_core/src/lib.rs`: public Core API, session orchestration, public re-exports, and integration-facing methods.
|
||||
- `core/chanora_core/src/events.rs`: Core public event DTOs re-exported by `lib.rs`.
|
||||
- `core/chanora_core/src/network_diagnostics.rs`: private bounded network diagnostic history and its local regression tests.
|
||||
- `crates/chanora_audio/src/voice_render.rs`: shared render/downmix helpers.
|
||||
- `crates/chanora_audio/src/ios_raw_unit.rs`, `ios_voice_unit.rs`, `android_voice_unit.rs`, `engine.rs`: platform/audio backends; avoid broad rewrites without device evidence.
|
||||
- `crates/chanora_audio/src/ptt_backends/mod.rs`: PTT backend descriptor and error definitions.
|
||||
- `crates/chanora_state/src/lib.rs`: snapshot reducers and state deltas.
|
||||
- `crates/chanora_protocol/src/adapter.rs`: protocol adapter event ordering and DTO projection.
|
||||
- `crates/chanora_bridge/src/api.rs`: bridge-facing API and DTO mapping; generated files are not manually edited.
|
||||
- `crates/chanora_diagnostics/src/lib.rs`: bounded diagnostics, redaction, and export data.
|
||||
- `docs/governance/maintainability-review-2026-06-08.md`: working review record and fail-safe gap log.
|
||||
- `docs/governance/document-index.md`: navigation index for review records.
|
||||
- `docs/architecture/sad.md`, `docs/architecture/sdd.md`: architecture updates for seams and implementation boundaries.
|
||||
- `docs/implementation-status-2026-05-28.md`: implementation status updates.
|
||||
- `docs/verification/swe4-unit-verification-plan.md`, `docs/verification/swe5-software-integration-verification-plan.md`: verification evidence and requirements updates.
|
||||
|
||||
## Review Findings To Remediate First
|
||||
|
||||
The full review added these priority fixes before the original maintainability cleanup sequence:
|
||||
|
||||
- Flutter privacy fail-safe: talk-power recovery must not clear a user/manual mute. Fixed in commit `d835394`.
|
||||
- Flutter stuck-transmit fail-safe: touch PTT must release when disposed while held. Fixed in commit `d835394`.
|
||||
- Flutter/iOS fail-safe: iOS audio-session activation errors must be caught, and activation should be moved before Rust VoiceProcessingIO startup where the current app flow allows. Missing-plugin/error hardening fixed in commit `d835394`; iOS device runtime verification remains required.
|
||||
- Rust realtime safety: Android and iOS raw render-reference buffers must not use unsynchronized mutable aliasing. Callback-path hardening fixed in commit `8606eb4`; the full lock-free `AudioHandler` / config / debug-recorder redesign remains a follow-up.
|
||||
- Rust realtime safety: Android input callback must not block on a mutex, and non-48 kHz capture should not allocate/clone per callback. Focused callback-path hardening fixed in commit `8606eb4`; Android target compilation and runtime verification remain blocked locally until the missing NDK compiler and an authorized ADB target are available.
|
||||
- Rust control-plane safety: disconnect and protocol control requests must remain bounded under broken transport or sustained voice traffic.
|
||||
- Governance correctness: README/release/VAD/Android API/product-decision docs must not contradict code or verification status.
|
||||
|
||||
## Task 0: Fix Flutter Privacy and Stuck-Transmit Fail-Safes
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/chanora_flutter/lib/main.dart`
|
||||
- Modify: `apps/chanora_flutter/lib/widgets/voice_compact.dart`
|
||||
- Modify: `apps/chanora_flutter/lib/services/ios_audio_session_controller.dart`
|
||||
- Test: `apps/chanora_flutter/test/widgets/voice_compact_test.dart`
|
||||
- Test: existing Flutter tests under `apps/chanora_flutter/test/`
|
||||
|
||||
- [ ] **Step 1: Add failing touch PTT disposal regression test**
|
||||
|
||||
Create or update `apps/chanora_flutter/test/widgets/voice_compact_test.dart` with a widget test that presses the touch PTT button, replaces the widget without sending pointer-up, and expects the callback sequence `[true, false]`.
|
||||
|
||||
- [ ] **Step 2: Run touch PTT test and verify RED**
|
||||
|
||||
Run from `apps/chanora_flutter`: `flutter test test/widgets/voice_compact_test.dart`
|
||||
|
||||
Expected before production fix: FAIL because disposal does not emit `false`.
|
||||
|
||||
- [ ] **Step 3: Implement touch PTT release-on-dispose**
|
||||
|
||||
Add `dispose()` to the touch PTT button state so an active press calls `widget.onHeldChanged(false)` exactly once before disposal.
|
||||
|
||||
- [ ] **Step 4: Run touch PTT test and verify GREEN**
|
||||
|
||||
Run from `apps/chanora_flutter`: `flutter test test/widgets/voice_compact_test.dart`
|
||||
|
||||
Expected after fix: PASS.
|
||||
|
||||
- [ ] **Step 5: Add or preserve mute-owner regression coverage**
|
||||
|
||||
If an existing pure reducer seam is available, add a failing test for manual mute true -> talk power blocked -> talk power restored. If no testable seam exists, first extract the smallest voice mute owner helper from `main.dart` and test it directly.
|
||||
|
||||
- [ ] **Step 6: Implement independent mute owners**
|
||||
|
||||
Ensure talk-power recovery clears only the talk-power owner and does not clear manual/user mute or permission mute. Effective hard mute is the OR of manual, permission, and talk-power owners.
|
||||
|
||||
- [ ] **Step 7: Harden iOS audio-session controller errors**
|
||||
|
||||
Add tests for `MissingPluginException` in `ios_audio_session_controller_test.dart`, then catch `MissingPluginException` or `Object` so activation/deactivation failures do not become unhandled async errors.
|
||||
|
||||
- [ ] **Step 8: Run focused Flutter verification**
|
||||
|
||||
Run from `apps/chanora_flutter`: `flutter test test/widgets/voice_compact_test.dart test/services/ios_audio_session_controller_test.dart && flutter analyze`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 9: Commit Flutter fail-safe slice**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
git add apps/chanora_flutter/lib/main.dart apps/chanora_flutter/lib/widgets/voice_compact.dart apps/chanora_flutter/lib/services/ios_audio_session_controller.dart apps/chanora_flutter/test/widgets/voice_compact_test.dart apps/chanora_flutter/test/services/ios_audio_session_controller_test.dart
|
||||
git commit -m "fix(voice): preserve mute owners and release touch ptt"
|
||||
```
|
||||
|
||||
Expected: one commit containing only Flutter fail-safe fixes and tests.
|
||||
|
||||
## Task 0.1: Fix Rust Realtime and Control-Plane Safety Findings
|
||||
|
||||
**Files:**
|
||||
- Modify: `crates/chanora_audio/src/android_voice_unit.rs`
|
||||
- Modify: `crates/chanora_audio/src/ios_raw_unit.rs`
|
||||
- Modify: `crates/chanora_audio/src/engine.rs`
|
||||
- Modify: `crates/chanora_protocol/src/adapter.rs`
|
||||
- Modify: `core/chanora_core/src/lib.rs`
|
||||
- Test: Rust tests in affected crates
|
||||
|
||||
- [ ] **Step 1: Add failing bounded-buffer regression for render-reference handoff**
|
||||
|
||||
Add host-testable unit coverage around the render-reference buffer behavior so a writer can publish a frame and a reader can read a complete latest frame without unsynchronized mutation.
|
||||
|
||||
- [ ] **Step 2: Replace unsafe shared mutable render-reference buffers**
|
||||
|
||||
Replace unsynchronized mutable aliasing in Android and iOS raw render-reference buffers with a realtime-safe handoff such as an `ArrayQueue` of complete frames or a documented atomic double-buffer. Do not add mutex locking to realtime callbacks.
|
||||
|
||||
- [ ] **Step 3: Add failing protocol progress regression where feasible**
|
||||
|
||||
Add or isolate a test proving control requests are not starved by sustained voice packet drain.
|
||||
|
||||
- [ ] **Step 4: Bound voice draining and disconnect shutdown**
|
||||
|
||||
Cap voice packet draining per protocol loop and make disconnect/shutdown bounded so UI/Core locks are not held across unbounded transport waits.
|
||||
|
||||
- [ ] **Step 5: Run focused Rust verification**
|
||||
|
||||
Run: `cargo test -p chanora_audio -p chanora_protocol -p chanora_core`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 6: Commit Rust safety slice**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
git add crates/chanora_audio/src/android_voice_unit.rs crates/chanora_audio/src/ios_raw_unit.rs crates/chanora_audio/src/engine.rs crates/chanora_protocol/src/adapter.rs core/chanora_core/src/lib.rs
|
||||
git commit -m "fix(audio): harden realtime and protocol fail-safes"
|
||||
```
|
||||
|
||||
Expected: one commit containing only Rust safety fixes and tests.
|
||||
|
||||
## Task 0.2: Align Review Findings With Specs, Plans, and Governance Docs
|
||||
|
||||
**Status:** In progress / documentation-only alignment. Do not commit from this task unless explicitly requested.
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/superpowers/specs/2026-06-08-maintainability-continuation-design.md`
|
||||
- Modify: `docs/superpowers/plans/2026-06-08-maintainability-continuation.md`
|
||||
- Modify: `docs/governance/maintainability-review-2026-06-08.md`
|
||||
- Modify: `README.md`
|
||||
- Modify if needed: `CHANGELOG.md`
|
||||
- Modify if needed: `docs/governance/product-decision-register.md`
|
||||
- Modify if needed: `docs/release/release-readiness-go-nogo-record.md`
|
||||
- Modify if needed: `docs/release/dv-waiver-register.md`
|
||||
- Modify if needed: `docs/verification/verification-master-plan.md`
|
||||
- Modify if needed: `docs/verification/sys4-system-integration-verification-plan.md`
|
||||
|
||||
- [x] **Step 1: Record full-review findings in maintainability review**
|
||||
|
||||
Update `docs/governance/maintainability-review-2026-06-08.md` with the full code-review findings, fixed items, blocked items, and follow-up Module split candidates.
|
||||
|
||||
- [x] **Step 2: Fix stale platform/release claims**
|
||||
|
||||
Update Android minimum runtime claims to API 28 where code and requirements require it. Update release metadata so Flutter app version/build and Rust workspace version are clearly distinguished.
|
||||
|
||||
- [x] **Step 3: Clarify VAD/VoiceActivity status**
|
||||
|
||||
Document the difference between VAD scaffolding/assets/tests and product-enabled VoiceActivity behavior. Do not claim runtime VoiceActivity is shipped unless verified.
|
||||
|
||||
- [x] **Step 4: Promote Android runtime verification blocker**
|
||||
|
||||
Add Android ADB/build/install/smoke as a blocker or waiver in governing release/verification docs when no authorized target is connected.
|
||||
|
||||
- [ ] **Step 5: Commit plan/spec/governance alignment slice**
|
||||
|
||||
Skipped in this subagent run because the instruction for Task 0.2 explicitly says not to commit.
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
git add docs/superpowers/specs/2026-06-08-maintainability-continuation-design.md docs/superpowers/plans/2026-06-08-maintainability-continuation.md docs/governance/maintainability-review-2026-06-08.md README.md CHANGELOG.md docs/governance/product-decision-register.md docs/release/release-readiness-go-nogo-record.md docs/release/dv-waiver-register.md docs/verification/verification-master-plan.md docs/verification/sys4-system-integration-verification-plan.md
|
||||
git commit -m "docs: align review findings and verification gates"
|
||||
```
|
||||
|
||||
Expected: one documentation/governance commit, with unavailable optional files omitted only if unchanged.
|
||||
|
||||
## Task 1: Verify Current Branch Baseline
|
||||
|
||||
**Files:**
|
||||
- Read: `docs/governance/maintainability-review-2026-06-08.md`
|
||||
- Read: `docs/superpowers/plans/2026-06-08-core-internal-split.md`
|
||||
- Inspect: all currently modified files from `git status --short`
|
||||
- Modify: none unless verification exposes a small mechanical fix
|
||||
|
||||
- [ ] **Step 1: Inspect current status**
|
||||
|
||||
Run: `git status --short`
|
||||
|
||||
Expected: output includes the existing maintainability branch changes and no staged files from unrelated work.
|
||||
|
||||
- [ ] **Step 2: Inspect current diff summary**
|
||||
|
||||
Run: `git diff --stat`
|
||||
|
||||
Expected: diff remains focused on Core split, Audio simplifications, State/Protocol/Bridge/Diagnostics cleanup, and documentation updates.
|
||||
|
||||
- [ ] **Step 3: Verify Rust formatting**
|
||||
|
||||
Run: `cargo fmt --all --check`
|
||||
|
||||
Expected: PASS with no output. If it fails, run `cargo fmt --all`, inspect the resulting diff, and include formatter-only changes in the smallest relevant commit.
|
||||
|
||||
- [ ] **Step 4: Verify Rust compilation**
|
||||
|
||||
Run: `cargo check --workspace`
|
||||
|
||||
Expected: PASS for the full workspace.
|
||||
|
||||
- [ ] **Step 5: Verify Rust tests**
|
||||
|
||||
Run: `cargo test --workspace`
|
||||
|
||||
Expected: PASS for the full workspace.
|
||||
|
||||
- [ ] **Step 6: Verify Flutter analysis**
|
||||
|
||||
Run from `apps/chanora_flutter`: `flutter analyze`
|
||||
|
||||
Expected: PASS with no new analyzer errors.
|
||||
|
||||
- [ ] **Step 7: Verify Flutter tests**
|
||||
|
||||
Run from `apps/chanora_flutter`: `flutter test --exclude-tags e2e`
|
||||
|
||||
Expected: PASS for non-e2e Flutter tests.
|
||||
|
||||
- [ ] **Step 8: Check Android device availability**
|
||||
|
||||
Run: `adb devices -l`
|
||||
|
||||
Expected if a target is connected: at least one `device` row. Expected if no target is connected: only the header and no `device` row; record Android runtime verification as blocked in `docs/governance/maintainability-review-2026-06-08.md`.
|
||||
|
||||
- [ ] **Step 9: Commit verified existing slice**
|
||||
|
||||
Only after Steps 1-8 have been completed or any blocked Android status has been documented, stage the smallest coherent existing slice.
|
||||
|
||||
Recommended first slice if tests pass:
|
||||
|
||||
```bash
|
||||
git add core/chanora_core/src/lib.rs core/chanora_core/src/events.rs core/chanora_core/src/network_diagnostics.rs docs/superpowers/plans/2026-06-08-core-internal-split.md
|
||||
git commit -m "refactor(core): split event and diagnostics internals"
|
||||
```
|
||||
|
||||
Expected: one commit containing only the Core split and its plan.
|
||||
|
||||
## Task 2: Commit Existing Built-In and Helper Reuse Simplifications
|
||||
|
||||
**Files:**
|
||||
- Modify or stage: `crates/chanora_audio/src/ptt_backends/mod.rs`
|
||||
- Modify or stage: `crates/chanora_audio/src/voice_render.rs`
|
||||
- Modify or stage: `crates/chanora_audio/src/ios_raw_unit.rs`
|
||||
- Modify or stage: `crates/chanora_diagnostics/src/lib.rs`
|
||||
- Modify or stage: `crates/chanora_state/src/lib.rs`
|
||||
- Modify or stage: `crates/chanora_resolver/Cargo.toml`
|
||||
- Modify or stage: `Cargo.lock`
|
||||
|
||||
- [ ] **Step 1: Inspect simplification diffs**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
git diff -- crates/chanora_audio/src/ptt_backends/mod.rs crates/chanora_audio/src/voice_render.rs crates/chanora_audio/src/ios_raw_unit.rs crates/chanora_diagnostics/src/lib.rs crates/chanora_state/src/lib.rs crates/chanora_resolver/Cargo.toml Cargo.lock
|
||||
```
|
||||
|
||||
Expected: diffs show built-in/helper reuse only: `thiserror::Error`, `VecDeque`, render helper reuse, reducer reuse, and workspace metadata inheritance.
|
||||
|
||||
- [ ] **Step 2: Run focused Rust tests for changed areas**
|
||||
|
||||
Run: `cargo test -p chanora_audio -p chanora_diagnostics -p chanora_state -p chanora_resolver`
|
||||
|
||||
Expected: PASS for all listed crates.
|
||||
|
||||
- [ ] **Step 3: Run workspace Rust verification**
|
||||
|
||||
Run: `cargo check --workspace && cargo test --workspace`
|
||||
|
||||
Expected: PASS for workspace compile and tests.
|
||||
|
||||
- [ ] **Step 4: Commit built-in/helper reuse slice**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
git add crates/chanora_audio/src/ptt_backends/mod.rs crates/chanora_audio/src/voice_render.rs crates/chanora_audio/src/ios_raw_unit.rs crates/chanora_diagnostics/src/lib.rs crates/chanora_state/src/lib.rs crates/chanora_resolver/Cargo.toml Cargo.lock
|
||||
git commit -m "refactor: reuse built-ins and shared helpers"
|
||||
```
|
||||
|
||||
Expected: one commit containing only the built-in/helper reuse simplifications.
|
||||
|
||||
## Task 3: Review Remaining Audio Platform Diffs Before Committing
|
||||
|
||||
**Files:**
|
||||
- Inspect: `crates/chanora_audio/src/android_voice_unit.rs`
|
||||
- Inspect: `crates/chanora_audio/src/engine.rs`
|
||||
- Inspect: `crates/chanora_audio/src/ios_voice_unit.rs`
|
||||
- Modify: `docs/governance/maintainability-review-2026-06-08.md` if Android runtime verification is blocked or audio fail-safe evidence changes
|
||||
|
||||
- [ ] **Step 1: Inspect platform-audio diffs**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
git diff -- crates/chanora_audio/src/android_voice_unit.rs crates/chanora_audio/src/engine.rs crates/chanora_audio/src/ios_voice_unit.rs
|
||||
```
|
||||
|
||||
Expected: diffs are understandable as local simplification or fail-safe improvements. If a diff changes platform runtime behavior and no Android/iOS device evidence is available, keep it separate from non-platform commits.
|
||||
|
||||
- [ ] **Step 2: Run audio crate tests**
|
||||
|
||||
Run: `cargo test -p chanora_audio`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 3: Check Android runtime availability**
|
||||
|
||||
Run: `adb devices -l`
|
||||
|
||||
Expected if connected: at least one target row with `device`. Expected if blocked: no target row.
|
||||
|
||||
- [ ] **Step 4: Run Android build when a target is available**
|
||||
|
||||
Run from `apps/chanora_flutter`: `flutter build apk --debug`
|
||||
|
||||
Expected: PASS and a debug APK is produced.
|
||||
|
||||
- [ ] **Step 5: Run Android install and smoke when a target is available**
|
||||
|
||||
Run from `apps/chanora_flutter`: `flutter install`
|
||||
|
||||
Expected: PASS and app installs on the connected target.
|
||||
|
||||
Manual smoke expectations:
|
||||
|
||||
- App launches without crash.
|
||||
- Permission UI can be reached.
|
||||
- Audio device/permission screen does not crash.
|
||||
- Voice controls remain responsive.
|
||||
|
||||
- [ ] **Step 6: Record blocked Android evidence if no target is available**
|
||||
|
||||
Modify `docs/governance/maintainability-review-2026-06-08.md` Section 6 so it includes the exact `adb devices -l` result and states Android runtime verification is blocked until a device or emulator is connected.
|
||||
|
||||
- [ ] **Step 7: Commit platform-audio slice**
|
||||
|
||||
If Android runtime was verified, run:
|
||||
|
||||
```bash
|
||||
git add crates/chanora_audio/src/android_voice_unit.rs crates/chanora_audio/src/engine.rs crates/chanora_audio/src/ios_voice_unit.rs docs/governance/maintainability-review-2026-06-08.md
|
||||
git commit -m "refactor(audio): simplify platform voice internals"
|
||||
```
|
||||
|
||||
If Android runtime was blocked, run:
|
||||
|
||||
```bash
|
||||
git add crates/chanora_audio/src/android_voice_unit.rs crates/chanora_audio/src/engine.rs crates/chanora_audio/src/ios_voice_unit.rs docs/governance/maintainability-review-2026-06-08.md
|
||||
git commit -m "refactor(audio): simplify platform voice internals"
|
||||
```
|
||||
|
||||
Expected: commit message is the same, but the maintainability review explicitly records the blocked Android runtime evidence.
|
||||
|
||||
## Task 4: Review Protocol and Bridge Diffs as One Boundary Slice
|
||||
|
||||
**Files:**
|
||||
- Inspect: `crates/chanora_protocol/src/adapter.rs`
|
||||
- Inspect: `crates/chanora_bridge/src/api.rs`
|
||||
- Modify: `docs/architecture/sad.md`
|
||||
- Modify: `docs/architecture/sdd.md`
|
||||
- Modify: `docs/governance/maintainability-review-2026-06-08.md`
|
||||
|
||||
- [ ] **Step 1: Inspect protocol/bridge diffs**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
git diff -- crates/chanora_protocol/src/adapter.rs crates/chanora_bridge/src/api.rs
|
||||
```
|
||||
|
||||
Expected: diffs preserve protocol isolation and bridge DTO shape unless bridge generation and Flutter tests are included.
|
||||
|
||||
- [ ] **Step 2: Verify protocol tests**
|
||||
|
||||
Run: `cargo test -p chanora_protocol`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 3: Verify bridge tests and compile**
|
||||
|
||||
Run: `cargo test -p chanora_bridge`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 4: Verify Flutter after bridge/API changes**
|
||||
|
||||
Run from `apps/chanora_flutter`: `flutter analyze && flutter test --exclude-tags e2e`
|
||||
|
||||
Expected: PASS for analyzer and non-e2e tests.
|
||||
|
||||
- [ ] **Step 5: Document seam findings**
|
||||
|
||||
Update `docs/governance/maintainability-review-2026-06-08.md` so remaining bridge DTO drift and protocol voice packet seam risks are listed under fail-safe gaps or follow-up opportunities.
|
||||
|
||||
- [ ] **Step 6: Update architecture docs if seam wording changed**
|
||||
|
||||
If the code diff clarifies protocol/bridge boundaries, update `docs/architecture/sad.md` and `docs/architecture/sdd.md` with one concise note each. The note should state whether protocol voice packet handling is an intentional exception and whether bridge DTO mirrors remain required by Flutter Rust Bridge.
|
||||
|
||||
- [ ] **Step 7: Commit protocol/bridge slice**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
git add crates/chanora_protocol/src/adapter.rs crates/chanora_bridge/src/api.rs docs/architecture/sad.md docs/architecture/sdd.md docs/governance/maintainability-review-2026-06-08.md
|
||||
git commit -m "refactor: clarify protocol bridge boundaries"
|
||||
```
|
||||
|
||||
Expected: one commit for protocol/bridge boundary cleanup plus matching architecture documentation.
|
||||
|
||||
## Task 5: Run a Second-Pass Simplification Search
|
||||
|
||||
**Files:**
|
||||
- Inspect: Rust and Dart source files only
|
||||
- Modify: only if the simplification is mechanical, local, and covered by tests
|
||||
- Modify: `docs/governance/maintainability-review-2026-06-08.md`
|
||||
|
||||
- [ ] **Step 1: Search for custom queue front removal**
|
||||
|
||||
Run: `rg "remove\(0\)|removeAt\(0\)" core crates apps/chanora_flutter/lib apps/chanora_flutter/test`
|
||||
|
||||
Expected: no results. If results exist, replace with `VecDeque` in Rust or a clearer Dart queue structure only when behavior is covered by a local test.
|
||||
|
||||
- [ ] **Step 2: Search for manual Rust error formatting**
|
||||
|
||||
Run: `rg "impl (std::fmt::)?Display for .*Error|impl std::error::Error for" core crates`
|
||||
|
||||
Expected: only intentional manual implementations remain. For simple enum error types, replace with `thiserror::Error` and add or preserve tests for user-facing strings.
|
||||
|
||||
- [ ] **Step 3: Search for duplicate mono downmix loops**
|
||||
|
||||
Run: `rg "chunks_exact\(2\)|downmix|mono" crates/chanora_audio/src`
|
||||
|
||||
Expected: duplicate i16/f32 mono downmix code is either absent or justified. If a duplicate remains, route it through an existing helper and run `cargo test -p chanora_audio`.
|
||||
|
||||
- [ ] **Step 4: Search for shallow modules worth documenting**
|
||||
|
||||
Run: `rg "^pub struct|^pub enum|^pub fn|^fn" crates/chanora_audio/src/processor crates/chanora_prefetch/src crates/chanora_resolver/src core/chanora_core/src`
|
||||
|
||||
Expected: identify candidates, but do not merge modules in this task. Record speculative merges in `docs/governance/maintainability-review-2026-06-08.md` unless a candidate is trivial and already covered by tests.
|
||||
|
||||
- [ ] **Step 5: Commit second-pass mechanical simplifications if any**
|
||||
|
||||
If code changed, run the relevant focused tests plus `cargo check --workspace && cargo test --workspace`, then inspect the changed files:
|
||||
|
||||
```bash
|
||||
git status --short
|
||||
```
|
||||
|
||||
Stage only the files changed by the second-pass mechanical simplification. Example for a Rust-only diagnostics simplification:
|
||||
|
||||
```bash
|
||||
git add crates/chanora_diagnostics/src/lib.rs docs/governance/maintainability-review-2026-06-08.md
|
||||
git commit -m "refactor: apply second-pass mechanical simplifications"
|
||||
```
|
||||
|
||||
Expected: commit contains only local mechanical simplifications and the review record.
|
||||
|
||||
- [ ] **Step 6: Commit review-only findings if no code changed**
|
||||
|
||||
If no code changed and only findings were added, run:
|
||||
|
||||
```bash
|
||||
git add docs/governance/maintainability-review-2026-06-08.md
|
||||
git commit -m "docs: record maintainability follow-up findings"
|
||||
```
|
||||
|
||||
Expected: documentation-only commit.
|
||||
|
||||
## Task 6: Final Documentation Alignment
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/governance/maintainability-review-2026-06-08.md`
|
||||
- Modify: `docs/governance/document-index.md`
|
||||
- Modify: `docs/implementation-status-2026-05-28.md`
|
||||
- Modify: `docs/verification/swe4-unit-verification-plan.md`
|
||||
- Modify: `docs/verification/swe5-software-integration-verification-plan.md`
|
||||
- Modify if needed: `docs/architecture/sad.md`
|
||||
- Modify if needed: `docs/architecture/sdd.md`
|
||||
|
||||
- [ ] **Step 1: Update maintainability review completion status**
|
||||
|
||||
Edit `docs/governance/maintainability-review-2026-06-08.md` so these sections are current:
|
||||
|
||||
- Changes applied
|
||||
- Remaining simplification opportunities
|
||||
- Fail-safe gaps that need evidence
|
||||
- Verification policy
|
||||
- Android ADB status
|
||||
- Git policy
|
||||
|
||||
- [ ] **Step 2: Update document index**
|
||||
|
||||
Ensure `docs/governance/document-index.md` includes `docs/governance/maintainability-review-2026-06-08.md` and this plan/spec if the repository convention indexes superpowers documents.
|
||||
|
||||
- [ ] **Step 3: Update implementation status**
|
||||
|
||||
Ensure `docs/implementation-status-2026-05-28.md` describes maintainability review results without claiming production readiness.
|
||||
|
||||
- [ ] **Step 4: Update SWE.4 verification plan**
|
||||
|
||||
Ensure `docs/verification/swe4-unit-verification-plan.md` lists Rust unit verification expectations for Core, Audio, State, Protocol, Bridge, Diagnostics, Resolver, and Prefetch when those crates are touched.
|
||||
|
||||
- [ ] **Step 5: Update SWE.5 verification plan**
|
||||
|
||||
Ensure `docs/verification/swe5-software-integration-verification-plan.md` lists integration expectations for Bridge DTO drift, protocol event folding, Flutter analyze/test, and Android runtime smoke evidence.
|
||||
|
||||
- [ ] **Step 6: Run documentation cross-link search**
|
||||
|
||||
Run: `rg "maintainability-review-2026-06-08|core-internal-split|maintainability-continuation" docs README.md`
|
||||
|
||||
Expected: references point to existing files and no stale path is introduced.
|
||||
|
||||
- [ ] **Step 7: Commit final documentation alignment**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
git add docs/governance/maintainability-review-2026-06-08.md docs/governance/document-index.md docs/implementation-status-2026-05-28.md docs/verification/swe4-unit-verification-plan.md docs/verification/swe5-software-integration-verification-plan.md docs/architecture/sad.md docs/architecture/sdd.md
|
||||
git commit -m "docs: align maintainability verification records"
|
||||
```
|
||||
|
||||
Expected: one documentation-focused commit.
|
||||
|
||||
## Task 7: Final Verification and Android Evidence
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/governance/maintainability-review-2026-06-08.md` only if final verification status changes
|
||||
|
||||
- [ ] **Step 1: Run full Rust verification**
|
||||
|
||||
Run: `cargo fmt --all --check && cargo check --workspace && cargo test --workspace`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 2: Run full Flutter verification**
|
||||
|
||||
Run from `apps/chanora_flutter`: `flutter analyze && flutter test --exclude-tags e2e`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 3: Run ADB check**
|
||||
|
||||
Run: `adb devices -l`
|
||||
|
||||
Expected if connected: at least one target row with `device`. Expected if blocked: no target row and the maintainability review states Android runtime verification is blocked.
|
||||
|
||||
- [ ] **Step 4: Run Android build/install/smoke when connected**
|
||||
|
||||
Run from `apps/chanora_flutter`: `flutter build apk --debug && flutter install`
|
||||
|
||||
Expected: PASS. Manually verify app launch, permission screen access, audio settings access, and voice control responsiveness.
|
||||
|
||||
- [ ] **Step 5: Commit final verification evidence if docs changed**
|
||||
|
||||
If the maintainability review was updated with final verification evidence, run:
|
||||
|
||||
```bash
|
||||
git add docs/governance/maintainability-review-2026-06-08.md
|
||||
git commit -m "docs: record maintainability verification evidence"
|
||||
```
|
||||
|
||||
Expected: one small evidence-only documentation commit.
|
||||
|
||||
- [ ] **Step 6: Inspect final history and status**
|
||||
|
||||
Run: `git status --short && git log --oneline -10`
|
||||
|
||||
Expected: no unexpected unstaged changes related to this work; recent commits are small and logically separated.
|
||||
|
||||
---
|
||||
|
||||
## Self-Review
|
||||
|
||||
Spec coverage:
|
||||
|
||||
- Safe simplifications are covered by Tasks 1, 2, 3, 4, and 5.
|
||||
- Built-in replacement opportunities are covered by Tasks 2 and 5.
|
||||
- Fail-safe gaps are covered by Tasks 3, 4, 6, and 7.
|
||||
- Rust, Flutter, and Android verification are covered by Tasks 1 and 7, with focused verification in Tasks 2 through 4.
|
||||
- Documentation updates are covered by Tasks 4, 6, and 7.
|
||||
- Small commit policy is covered by every task's dedicated commit step.
|
||||
|
||||
Placeholder scan: The plan contains no open placeholders. Task 5 uses `git status --short` before staging because the exact second-pass files are only known after the search runs; the example command shows the required staging style.
|
||||
|
||||
Type consistency: The plan does not introduce new APIs or types. It preserves current crate and file boundaries from the approved design.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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_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_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_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_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_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
|
||||
```
|
||||
@@ -0,0 +1,266 @@
|
||||
# Chanora Adaptive 3-Panel Layout Design
|
||||
|
||||
**Date:** 2026-06-05
|
||||
**Status:** Draft
|
||||
**Scope:** Desktop adaptive layout for ≥1024dp three-panel mode, centralized breakpoint system, and chat panel integration.
|
||||
|
||||
---
|
||||
|
||||
## 1. Problem Statement
|
||||
|
||||
Chanora's current responsive layout uses a single breakpoint (`_wideBreakpoint = 600dp`) scattered across 9 files in 8 duplicatable clusters (5 `LayoutBuilder` sites, 7 `MediaQuery.sizeOf` sites). The desktop layout is a 2-panel split (VoiceBar 320px + SnapshotView flex) with no persistent chat surface.
|
||||
|
||||
Research across Discord, Mattermost, Rocket.Chat, Element, and hardware resolution data shows:
|
||||
|
||||
- **1024dp** is the industry-standard threshold where a third panel becomes viable (Discord member list, Mattermost RHS, Rocket.Chat contextual bar all use this value).
|
||||
- At 1024dp, Chanora's math works: `320 + 12 + 300 + 12 + 380 = 1024` — minimum viable for VoicePanel + ChannelTree + ChatPanel.
|
||||
- Production apps use **push/replace navigation** for chat on constrained widths, reserving persistent panels for ≥1024dp.
|
||||
- Centralized breakpoint logic is standard practice (Rocket.Chat `LayoutProvider`, Mattermost `WindowSizes`).
|
||||
|
||||
## 2. Design Decisions
|
||||
|
||||
| Decision | Choice | Rationale |
|
||||
|---|---|---|
|
||||
| 3-panel activation threshold | **1024dp** | Industry consensus (Discord, Mattermost, Rocket.Chat). Chanora math: center pane = 300dp minimum. |
|
||||
| Chat behavior at 600–1023dp | **Push route (unchanged)** | Research validates current pattern. Overlays are for contextual info, not primary conversation. |
|
||||
| Chat behavior at ≥1024dp | **Inline panel** | Chat renders in a 380dp right panel alongside the channel tree. No route push. |
|
||||
| Centralized breakpoints | **New `ChanoraBreakpoints` + `ViewportInfo`** | Replaces 8 duplicated responsive clusters with single source of truth. |
|
||||
| Architecture approach | **Adaptive Scaffold Shell** | Extends existing widget tree with centralized layout logic. Not a full rewrite. |
|
||||
| AdaptiveScaffold package | **Not used** | Package discontinued (flutter/flutter#162965). Manual layout gives better control for voice-first UX. |
|
||||
|
||||
## 3. Breakpoint System
|
||||
|
||||
### 3.1 Layout Classes
|
||||
|
||||
Three tiers, aligned with Material 3 adaptive guidance:
|
||||
|
||||
| Class | Width Range | Primary Behavior |
|
||||
|---|---|---|
|
||||
| `compact` | < 600dp | Single column. VoiceStatusChip at bottom. Chat as pushed route. |
|
||||
| `medium` | 600–1023dp | 2-panel row (VoicePanel 320px + SnapshotView flex). Chat as pushed route. |
|
||||
| `expanded` | ≥ 1024dp | 3-panel row (VoicePanel 320px + SnapshotView flex + ChatPanel 380px). Chat inline. |
|
||||
|
||||
### 3.2 New Files
|
||||
|
||||
**`lib/design/breakpoints.dart`** — canonical breakpoint tokens:
|
||||
|
||||
```dart
|
||||
class ChanoraBreakpoints {
|
||||
static const double compact = 0;
|
||||
static const double medium = 600;
|
||||
static const double expanded = 1024;
|
||||
|
||||
static const double voicePanelWidth = 320;
|
||||
static const double chatPanelWidth = 380;
|
||||
static const double panelGap = 12;
|
||||
}
|
||||
|
||||
enum LayoutClass { compact, medium, expanded }
|
||||
|
||||
LayoutClass layoutClassFromWidth(double width) {
|
||||
if (width >= ChanoraBreakpoints.expanded) return LayoutClass.expanded;
|
||||
if (width >= ChanoraBreakpoints.medium) return LayoutClass.medium;
|
||||
return LayoutClass.compact;
|
||||
}
|
||||
```
|
||||
|
||||
**`lib/design/viewport_info.dart`** — inherited widget that computes layout class once per frame:
|
||||
|
||||
```dart
|
||||
class ViewportInfo extends InheritedWidget {
|
||||
const ViewportInfo({
|
||||
super.key,
|
||||
required this.layoutClass,
|
||||
required this.width,
|
||||
required this.height,
|
||||
required super.child,
|
||||
});
|
||||
|
||||
final LayoutClass layoutClass;
|
||||
final double width;
|
||||
final double height;
|
||||
|
||||
static ViewportInfo of(BuildContext context) {
|
||||
final info = context.dependOnInheritedWidgetOfExactType<ViewportInfo>();
|
||||
assert(info != null, 'No ViewportInfo found in widget tree');
|
||||
return info!;
|
||||
}
|
||||
|
||||
bool get isCompact => layoutClass == LayoutClass.compact;
|
||||
bool get isMedium => layoutClass == LayoutClass.medium;
|
||||
bool get isExpanded => layoutClass == LayoutClass.expanded;
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(ViewportInfo old) =>
|
||||
layoutClass != old.layoutClass ||
|
||||
width != old.width ||
|
||||
height != old.height;
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 What This Replaces
|
||||
|
||||
The audit identified these duplicatable clusters that get consolidated:
|
||||
|
||||
| Cluster | Current Locations | Replacement |
|
||||
|---|---|---|
|
||||
| 600dp breakpoint (×3) | `main.dart:304,2280,2468` | `ChanoraBreakpoints.medium` |
|
||||
| 400px cap (×2) | `connect_widgets.dart`, `voice_settings.dart` | Named token in `ChanoraBreakpoints` |
|
||||
| 72% modal height (×2) | `audio_output_tile.dart`, `ptt_capability_badge.dart` | Named token |
|
||||
| 320px voice bar width | `main.dart:2467` | `ChanoraBreakpoints.voicePanelWidth` |
|
||||
| Platform capability branching | `voice_settings.dart`, `voice_compact.dart`, `audio_processing_config_state.dart` | Centralized capability helper |
|
||||
|
||||
## 4. Adaptive Shell
|
||||
|
||||
### 4.1 Widget Tree
|
||||
|
||||
The existing `_BetaHome` widget tree is restructured to use `ViewportInfo`:
|
||||
|
||||
```
|
||||
_BetaHome
|
||||
├─ macOS: Scaffold with traffic-light padding (unchanged)
|
||||
├─ Mobile: ChanoraMobileScaffold (unchanged)
|
||||
└─ bodyContent:
|
||||
└─ LayoutBuilder
|
||||
└─ ViewportInfo (computes layoutClass from constraints)
|
||||
├─ compact: Column [SnapshotView, VoiceStatusChip, PTT]
|
||||
├─ medium: Row [VoicePanel, SnapshotView]
|
||||
└─ expanded: Row [VoicePanel, SnapshotView, ChatPanel]
|
||||
```
|
||||
|
||||
`AdaptiveShell` is a pure layout widget — it reads `ViewportInfo` and composes the appropriate children. All state remains in `_BetaHome`.
|
||||
|
||||
### 4.2 Platform Handling
|
||||
|
||||
Platform-specific scaffolding stays at the top level, unchanged:
|
||||
|
||||
- **macOS**: `Scaffold` with `_macOSTrafficLightPad` top padding (28dp)
|
||||
- **Mobile**: `ChanoraMobileScaffold` with compact idle chrome
|
||||
- **Windows/Linux**: Default `Scaffold`
|
||||
|
||||
The `ViewportInfo` + layout switch only affects the body content inside the scaffold.
|
||||
|
||||
## 5. Chat Panel Behavior
|
||||
|
||||
### 5.1 Compact (< 600dp)
|
||||
|
||||
No change. Chat opens as a pushed `MaterialPageRoute`:
|
||||
|
||||
```
|
||||
main.dart:_onOpenChat → Navigator.push(ChatPage)
|
||||
```
|
||||
|
||||
Channel tree is fully replaced. Back button returns to main view.
|
||||
|
||||
### 5.2 Medium (600–1023dp)
|
||||
|
||||
Same as compact. Chat is a pushed route. The 2-panel layout (VoicePanel + SnapshotView) stays as the home screen.
|
||||
|
||||
### 5.3 Expanded (≥ 1024dp)
|
||||
|
||||
Chat renders inline in a 380dp right panel. The flow:
|
||||
|
||||
1. User taps "Open Text Chat" on a client, or taps the chat badge
|
||||
2. `_onOpenChat` reads `ViewportInfo.of(context).isExpanded`
|
||||
3. If expanded: sets `_inlineChatTarget` state → `ChatPanel` renders in the third column
|
||||
4. If not expanded: pushes `ChatPage` route (unchanged behavior)
|
||||
|
||||
### 5.4 ChatPanel Widget
|
||||
|
||||
New widget for ≥1024dp only:
|
||||
|
||||
```
|
||||
ChatPanel (380dp fixed width)
|
||||
├─ Header: target name + close button
|
||||
├─ Message list (scrollable, max-width ~500dp for readability)
|
||||
└─ Input field
|
||||
```
|
||||
|
||||
**State sharing:** The `_chatMessages` list and `_chatFeedRevision` listenable in `_BetaHome` already track all messages. `ChatPanel` reads from the same source — no duplication.
|
||||
|
||||
**Close behavior:** User taps close button → `_inlineChatTarget` set to null → `ChatPanel` removed from tree.
|
||||
|
||||
### 5.5 Width Transition
|
||||
|
||||
When the user resizes from ≥1024dp to <1024dp while chat is open inline:
|
||||
|
||||
1. `ChatPanel` disappears (it's only in the expanded layout branch)
|
||||
2. A brief snackbar appears: "Tap the chat button to continue your conversation"
|
||||
3. The `_inlineChatTarget` state is preserved — tapping the chat button reopens the pushed `ChatPage` route with the same target
|
||||
|
||||
This matches Discord's behavior when the member list collapses on resize.
|
||||
|
||||
## 6. Panel Sizing
|
||||
|
||||
| Element | Width | Behavior |
|
||||
|---|---|---|
|
||||
| VoicePanel (left) | 320dp fixed | VoiceBar, connection status, PTT controls. Unchanged. |
|
||||
| Panel gaps | 12dp | Between each panel. Unchanged. |
|
||||
| SnapshotView (center) | flex (1fr) | Grows to fill remaining space. |
|
||||
| ChatPanel (right) | 380dp fixed | Only rendered at ≥1024dp. |
|
||||
| Chat messages | max-width ~500dp | Centered within ChatPanel for readability. |
|
||||
| macOS traffic light pad | 28dp top | Unchanged. Only affects height. |
|
||||
|
||||
**Center pane widths at common viewports:**
|
||||
|
||||
| Viewport | Center Width | Feel |
|
||||
|---|---|---|
|
||||
| 1024dp | 300dp | Minimum viable (matches Discord at same width) |
|
||||
| 1200dp | 476dp | Comfortable |
|
||||
| 1280dp | 556dp | Spacious (Chanora's default window size) |
|
||||
| 1440dp | 716dp | Very spacious |
|
||||
| 1920dp | 1184dp | Ultra-wide — consider capping center max-width post-MVP |
|
||||
|
||||
## 7. Migration Map
|
||||
|
||||
| File | Change | Scope |
|
||||
|---|---|---|
|
||||
| `lib/design/breakpoints.dart` | **New** — breakpoint tokens + `LayoutClass` enum | New file |
|
||||
| `lib/design/viewport_info.dart` | **New** — `ViewportInfo` inherited widget | New file |
|
||||
| `lib/main.dart` | Replace `_wideBreakpoint = 600.0` with `ChanoraBreakpoints.medium`. Wrap body in `ViewportInfo`. Add `_inlineChatTarget` state. Branch `_onOpenChat` for expanded vs compact/medium. Add `ChatPanel` to expanded Row. | Significant |
|
||||
| `lib/widgets/chat_views.dart` | Replace `_chatMobileBreakpoint` with `ChanoraBreakpoints.medium`. No structural changes. | Token swap |
|
||||
| `lib/widgets/connect_widgets.dart` | Replace hardcoded 400px with token. | Token swap |
|
||||
| `lib/widgets/app_snack_bar.dart` | Replace hardcoded 600/560px with tokens. | Token swap |
|
||||
| `lib/widgets/snapshot_view.dart` | No changes. Local spacer math stays local. | None |
|
||||
| `lib/widgets/voice_compact.dart` | Replace platform branching with centralized helper (optional, post-MVP). | Optional |
|
||||
|
||||
**Unchanged:** macOS scaffold, ChanoraMobileScaffold, all voice controls, channel tree, chat route for compact/medium, all Rust bridge code.
|
||||
|
||||
## 8. Hardware Coverage
|
||||
|
||||
The 1024dp threshold coverage based on 2026 resolution data:
|
||||
|
||||
| Setup | Logical Width | Sees 3-Panel? |
|
||||
|---|---|---|
|
||||
| 1920×1080 @100% fullscreen | 1920dp | Yes |
|
||||
| 1920×1080 @125% fullscreen | 1536dp | Yes |
|
||||
| 1920×1080 @150% fullscreen | 1280dp | Yes |
|
||||
| 1366×768 @100% fullscreen | 1366dp | Yes |
|
||||
| 1366×768 @125% fullscreen | 1093dp | Yes |
|
||||
| 1366×768 @125% windowed (~85%) | ~930dp | No (2-panel) |
|
||||
| 2560×1440 @100% half-screen | ~1280dp | Yes |
|
||||
| 2560×1440 @125% half-screen | ~1024dp | Yes (edge) |
|
||||
| MacBook 13" Split View | ~708dp | No (2-panel) |
|
||||
| MacBook 14" Split View | ~744dp | No (2-panel) |
|
||||
| MacBook 16" Split View | ~852dp | No (2-panel) |
|
||||
|
||||
Chanora's default window (1280×720 on Windows/Linux) starts in 3-panel mode immediately.
|
||||
|
||||
## 9. Out of Scope (Post-MVP)
|
||||
|
||||
- Resizable panels (drag-to-resize VoicePanel/ChatPanel width)
|
||||
- NavigationRail for ultra-wide monitors
|
||||
- ChatPanel showing user profile or channel info
|
||||
- Center pane max-width cap for ultra-wide monitors
|
||||
- Centralized platform capability helper (consolidating voice_settings/voice_compact/audio_processing branching)
|
||||
- Animated transitions between layout classes
|
||||
- ChatPanel as a sheet/drawer on medium widths
|
||||
|
||||
## 10. References
|
||||
|
||||
- Discord member list collapse at 1024px: [compact-discord](https://github.com/asportnoy/compact-discord)
|
||||
- Mattermost RHS persistent at ≥1024px: [structure.scss](https://github.com/mattermost/mattermost/blob/3440453d82613b1d8d67c93011c11d56a1380869/webapp/channels/src/sass/base/_structure.scss)
|
||||
- Rocket.Chat contextual bar persistent at ≥1024px (lg breakpoint): [fuselage-tokens](https://github.com/RocketChat/fuselage/blob/ed91cb04db9fd6c35b43390190cbf7327c3eab9e/packages/fuselage-tokens/src/breakpoints.jsonc)
|
||||
- Flutter AdaptiveScaffold discontinued: [flutter/flutter#162965](https://github.com/flutter/flutter/issues/162965)
|
||||
- Material 3 canonical breakpoints: [m3.material.io/foundations/layout](https://m3.material.io/foundations/layout/breakpoints/overview)
|
||||
- Chanora adaptive layout policy: [docs/ui-ux/adaptive-layout-platform-guide.md](../ui-ux/adaptive-layout-platform-guide.md)
|
||||
@@ -0,0 +1,160 @@
|
||||
# Maintainability Continuation Design
|
||||
|
||||
**Date:** 2026-06-08
|
||||
**Status:** Approved design for implementation and full code-review remediation; Task 0 and focused audio-realtime fixes landed, documentation/governance alignment in progress
|
||||
**Scope:** Continue the current working-branch maintainability pass, add full code-review findings, and fix high-risk bugs before broad rewrites.
|
||||
|
||||
## Purpose
|
||||
|
||||
This design continues the project review already present in the working tree. The goal is to simplify the project where changes are low-risk, testable, and documented, while avoiding speculative architecture churn.
|
||||
|
||||
The work covers unnecessary functions, structs, files, modules, duplicated custom implementations, built-in replacement opportunities, outdated documents, fail-safe gaps, Android runtime verification requirements, and full code-review remediation for hidden bugs.
|
||||
|
||||
## Recommended Approach
|
||||
|
||||
Use a targeted continuation of the current maintainability pass, now ordered by safety risk.
|
||||
|
||||
The existing branch already contains a first slice of simplification: core event DTO extraction, network diagnostics locality, `VecDeque` queue improvements, derived PTT backend errors, render downmix helper reuse, state reducer reuse, workspace metadata cleanup, and documentation updates. This design treats those changes as the baseline, but the full review found privacy, realtime-audio, disconnect, and documentation-governance issues that take priority over cosmetic simplification.
|
||||
|
||||
The remediation order is:
|
||||
|
||||
- Privacy and stuck-transmit fail-safes in Flutter voice state. Fixed in commit `d835394`.
|
||||
- iOS audio-session error hardening before Rust VoiceProcessingIO startup. Missing-plugin/error handling fixed in commit `d835394`; iOS device runtime verification remains required.
|
||||
- Rust realtime audio safety, especially unsynchronized render-reference buffers and blocking/allocating callbacks. Focused callback-path hardening fixed in commit `8606eb4`; full lock-free `AudioHandler` / config / debug-recorder redesign remains a follow-up.
|
||||
- Bounded disconnect/control-plane progress in Rust protocol/core. Pending unless later code-review evidence closes it.
|
||||
- Documentation and release/governance contradictions that can cause wrong verification claims. Addressed by Task 0.2 documentation alignment.
|
||||
- Larger Module splits after behavior is protected by tests.
|
||||
|
||||
Rejected alternatives:
|
||||
|
||||
- Documentation-only audit: safer, but leaves clear simplifications unimplemented.
|
||||
- Broad architectural cleanup: may produce long-term wins, but is too risky for this pass because bridge, protocol, audio, and Android behavior have high regression cost.
|
||||
|
||||
## Architecture Boundaries
|
||||
|
||||
The existing responsibilities remain intact:
|
||||
|
||||
- Flutter owns presentation, navigation, Material 3 behavior, accessibility, localization presentation, and platform UI behavior.
|
||||
- Flutter Rust Bridge owns typed DTO/API glue and generated bindings.
|
||||
- Rust Core owns session orchestration, cross-crate coordination, bridge-facing public events, and stable public APIs.
|
||||
- Protocol owns TeamSpeak-compatible protocol isolation behind `tsclientlib`.
|
||||
- Audio owns capture, render, processing, PTT backends, platform audio behavior, and voice packet handling where explicitly documented.
|
||||
- Diagnostics owns redaction, logs, export records, and bounded diagnostic history.
|
||||
|
||||
Public interfaces should stay stable unless a change clearly removes duplicated or unnecessary code and has direct verification.
|
||||
|
||||
## Review Targets
|
||||
|
||||
The implementation review should inspect these areas first:
|
||||
|
||||
- `core/chanora_core/src/lib.rs`, `events.rs`, `network_diagnostics.rs`, and `ptt.rs`
|
||||
- `crates/chanora_audio`, especially duplicated render, capture, PTT, and platform-audio helpers
|
||||
- `crates/chanora_state` reducer paths
|
||||
- `crates/chanora_protocol` adapter ordering, event, and DTO mapping paths
|
||||
- `crates/chanora_bridge/src/api.rs`, excluding generated bridge files unless regeneration is intentionally part of a change
|
||||
- `crates/chanora_diagnostics/src/lib.rs`
|
||||
- `crates/chanora_prefetch` and `crates/chanora_resolver` as a documented follow-up seam decision unless a trivial cleanup appears
|
||||
- `apps/chanora_flutter/lib`, excluding generated localization and bridge files unless an API change requires updates
|
||||
- governance, architecture, implementation-status, and verification documents affected by the code review
|
||||
|
||||
Full code-review remediation targets:
|
||||
|
||||
- `apps/chanora_flutter/lib/main.dart`: mute ownership, iOS audio-session preflight, chat/unread follow-ups, and oversized session-controller extraction candidates.
|
||||
- `apps/chanora_flutter/lib/widgets/voice_compact.dart`: touch PTT release-on-dispose fail-safe.
|
||||
- `apps/chanora_flutter/lib/services/ios_audio_session_controller.dart`: missing-plugin fail-safe handling.
|
||||
- `apps/chanora_flutter/lib/services/audio_lifecycle_service.dart`: macOS route/default-device no-op documentation or future adapter seam.
|
||||
- `crates/chanora_audio/src/android_voice_unit.rs`, `ios_raw_unit.rs`, `ios_voice_unit.rs`, and `engine.rs`: realtime callback safety and platform lifecycle rollback.
|
||||
- `core/chanora_core/src/lib.rs` and `crates/chanora_protocol/src/adapter.rs`: bounded disconnect and control-plane progress under voice load.
|
||||
- `README.md`, `CHANGELOG.md`, `docs/release/*`, `docs/verification/*`, and `docs/governance/product-decision-register.md`: stale platform, release, VAD, Android runtime, and decision-register claims.
|
||||
|
||||
## Simplification Rules
|
||||
|
||||
Every code change must satisfy these rules:
|
||||
|
||||
- Prefer deletion, built-in APIs, derives, or reuse of existing helpers over new abstractions.
|
||||
- Merge files or modules only when the merged unit has a clearer single responsibility.
|
||||
- Split files only when it improves locality around a stable responsibility and preserves public API shape.
|
||||
- Do not manually edit generated files unless the generation process is part of the verified change.
|
||||
- Do not introduce backward-compatibility shims unless there is a persisted-data, shipped-API, external-consumer, or explicit product need.
|
||||
- Record larger architectural opportunities in the maintainability review instead of forcing them into this pass.
|
||||
|
||||
Full code-review fix rules:
|
||||
|
||||
- Fix safety bugs before Module split work.
|
||||
- Use test-driven development for production behavior changes: write the failing test, run it, implement the minimal fix, then rerun the test.
|
||||
- Keep manual/generated bridge files out of direct edits unless regeneration is intentionally verified.
|
||||
- Split huge Modules only when the split creates a deeper Module with leverage and locality; file-size-only sharding is not sufficient.
|
||||
- Compare architecture choices against established voice/chat client practice: Mumble-style bounded voice/control separation, Discord/TeamSpeak-style independent mute owners, WebRTC-style realtime callback minimalism, and Matrix/Element-style coherent state replication.
|
||||
|
||||
## Testing Design
|
||||
|
||||
Verification is tied to change type:
|
||||
|
||||
- Rust-only changes require `cargo fmt --all`, `cargo check --workspace`, and `cargo test --workspace`.
|
||||
- Flutter changes require `flutter analyze` and `flutter test --exclude-tags e2e` from `apps/chanora_flutter`.
|
||||
- Bridge DTO/API changes require Rust verification, bridge generation check, Flutter analyze, and Flutter tests.
|
||||
- Android platform, permission, lifecycle, or audio changes require Rust and Flutter verification plus Android NDK target compilation, `adb devices -l`, Android build/install, and a device or emulator smoke test.
|
||||
- Documentation-only changes require affected docs and cross-links to be read and checked; code tests are not required unless the docs describe a code change just made.
|
||||
|
||||
If no ADB target is connected, Android runtime verification must be recorded as blocked. If Android target compilation cannot find the NDK compiler, for example `aarch64-linux-android-clang`, Android build evidence must also be recorded as blocked. The implementation must not claim Android runtime success without build/install/smoke evidence from an authorized device or emulator.
|
||||
|
||||
## Fail-Safe Review
|
||||
|
||||
The review must identify fail-safe gaps and either verify them, fix them, or record the missing evidence.
|
||||
|
||||
Priority fail-safe areas:
|
||||
|
||||
- User mute ownership must not be cleared by talk-power or permission recovery.
|
||||
- Touch and keyboard PTT must release on cancellation, disposal, disconnect, lifecycle transition, or missed-up conditions.
|
||||
- iOS AVAudioSession must be configured and activated before VoiceProcessingIO startup.
|
||||
- Realtime callbacks must not block, allocate repeatedly, or use unsynchronized mutable aliasing.
|
||||
- Disconnect and control requests must be bounded and must not hold global session locks across unbounded transport waits.
|
||||
- Android and iOS device runtime behavior must be verified on hardware or an authorized emulator/simulator where applicable before platform success is claimed.
|
||||
- Android secure storage and Keystore-backed data-encryption-key handling
|
||||
- Android permission and audio lifecycle behavior
|
||||
- Stuck PTT prevention and missed-key-up recovery
|
||||
- Diagnostic redaction and privacy-sensitive event export
|
||||
- Bridge DTO drift between Core, Bridge, and Dart generated bindings
|
||||
- Protocol isolation exceptions for voice packet handling
|
||||
- Runtime behavior gaps not covered by unit tests
|
||||
|
||||
No release-readiness or production-safety claim should be made without matching evidence.
|
||||
|
||||
## Documentation Design
|
||||
|
||||
The working review record remains `docs/governance/maintainability-review-2026-06-08.md`.
|
||||
|
||||
Documents to update when affected:
|
||||
|
||||
- `README.md`
|
||||
- `CHANGELOG.md`
|
||||
- `docs/governance/document-index.md`
|
||||
- `docs/governance/product-decision-register.md`
|
||||
- `docs/architecture/sad.md`
|
||||
- `docs/architecture/sdd.md`
|
||||
- `docs/implementation-status-2026-05-28.md`
|
||||
- `docs/verification/swe4-unit-verification-plan.md`
|
||||
- `docs/verification/swe5-software-integration-verification-plan.md`
|
||||
- `docs/verification/verification-master-plan.md`
|
||||
- `docs/verification/sys4-system-integration-verification-plan.md`
|
||||
- `docs/release/release-readiness-go-nogo-record.md`
|
||||
- `docs/release/dv-waiver-register.md`
|
||||
- release or fail-safe records if verification status changes
|
||||
|
||||
Documentation should distinguish completed changes, follow-up opportunities, blocked verification, and release limitations.
|
||||
|
||||
## Commit Policy
|
||||
|
||||
No commit is created automatically. A commit happens only when explicitly requested, after inspecting `git status`, `git diff`, and recent commits.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
This work is successful when:
|
||||
|
||||
- Safe simplifications are implemented or recorded as follow-up opportunities.
|
||||
- Built-in replacement opportunities are applied only when behavior remains covered by tests.
|
||||
- Fail-safe gaps are documented with required evidence or fixed with verification.
|
||||
- Rust and Flutter verification are run as required by the touched files, including targeted regression tests for every fixed bug.
|
||||
- Android ADB runtime verification is run when a target is available or explicitly recorded as blocked.
|
||||
- Documents reflect the final code and verification state.
|
||||
- Full code-review findings are either fixed, downgraded with evidence, or recorded as follow-up risks with verification requirements.
|
||||
@@ -0,0 +1,176 @@
|
||||
# Poke Without Message Design
|
||||
|
||||
**Date:** 2026-06-09
|
||||
**Status:** Approved design for implementation
|
||||
**Scope:** Allow intentional TeamSpeak-compatible pokes without message text while preserving empty-message blocking for normal chat targets.
|
||||
|
||||
## 1. Goal
|
||||
|
||||
Chanora should let a user poke another connected client without typing a message. A poke is an attention event, not an empty chat message. The UI should make that distinction explicit so the empty state is intentional, understandable, and safe from accidental spam.
|
||||
|
||||
The implementation target is narrow:
|
||||
|
||||
- Sending a poke with an empty message is allowed.
|
||||
- Sending an empty normal chat message remains blocked.
|
||||
- Incoming and historical empty pokes continue to render as poke events, not blank chat bubbles.
|
||||
- Existing poke notification behavior remains compatible with message and no-message pokes.
|
||||
|
||||
## 2. Research Summary
|
||||
|
||||
TeamSpeak-compatible poke behavior is command-like: the ServerQuery shape is `clientpoke clid={clientID} msg={text}`, backed by poke permissions such as `i_client_poke_power` and `i_client_needed_poke_power`. The product semantics are closer to an attention nudge than to a private text message.
|
||||
|
||||
Client behavior and community expectations point to two UX risks:
|
||||
|
||||
- The action can be useful without text because the sender often only wants attention.
|
||||
- The action can be abused as interruption spam, so the UI must keep the action deliberate and preserve existing receiver-side suppression and notification preferences.
|
||||
|
||||
The approved product direction is therefore to model no-message poke as a first-class attention event with optional text, rather than as an exception in the normal chat composer.
|
||||
|
||||
## 3. Recommended UX
|
||||
|
||||
Poke uses a poke-specific sending surface. The surface may reuse the current chat detail implementation internally, but the user-facing copy and validation must make the target type clear.
|
||||
|
||||
Required poke-target behavior:
|
||||
|
||||
| Element | Behavior |
|
||||
|---|---|
|
||||
| Header | Shows that the current surface is for poking the selected user. |
|
||||
| Text field | Optional message input. Placeholder should communicate that the message is optional. |
|
||||
| Primary action | Label is `Poke`, not `Send`. Enabled even when the trimmed message is empty. |
|
||||
| Empty send | Sends an intentional poke with `message: ''`. |
|
||||
| Non-empty send | Sends a poke with the typed message. |
|
||||
| History row | Empty poke renders as an attention event such as `Alice poked you`, never as a blank message. |
|
||||
|
||||
Required non-poke chat behavior:
|
||||
|
||||
| Target | Empty text behavior |
|
||||
|---|---|
|
||||
| Channel chat | Block send. |
|
||||
| Server chat | Block send. |
|
||||
| Private chat | Block send. |
|
||||
| Any future text-chat target | Block send unless it is explicitly modeled as a poke-like attention event. |
|
||||
|
||||
## 4. Architecture Boundaries
|
||||
|
||||
The change should stay inside the existing UI and bridge boundaries:
|
||||
|
||||
- Flutter owns presentation, composer validation, button enablement, localization copy, and widget tests.
|
||||
- Flutter Rust Bridge continues to pass typed `BridgeMessageTarget` and message text across the bridge.
|
||||
- Rust Core and Protocol continue to route `MessageTarget::Poke(client_id)` through the existing poke send path.
|
||||
- Protocol remains the only layer that knows how `tsclientlib` sends a TeamSpeak-compatible poke.
|
||||
|
||||
No new protocol concept is required. The existing bridge/protocol model already has `BridgeMessageTarget.poke` / `MessageTarget::Poke(u64)` and `client.poke(message)`. The key design change is target-aware composer validation in Flutter.
|
||||
|
||||
## 5. Implementation Design
|
||||
|
||||
The implementation should use a target-aware send policy.
|
||||
|
||||
For `BridgeMessageTarget.poke`:
|
||||
|
||||
- Do not reject an empty trimmed input.
|
||||
- Send the original or trimmed message according to the existing chat composer convention. If the current send path trims normal messages before sending, apply the same text normalization before passing the poke message.
|
||||
- Clear the composer after successful send, including empty-poke sends.
|
||||
- Preserve existing error and snackbar behavior for failed sends.
|
||||
|
||||
For all other `BridgeMessageTarget` variants:
|
||||
|
||||
- Keep the existing empty-trimmed-text guard.
|
||||
- Keep current button enablement and keyboard submit behavior unless those paths need target-aware adjustment to preserve the same empty-message block.
|
||||
|
||||
A simple policy helper is preferred over scattered conditionals. Example shape:
|
||||
|
||||
```dart
|
||||
bool canSendMessage({
|
||||
required BridgeMessageTarget target,
|
||||
required String text,
|
||||
}) {
|
||||
if (target is BridgeMessageTarget_Poke) {
|
||||
return true;
|
||||
}
|
||||
return text.trim().isNotEmpty;
|
||||
}
|
||||
```
|
||||
|
||||
The exact Dart type checks should follow the generated bridge type names used in the current codebase.
|
||||
|
||||
## 6. Notification And History Behavior
|
||||
|
||||
Existing no-message receiving behavior should remain the reference behavior:
|
||||
|
||||
- Incoming empty poke notification body falls back to text equivalent to `Alice pokes you`.
|
||||
- Incoming poke with message includes the message in the notification body.
|
||||
- Active-chat suppression and muted-sender preferences continue to apply.
|
||||
- Poke history rows distinguish poke events from normal chat rows.
|
||||
|
||||
The send-side change must not introduce a new blank message row shape. If the sender's local history records sent pokes, empty poke history should render as a poke action line with no empty bubble.
|
||||
|
||||
## 7. Abuse And Safety Rules
|
||||
|
||||
This slice does not add new anti-spam controls. It relies on existing TeamSpeak-compatible permissions, inbound poke strength/rate suppression, notification preferences, active-chat suppression, and muted sender handling.
|
||||
|
||||
The implementation must not weaken any existing receiver-side controls. If testing reveals that empty sent pokes bypass suppression, notification preferences, or history classification, that is a bug to fix in the same implementation pass.
|
||||
|
||||
Future follow-ups, not part of this slice:
|
||||
|
||||
- Per-sender or per-server outbound poke cooldown UI.
|
||||
- Receiver-side "never show poke dialog" equivalent beyond current notification preferences.
|
||||
- Dedicated poke inbox or grouped poke history.
|
||||
|
||||
## 8. Files Expected To Change
|
||||
|
||||
Expected implementation targets:
|
||||
|
||||
| File | Expected change |
|
||||
|---|---|
|
||||
| `apps/chanora_flutter/lib/widgets/chat_views.dart` | Make composer validation and action enablement target-aware for poke. Update poke placeholder/action copy if needed. |
|
||||
| `apps/chanora_flutter/test/widgets/chat_views_test.dart` | Add widget coverage for empty poke send and normal empty chat blocking. |
|
||||
|
||||
Optional targets if the implementation exposes missing copy or routing seams:
|
||||
|
||||
| File | Possible change |
|
||||
|---|---|
|
||||
| `apps/chanora_flutter/lib/main.dart` | Only if opening a poke target needs a clearer poke-specific title or route configuration. |
|
||||
| `apps/chanora_flutter/lib/l10n/*.arb` | Only if current copy cannot express optional poke messages without hard-coded strings. |
|
||||
| `apps/chanora_flutter/test/services/poke_notification_service_test.dart` | Only if send-side changes affect notification payload assumptions. |
|
||||
|
||||
The Rust protocol path should not need behavior changes unless tests prove that empty strings are blocked below Flutter.
|
||||
|
||||
## 9. Test Design
|
||||
|
||||
Required tests:
|
||||
|
||||
- Poke target shows an enabled primary `Poke` action when the text field is empty.
|
||||
- Tapping `Poke` on an empty poke target calls the send callback with `BridgeMessageTarget.poke` and an empty message.
|
||||
- Poke target still sends a typed message when text is present.
|
||||
- Normal channel/server/private chat targets keep blocking empty sends.
|
||||
- Empty poke history renders as a poke event line, not an empty text bubble.
|
||||
|
||||
Useful regression checks if already easy to target:
|
||||
|
||||
- Keyboard submit follows the same target-aware validation as the button.
|
||||
- Failed empty-poke send keeps existing error presentation.
|
||||
- Incoming empty poke notification tests still pass unchanged.
|
||||
|
||||
## 10. Validation
|
||||
|
||||
For the implementation branch, run focused Flutter verification first:
|
||||
|
||||
```text
|
||||
flutter test test/widgets/chat_views_test.dart
|
||||
flutter test test/services/poke_notification_service_test.dart
|
||||
flutter test test/services/poke_active_chat_test.dart
|
||||
flutter analyze
|
||||
```
|
||||
|
||||
If Rust or bridge files are touched, also run the matching Rust and bridge checks for the touched layer. Documentation-only changes require reading the affected spec and checking the diff; code tests are not required for this design commit.
|
||||
|
||||
## 11. Success Criteria
|
||||
|
||||
This design is implemented successfully when:
|
||||
|
||||
- A user can send a poke with no typed message.
|
||||
- Normal chat targets still reject empty sends.
|
||||
- The poke composer communicates that message text is optional.
|
||||
- Empty pokes are represented as poke events in history and notifications.
|
||||
- Existing poke notification preferences and suppression behavior remain intact.
|
||||
- Focused widget/service tests and `flutter analyze` pass, or any unrelated pre-existing failure is named with evidence.
|
||||
@@ -0,0 +1,472 @@
|
||||
# Documentation Site & ASPICE Traceability System Design
|
||||
|
||||
**Date:** 2026-06-13
|
||||
**Status:** Approved design for implementation
|
||||
**Scope:** Docusaurus doc site, git submodule separation, tag-based ASPICE traceability with custom validation plugin, Cloudflare Pages hosting with access control
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
Replace the current flat markdown documentation tree with a browseable, searchable, access-controlled doc site that serves three audiences: developers, ASPICE assessors, and non-technical stakeholders. Introduce automated traceability enforcement that validates the ASPICE requirement chain on every build.
|
||||
|
||||
## 2. Current State
|
||||
|
||||
- 65+ markdown files in `docs/` with no sidebar, no search, no visual hierarchy
|
||||
- ASPICE traceability maintained in manual markdown tables (`traceability-matrix.md`)
|
||||
- Cross-references are backtick-quoted paths in prose, not clickable links
|
||||
- Link coverage report found 2 broken links + 5 broken path references
|
||||
- No CI enforcement of traceability integrity
|
||||
- No access control — docs only viewable via GitHub repo browsing or local clone
|
||||
|
||||
## 3. Design Decisions
|
||||
|
||||
| Decision | Choice | Rationale |
|
||||
|---|---|---|
|
||||
| Repo structure | Git submodule (`docs/` → `chanora-docs` repo) | Cleaner separation, access control, CI independence, separate versioning |
|
||||
| Doc site generator | Docusaurus | Meta-maintained, full plugin API, built-in tags and versioning, active ecosystem |
|
||||
| Traceability mechanism | Tag-based + custom Docusaurus plugin | Tags for browsing, plugin for automated chain validation and coverage reports |
|
||||
| Hosting | Cloudflare Pages | Free tier, global CDN, auto-deploy from CI |
|
||||
| Access control | Cloudflare Access | Free for up to 50 users, email-based auth, SSO support |
|
||||
| Code path references in docs | Remove from ASPICE docs, move to `impl-mapping.md` | ASPICE traces requirement IDs, not file paths. Code paths are developer convenience |
|
||||
| Provenance records | Deferred | Completed ASPICE-related plans archived in `dev-docs/superpowers/plans/_archived/` for now |
|
||||
|
||||
## 4. Repo Structure
|
||||
|
||||
### 4.1 Docs submodule (`chanora-docs` repo)
|
||||
|
||||
```
|
||||
chanora-docs/
|
||||
├── mkdocs.yml
|
||||
├── requirements.txt
|
||||
├── pyproject.toml
|
||||
├── docs/
|
||||
│ ├── index.md
|
||||
│ ├── .meta.yml
|
||||
│ ├── requirements/
|
||||
│ │ ├── .meta.yml
|
||||
│ │ ├── sysrs.md
|
||||
│ │ ├── sysdes.md
|
||||
│ │ └── srs.md
|
||||
│ ├── architecture/
|
||||
│ │ ├── .meta.yml
|
||||
│ │ ├── sad.md
|
||||
│ │ ├── sdd.md
|
||||
│ │ ├── file-transfer-design.md
|
||||
│ │ ├── file-transfer-research.md
|
||||
│ │ ├── file-transfer-implementation-plan.md
|
||||
│ │ └── desktop-ptt-architecture.md
|
||||
│ ├── verification/
|
||||
│ │ ├── .meta.yml
|
||||
│ │ ├── verification-master-plan.md
|
||||
│ │ ├── swe4-unit-verification-plan.md
|
||||
│ │ ├── swe5-software-integration-verification-plan.md
|
||||
│ │ ├── swe6-software-verification-plan.md
|
||||
│ │ └── sys4-system-integration-verification-plan.md
|
||||
│ ├── governance/
|
||||
│ │ ├── .meta.yml
|
||||
│ │ ├── document-index.md
|
||||
│ │ ├── traceability-matrix.md
|
||||
│ │ ├── product-decision-register.md
|
||||
│ │ ├── baseline-approval-record.md
|
||||
│ │ ├── baseline-candidate-validation-report.md
|
||||
│ │ ├── document-review-report.md
|
||||
│ │ ├── document-naming-convention.md
|
||||
│ │ ├── decision-impact-assessment.md
|
||||
│ │ ├── git-commit-message-convention.md
|
||||
│ │ ├── repo-format-validation-report.md
|
||||
│ │ ├── path-migration-map.md
|
||||
│ │ └── maintainability-review-2026-06-08.md
|
||||
│ ├── security/
|
||||
│ │ ├── .meta.yml
|
||||
│ │ ├── security-privacy-legal-guideline.md
|
||||
│ │ ├── threat-model.md
|
||||
│ │ ├── secure-storage-audit-report.md
|
||||
│ │ ├── diagnostic-redaction-audit-report.md
|
||||
│ │ ├── dependency-and-supply-chain-report.md
|
||||
│ │ ├── license-inventory.md
|
||||
│ │ └── flutter-license-inventory.md
|
||||
│ ├── privacy/
|
||||
│ │ └── privacy-policy.md
|
||||
│ ├── legal/
|
||||
│ │ └── trademark-and-attribution-review.md
|
||||
│ ├── release/
|
||||
│ │ ├── platform-release-policy.md
|
||||
│ │ ├── release-readiness-go-nogo-record.md
|
||||
│ │ └── dv-waiver-register.md
|
||||
│ ├── references/
|
||||
│ │ ├── aspice-swe2-swe3-integration-note.md
|
||||
│ │ ├── external-references.md
|
||||
│ │ ├── yatqa-en.md (moved from offline-knowledge/external/)
|
||||
│ │ ├── yatqa-de.md (moved from offline-knowledge/external/)
|
||||
│ │ ├── teaspeak-overview.md (moved from offline-knowledge/external/)
|
||||
│ │ └── respeak-overview.md (moved from offline-knowledge/external/)
|
||||
│ ├── ui-ux/
|
||||
│ │ ├── material3-guideline.md
|
||||
│ │ ├── material3-design-tokens.md
|
||||
│ │ ├── material3-component-catalog.md
|
||||
│ │ └── adaptive-layout-platform-guide.md
|
||||
│ ├── i18n/
|
||||
│ │ └── localization-architecture.md
|
||||
│ └── tags.md
|
||||
├── plugins/
|
||||
│ └── traceability/
|
||||
│ ├── __init__.py
|
||||
│ └── traceability.py
|
||||
├── scripts/
|
||||
│ └── validate_traceability.py
|
||||
├── .github/
|
||||
│ └── workflows/
|
||||
│ └── deploy.yml
|
||||
├── wrangler.toml
|
||||
└── README.md
|
||||
```
|
||||
|
||||
### 4.2 Code repo local files
|
||||
|
||||
```
|
||||
chanora/
|
||||
├── docs/ → chanora-docs (submodule)
|
||||
├── dev-docs/
|
||||
│ ├── superpowers/
|
||||
│ │ ├── specs/
|
||||
│ │ │ ├── 2026-05-28-server-resolution-prefetch-design.md
|
||||
│ │ │ ├── 2026-05-28-chanora-server-prefetch-crate-design.md
|
||||
│ │ │ ├── 2026-05-29-state-sync-ui-settings-validation-design.md
|
||||
│ │ │ ├── 2026-06-05-adaptive-3-panel-layout-design.md
|
||||
│ │ │ ├── 2026-06-08-maintainability-continuation-design.md
|
||||
│ │ │ ├── 2026-06-09-poke-without-message-design.md
|
||||
│ │ │ └── 2026-06-13-documentation-site-design.md
|
||||
│ │ └── plans/
|
||||
│ │ ├── _archived/
|
||||
│ │ │ ├── 2026-05-29-finish-dv-document-tree.md
|
||||
│ │ │ ├── 2026-05-29-dv-evidence-pack.md
|
||||
│ │ │ ├── 2026-05-29-swe2-swe3-baselines.md
|
||||
│ │ │ └── 2026-05-29-state-sync-ui-settings-validation.md
|
||||
│ │ ├── 2026-05-28-server-resolution-prefetch.md
|
||||
│ │ ├── 2026-05-28-chanora-server-prefetch-crate.md
|
||||
│ │ ├── 2026-06-06-chat-panel-switching.md
|
||||
│ │ ├── 2026-06-08-core-internal-split.md
|
||||
│ │ └── 2026-06-08-maintainability-continuation.md
|
||||
│ ├── offline-knowledge/
|
||||
│ │ ├── coverage-analysis.md
|
||||
│ │ ├── doc-quality-analysis.md
|
||||
│ │ ├── link-coverage-report.md
|
||||
│ │ └── reviews/
|
||||
│ ├── implementation-status-2026-05-28.md
|
||||
│ ├── release/ios-build.md
|
||||
│ └── impl-mapping.md
|
||||
├── apps/, crates/, core/
|
||||
├── AGENTS.md
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## 5. Docusaurus Configuration
|
||||
|
||||
### 5.1 Site configuration (`docusaurus.config.js`)
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
title: 'Chanora Engineering Docs',
|
||||
tagline: 'ASPICE-compliant engineering documentation with automated traceability',
|
||||
url: 'https://docs.chanora.dev',
|
||||
baseUrl: '/',
|
||||
organizationName: 'chanoraapp',
|
||||
projectName: 'docs',
|
||||
onBrokenLinks: 'throw',
|
||||
onBrokenMarkdownLinks: 'warn',
|
||||
i18n: { defaultLocale: 'en', locales: ['en'] },
|
||||
themes: ['@docusaurus/theme-classic'],
|
||||
plugins: [
|
||||
'./plugins/traceability',
|
||||
],
|
||||
themeConfig: {
|
||||
navbar: {
|
||||
title: 'Chanora Docs',
|
||||
items: [
|
||||
{ type: 'doc', position: 'left', label: 'Requirements', docId: 'requirements/sysrs' },
|
||||
{ type: 'doc', position: 'left', label: 'Architecture', docId: 'architecture/sad' },
|
||||
{ type: 'doc', position: 'left', label: 'Verification', docId: 'verification/verification-master-plan' },
|
||||
{ type: 'doc', position: 'left', label: 'Governance', docId: 'governance/document-index' },
|
||||
{ type: 'doc', position: 'left', label: 'Security', docId: 'security/security-privacy-legal-guideline' },
|
||||
{ type: 'doc', position: 'left', label: 'Release', docId: 'release/platform-release-policy' },
|
||||
{ type: 'doc', position: 'left', label: 'References', docId: 'references/external-references' },
|
||||
{ type: 'doc', position: 'left', label: 'UI/UX', docId: 'ui-ux/material3-guideline' },
|
||||
{ type: 'tags' },
|
||||
],
|
||||
},
|
||||
footer: {
|
||||
style: 'dark',
|
||||
links: [
|
||||
{ title: 'Docs', items: [
|
||||
{ label: 'Requirements', to: '/docs/requirements/sysrs' },
|
||||
{ label: 'Architecture', to: '/docs/architecture/sad' },
|
||||
{ label: 'Verification', to: '/docs/verification/verification-master-plan' },
|
||||
]},
|
||||
{ title: 'Governance', items: [
|
||||
{ label: 'Traceability Matrix', to: '/docs/governance/traceability-matrix' },
|
||||
{ label: 'Decision Register', to: '/docs/governance/product-decision-register' },
|
||||
{ label: 'Document Index', to: '/docs/governance/document-index' },
|
||||
]},
|
||||
],
|
||||
},
|
||||
prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula },
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### 5.2 Sidebar (`sidebars.js`)
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
requirements: [
|
||||
'requirements/sysrs',
|
||||
'requirements/sysdes',
|
||||
'requirements/srs',
|
||||
],
|
||||
architecture: [
|
||||
'architecture/sad',
|
||||
'architecture/sdd',
|
||||
'architecture/file-transfer-design',
|
||||
'architecture/file-transfer-research',
|
||||
'architecture/file-transfer-implementation-plan',
|
||||
'architecture/desktop-ptt-architecture',
|
||||
],
|
||||
verification: [
|
||||
{
|
||||
type: 'category',
|
||||
label: 'System Level',
|
||||
items: ['verification/sys4-system-integration-verification-plan'],
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Software Integration',
|
||||
items: ['verification/swe5-software-integration-verification-plan'],
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Unit Level',
|
||||
items: ['verification/swe4-unit-verification-plan'],
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Software Qualification',
|
||||
items: ['verification/swe6-software-verification-plan'],
|
||||
},
|
||||
'verification/verification-master-plan',
|
||||
],
|
||||
governance: [
|
||||
'governance/document-index',
|
||||
'governance/traceability-matrix',
|
||||
'governance/product-decision-register',
|
||||
'governance/baseline-approval-record',
|
||||
'governance/baseline-candidate-validation-report',
|
||||
'governance/document-review-report',
|
||||
'governance/document-naming-convention',
|
||||
'governance/decision-impact-assessment',
|
||||
'governance/git-commit-message-convention',
|
||||
'governance/repo-format-validation-report',
|
||||
'governance/path-migration-map',
|
||||
'governance/maintainability-review-2026-06-08',
|
||||
],
|
||||
security: [
|
||||
'security/security-privacy-legal-guideline',
|
||||
'security/threat-model',
|
||||
'security/secure-storage-audit-report',
|
||||
'security/diagnostic-redaction-audit-report',
|
||||
'security/dependency-and-supply-chain-report',
|
||||
'security/license-inventory',
|
||||
'security/flutter-license-inventory',
|
||||
'privacy/privacy-policy',
|
||||
'legal/trademark-and-attribution-review',
|
||||
],
|
||||
release: [
|
||||
'release/platform-release-policy',
|
||||
'release/release-readiness-go-nogo-record',
|
||||
'release/dv-waiver-register',
|
||||
],
|
||||
references: [
|
||||
'references/external-references',
|
||||
'references/aspice-swe2-swe3-integration-note',
|
||||
'references/yatqa-en',
|
||||
'references/yatqa-de',
|
||||
'references/teaspeak-overview',
|
||||
'references/respeak-overview',
|
||||
],
|
||||
uiux: [
|
||||
'ui-ux/material3-guideline',
|
||||
'ui-ux/material3-design-tokens',
|
||||
'ui-ux/material3-component-catalog',
|
||||
'ui-ux/adaptive-layout-platform-guide',
|
||||
'i18n/localization-architecture',
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
## 6. Tag-Based Traceability
|
||||
|
||||
### 6.1 Front matter schema
|
||||
|
||||
Every document includes YAML front matter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
tags: [swe.2, architecture, SRS-003, SRS-008, SRS-016]
|
||||
upstream: [srs, sysdes] # Custom metadata for traceability plugin
|
||||
downstream: [sdd, swe4, swe5] # Custom metadata for traceability plugin
|
||||
lifecycle: SWE.2 # Custom metadata for traceability plugin
|
||||
status: baseline # Custom metadata for traceability plugin
|
||||
---
|
||||
```
|
||||
|
||||
The `tags` field is consumed by the MkDocs Material tags plugin for browsing. The `upstream`, `downstream`, `lifecycle`, and `status` fields are custom metadata consumed by the traceability plugin for chain validation.
|
||||
|
||||
### 6.2 Tag categories
|
||||
|
||||
| Tag pattern | Purpose | Example |
|
||||
|---|---|---|
|
||||
| `swe.1` through `swe.6`, `sys.4` | ASPICE lifecycle stage | Every doc gets at least one |
|
||||
| `sysrs`, `sysdes`, `srs`, `sad`, `sdd` | Document type | Identifies the doc in the chain |
|
||||
| `requirements`, `architecture`, `verification`, `governance` | Section category | For filtering |
|
||||
| `SysRS-233`, `SRS-045`, `SDD-MOD-009` | Requirement/module IDs | Traceability links |
|
||||
| `baseline`, `draft`, `candidate` | Document status | Assessor visibility |
|
||||
| `dec-012`, `dec-020` | Decision register refs | Cross-ref to governance |
|
||||
|
||||
### 6.3 Section defaults via `.meta.yml`
|
||||
|
||||
```yaml
|
||||
# docs/verification/.meta.yml
|
||||
tags: [verification]
|
||||
status: candidate
|
||||
```
|
||||
|
||||
### 6.4 Verification page trace mappings
|
||||
|
||||
| Verification plan | Upstream traces | Tags |
|
||||
|---|---|---|
|
||||
| SYS.4 System Integration | SysDes, SysRS | `[sys.4, verification, SysDes-102, SysDes-103, ...]` |
|
||||
| SWE.5 Software Integration | SAD (SWE.2) | `[swe.5, verification, sad-component-bridge, ...]` |
|
||||
| SWE.4 Unit Verification | SDD (SWE.3) | `[swe.4, verification, SDD-MOD-001, ...]` |
|
||||
| SWE.6 Software Verification | SRS | `[swe.6, verification, SRS-128, ...]` |
|
||||
|
||||
## 7. Custom Traceability Plugin
|
||||
|
||||
### 7.1 Location
|
||||
|
||||
`plugins/traceability/traceability.py` — MkDocs plugin, ~200 lines Python.
|
||||
|
||||
### 7.2 Behavior
|
||||
|
||||
On `on_page_markdown` event:
|
||||
- Scan each page for requirement ID patterns: `SysRS-\d+`, `SysDes-\d+`, `SRS-\d+`, `SDD-MOD-\d+`, `DEC-\d+`
|
||||
- Build an in-memory traceability graph: upstream ID → downstream document → verification plan
|
||||
|
||||
On `on_post_build` event:
|
||||
- Validate every requirement ID referenced downstream exists in its source document
|
||||
- Validate every upstream document ID has at least one downstream allocation
|
||||
- Flag orphaned references (IDs mentioned but never defined)
|
||||
- Verify bidirectional completeness
|
||||
|
||||
### 7.3 Outputs
|
||||
|
||||
- `traceability-coverage.json` — machine-readable coverage report with chain completeness percentages
|
||||
- Console output with pass/fail summary
|
||||
- Traceability dashboard page with coverage table and broken chain details
|
||||
- Build failure (`sys.exit(1)`) on broken chains when `strict: true`
|
||||
|
||||
### 7.4 Standalone CI validator
|
||||
|
||||
`scripts/validate_traceability.py` — same validation logic, runnable without MkDocs build:
|
||||
|
||||
```
|
||||
python scripts/validate_traceability.py docs/
|
||||
```
|
||||
|
||||
Exit code 0 = all chains valid. Exit code 1 = broken chains with details on stderr.
|
||||
|
||||
## 8. Hosting & Deployment
|
||||
|
||||
### 8.1 Architecture
|
||||
|
||||
```
|
||||
chanora-docs repo → push to main → GitHub Actions
|
||||
→ validate_traceability.py
|
||||
→ mkdocs build --strict
|
||||
→ Cloudflare Pages (via Wrangler)
|
||||
→ Cloudflare Access policy (email-based auth)
|
||||
```
|
||||
|
||||
### 8.2 CI workflow
|
||||
|
||||
On pull request: build + validate only (no deploy).
|
||||
On push to main: build + validate + deploy to Cloudflare Pages.
|
||||
|
||||
### 8.3 Cloudflare Access policy
|
||||
|
||||
- Free tier for up to 50 users
|
||||
- Email-based authentication with optional Google/GitHub SSO
|
||||
- One-time PIN for external assessors
|
||||
- Access rules: allow company emails, specific assessor emails; block all others
|
||||
|
||||
## 9. Migration Plan
|
||||
|
||||
### 9.1 Code path reference cleanup
|
||||
|
||||
SAD and SDD currently list file paths (`crates/chanora_protocol/src/`) in component tables. These references will be:
|
||||
- Replaced with component/module IDs only in the docs submodule
|
||||
- Preserved in `dev-docs/impl-mapping.md` in the code repo for developer convenience
|
||||
|
||||
### 9.2 File moves
|
||||
|
||||
| From (code repo) | To | Action |
|
||||
|---|---|---|
|
||||
| `docs/sysrs.md` | docs submodule | Move + add front matter |
|
||||
| `docs/sysdes.md` | docs submodule | Move + add front matter |
|
||||
| `docs/srs.md` | docs submodule | Move + add front matter |
|
||||
| `docs/requirements/*` | docs submodule | Move (path records) |
|
||||
| `docs/architecture/*` | docs submodule | Move + cleanup code paths |
|
||||
| `docs/verification/*` | docs submodule | Move + add front matter |
|
||||
| `docs/governance/*` | docs submodule | Move + add front matter |
|
||||
| `docs/security/*` | docs submodule | Move + add front matter |
|
||||
| `docs/privacy/*` | docs submodule | Move |
|
||||
| `docs/legal/*` | docs submodule | Move |
|
||||
| `docs/release/policy+go-nogo+waiver` | docs submodule | Move |
|
||||
| `docs/references/*` | docs submodule | Move |
|
||||
| `docs/ui-ux/*` | docs submodule | Move |
|
||||
| `docs/i18n/*` | docs submodule | Move |
|
||||
| `docs/material3-guideline.md` | docs submodule | Move |
|
||||
| `docs/offline-knowledge/external/*` | docs submodule `references/` (flattened) | Move + rename |
|
||||
| `docs/superpowers/*` | `dev-docs/superpowers/` | Move |
|
||||
| `docs/offline-knowledge/` (remaining) | `dev-docs/offline-knowledge/` | Move |
|
||||
| `docs/implementation-status-*` | `dev-docs/` | Move |
|
||||
| `docs/release/ios-build.md` | `dev-docs/release/` | Move |
|
||||
|
||||
### 9.3 Cross-reference updates
|
||||
|
||||
All backtick path references (`docs/srs.md`) should become markdown links (`[SRS](../srs.md)` or `[SRS](srs.md)`) for both GitHub and MkDocs rendering.
|
||||
|
||||
### 9.4 Post-migration
|
||||
|
||||
- Remove `docs/` contents from code repo
|
||||
- Add `chanora-docs` as git submodule at `docs/`
|
||||
- Create `dev-docs/` directory with local-only files
|
||||
- Update README references to new paths
|
||||
- Write `AGENTS.md` with new conventions
|
||||
- Update `opencode.json` or `.opencode/` references
|
||||
|
||||
## 10. AGENTS.md
|
||||
|
||||
An `AGENTS.md` file will be written at the code repo root documenting:
|
||||
- The two-repo model (docs/ as submodule, dev-docs/ as local)
|
||||
- What content goes where
|
||||
- ASPICE traceability chain and rules
|
||||
- Code architecture overview
|
||||
- Verification commands
|
||||
- Agent working conventions (no edits in docs/ without submodule awareness)
|
||||
|
||||
## 11. Deferred Items
|
||||
|
||||
| Item | Reason | When |
|
||||
|---|---|---|
|
||||
| Document provenance records | Convert completed ASPICE plans into provenance evidence | Follow-up task |
|
||||
| Custom MkDocs traceability plugin | Core feature, built during implementation | Phase 1 |
|
||||
| Cloudflare Pages + Access setup | Requires account creation, domain config | During deployment |
|
||||
| `impl-mapping.md` creation | Extract code paths from SAD/SDD during migration | During migration |
|
||||
Reference in New Issue
Block a user