Skip to content

feat: wire relay toggle, mostro node selector, log stream, and disput… - #90

Merged
grunch merged 4 commits into
mainfrom
fix/multiple-fixes
Apr 4, 2026
Merged

feat: wire relay toggle, mostro node selector, log stream, and disput…#90
grunch merged 4 commits into
mainfrom
fix/multiple-fixes

Conversation

@grunch

@grunch grunch commented Apr 4, 2026

Copy link
Copy Markdown
Member

…e stubs to Rust bridge

  • Wire relay toggle persistence via addRelay/removeRelay with optimistic UI and safe rollback by URL lookup
  • Add runtime mostro pubkey override (config.rs + settings API) and update all order/reputation/dispute/relay code to use active_mostro_pubkey()
  • Wire MostroNodeSelector to call setMostroPubkey on confirm/reset
  • Add rust/src/api/logging.rs with install_log_bridge, forward_log, and on_log_entry stream; create logEntriesProvider and replace mock entries in LogReportScreen with live Rust log stream
  • Replace TODO stubs in DisputeChatScreen with Phase 12 context comments
  • Regenerate flutter_rust_bridge bindings

Summary by CodeRabbit

  • New Features

    • Live app log stream: in-app viewing and sharing of real-time log entries; logs are forwarded from the native layer to the app.
    • Mostro node selection persists and is synced with native settings; runtime override for the Mostro public key exposed in settings.
  • Bug Fixes

    • Relay activation/deactivation now syncs with the backend, restores UI on failure, and shows localized error snackbars.

…e stubs to Rust bridge

- Wire relay toggle persistence via addRelay/removeRelay with optimistic
  UI and safe rollback by URL lookup
- Add runtime mostro pubkey override (config.rs + settings API) and
  update all order/reputation/dispute/relay code to use active_mostro_pubkey()
- Wire MostroNodeSelector to call setMostroPubkey on confirm/reset
- Add rust/src/api/logging.rs with install_log_bridge, forward_log,
  and on_log_entry stream; create logEntriesProvider and replace mock
  entries in LogReportScreen with live Rust log stream
- Replace TODO stubs in DisputeChatScreen with Phase 12 context comments
- Regenerate flutter_rust_bridge bindings
@coderabbitai

coderabbitai Bot commented Apr 4, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 29b00cf7-7cfe-4712-b0f1-1572ea9231c1

📥 Commits

Reviewing files that changed from the base of the PR and between bd1630f and e0b37f4.

📒 Files selected for processing (3)
  • lib/features/settings/widgets/mostro_node_selector.dart
  • rust/src/api/logging.rs
  • rust/src/lib.rs

Walkthrough

Adds a Rust→Flutter live logging bridge and FRB bindings, a runtime Mostro pubkey override with settings APIs, rewires Dart settings/widgets to call Rust bridge functions, replaces dispute chat TODOs with Phase‑12 documentation, and updates multiple Rust modules to use the runtime pubkey.

Changes

Cohort / File(s) Summary
Dispute Chat Documentation
lib/features/disputes/screens/dispute_chat_screen.dart
Replaced TODOs with Phase‑12 documentation for _onSendText and _onAttachFile describing adminSharedKey derivation and planned Rust bridge APIs; no runtime/UI behavior changes.
Flutter: Logging UI & Provider
lib/features/settings/providers/log_provider.dart, lib/features/settings/screens/log_report_screen.dart
Added logEntriesProvider consuming logging_api.onLogEntry() (StreamProvider.autoDispose); log report screen now reads live LogEntry list (newest‑first, capped at 500) and adapts share/enable state to live entries.
Flutter: Settings UI Integration
lib/features/settings/widgets/mostro_node_selector.dart, lib/features/settings/widgets/relay_management_card.dart
mostro_node_selector now calls settings_api.setMostroPubkey(...) with async error handling and state restore; relay toggle _toggleRelay became async and calls nostr_api.addRelay()/removeRelay() with UI rollback and SnackBar on failure.
Rust: Logging Bridge
rust/src/api/logging.rs, rust/src/api/mod.rs
New logging module implements BridgeLogger, std→tokio forwarding thread, broadcast channel for LogEntry, install_log_bridge(), LogEntryStream with async next(), and on_log_entry(); exported via pub mod logging;.
Rust: FRB & Generated Bindings
rust/src/frb_generated.rs
Updated codegen hash and added FRB wire handlers, SseEncode/SseDecode/IntoDart support and strong-count exports for LogEntryStream, LogEntry, LogLevel, plus settings get/set wire handlers.
Rust: Runtime Mostro Pubkey Override
rust/src/config.rs, rust/src/api/settings.rs
Introduced ACTIVE_MOSTRO_PUBKEY (RwLock), active_mostro_pubkey() and set_active_mostro_pubkey(...); added get_mostro_pubkey() and set_mostro_pubkey(Option<String>) with pubkey hex validation.
Rust: Rewiring Pubkey Usage
rust/src/api/orders.rs, rust/src/api/disputes.rs, rust/src/api/reputation.rs, rust/src/nostr/relay_pool.rs
Replaced DEFAULT_MOSTRO_PUBKEY usages with active_mostro_pubkey() calls and updated related error messages; control flow unchanged.
Rust: App Init
rust/src/lib.rs
init_app() now calls api::logging::install_log_bridge() unconditionally to enable forwarding Rust logs into the Flutter stream.

Sequence Diagram(s)

sequenceDiagram
    participant Flutter as Flutter App (log_provider & UI)
    participant FRB as FRB Bridge (frb_generated)
    participant LogAPI as Rust Logging API (api/logging.rs)
    participant Relay as Background Relay Thread (std→tokio broadcast)

    Flutter->>FRB: call on_log_entry() -> LogEntryStream
    FRB->>LogAPI: on_log_entry()
    LogAPI->>Relay: subscribe (broadcast::Receiver)
    LogAPI-->>FRB: return LogEntryStream wrapper
    FRB-->>Flutter: stream handle

    Note over Relay,LogAPI: install_log_bridge() spawns forwarder thread once

    LogAPI->>Relay: forward_log(level,target,msg) via std channel
    Relay->>LogAPI: broadcast LogEntry
    LogAPI->>FRB: LogEntryStream.next() yields LogEntry
    FRB-->>Flutter: LogEntry delivered
    Flutter->>Flutter: accumulate entries (max 500, newest first)
