Skip to content

fix(router): eliminate first-run race condition on startup - #72

Merged
grunch merged 8 commits into
mainfrom
fix/first-run
Apr 1, 2026
Merged

fix(router): eliminate first-run race condition on startup#72
grunch merged 8 commits into
mainfrom
fix/first-run

Conversation

@grunch

@grunch grunch commented Apr 1, 2026

Copy link
Copy Markdown
Member

Pre-read SharedPreferences in main() before runApp() and inject firstRunProvider and backupReminderProvider with synchronous initial values via ProviderScope.overrides.

Previously both notifiers started as AsyncValue.loading(), causing the router to skip the walkthrough redirect on first launch and incorrectly routing bell taps to /walkthrough instead of /notifications.

Summary by CodeRabbit

  • New Features

    • Secret words fully masked by default; tapping Show reveals all words plus an explicit backup-confirmation checkbox.
    • Backup reminder becomes a pinned notification; bell shows a red-dot until the checkbox is confirmed.
    • New identity generation is performed early and can regenerate identity from app UI.
  • Refactor

    • App startup now preloads identity and preferences so initial screens reflect backup/first-run state immediately.

Pre-read SharedPreferences in main() before runApp() and inject
firstRunProvider and backupReminderProvider with synchronous initial
values via ProviderScope.overrides.

Previously both notifiers started as AsyncValue.loading(), causing
the router to skip the walkthrough redirect on first launch and
incorrectly routing bell taps to /walkthrough instead of /notifications.
@coderabbitai

coderabbitai Bot commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@grunch has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 37 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 4 minutes and 37 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bb6efa5b-acc3-4ad3-a732-69773f57f8e1

📥 Commits

Reviewing files that changed from the base of the PR and between 5d3df3d and 2a6b8c5.

📒 Files selected for processing (2)
  • lib/features/account/screens/account_screen.dart
  • specs/004-mostro-p2p-client/plan.md

Walkthrough

Providers now accept optional synchronous initial values to avoid automatic async loads. main() preloads SharedPreferences and initializes IdentityService before nostr, derives initial flags, and starts the app with ProviderScope overrides for first-run and backup state. Account UI reads identity via IdentityService, fully masks mnemonics by default, and requires explicit user checkbox to confirm backup.

Changes

Cohort / File(s) Summary
Provider constructors & keys
lib/features/account/providers/backup_reminder_provider.dart, lib/features/walkthrough/providers/first_run_provider.dart
Constructors now accept bool? initialValue and initialize synchronously when provided (skipping async .load()/_load()). Private SharedPreferences key constants were renamed to exported k* key identifiers.
App startup & provider seeding
lib/main.dart
Preloads SharedPreferences, awaits IdentityService.initialize(), derives firstRunComplete and backupPending, and uses ProviderScope overrides to seed FirstRunNotifier(initialValue: ...) and BackupReminderNotifier(initialValue: ...) before runApp.
Identity service
lib/core/services/identity_service.dart
New IdentityService added: secure-storage-backed initialize/load/create/regenerate/delete APIs, getters for mnemonic words, and persistence helpers (including Android encrypted prefs config).
Account screen UI & flows
lib/features/account/screens/account_screen.dart
Now uses IdentityService.getMnemonicWords(), shows fully-masked 12-word view by default, reveals words on user action, displays animated backup-confirmation checkbox, requires explicit _confirmBackup() to mark backup complete, and updates "Generate New User" to call IdentityService.regenerate().
Specs / docs / metadata
specs/..., CLAUDE.md
Specs updated for first-launch identity, pinned non-dismissible backup reminder, explicit checkbox confirmation UX, notification bell behavior; minor CLAUDE.md date/duplication edits.

Sequence Diagram(s)

sequenceDiagram
    participant App as App Startup
    participant Prefs as SharedPreferences
    participant Identity as IdentityService
    participant Nostr as Nostr API
    participant Providers as Riverpod Providers
    participant UI as Router/Widgets

    App->>Prefs: preload SharedPreferences
    Prefs-->>App: return flags (firstRunComplete, backupDismissed, backupActive)
    App->>App: compute backupPending = backupActive && !backupDismissed
    App->>Identity: initialize()
    Identity-->>App: mnemonic / identity ready
    App->>Nostr: initialize(with identity keys)
    Nostr-->>App: nostr ready
    App->>Providers: ProviderScope(overrides: FirstRunNotifier(initialValue), BackupReminderNotifier(initialValue))
    Providers-->>App: providers created with sync initial state
    App->>UI: runApp (UI starts without initial loading)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Poem

