Module 12 Release Notes — Cross-Platform Mobile Application (Flutter)
Full decision record: docs/adr/0012-mobile-application.md.
Implementation record: docs/modules/12-mobile-application.md.
Features added
apps/mobile — a new Flutter app (Clean Architecture, feature-first,
Riverpod, GoRouter, Dio, Hive + sqflite) joining apps/web and
apps/desktop as a third first-class client of apps/api.
Auth — full Module 1 integration: email login/register, MFA challenge, Google/GitHub OAuth (in-app WebView), password reset, refresh-token rotation, session management + remote logout, device registration, PIN lock + Face ID/Fingerprint (a device-unlock gate layered on top of an already-valid session, not a second server-side factor), secure token storage (platform keychain).
Dashboard — workspace stats, pinned projects, latest findings, recent activity, quick actions.
Projects / Targets / Findings — full offline-first CRUD (create,
update, archive/favorite, stage transitions) with a shared
SyncEngine/outbox pattern: writes made offline apply optimistically to a
local sqflite row and queue a mutation that a per-entity
SyncPushHandler pushes on reconnect, swapping the client-generated
placeholder ID for the server's real one.
Reports — view/AI-draft-generate/export/download/share, Markdown preview.
AI Security Copilot — streaming chat (manual SSE, matching apps/web's
own non-EventSource approach) with Markdown, syntax-highlighted code
blocks, and Mermaid diagram rendering; voice push-to-talk; Master
Orchestrator run start/live task-tree/cancel/Explainability; Knowledge
Base search.
Voice — on-device speech-to-text (speech_to_text) and text-to-speech
(flutter_tts), wired into the AI chat composer.
Notifications — in-app inbox + FCM/local-notification registration scaffold.
Evidence capture — camera/gallery/file picker/video/microphone/QR scanner, on-device SHA-256 hashing before upload, an offline upload queue.
File manager — local documents cache (downloaded reports + evidence) with browse/preview/rename/delete/search.
Global search — online /search + an offline FTS5 fallback over the
same locally-cached entities.
Settings — theme, security (PIN/biometric/sessions), AI Providers (Module 11 Privacy Mode), voice toggles, storage/sync status, developer mode.
Workspace, Analytics — read-only workspace/member info + usage stats;
severity-distribution and projects-by-status charts (fl_chart).
Security hardening — encrypted local storage, certificate pinning, jailbreak/root detection (dismissible warning, fail-open by design), clipboard auto-clear utility, session timeout, remote logout.
CI/CD — .github/workflows/mobile-ci.yml (analyze/test/Android
APK+AAB/iOS no-codesign build) + Fastlane lanes for both platforms.
Bugs found and fixed
- A data-mapping typo in
AnalyticsScreen's bar chart:entry.value.value.value.toDouble()had one.valuetoo many for the actual.asMap().entriesshape it was reading — caught during the same authoring pass via careful manual type-tracing (no compiler available to catch it automatically) and fixed toentry.value.value. - A malformed hex color literal (
Color(0xFF12172233;, missing its closing paren and with two extra digits) incore/theme/app_colors.dart— caught immediately after writing it and fixed toColor(0xFF121722). - A
ProviderContainerdouble-construction bug in the original draft ofmain.dart's bootstrap:ApiClient'sonSessionExpiredcallback needs to read from the same container the widget tree uses, but the container itself needsApiClientas one of its overrides — an initial draft built a throwaway container just to satisfy the callback, then built a second, real container the widget tree actually used, meaning the callback would have calledlogout()on an already-disposed container in production. Fixed by declaringlate final ProviderContainer container;and having the closure capture that variable by reference (safe in Dart, since the closure only reads it when actually invoked, long after assignment).
Known limitations
- No Flutter/Dart toolchain was reachable in this sandbox at all — the
network allowlist blocks
storage.googleapis.com(where Flutter's engine/Dart-SDK binaries are fetched from), soflutter analyze/flutter test/flutter buildnever ran. Every source file was hand-authored and manually cross-referenced against realpackages/sharedcontracts instead of compiler-verified — see ADR 0012 §8 and the module doc's Verification section (§23) for the full account and the two bugs that manual review did catch. android//ios/are hand-authored partials, not fullflutter createscaffolds —AndroidManifest.xml/Info.plistexist; Gradle/Xcode project files do not, and must be generated on a real dev machine viaflutter create --platforms=android,ios .before this module builds at all.- iOS IPA generation is categorically impossible outside macOS+Xcode,
independent of sandbox — the CI workflow's
ios-buildjob targets amacos-latestGitHub-hosted runner for this reason and is itself unexecuted/unverified. - Deferred within several features (each disclosed in the relevant
task's completion note and the module doc's per-section write-up):
finding comments/attachments/timeline UI; Recon/Scanner job dashboard
widgets and their Analytics charts; DOCX/native-PDF in-app report
preview; AI chat conversation branching UI and a dedicated bookmarks/
pinned-conversations screen; Document Scanner edge-detection/crop;
in-app language switcher; screenshot protection as a runtime toggle;
cursor-based pagination; a background
workmanagerperiodic sync task (connectivity-triggered sync covers the spec's Background Sync requirement without it, for now). - Integration, golden, offline, sync, and performance test suites (5 of the spec's 7 testing categories) are not written this pass — see module doc §21 for why (need a running app/emulator or golden-image baselines, neither available here).
Testing
New Dart test files under apps/mobile/test/: core/error/result_test.dart,
core/sync/outbox_operation_test.dart, core/theme/app_theme_test.dart,
features/projects/domain/project_test.dart, features/findings/domain/ finding_test.dart, features/auth/domain/auth_state_test.dart,
features/findings/presentation/widgets/severity_badge_test.dart — unit
and widget tests covering codec round-trips, JSON/row parsing, and
exhaustive sealed-class matching. Not executed (no Flutter SDK this
session — see Known limitations).
Before merging, on a real machine with Flutter installed, run from
apps/mobile:
flutter create --platforms=android,ios . (first time only) →
flutter pub get → flutter analyze --fatal-infos → flutter test →
flutter build apk --release → flutter build ios --release --no-codesign