Loading
sequenceDiagram
    participant Flutter as Flutter Widget (mostro_node_selector)
    participant FRB as FRB Bridge
    participant SettingsAPI as Rust Settings API (api/settings.rs)
    participant Config as Config State (config.rs)

    Flutter->>FRB: settings_api.setMostroPubkey(pubkey)
    FRB->>SettingsAPI: set_mostro_pubkey(Option<String>)
    SettingsAPI->>SettingsAPI: validate hex (if Some)
    SettingsAPI->>Config: set_active_mostro_pubkey(pubkey)
    Config->>Config: update ACTIVE_MOSTRO_PUBKEY (RwLock)
    SettingsAPI-->>FRB: return Result
    FRB-->>Flutter: success / error (UI updates)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through code with eager paws,

Logs now tumble without a pause,
Mostro's key can twist and sing,
Bridges hum and messages spring,
Hop! — the rabbit cheers the cause.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: wiring relay toggle, mostro node selector, log stream, and dispute stubs to the Rust bridge—all primary objectives of this PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/multiple-fixes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@mostronatorcoder mostronatorcoder Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

Good work across all four tasks. The architecture is correct throughout. Two issues to fix before merge.


🔴 Blocker — forward_log is dead code: nothing calls it

rust/src/api/logging.rs implements install_log_bridge() and forward_log() correctly, but install_log_bridge() is never called from the app's initialization path, and forward_log() is never invoked at any Rust log site.

The result: on_log_entry() is wired to Dart and the stream exists, but it will never emit a single entry — LogReportScreen will always show "No log entries" even when logging is enabled.

The fix has two parts:

Part A — Call install_log_bridge() during app startup. Find where the Rust runtime is initialized (likely rust/src/api/app.rs or rust/src/lib.rs) and add:

crate::api::logging::install_log_bridge();

Part B — Actually forward log records. The simplest approach is to add explicit forward_log calls at the key sites that matter to users, for example in rust/src/api/orders.rs where order state changes are logged:

use crate::api::logging::forward_log;

// After publishing an order event:
forward_log(log::Level::Info, "orders", &format!("order created: {}", order_id));

Alternatively — and more correctly — integrate forward_log into a custom log::Log impl so that every log::info!() / log::warn!() / log::error!() call in the codebase is automatically forwarded. Either approach works; the important thing is that something actually calls forward_log.


🟡 Major — logEntriesProvider never completes on widget disposal, stream leaks

lib/features/settings/providers/log_provider.dart uses:

final logEntriesProvider = StreamProvider.autoDispose<List<LogEntry>>((ref) async* {
  final stream = await logging_api.onLogEntry();
  while (true) {
    final entry = await stream.next();
    if (entry == null) break;
    ...
  }
});

The problem: stream.next() is a plain await with no cancellation path. When the widget is disposed, Riverpod cancels the autoDispose provider — but the await stream.next() is already in-flight and holds the async generator alive until the next log entry arrives. This is a resource leak; the Dart generator and the underlying Rust broadcast receiver are both kept alive indefinitely.

Fix: use ref.onDispose to signal cancellation:

final logEntriesProvider = StreamProvider.autoDispose<List<LogEntry>>((ref) async* {
  var cancelled = false;
  ref.onDispose(() => cancelled = true);

  final stream = await logging_api.onLogEntry();
  final entries = <LogEntry>[];
  while (!cancelled) {
    final entry = await stream.next();
    if (entry == null || cancelled) break;
    entries.insert(0, entry);
    if (entries.length > 500) entries.removeLast();
    yield List.unmodifiable(entries);
  }
});

✅ What's good

  • ACTIVE_MOSTRO_PUBKEY: RwLock<Option<String>> in config.rs is the right pattern for a runtime override — single source of truth, no Arc needed, read path is fast.
  • All six call sites (orders.rs, reputation.rs, disputes.rs, relay_pool.rs) correctly migrated from DEFAULT_MOSTRO_PUBKEY to active_mostro_pubkey(). Consistent.
  • set_mostro_pubkey validates the hex pubkey via nostr_sdk::PublicKey::from_hex before writing to the store. Correct — the Rust layer is the source of truth for validation.
  • _toggleRelay rollback uses indexWhere by URL instead of the captured index — safe against list mutations during the async call. Good catch.
  • l10n strings used for toggle snackbars (relayAddFailed, relayRemoveFailed) instead of hardcoded strings. Correct.
  • _sanitizeForShare in log_report_screen.dart redacts hex keys, nsec/npub, Bearer tokens, and key=value secrets before sharing. Solid.
  • _formatTimestamp shows only time for same-day entries, full date otherwise. Clean UX.
  • Dispute chat stubs replaced with l10n.disputeMessagingComingSoon and l10n.disputeAttachmentsComingSoon — correct use of l10n, good UX.
  • MostroNodeSelector calls settings_api.setMostroPubkey(pubkey: null) on reset and settings_api.setMostroPubkey(pubkey: pubkey) on confirm — correctly wired to the Rust bridge.
  • Input normalized to lowercase before storing: final pubkey = input.toLowerCase(). Correct.
  • FRB bindings regenerated. Good.

Install BridgeLogger as the global log::Log impl in init_app() so
every log::info!/warn!/error! call in Rust is forwarded to the
Flutter on_log_entry() stream automatically.

Fix logEntriesProvider stream leak: add ref.onDispose cancellation
flag so the async generator stops when the provider is disposed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/features/settings/widgets/mostro_node_selector.dart (1)