🐰 I nibble prefs before the dawn,
A seed of identity safely drawn,
Words tucked tight, all dots in place,
Tick the box — I hop with grace.
Providers awake, the bell finds peace.

🚥 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 accurately describes the main objective of the changeset: eliminating a first-run race condition by pre-reading SharedPreferences and injecting provider initial values in main().
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/first-run

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.

@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.

🧹 Nitpick comments (2)
lib/features/account/providers/backup_reminder_provider.dart (1)

17-21: Consider setting _loaded = true when initialValue is provided.

When initialValue is non-null, load() is skipped in the constructor but _loaded remains false. If showBackupReminder() is later called, it will invoke await load() which will re-read from SharedPreferences and potentially overwrite the state with the same (or stale) values.

While this likely won't cause functional issues since the values are pre-read from the same source, setting _loaded = true when an initial value is provided would be more semantically correct and avoid unnecessary SharedPreferences reads.

♻️ Suggested improvement
   BackupReminderNotifier({bool? initialValue}) : super(initialValue ?? false) {
-    if (initialValue == null) load();
+    if (initialValue == null) {
+      load();
+    } else {
+      _loaded = true;
+    }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/account/providers/backup_reminder_provider.dart` around lines 17
- 21, The constructor for BackupReminderNotifier currently skips load() when an
initialValue is provided but leaves _loaded false; update the
BackupReminderNotifier constructor so that when initialValue is non-null it also
sets _loaded = true to reflect that state is already initialized, preventing
later calls (e.g., showBackupReminder()) from needlessly invoking load() and
re-reading SharedPreferences.
lib/main.dart (1)

24-28: Extract SharedPreferences keys to shared constants to avoid duplication.

The key strings ('firstRunComplete', 'backupReminderDismissed', 'backupReminderActive') are duplicated here and in the provider files. If someone updates a constant in a provider file but forgets to update main.dart, the pre-read values won't match the values the providers read/write, potentially reintroducing the race condition or causing subtle bugs.

Consider exporting the key constants from the provider files or defining them in a shared location.

♻️ Example: export constants from provider files

In first_run_provider.dart:

-const _kFirstRunComplete = 'firstRunComplete';
+const kFirstRunComplete = 'firstRunComplete';

In backup_reminder_provider.dart:

-const _kBackupReminderDismissed = 'backupReminderDismissed';
-const _kBackupReminderActive = 'backupReminderActive';
+const kBackupReminderDismissed = 'backupReminderDismissed';
+const kBackupReminderActive = 'backupReminderActive';

Then in main.dart:

-  final firstRunComplete = prefs.getBool('firstRunComplete') ?? false;
-  final backupDismissed = prefs.getBool('backupReminderDismissed') ?? false;
-  final backupActive = prefs.getBool('backupReminderActive') ?? false;
+  final firstRunComplete = prefs.getBool(kFirstRunComplete) ?? false;
+  final backupDismissed = prefs.getBool(kBackupReminderDismissed) ?? false;
+  final backupActive = prefs.getBool(kBackupReminderActive) ?? false;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/main.dart` around lines 24 - 28, Extract the SharedPreferences key
strings into shared constants and import them into main.dart instead of using
literal strings: define/export constants (e.g. FIRST_RUN_COMPLETE_KEY,
BACKUP_REMINDER_DISMISSED_KEY, BACKUP_REMINDER_ACTIVE_KEY) from the provider
modules (first_run_provider.dart and backup_reminder_provider.dart) or a new
shared constants file, update all prefs.getBool calls in main.dart (the block
using SharedPreferences.getInstance() and prefs.getBool(...)) to use these
constants, and update the providers to reference the same constants so there is
a single source of truth for those keys.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@lib/features/account/providers/backup_reminder_provider.dart`:
- Around line 17-21: The constructor for BackupReminderNotifier currently skips
load() when an initialValue is provided but leaves _loaded false; update the
BackupReminderNotifier constructor so that when initialValue is non-null it also
sets _loaded = true to reflect that state is already initialized, preventing
later calls (e.g., showBackupReminder()) from needlessly invoking load() and
re-reading SharedPreferences.

In `@lib/main.dart`:
- Around line 24-28: Extract the SharedPreferences key strings into shared
constants and import them into main.dart instead of using literal strings:
define/export constants (e.g. FIRST_RUN_COMPLETE_KEY,
BACKUP_REMINDER_DISMISSED_KEY, BACKUP_REMINDER_ACTIVE_KEY) from the provider
modules (first_run_provider.dart and backup_reminder_provider.dart) or a new
shared constants file, update all prefs.getBool calls in main.dart (the block
using SharedPreferences.getInstance() and prefs.getBool(...)) to use these
constants, and update the providers to reference the same constants so there is
a single source of truth for those keys.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 970f5edf-7f98-4b97-895e-ecdba7b67fe3

📥 Commits

Reviewing files that changed from the base of the PR and between 8fd2615 and bb98b40.

📒 Files selected for processing (3)
  • lib/features/account/providers/backup_reminder_provider.dart
  • lib/features/walkthrough/providers/first_run_provider.dart
  • lib/main.dart

…flow

- Add IdentityService to manage identity lifecycle (create on first
  launch, reload on subsequent launches) via flutter_secure_storage
- Initialize identity in main() before Nostr relay pool setup
- Account screen: display real mnemonic words from secure storage
  instead of placeholder, fully mask words by default
- Show backup checkbox alongside words only when backup is still pending;
  checkbox is the sole confirmation method and is permanent once ticked
- Reset words/checkbox state when generating a new identity

@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: 3

🧹 Nitpick comments (2)
lib/features/account/screens/account_screen.dart (1)

305-321: Add error handling for identity regeneration to prevent inconsistent state.

The sequence deleteAll()initialize()showBackupReminder() can fail at any step. If deleteAll() succeeds but initialize() fails, the user loses their identity with no recovery path. Consider wrapping in try-catch with user feedback and rollback strategy.

🛡️ Proposed error handling
 onPressed: () async {
   Navigator.pop(context);
   // Reset local state before generating new identity.
   setState(() {
     _wordsVisible = false;
     _showBackupCheckbox = false;
     _words = null;
   });
-  // Delete old secure storage data, then generate + store new identity.
-  await IdentityService.deleteAll();
-  await IdentityService.initialize();
-  await ref
-      .read(backupReminderProvider.notifier)
-      .showBackupReminder();
+  try {
+    // Delete old secure storage data, then generate + store new identity.
+    await IdentityService.deleteAll();
+    await IdentityService.initialize();
+    await ref
+        .read(backupReminderProvider.notifier)
+        .showBackupReminder();
+  } catch (e) {
+    if (!context.mounted) return;
+    ScaffoldMessenger.of(context).showSnackBar(
+      SnackBar(content: Text('Failed to generate new identity: $e')),
+    );
+    return;
+  }
   if (!context.mounted) return;
   context.go(AppRoute.walkthrough);
 },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/account/screens/account_screen.dart` around lines 305 - 321,
Wrap the identity rotation sequence inside a try-catch and avoid mutating
UI/navigation until it succeeds: in the onPressed handler, perform the
operations in a guarded flow (call IdentityService.deleteAll() and
IdentityService.initialize() inside try), and on success run the existing
backupReminderProvider.showBackupReminder(), setState updates, Navigator.pop and
context.go(AppRoute.walkthrough); on failure catch the exception, log it,
present user-facing feedback (Snackbar/Dialog) and do not navigate away; attempt
a recovery step such as calling IdentityService.initialize() again or invoking a
restore/rollback method if available, and ensure setState does not clear
_words/_showBackupCheckbox unless the new identity was created successfully.
lib/core/services/identity_service.dart (1)

43-67: Consider adding error handling for secure storage operations.

The storage read/write/delete operations can throw exceptions (e.g., if the keychain/keystore is locked or corrupted). Currently, these exceptions would propagate to callers. This is acceptable if callers handle errors, but consider wrapping in try-catch with logging for better diagnostics during debugging.

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

In `@lib/core/services/identity_service.dart` around lines 43 - 67, Wrap all
secure storage operations on _storage in try-catch blocks to capture and log
exceptions (including the key and the caught exception) rather than letting them
silently propagate; for getMnemonicWords (use _kMnemonic) catch errors, log them
and return an empty list as a safe default, for saveTradeKeyIndex (use
_kTradeKeyIndex) and savePrivacyMode (use _kPrivacyMode) catch and log errors
and rethrow if you want callers to handle failures (or return/complete silently
if you prefer non-fatal), and for deleteAll catch and log failures per delete
call so one failing delete doesn't prevent attempting the others; use your
existing logger or create a simple logger instance and include the function name
(getMnemonicWords, saveTradeKeyIndex, savePrivacyMode, deleteAll) and key names
in the log messages for diagnostics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CLAUDE.md`:
- Line 11: Remove the duplicated Active Technologies bullet that repeats the
exact text "Sembast (Dart, all platforms) for UI-layer state; SQLite via `sqlx`
(Rust, native) / IndexedDB via `indexed_db_futures` (Rust, web) for
protocol-layer persistence. Feature-gated via `#[cfg(target_arch =
\"wasm32\")]`. (004-mostro-p2p-client)"; keep a single instance, ensure
surrounding bullets/spacing remain consistent, and run a quick scan for any
other accidental verbatim duplicates in the file.
- Line 33: Remove the duplicated Recent Changes entry by deleting the extra line
that reads "- 004-mostro-p2p-client: Added Rust stable 1.94+ (core); Dart 3.x /
Flutter 3.x (UI shell)" so that only one identical entry remains in the Recent
Changes section; locate the duplicate string in the CLAUDE.md Recent Changes
block and remove the redundant occurrence.

In `@specs/004-mostro-p2p-client/plan.md`:
- Around line 225-227: The plan.md file describes Rust-side backup confirmation
APIs and persistence that do not exist in the current implementation, which
instead manages backup state entirely in Dart with SharedPreferences and the
BackupReminderNotifier. To fix this, clarify the plan by indicating that the
described Rust APIs and persistence are planned future work or update the
plan.md content so it accurately reflects the current Dart-based architecture
using BackupReminderNotifier and backupReminderProvider instead of Rust-side
features.

---

Nitpick comments:
In `@lib/core/services/identity_service.dart`:
- Around line 43-67: Wrap all secure storage operations on _storage in try-catch
blocks to capture and log exceptions (including the key and the caught
exception) rather than letting them silently propagate; for getMnemonicWords
(use _kMnemonic) catch errors, log them and return an empty list as a safe
default, for saveTradeKeyIndex (use _kTradeKeyIndex) and savePrivacyMode (use
_kPrivacyMode) catch and log errors and rethrow if you want callers to handle
failures (or return/complete silently if you prefer non-fatal), and for
deleteAll catch and log failures per delete call so one failing delete doesn't
prevent attempting the others; use your existing logger or create a simple
logger instance and include the function name (getMnemonicWords,
saveTradeKeyIndex, savePrivacyMode, deleteAll) and key names in the log messages
for diagnostics.

In `@lib/features/account/screens/account_screen.dart`:
- Around line 305-321: Wrap the identity rotation sequence inside a try-catch
and avoid mutating UI/navigation until it succeeds: in the onPressed handler,
perform the operations in a guarded flow (call IdentityService.deleteAll() and
IdentityService.initialize() inside try), and on success run the existing
backupReminderProvider.showBackupReminder(), setState updates, Navigator.pop and
context.go(AppRoute.walkthrough); on failure catch the exception, log it,
present user-facing feedback (Snackbar/Dialog) and do not navigate away; attempt
a recovery step such as calling IdentityService.initialize() again or invoking a
restore/rollback method if available, and ensure setState does not clear
_words/_showBackupCheckbox unless the new identity was created successfully.
🪄 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: 3ea265bb-5f37-4366-807d-565da1cf583b

📥 Commits

Reviewing files that changed from the base of the PR and between bb98b40 and 7b32302.

📒 Files selected for processing (6)
  • CLAUDE.md
  • lib/core/services/identity_service.dart
  • lib/features/account/screens/account_screen.dart
  • specs/004-mostro-p2p-client/plan.md
  • specs/004-mostro-p2p-client/spec.md
  • specs/004-mostro-p2p-client/tasks.md
✅ Files skipped from review due to trivial changes (1)
  • specs/004-mostro-p2p-client/tasks.md

Comment thread CLAUDE.md Outdated
Comment thread CLAUDE.md Outdated
Comment thread specs/004-mostro-p2p-client/plan.md Outdated
grunch added 2 commits April 1, 2026 09:23
- CLAUDE.md: remove duplicate Active Technologies and Recent Changes entries
- plan.md: clarify backup_confirmed is currently Dart/SharedPreferences,
  Rust-side APIs are planned future work
- IdentityService: wrap all secure storage calls in try-catch with
  diagnostic logging; deleteAll continues deleting remaining keys
  even if one fails
- AccountScreen: guard identity rotation in try-catch; only clear state
  and navigate on success, show SnackBar on failure
…re-seeded

- Expose kFirstRunCompleteKey, kBackupReminderDismissedKey,
  kBackupReminderActiveKey as public constants so main() shares a
  single source of truth instead of duplicating literal strings
- Set _loaded = true in BackupReminderNotifier when initialValue is
  provided, preventing showBackupReminder() from re-reading
  SharedPreferences unnecessarily

@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.

🧹 Nitpick comments (1)
lib/features/account/screens/account_screen.dart (1)

322-330: Consider showing a user-friendly error message instead of raw exception.

Exposing the raw exception $e in the SnackBar may leak internal implementation details or confusing technical jargon to users. The debugPrint already logs the technical details for debugging.

💡 Suggested improvement
              } catch (e) {
                debugPrint('[account] generateNewUser error: $e');
                if (!context.mounted) return;
                ScaffoldMessenger.of(context).showSnackBar(
-                  SnackBar(
-                    content: Text('Failed to generate new identity: $e'),
-                  ),
+                  const SnackBar(
+                    content: Text('Failed to generate new identity. Please try again.'),
+                  ),
                );
              }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/account/screens/account_screen.dart` around lines 322 - 330, The
catch block handling generateNewUser currently shows the raw exception in the
SnackBar; keep the debugPrint(debugPrint('[account] generateNewUser error: $e'))
for diagnostics but change the ScaffoldMessenger.of(context).showSnackBar
invocation to display a user-friendly message (e.g., "Failed to generate
identity. Please try again.") instead of "$e"; optionally include the raw error
only in debug builds (wrap extra details with kDebugMode) and keep the early
context.mounted check as-is.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@lib/features/account/screens/account_screen.dart`:
- Around line 322-330: The catch block handling generateNewUser currently shows
the raw exception in the SnackBar; keep the debugPrint(debugPrint('[account]
generateNewUser error: $e')) for diagnostics but change the
ScaffoldMessenger.of(context).showSnackBar invocation to display a user-friendly
message (e.g., "Failed to generate identity. Please try again.") instead of
"$e"; optionally include the raw error only in debug builds (wrap extra details
with kDebugMode) and keep the early context.mounted check as-is.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 55f4bcb9-0912-49d4-be07-d3b3583702f6

📥 Commits

Reviewing files that changed from the base of the PR and between 7b32302 and f87f562.

📒 Files selected for processing (4)
  • CLAUDE.md
  • lib/core/services/identity_service.dart
  • lib/features/account/screens/account_screen.dart
  • specs/004-mostro-p2p-client/plan.md
✅ Files skipped from review due to trivial changes (1)
  • CLAUDE.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • specs/004-mostro-p2p-client/plan.md
  • lib/core/services/identity_service.dart

grunch added 3 commits April 1, 2026 09:35
identity_service.dart:
- Write mnemonic to secure storage before metadata so a partial failure
  never leaves the user without recoverable keys
- Add regenerate(): creates new identity and overwrites storage atomically,
  replacing the deleteAll→initialize sequence that had a data-loss window

account_screen.dart:
- _loadAndRevealWords: add try/catch; surface a SnackBar on error or when
  no identity is found instead of silently showing stale words
- _confirmBackup: wrap in try/catch with SnackBar on failure
- _confirmGenerateNewUser: use IdentityService.regenerate() so the old
  identity is only replaced once the new one is safely persisted
- _BackupConfirmRow: change onConfirm to Future<void> Function(); only
  lock the checkbox on success, show a spinner while pending, leave
  unchecked on failure so the user can retry
spec.md:
- Add FR-002a: define blocking error behavior when secure storage
  persistence fails on first launch

plan.md:
- Resolve contradiction between objectives (Dart) and Key Files (Rust):
  Key Files now documents the current Dart/SharedPreferences implementation
  and marks Rust API additions as planned future work
- Update Constitution compliance to reflect Dart-based tracking
- Update Acceptance Tests to reference Dart symbols

tasks.md:
- T147: add l10n key requirement for the backup checkbox label
Show a user-friendly message in production; include the exception
detail only in debug builds via kDebugMode.

@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: 3

🤖 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/account/screens/account_screen.dart`:
- Around line 54-59: In the catch blocks (e.g., in _loadAndRevealWords and the
other error handler around lines 73-75) stop showing the raw exception ($e) in
the SnackBar in release builds: import flutter/foundation and use kDebugMode to
decide message text — keep debugPrint or processLogger logging with the full
error, but pass a generic user-facing string (e.g., "Failed to load secret
words" or "An error occurred") to ScaffoldMessenger.of(context).showSnackBar
when not kDebugMode; when kDebugMode is true include the exception details in
the SnackBar for debugging.
- Around line 66-77: The try/catch in _confirmBackup is swallowing errors and
causing _BackupConfirmRowState._handleConfirm to treat failures as success;
remove the catch (or rethrow the caught exception) so that errors from
ref.read(backupReminderProvider.notifier).confirmBackupComplete() propagate to
the caller, and only update UI state (_showBackupCheckbox) after the awaited
call completes successfully; reference the _confirmBackup function,
confirmBackupComplete on backupReminderProvider.notifier, and the
_showBackupCheckbox state to locate and fix the logic.

In `@specs/004-mostro-p2p-client/plan.md`:
- Around line 284-285: Update the checklist so it reflects the new behavior
where the pending-backup state is already active after the first identity is
created: change the line referring to "Fresh install: identity is generated
before walkthrough renders; `backupReminderProvider` is `false`" to indicate
that `backupReminderProvider` is true (or mark that checklist item as already
satisfied/checked) so the spec matches the implementation that sets
pending-backup immediately after identity creation.
🪄 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: c3526449-7099-48d1-b44a-33c042e42f9e

📥 Commits

Reviewing files that changed from the base of the PR and between f87f562 and 5d3df3d.

📒 Files selected for processing (8)
  • lib/core/services/identity_service.dart
  • lib/features/account/providers/backup_reminder_provider.dart
  • lib/features/account/screens/account_screen.dart
  • lib/features/walkthrough/providers/first_run_provider.dart
  • lib/main.dart
  • specs/004-mostro-p2p-client/plan.md
  • specs/004-mostro-p2p-client/spec.md
  • specs/004-mostro-p2p-client/tasks.md
✅ Files skipped from review due to trivial changes (1)
  • specs/004-mostro-p2p-client/tasks.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • lib/main.dart
  • lib/features/account/providers/backup_reminder_provider.dart
  • lib/features/walkthrough/providers/first_run_provider.dart

Comment thread lib/features/account/screens/account_screen.dart
Comment thread lib/features/account/screens/account_screen.dart
Comment thread specs/004-mostro-p2p-client/plan.md Outdated
  - _loadAndRevealWords catch: raw $e now only shown in debug builds
  - _confirmBackup: adds rethrow after showing the SnackBar — _handleConfirm now correctly sees the failure and leaves the checkbox unchecked for retry
  - plan.md line 284: checklist item corrected — backupReminderProvider is true (not false) after walkthrough completes
@grunch
grunch merged commit 4d60fe9 into main Apr 1, 2026
1 check passed
@grunch
grunch deleted the fix/first-run branch April 1, 2026 13:10
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