62-68: ⚠️ Potential issue | 🟡 Minor

Fire-and-forget async call lacks error handling.

settings_api.setMostroPubkey(pubkey: null) returns a Future but is not awaited and has no error handling. If the Rust bridge call fails, the UI will show the default pubkey while Rust retains the previous value, causing a state desync.

Consider awaiting with error handling and rolling back the UI state on failure:

♻️ Suggested improvement
-  void _useDefault() {
+  Future<void> _useDefault() async {
+    final previous = ref.read(mostroPubkeyProvider);
     ref.read(mostroPubkeyProvider.notifier).state = _defaultMostroPubkey;
-    settings_api.setMostroPubkey(pubkey: null);
+    try {
+      await settings_api.setMostroPubkey(pubkey: null);
+    } catch (e) {
+      ref.read(mostroPubkeyProvider.notifier).state = previous;
+      debugPrint('Failed to reset Mostro pubkey: $e');
+      return;
+    }
     _controller.clear();
     setState(() => _errorText = null);
     Navigator.of(context).pop();
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/settings/widgets/mostro_node_selector.dart` around lines 62 -
68, In _useDefault(), the async call settings_api.setMostroPubkey(pubkey: null)
is being fire-and-forgotten; change it to await the Future inside a try/catch so
failures can be detected, capture the current pubkey before mutating
ref.read(mostroPubkeyProvider.notifier).state so you can restore it on error,
and on failure setState to show an error (e.g. set _errorText) and avoid closing
the dialog (remove or defer Navigator.of(context).pop() until after success);
ensure you still clear _controller and reset UI only after the Rust bridge call
succeeds.
🧹 Nitpick comments (2)
rust/src/api/settings.rs (1)

183-201: Consider broadcasting pubkey changes for UI consistency.

Unlike other settings setters (e.g., set_theme, set_language), set_mostro_pubkey does not notify the SettingsStream subscribers. While this may be intentional since the pubkey isn't part of AppSettings, it creates an inconsistency — UI components watching on_settings_changed() won't receive updates when the Mostro node changes.

If subscribers should react to pubkey changes, consider either:

  1. Adding mostro_pubkey to AppSettings and broadcasting, or
  2. Creating a separate stream for pubkey changes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rust/src/api/settings.rs` around lines 183 - 201, set_mostro_pubkey currently
validates and writes the value via crate::config::set_active_mostro_pubkey but
does not notify SettingsStream subscribers (unlike set_theme/set_language),
causing UI inconsistency; update set_mostro_pubkey to emit a change event after
persisting the pubkey by either (A) adding mostro_pubkey to AppSettings and
calling the existing SettingsStream/on_settings_changed notification path so
subscribers see the update, or (B) creating a dedicated MostroPubkeyStream and
broadcasting the new value from set_mostro_pubkey so components can subscribe to
pubkey changes; reference set_mostro_pubkey,
crate::config::set_active_mostro_pubkey, AppSettings, SettingsStream, and
on_settings_changed when implementing the notification.
rust/src/api/logging.rs (1)

25-26: Unnecessary #[allow(dead_code)] on COUNTER.

COUNTER is actively used in forward_log() at line 60. The allow attribute is misleading.

-#[allow(dead_code)]
 static COUNTER: AtomicU32 = AtomicU32::new(0);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rust/src/api/logging.rs` around lines 25 - 26, The #[allow(dead_code)]
attribute on the static COUNTER is misleading because COUNTER is used by
forward_log(); remove the #[allow(dead_code)] annotation above the static
COUNTER declaration so the code accurately reflects usage and lets the compiler
warn if it ever becomes unused, leaving the static COUNTER: AtomicU32 =
AtomicU32::new(0) definition intact and ensuring forward_log() continues to
reference COUNTER.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/features/settings/widgets/mostro_node_selector.dart`:
- Around line 80-83: The _confirm() handler currently sets mostroPubkeyProvider
and calls settings_api.setMostroPubkey without awaiting or handling errors;
change it to await settings_api.setMostroPubkey inside a try/catch, only update
ref.read(mostroPubkeyProvider.notifier).state and call
Navigator.of(context).pop() on success, and on failure revert any local state
change (or avoid mutating state until after the await) and surface the error
(e.g., show a SnackBar or dialog) in the catch block so the UI rolls back on
Rust call failure.
- Around line 12-16: mostroPubkeyProvider currently always initializes from
_defaultMostroPubkey so a user-selected pubkey is lost on restart; fix by
persisting/restoring it: either add a mostroPubkey field to AppSettingsState and
include it in fromPrefs()/SharedPreferences read/write (and ensure
set_mostro_pubkey() writes to prefs when changed), or hydrate the provider at
app startup by calling settings_api.getMostroPubkey() from main.dart and setting
mostroPubkeyProvider accordingly so the Dart state matches the Rust bridge
persisted value; reference symbols: mostroPubkeyProvider, _defaultMostroPubkey,
AppSettingsState.fromPrefs, set_mostro_pubkey(), getMostroPubkey(),
settings_api.getMostroPubkey(), main.dart.

In `@rust/src/api/logging.rs`:
- Around line 28-49: Call install_log_bridge() from the app initialization path
so the logging bridge and LOG_STD_TX are initialized before any forward_log()
calls; specifically, add a call to crate::api::logging::install_log_bridge()
inside the init_app() function (the flutter_rust_bridge frb init hook) during
startup, placing it before the log::info!("[init] Rust core initialized") line
so the background thread and std mpsc sender are set up and log forwarding will
not drop entries.
- Around line 51-78: forward_log is never registered so production logs never
reach Flutter; implement a logger that uses forward_log and register it with the
log crate (or call forward_log directly at key sites). Create a struct
implementing log::Log (e.g., FlutterLogger) whose log(&self, record:
&log::Record) converts record.level/target/message into a LogEntry and calls
forward_log (or sends via LOG_STD_TX), then call
log::set_boxed_logger(Box::new(FlutterLogger)) and set the max_level via
log::set_max_level during init; alternatively, add explicit forward_log(...)
calls at strategic production emit points using the existing forward_log,
LOG_STD_TX, COUNTER, and LogEntry symbols.

In `@rust/src/frb_generated.rs`:
- Around line 2462-2495: The generated FFI binding
wire__crate__api__logging__install_log_bridge_impl currently exposes
install_log_bridge but the function is never invoked from startup; call
crate::api::logging::install_log_bridge() during app initialization (preferably
inside your Rust init_app() function) or ensure Dart invokes the exposed
installLogBridge() from main.dart before any forward_log() calls so the
std::sync::Once guard runs and the logging bridge is actually installed; update
init_app() to call install_log_bridge() (or add a Dart-side startup call to
installLogBridge()) and verify the bridge is initialized before forwarding logs.
- Line 48: The Dart bindings for the updated Rust FRB API are missing: run the
flutter_rust_bridge_codegen generate command to regenerate the Dart files for
the Rust API (so that lib/src/rust/api/logging.dart,
lib/src/rust/api/settings.dart, etc. are created), add and commit those
generated files alongside the Rust changes (ensure the generated files
import/reflect the symbols used in
lib/features/settings/providers/log_provider.dart and
lib/features/settings/widgets/mostro_node_selector.dart such as
logging_api.onLogEntry and settings_api.setMostroPubkey), and verify the
rust/src/frb_generated.rs hash and Dart bindings are in sync before pushing.

---

Outside diff comments:
In `@lib/features/settings/widgets/mostro_node_selector.dart`:
- Around line 62-68: In _useDefault(), the async call
settings_api.setMostroPubkey(pubkey: null) is being fire-and-forgotten; change
it to await the Future inside a try/catch so failures can be detected, capture
the current pubkey before mutating ref.read(mostroPubkeyProvider.notifier).state
so you can restore it on error, and on failure setState to show an error (e.g.
set _errorText) and avoid closing the dialog (remove or defer
Navigator.of(context).pop() until after success); ensure you still clear
_controller and reset UI only after the Rust bridge call succeeds.

---

Nitpick comments:
In `@rust/src/api/logging.rs`:
- Around line 25-26: The #[allow(dead_code)] attribute on the static COUNTER is
misleading because COUNTER is used by forward_log(); remove the
#[allow(dead_code)] annotation above the static COUNTER declaration so the code
accurately reflects usage and lets the compiler warn if it ever becomes unused,
leaving the static COUNTER: AtomicU32 = AtomicU32::new(0) definition intact and
ensuring forward_log() continues to reference COUNTER.

In `@rust/src/api/settings.rs`:
- Around line 183-201: set_mostro_pubkey currently validates and writes the
value via crate::config::set_active_mostro_pubkey but does not notify
SettingsStream subscribers (unlike set_theme/set_language), causing UI
inconsistency; update set_mostro_pubkey to emit a change event after persisting
the pubkey by either (A) adding mostro_pubkey to AppSettings and calling the
existing SettingsStream/on_settings_changed notification path so subscribers see
the update, or (B) creating a dedicated MostroPubkeyStream and broadcasting the
new value from set_mostro_pubkey so components can subscribe to pubkey changes;
reference set_mostro_pubkey, crate::config::set_active_mostro_pubkey,
AppSettings, SettingsStream, and on_settings_changed when implementing the
notification.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 076fe717-219e-4ff0-aa70-cd781aff22be

📥 Commits

Reviewing files that changed from the base of the PR and between 162d023 and 9594227.

📒 Files selected for processing (14)
  • lib/features/disputes/screens/dispute_chat_screen.dart
  • lib/features/settings/providers/log_provider.dart
  • lib/features/settings/screens/log_report_screen.dart
  • lib/features/settings/widgets/mostro_node_selector.dart
  • lib/features/settings/widgets/relay_management_card.dart
  • rust/src/api/disputes.rs
  • rust/src/api/logging.rs
  • rust/src/api/mod.rs
  • rust/src/api/orders.rs
  • rust/src/api/reputation.rs
  • rust/src/api/settings.rs
  • rust/src/config.rs
  • rust/src/frb_generated.rs
  • rust/src/nostr/relay_pool.rs

Comment on lines +12 to 16
/// Active Mostro node pubkey — synced to the Rust bridge so outgoing events
/// are routed to the selected node.
final mostroPubkeyProvider = StateProvider<String>(
(ref) => _defaultMostroPubkey,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if getMostroPubkey is called anywhere during app initialization
rg -n 'getMostroPubkey|get_mostro_pubkey' --type dart --type rust

Repository: MostroP2P/app

Length of output: 550


🏁 Script executed:

# Find main.dart and related initialization code
fd -n "main\.dart" --type f

Repository: MostroP2P/app

Length of output: 287


🏁 Script executed:

# Search for AppSettingsState definition
rg -n "class AppSettingsState|AppSettingsState\.fromPrefs" --type dart -A 10

Repository: MostroP2P/app

Length of output: 2841


🏁 Script executed:

# Check if getMostroPubkey is called in Dart code (binding bridge)
rg -n "getMostroPubkey|settingsApi" --type dart -B 2 -A 2

Repository: MostroP2P/app

Length of output: 39


🏁 Script executed:

# Examine the mostro_node_selector.dart file for the full provider context
cat -n lib/features/settings/widgets/mostro_node_selector.dart

Repository: MostroP2P/app

Length of output: 9291


🏁 Script executed:

# Search for getMostroPubkey in ALL Dart files (case-insensitive)
rg -i "getmostroPubkey" --type dart

Repository: MostroP2P/app

Length of output: 39


🏁 Script executed:

# Check main.dart initialization section more comprehensively
sed -n '45,80p' lib/main.dart

Repository: MostroP2P/app

Length of output: 1557


🏁 Script executed:

# Search for any references to mostroPubkeyProvider initialization or hydration
rg "mostroPubkeyProvider" --type dart -B 2 -A 2

Repository: MostroP2P/app

Length of output: 3948


🏁 Script executed:

# Check if settings_api.setMostroPubkey/getMostroPubkey are mentioned in Rust settings.rs
cat -n rust/src/api/settings.rs | grep -A 5 -B 5 "get_mostro_pubkey\|set_mostro_pubkey" | head -50

Repository: MostroP2P/app

Length of output: 922


Custom Mostro node selection is ephemeral—will reset on app restart.

The mostroPubkeyProvider initializes with _defaultMostroPubkey on every app launch. Although the Rust bridge persists the value via set_mostro_pubkey(), Dart never restores it: main.dart does not call getMostroPubkey() on startup, and AppSettingsState.fromPrefs() does not include mostroPubkey.

To persist across restarts, either:

  1. Add mostroPubkey to AppSettingsState and SharedPreferences persistence, or
  2. Call settings_api.getMostroPubkey() during app initialization to hydrate the provider from Rust's persisted value.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/settings/widgets/mostro_node_selector.dart` around lines 12 -
16, mostroPubkeyProvider currently always initializes from _defaultMostroPubkey
so a user-selected pubkey is lost on restart; fix by persisting/restoring it:
either add a mostroPubkey field to AppSettingsState and include it in
fromPrefs()/SharedPreferences read/write (and ensure set_mostro_pubkey() writes
to prefs when changed), or hydrate the provider at app startup by calling
settings_api.getMostroPubkey() from main.dart and setting mostroPubkeyProvider
accordingly so the Dart state matches the Rust bridge persisted value; reference
symbols: mostroPubkeyProvider, _defaultMostroPubkey, AppSettingsState.fromPrefs,
set_mostro_pubkey(), getMostroPubkey(), settings_api.getMostroPubkey(),
main.dart.

Comment thread lib/features/settings/widgets/mostro_node_selector.dart Outdated
Comment on lines +82 to +89
} catch (e) {
debugPrint('[RelayManagement] toggleRelay failed: $e');
if (!mounted) return;
setState(() {
final currentIndex = _relays.indexWhere((r) => r.url == url);
if (currentIndex != -1) {
_relays[currentIndex].isActive = !value;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle typed relay errors and stale rollback explicitly.

Lines 82-89 revert with !value for every exception. With non-idempotent relay ops (RelayAlreadyExists, LastRelay) and rapid re-toggles, an older failed request can overwrite a newer successful state and desync UI/backend state.

Proposed hardening
   } catch (e) {
     debugPrint('[RelayManagement] toggleRelay failed: $e');
     if (!mounted) return;
+    final err = e.toString();
+    // Enabling an already-present relay is effectively desired state.
+    if (value && err.contains('RelayAlreadyExists')) {
+      return;
+    }
     setState(() {
       final currentIndex = _relays.indexWhere((r) => r.url == url);
-      if (currentIndex != -1) {
+      // Avoid stale rollback if user already toggled again.
+      if (currentIndex != -1 && _relays[currentIndex].isActive == value) {
         _relays[currentIndex].isActive = !value;
       }
     });
+    await _loadRelays(); // resync from source of truth after failure
     final l10n = AppLocalizations.of(context);
     ScaffoldMessenger.of(context).showSnackBar(
       SnackBar(content: Text(value ? l10n.relayAddFailed : l10n.relayRemoveFailed)),
     );
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (e) {
debugPrint('[RelayManagement] toggleRelay failed: $e');
if (!mounted) return;
setState(() {
final currentIndex = _relays.indexWhere((r) => r.url == url);
if (currentIndex != -1) {
_relays[currentIndex].isActive = !value;
}
} catch (e) {
debugPrint('[RelayManagement] toggleRelay failed: $e');
if (!mounted) return;
final err = e.toString();
// Enabling an already-present relay is effectively desired state.
if (value && err.contains('RelayAlreadyExists')) {
return;
}
setState(() {
final currentIndex = _relays.indexWhere((r) => r.url == url);
// Avoid stale rollback if user already toggled again.
if (currentIndex != -1 && _relays[currentIndex].isActive == value) {
_relays[currentIndex].isActive = !value;
}
});
await _loadRelays(); // resync from source of truth after failure
final l10n = AppLocalizations.of(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(value ? l10n.relayAddFailed : l10n.relayRemoveFailed)),
);
}

Comment thread rust/src/api/logging.rs Outdated
Comment thread rust/src/api/logging.rs
Comment thread rust/src/frb_generated.rs
);
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.11.1";
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1150097857;
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -327029727;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

fd -i '(frb_generated|bridge_generated).*\.(dart)$' -x sh -c '
  echo "=== $1 ==="
  rg -n "installLogBridge|onLogEntry|getMostroPubkey|setMostroPubkey|2\\.11\\.1|-327029727" "$1" || true
' sh {}

Repository: MostroP2P/app

Length of output: 39


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# First, find all .dart files in the repository
echo "=== All .dart files in repository ==="
fd '\.dart$' | head -20

echo ""
echo "=== Total .dart file count ==="
fd '\.dart$' | wc -l

echo ""
echo "=== Check for Flutter/Dart directories ==="
fd -type d -i 'flutter|dart|lib' | head -20

Repository: MostroP2P/app

Length of output: 1242


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for the symbols across all Dart files
echo "=== Searching for logging bridge symbols in .dart files ==="
rg "installLogBridge|onLogEntry|getMostroPubkey|setMostroPubkey" --type dart || echo "No matches found"

echo ""
echo "=== Searching for FRB version or hash markers in .dart files ==="
rg "2\.11\.1|-327029727|FLUTTER_RUST_BRIDGE|flutter_rust_bridge" --type dart || echo "No matches found"

Repository: MostroP2P/app

Length of output: 3296


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check if this is a Flutter project and where bindings might be
echo "=== Checking for pubspec.yaml or build configuration ==="
find . -maxdepth 3 -name "pubspec.yaml" -o -name "pubspec.lock" | head -5

echo ""
echo "=== Checking for codegen configuration ==="
rg "build_runner|frb|flutter_rust_bridge" --type yaml --type toml --type json 2>/dev/null | head -20

Repository: MostroP2P/app

Length of output: 1305


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find where logging_api and settings_api are imported from
echo "=== Imports of logging_api and settings_api ==="
rg "import.*logging_api|import.*settings_api" --no-filename

echo ""
echo "=== Search for any generated Dart files with different naming ==="
fd '.*\.dart$' | grep -E "(generated|api|bridge)" | head -30

echo ""
echo "=== Look for the actual bindings file ==="
find . -name "*_api*" -o -name "*generated*" | grep '\.dart$' | head -20

Repository: MostroP2P/app

Length of output: 510


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for where the FRB symbols are defined
echo "=== Files containing installLogBridge definition or export ==="
rg "installLogBridge" -B 2 -A 2

echo ""
echo "=== Files containing getMostroPubkey definition or export ==="
rg "getMostroPubkey|get_mostro_pubkey" -B 1 -A 1

Repository: MostroP2P/app

Length of output: 119


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check the rust bindings generated file
echo "=== rust_bindings_generated.dart info ==="
wc -l ./rust_builder/lib/rust_bindings_generated.dart

echo ""
echo "=== Check for logging and settings functions in bindings ==="
rg "onLogEntry|getMostroPubkey|setMostroPubkey" ./rust_builder/lib/rust_bindings_generated.dart || echo "Not in bindings"

echo ""
echo "=== List all Dart files under src/rust/api ==="
find ./lib -path "*src/rust/api*" -name "*.dart" 2>/dev/null || echo "Not found under lib"

echo ""
echo "=== Check different src structure ==="
fd "api.*\.dart" | grep -i "logging\|settings" || echo "Not found"

Repository: MostroP2P/app

Length of output: 324


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the actual API files
echo "=== Find all files with logging.dart or settings.dart ==="
fd -i "logging\.dart|settings\.dart"

echo ""
echo "=== Content of logging API file ==="
cat "lib/src/rust/api/logging.dart" 2>/dev/null | head -50 || echo "File not at lib/src/rust/api/logging.dart"

echo ""
echo "=== Content of settings API file (first 50 lines) ==="
cat "lib/src/rust/api/settings.dart" 2>/dev/null | head -50 || echo "File not at lib/src/rust/api/settings.dart"

Repository: MostroP2P/app

Length of output: 345


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search more broadly for where these symbols might be
echo "=== All files in src/rust ==="
find . -path "*/src/rust*" -name "*.dart" 2>/dev/null

echo ""
echo "=== Search pubspec.yaml to understand the package structure ==="
grep -A 5 -B 5 "name:" pubspec.yaml

Repository: MostroP2P/app

Length of output: 322


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the generated bindings file
echo "=== Full content of rust_bindings_generated.dart ==="
cat ./rust_builder/lib/rust_bindings_generated.dart

echo ""
echo "=== Check flutter_rust_bridge.yaml config ==="
cat flutter_rust_bridge.yaml 2>/dev/null | head -50

echo ""
echo "=== Look for where src/rust/api files should be generated ==="
rg "output.*api|api.*output" --type yaml

Repository: MostroP2P/app

Length of output: 2657


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check what the actual package structure is for the main app
echo "=== Search lib directory structure ==="
ls -la lib/ | head -20

echo ""
echo "=== Look for any generated or FFI related files ==="
find lib -name "*generated*" -o -name "*ffi*" -o -name "*bridge*" 2>/dev/null | head -20

echo ""
echo "=== Check if there's a build or generated directory ==="
ls -la | grep -E "(build|generated|\.dart_tool)"

Repository: MostroP2P/app

Length of output: 668


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check if lib/src/rust exists
echo "=== Check lib/src directory ==="
ls -la lib/src/ 2>/dev/null || echo "lib/src does not exist"

echo ""
echo "=== Check if lib/src/rust exists ==="
ls -la lib/src/rust/ 2>/dev/null || echo "lib/src/rust does not exist"

echo ""
echo "=== Check if the expected generated files are missing ==="
ls -la lib/src/rust/api/ 2>/dev/null || echo "lib/src/rust/api directory does not exist"

echo ""
echo "=== Verify imports in log_provider.dart ==="
grep -n "import\|logging_api\|onLogEntry" lib/features/settings/providers/log_provider.dart | head -20

echo ""
echo "=== Verify imports in mostro_node_selector.dart ==="
grep -n "import\|settings_api\|setMostroPubkey" lib/features/settings/widgets/mostro_node_selector.dart | head -20

Repository: MostroP2P/app

Length of output: 981


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check the actual Rust generated file mentioned in the review
echo "=== Check if rust/src/frb_generated.rs exists ==="
ls -la rust/src/frb_generated.rs

echo ""
echo "=== Line 48 showing the hash ==="
sed -n '46,50p' rust/src/frb_generated.rs

echo ""
echo "=== Search for new dispatcher entries mentioned (5247-5445) ==="
sed -n '5247,5260p' rust/src/frb_generated.rs

echo ""
echo "=== Check if logging related handlers exist in frb_generated.rs ==="
rg "install_log_bridge|LogEntry|get_mostro_pubkey|set_mostro_pubkey" rust/src/frb_generated.rs | head -10

Repository: MostroP2P/app

Length of output: 2148


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check git status to see what was actually modified
echo "=== Recent changes to frb_generated.rs ==="
git log --oneline -10 -- rust/src/frb_generated.rs 2>/dev/null || echo "Not a git repo or file not tracked"

echo ""
echo "=== Check if Dart bindings are expected to be generated or committed ==="
git log --oneline -10 -- lib/src/rust/ 2>/dev/null || echo "lib/src/rust not tracked"

echo ""
echo "=== Check .gitignore for generated files ==="
cat .gitignore 2>/dev/null | grep -E "generated|lib/src" || echo "No matches in gitignore"

Repository: MostroP2P/app

Length of output: 477


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check the git status and what was actually changed in this PR
echo "=== Git diff stat for the commit ==="
git show --stat 9594227 2>/dev/null | head -50

echo ""
echo "=== Check if any Dart files in lib/src/rust were modified in this commit ==="
git show 9594227 -- lib/src/rust/ 2>/dev/null | head -50 || echo "No changes to lib/src/rust"

echo ""
echo "=== Full list of files changed in the commit ==="
git show --name-only 9594227 2>/dev/null | tail -30

Repository: MostroP2P/app

Length of output: 3010


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check what happens if we try to build or analyze
echo "=== Check if there are build scripts that generate these files ==="
find . -maxdepth 2 -type f \( -name "build.rs" -o -name "build.yaml" -o -name "Makefile" \) 2>/dev/null

echo ""
echo "=== Check flutter_rust_bridge.yaml for output config again ==="
grep -E "dart_output|rust_input" flutter_rust_bridge.yaml

echo ""
echo "=== Check if there's a README explaining the build process ==="
head -50 README.md 2>/dev/null | grep -i "generate\|build\|bridge" || echo "No matches in README"

echo ""
echo "=== Verify the actual imports are in use - check all imports from lib/src/rust/api ==="
rg "from 'package:mostro/src/rust/api" lib/ --type dart

Repository: MostroP2P/app

Length of output: 429


🏁 Script executed:

#!/bin/bash
set -euo pipefixo

# Get the complete list of files changed in the commit
echo "=== All files changed in commit 9594227 ==="
git show 9594227 --name-status 2>/dev/null | head -100

echo ""
echo "=== Specifically check for lib/src/rust files ==="
git show 9594227 --name-status 2>/dev/null | grep "lib/src/rust"

echo ""
echo "=== Check if lib/src/rust/api files were generated but ignored ==="
git show 9594227 -- lib/src/rust/api/logging.dart 2>/dev/null | head -20 || echo "File not in commit"

Repository: MostroP2P/app

Length of output: 110


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Since lib/src/rust is in .gitignore, check if the generated files exist in the working directory
echo "=== Actually list what's in lib/src/rust if it exists ==="
ls -laR lib/src/rust/ 2>/dev/null || echo "lib/src/rust directory doesn't exist in working directory"

echo ""
echo "=== Check if there's a build process that should generate it ==="
grep -r "flutter_rust_bridge_codegen generate" . --include="*.sh" --include="*.md" --include="*.yaml" --include="Makefile" 2>/dev/null | head -10

echo ""
echo "=== Check the build.rs file ==="
head -50 rust/build.rs

Repository: MostroP2P/app

Length of output: 1982


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check the commit more carefully
echo "=== Check what files were actually added/modified in this commit ==="
git diff 9594227^ 9594227 --name-status 2>/dev/null | grep -E "lib/src|rust/src/api" | head -20

echo ""
echo "=== Check if Dart files from api were REALLY not committed ==="
git show 9594227:lib/src/rust/api/logging.dart 2>/dev/null | head -20 || echo "File not in commit"

echo ""
echo "=== Verify README requirements ==="
grep -A 2 "Bridge changes" README.md

Repository: MostroP2P/app

Length of output: 125


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check if there's a setup/install script that generates these
echo "=== Look for setup or install scripts ==="
fd -i "setup|install" --type f | grep -E "\.(sh|dart)$" | head -10

echo ""
echo "=== Check pubspec.yaml for build scripts ==="
grep -E "build_runner|build|codegen|generate" pubspec.yaml

echo ""
echo "=== Understand the project structure better - check if files should exist at build time ==="
grep -B 5 -A 5 "lib/src/rust" .gitignore

Repository: MostroP2P/app

Length of output: 98


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check if git diff works differently
echo "=== Show the actual commit content ==="
git log -1 9594227 --stat 2>/dev/null | head -50

echo ""
echo "=== Check if api/logging.rs was added ==="
git show 9594227:rust/src/api/logging.rs 2>/dev/null | head -30 || echo "File not found in commit"

echo ""
echo "=== Verify the actual problem: search for where logging_api is coming from ==="
rg "pub.*onLogEntry|pub.*setMostroPubkey|pub.*getMostroPubkey" lib/ --type dart 2>/dev/null || echo "Not in lib"

Repository: MostroP2P/app

Length of output: 3012


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# The key question: are lib/src/rust files supposed to be generated at build time or committed?
echo "=== Check pubspec.yaml for build_runner configuration ==="
cat pubspec.yaml | grep -A 10 "build_runner\|dev_dependencies"

echo ""
echo "=== Look for build.yaml or other codegen config in lib ==="
find lib -name "build.yaml" -o -name "pubspec_overrides.yaml" 2>/dev/null

echo ""
echo "=== Check if there's a .dart_tool directory (generated at runtime) ==="
ls -la .dart_tool/ 2>/dev/null | head -10 || echo "No .dart_tool directory"

Repository: MostroP2P/app

Length of output: 556


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Final verification - see if the git log shows the full file list
echo "=== Full file list from commit (might be truncated earlier) ==="
git show 9594227 --name-only 2>/dev/null | tail -100

echo ""
echo "=== Count of files in the commit ==="
git show 9594227 --name-only 2>/dev/null | wc -l

echo ""
echo "=== Specific check: was rust/src/api/logging.rs added? ==="
git show 9594227:rust/src/api/logging.rs 2>/dev/null | wc -l || echo "File not in commit"

Repository: MostroP2P/app

Length of output: 4814


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify if lib/src/rust was ignored in this commit
echo "=== Current .gitignore entry ==="
cat .gitignore | grep "lib/src/rust"

echo ""
echo "=== What the README says about committing generated bindings ==="
grep -B 2 -A 3 "Commit the generated files" README.md

echo ""
echo "=== Try to understand if the imports would even work ==="
echo "Can Dart resolve these imports at compile time if files don't exist?"
echo "Files needed:"
echo "  - lib/src/rust/api/logging.dart (used in log_provider.dart and settings/widgets)"
echo "  - lib/src/rust/api/settings.dart (used in mostro_node_selector.dart)"
echo "  - lib/src/rust/api/types.dart (used in log_provider.dart)"
echo ""
echo "Actual files found:"
find lib/src -type f 2>/dev/null || echo "lib/src does not exist"

Repository: MostroP2P/app

Length of output: 1333


Dart FRB generated bindings were not committed; this will cause Dart compilation failure.

The commit updated rust/src/api/logging.rs and regenerated rust/src/frb_generated.rs (line 48 hash now -327029727), but the matching Dart binding files (lib/src/rust/api/logging.dart, lib/src/rust/api/settings.dart, etc.) do not exist. Dart code in lib/features/settings/providers/log_provider.dart and lib/features/settings/widgets/mostro_node_selector.dart imports from these non-existent files:

import 'package:mostro/src/rust/api/logging.dart' as logging_api;
import 'package:mostro/src/rust/api/settings.dart' as settings_api;

and uses them (logging_api.onLogEntry(), settings_api.setMostroPubkey()). Although lib/src/rust/ is in .gitignore, the README states: "Any modification to rust/src/api/ requires re-running flutter_rust_bridge_codegen generate. Commit the generated files together with the Rust changes."

Run flutter_rust_bridge_codegen generate locally and commit the generated files in lib/src/rust/.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rust/src/frb_generated.rs` at line 48, The Dart bindings for the updated Rust
FRB API are missing: run the flutter_rust_bridge_codegen generate command to
regenerate the Dart files for the Rust API (so that
lib/src/rust/api/logging.dart, lib/src/rust/api/settings.dart, etc. are
created), add and commit those generated files alongside the Rust changes
(ensure the generated files import/reflect the symbols used in
lib/features/settings/providers/log_provider.dart and
lib/features/settings/widgets/mostro_node_selector.dart such as
logging_api.onLogEntry and settings_api.setMostroPubkey), and verify the
rust/src/frb_generated.rs hash and Dart bindings are in sync before pushing.

Comment thread rust/src/frb_generated.rs
Make _confirm() and _useDefault() in MostroNodeSelector async, await
the Rust bridge call, and rollback mostroPubkeyProvider on failure
with user-visible error text.

Remove stale #[allow(dead_code)] on COUNTER in logging.rs.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
rust/src/lib.rs (1)

21-30: ⚠️ Potential issue | 🔴 Critical

Log bridge will silently fail to install on Android due to logger initialization order.

Rust's log::set_logger() can only succeed once per process. On Android:

  1. android_logger::init_once() registers its logger first (line 22-26).
  2. install_log_bridge() then calls log::set_logger(&BRIDGE_LOGGER) which fails—the error is discarded with let _ = (context snippet 1, line 54).

Result: On Android, logs are not forwarded to the Flutter on_log_entry() stream, defeating the purpose of this change.

Since BridgeLogger already handles Android output via eprintln! (context snippet 2, lines 79-86), you can remove the android_logger::init_once() call entirely, or reverse the order so the bridge is installed first.

Proposed fix: Remove redundant android_logger initialization
 #[flutter_rust_bridge::frb(init)]
 pub fn init_app() {
-    #[cfg(target_os = "android")]
-    android_logger::init_once(
-        android_logger::Config::default()
-            .with_max_level(log::LevelFilter::Debug)
-            .with_tag("mostro_rust"),
-    );
-
     // Install the log bridge so every log::info!/warn!/error! is forwarded
     // to the Flutter on_log_entry() stream.
     api::logging::install_log_bridge();

     log::info!("[init] Rust core initialized");
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rust/src/lib.rs` around lines 21 - 30, The android_logger::init_once() call
is installed before api::logging::install_log_bridge(), causing
log::set_logger() in install_log_bridge() (which sets
BRIDGE_LOGGER/BridgeLogger) to fail silently on Android; remove the
android_logger::init_once() invocation (or alternatively move it after
api::logging::install_log_bridge()) so that install_log_bridge() can
successfully call log::set_logger(&BRIDGE_LOGGER) and forward logs to the
Flutter on_log_entry() stream.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@rust/src/lib.rs`:
- Around line 21-30: The android_logger::init_once() call is installed before
api::logging::install_log_bridge(), causing log::set_logger() in
install_log_bridge() (which sets BRIDGE_LOGGER/BridgeLogger) to fail silently on
Android; remove the android_logger::init_once() invocation (or alternatively
move it after api::logging::install_log_bridge()) so that install_log_bridge()
can successfully call log::set_logger(&BRIDGE_LOGGER) and forward logs to the
Flutter on_log_entry() stream.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b689c6ba-0ecc-447e-99c1-0c5a5b794824

📥 Commits

Reviewing files that changed from the base of the PR and between 9594227 and bd1630f.

📒 Files selected for processing (3)
  • lib/features/settings/providers/log_provider.dart
  • rust/src/api/logging.rs
  • rust/src/lib.rs
✅ Files skipped from review due to trivial changes (1)
  • lib/features/settings/providers/log_provider.dart
🚧 Files skipped from review as they are similar to previous changes (1)
  • rust/src/api/logging.rs

android_logger::init_once() called log::set_logger() before
install_log_bridge(), causing BridgeLogger registration to silently
fail on Android. Remove android_logger — BridgeLogger prints to
stderr which logcat captures, and forwards to the Flutter stream.
@grunch
grunch merged commit 32e73c6 into main Apr 4, 2026
1 check was pending
@grunch
grunch deleted the fix/multiple-fixes branch April 4, 2026 17:28
@coderabbitai coderabbitai Bot mentioned this pull request Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant