Skip to content

feat(reputation): add the reputation settings screen - #677

Closed
grunch wants to merge 6 commits into
mainfrom
feat/reputation-settings-ui
Closed

grunch wants to merge 6 commits into
mainfrom
feat/reputation-settings-ui

Conversation

@grunch

@grunch grunch commented Oct 3, 2026

Copy link
Copy Markdown
Member

Linked issue

Part of #674 (phase 5b, PR 5b.3), the last app PR of the plan. It is stacked on #676 (5b.2).

Type

feat

What changes and why

Settings gains a Reputation row in the Network group, leading to the new ReputationScreen (AppRoute.reputation):

  • Import: an explanation and a button that opens the import screen (5b.2).
  • Export. It appears only when the node advertises reputation_issuer (get_reputation_support); otherwise the screen says the node does not export.
    • Confirm. "Export my reputation" first opens a MostroDialog that shows the user's identity as an npub (npub.dart, NIP-19 bech32) and explains that the export binds this node's account to it. This is the confirmation the protocol requires before a first binding.
    • Result. On success the attestation is shown to copy, and it is also kept as pending (5b.1). A refusal is shown localized.
  • Moving to a new identity:
  • Privacy mode: a notice explains why both directions are off, and the buttons are disabled.

Affected flow

  • New files: lib/features/reputation/screens/reputation_screen.dart and npub.dart.
  • Settings: one SettingsRow in settings_screen.dart, with id settings.reputation.
  • Strings: 12 new ones, in all six ARB files (gen-l10n committed, nothing untranslated).
  • Automation ids: six reputation.* ids plus the settings row, all attached.
  • Rust: sign_reputation_rebind accepts an npub. The change is body-only, so the generated code is unchanged.

Blast radius

  • Settings goldens. The new row changes 10a_settings; I triggered update-goldens.yml on this branch to regenerate them, as docs/golden-tests.md prescribes.
  • No other screen changes.

Platforms tested

Linux (widget tests).

Manual testing

Setup

A build from this branch against a node with [reputation_export] (MostroP2P/mostro#1033–#1035), with an identity that has 10 completed and 5 rated trades there.

Steps

  1. Open Settings → Reputation.
    Expected: the Export card is shown.
  2. Tap "Export my reputation".
    Expected: the dialog shows your npub. Confirming shows "Your reputation is ready".
  3. Switch to a node that imports from the first, and use Import.
    Expected: "Your reputation was imported".
  4. Turn on privacy mode.
    Expected: both directions are off, with the notice.

Not covered

Not run on a device yet.

Screenshots

Not taken yet. The settings goldens are regenerated in CI.

Automated tests

  • npub_test.dart: the NIP-19 example key encodes to its published npub.
  • reputation_screen_test.dart:
    • the export dialog shows the npub, Cancel sends nothing, and confirming exports;
    • a node that does not export says so;
    • a node refusal is shown localized;
    • an invalid rebind identity is refused, and a valid one produces the authorisation;
    • privacy mode disables the buttons.
  • modal_guard_test passes: the dialog goes through showMostroDialog.
flutter analyze: No issues found!
flutter test (reputation, core, modal guard): +271 All tests passed!

Full suite: the only failures are the settings goldens this PR changes, being regenerated in CI, and the known attachment_bubbles goldens.

Checklist

  • flutter analyze and flutter test pass locally (goldens aside, see above)
  • New user-facing text: every locale updated, gen-l10n output committed
  • Automation identifiers: declared and attached
  • Commits are signed

AI assistance

Written with Claude Code. Look hardest at the export confirmation, the step that binds the account.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UrKanPgEfEBiEo3snHz4WZ

grunch and others added 3 commits October 3, 2026 12:39
Phase 5b, PR 5b.1 of the reputation portability plan (#674). Points
mostro-core at its reputation branch until the release is published.

- mostro::reputation_support keeps, per node, what its info event
  advertises: reputation_issuer and reputation_import_issuers.
- api::reputation_transfer: export_reputation, import_reputation,
  sign_reputation_rebind, parse_reputation_attestation,
  get_reputation_support and get_pending_reputation_attestation. Each
  request is sent only to a node that advertises it, never in privacy
  mode, from a fresh trade key with the identity proof, and correlated
  by its nonce through ask_daemon_with, which now takes the reply
  window and log category. Errors are markers Dart can localize.
- The exported attestation is kept until imported, under an
  identity-scoped settings key wiped with the identity on both stores.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UrKanPgEfEBiEo3snHz4WZ
Phase 5b, PR 5b.2 of the reputation portability plan (#674). A new
Import reputation screen (/import_reputation):

- "Get it from lnp2pBot" opens t.me/lnp2pbot?start=rep_<identity>, the
  identity as 43 base64url characters (Telegram caps the parameter).
- The user pastes what the bot sent, or finds an attestation exported
  from another Mostro already filled in; the Rust core verifies it and
  the screen shows its figures before importing them into the node.
- Refusals, local or from the node, are localized
  (reputation_errors.dart), in every ARB.

ReputationApi wraps the bridge calls so the screen is tested without
the Rust core.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UrKanPgEfEBiEo3snHz4WZ
Phase 5b, PR 5b.3 of the reputation portability plan (#674). Settings
gains a Reputation row (Network group) leading to a screen that:

- exports the user's reputation from the active node, after a dialog
  showing the identity as an npub and saying the export binds the
  account to it, then shows the attestation to copy;
- opens the import screen (5b.2);
- signs a rebind authorisation for a new identity (npub or hex; the
  core now parses either), to copy to the new device or into lnp2pBot;
- explains that privacy mode keeps both directions off.

Strings in every ARB; automation ids settings.reputation and
reputation.*. The settings goldens change with the new row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UrKanPgEfEBiEo3snHz4WZ
@coderabbitai

coderabbitai Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 54 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository YAML (base), Organization UI (inherited)
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: ebce117d-5f5b-4376-b612-e1f62ae7c88c
📥 Commits

Reviewing files that changed from the base of the PR and between fdacae0 and 88ee7ad.

⛔ Files ignored due to path filters: 3
  • rust/Cargo.lock is excluded by !**/*.lock
  • test/features/settings/screens/goldens/settings_10a_settings_dark.png is excluded by !**/*.png
  • test/features/settings/screens/goldens/settings_10a_settings_light.png is excluded by !**/*.png
📒 Files selected for processing: 45
  • lib/core/app_routes.dart
  • lib/core/automation/automation_ids.dart
  • lib/features/reputation/lnp2pbot.dart
  • lib/features/reputation/npub.dart
  • lib/features/reputation/reputation_api.dart
  • lib/features/reputation/reputation_errors.dart
  • lib/features/reputation/screens/import_reputation_screen.dart
  • lib/features/reputation/screens/reputation_screen.dart
  • lib/features/reputation/widgets/reputation_controls.dart
  • lib/features/settings/screens/settings_screen.dart
  • lib/l10n/app_de.arb
  • lib/l10n/app_en.arb
  • lib/l10n/app_es.arb
  • lib/l10n/app_fr.arb
  • lib/l10n/app_it.arb
  • lib/l10n/app_localizations.dart
  • lib/l10n/app_localizations_de.dart
  • lib/l10n/app_localizations_en.dart
  • lib/l10n/app_localizations_es.dart
  • lib/l10n/app_localizations_fr.dart
  • lib/l10n/app_localizations_it.dart
  • lib/l10n/app_localizations_nl.dart
  • lib/l10n/app_nl.arb
  • lib/src/rust/api/orders.dart
  • lib/src/rust/api/reputation_transfer.dart
  • lib/src/rust/frb_generated.dart
  • lib/src/rust/frb_generated.io.dart
  • lib/src/rust/frb_generated.web.dart
  • rust/Cargo.toml
  • rust/src/api/mod.rs
  • rust/src/api/nostr.rs
  • rust/src/api/orders.rs
  • rust/src/api/reputation_transfer.rs
  • rust/src/db/indexeddb.rs
  • rust/src/db/mod.rs
  • rust/src/db/sqlite.rs
  • rust/src/frb_generated.rs
  • rust/src/mostro/actions.rs
  • rust/src/mostro/mod.rs
  • rust/src/mostro/reputation_support.rs
  • rust/tests/fixtures/reputation_v1.json
  • test/features/reputation/import_reputation_screen_test.dart
  • test/features/reputation/lnp2pbot_test.dart
  • test/features/reputation/npub_test.dart
  • test/features/reputation/reputation_screen_test.dart
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-03T16:27:18.491299Z 507c461 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 507c461d0f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lib/features/reputation/screens/reputation_screen.dart
Comment thread lib/features/reputation/npub.dart
Comment thread lib/features/reputation/screens/reputation_screen.dart
Comment thread rust/src/api/reputation_transfer.rs
github-actions Bot and others added 3 commits October 3, 2026 16:28
The Design guide check refused the theme-default Material buttons and
the paste field's v1 decoration (DS-CMP-17, DS-CMP-19). The buttons
now come from reputation_controls.dart, styled like the NWC screen's
(lime primary and outlined secondary, radius 16), and the field sets
its enabled and focused borders and filled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UrKanPgEfEBiEo3snHz4WZ
Same fix as the import screen: the export, import and rebind buttons
use reputation_controls.dart instead of theme-default Material buttons
(DS-CMP-17), the new-identity field sets its borders and filled
(DS-CMP-19), and the card title takes a size on the type scale
(DS-TYP-4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UrKanPgEfEBiEo3snHz4WZ

@Catrya Catrya left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review evidence

Decision: Request changes

Tested commit: 88ee7ad3 (PR head). I reviewed the PR's own diff against #676's head (2615478f..88ee7ad3). I also merged the whole stack locally onto main at 588ff66c (not pushed).

Environment:

  • Harness: Neither. Plain cargo and flutter in a git worktree; no Ortsom or Mortsom run.
  • Platform: Linux. Web compile-checked only (wasm32-unknown-unknown).
  • Daemon source: Not applicable. No daemon here carries MostroP2P/mostro#1027.
  • App source: Pull request #677, with #675 and #676 below it, merged onto current main.

Commands and tests executed:

  • git merge --no-ff --no-commit origin/feat/reputation-settings-ui onto origin/main. 18 files conflict:
    • the six ARBs and the generated app_localizations*.dart;
    • lib/src/rust/api/orders.dart, lib/src/rust/frb_generated.dart, rust/src/frb_generated.rs;
    • rust/Cargo.toml and rust/Cargo.lock.
  • I resolved them locally:
    • the ARBs, by keeping both sides' keys;
    • Cargo.toml and Cargo.lock, with main's side (mostro-core = "0.17.1");
    • the generated files, by regenerating with ./scripts/frb-generate.sh and flutter gen-l10n.
  • On the merged tree:
    • cargo test --locked
    • cargo clippy --locked -- -D warnings
    • cargo check --locked --target wasm32-unknown-unknown
    • flutter analyze
    • dart tool/design_check.dart --base origin/main
    • flutter test (full suite)
  • Throwaway probe 1: hexToNpub against a reference bech32 encoder, on 202 keys (all-zero, all-0xff, 200 random), in lower and upper case.
  • Throwaway probe 2: ReputationScreen at 320 dp wide, 2× text, German, privacy mode on and off. It scrolls the whole ListView, checks for overflow and records which buttons are enabled.
  • Read the PR's own diff in full, plus rust/src/api/reputation_transfer.rs and reputation_api.dart from the stack.
  • Compared with v1: MostroP2P/mobile#745 (open).

Results:

  • Passed:
    • cargo test --locked: 1129 passed, 0 failed, 30 ignored. That is with mostro-core 0.17.1, not the PR's pin.
    • clippy and wasm check: clean.
    • flutter analyze: no issues.
    • design_check: no breaks (8 files).
    • flutter test: 2145 passed, including the PR's tests and the regenerated settings_10a goldens.
    • Probe 1: 202 of 202 match.
    • Probe 2: no overflow anywhere on the screen, and privacy mode disables the export, import and sign buttons.
  • Failed:
    • Merge with main: 18 conflicting files (see above).
    • flutter test: 2 failures, both attachment_bubbles_golden_test (dark and light). They fail the same way on main in this environment, so they are not caused by this PR.
  • Skipped: 4 in flutter test, also skipped on main. Web smoke test not run on the merge.
  • Flaky: None observed

Manual observations:

  • The export dialog shows the same key the export signs with. Both come from the identity key: identity_api.getIdentity().publicKey and get_active_keys(). Rust also refuses in privacy mode (identity_keys()), so the UI is not the only gate.
  • Nothing in the app ever passes rebind to export_reputation. _export() calls api.export() with no arguments, and the import screen does not accept an authorization.
  • The PR's own branch carries 145080b0, which is the same patch as #676's 2615478f. The branch is not rebased on #676's current head.
  • rust/Cargo.toml on the stack still pins mostro-core to branch = "feat/reputation-merge", which no longer exists. main is on 0.17.1, and the stack builds and passes against it.
  • The PR is a near-literal port of mobile#745. That PR has the same rebind gap and also signs without confirmation; it encodes the npub with v1's existing NostrUtils.
  • Codex's four comments on this PR (2026-10-03) have no reply and no change. The last push was 2026-10-03 16:29.

Not tested or limitations:

  • No export, import or rebind from this app against a daemon. No local daemon carries mostro#1027, and the PR's own body says "Not run on a device yet".
  • No web build or smoke test on the merge.
  • No device or screen check. The PR has no screenshots.
  • The stack was tested with mostro-core 0.17.1 from main, not with the PR's pin.

Unresolved findings:

Blocking:

  1. The Mostro-to-Mostro move to a new identity is a dead end.
    • The screen signs a rebind authorization and tells the user to "use it from the new identity", but no screen consumes it: _export() never passes rebind, and import does not take one.
    • From the new identity the node refuses with ReputationBoundToOtherIdentity. Its message asks the user to authorize the move from the bound identity, which they already did, so the user loops.
    • Only the lnp2pBot path (pasting into the bot chat) works.
    • Fix: add a field for the authorization on export from the new identity, or scope the rebind card explicitly to lnp2pBot. Codex P1, open.
  2. NIP-19 encoding is hand-written in Dart (lib/features/reputation/npub.dart).
    • The architecture rule puts Nostr protocol logic in Rust, and nostr-sdk already provides to_bech32().
    • The encoder is correct (202/202), but it is a second protocol implementation, and it sits in the dialog that confirms the binding.
    • Fix: expose the npub from Rust, for example as an npub field on IdentityInfo, and delete npub.dart. Codex P1, open.
  3. Signing the rebind authorization asks for no confirmation.
    • The authorization lets whatever key is typed export this reputation and take the binding. That is the more dangerous step, more so than the export the PR asks reviewers to examine.
    • A user persuaded to paste an attacker's npub loses their reputation.
    • Fix: show the target npub and the consequence in a MostroDialog before signing, as export does.
  4. No end-to-end run.
    • The Manual testing steps were written but never executed: "Not run on a device yet", "Screenshots: Not taken yet".
    • Maintainers are exempt from the contribution bar. But this is the first PR in the stack where the flow can be run in the app, #675 was not run end to end either, and export creates a binding on the node that does not undo itself.
    • Needed: one real export and one real import against a daemon with mostro#1027.
  5. The stack cannot merge as it stands.
    • It depends on #675 (changes requested) and #676 (no human review).
    • It needs a rebase onto #676's head, to drop the duplicate 145080b0.
    • It conflicts with main in 18 files.
    • It inherits the mostro-core pin to a deleted branch. Switch to 0.17.1, which works (verified above).

Non-blocking:

  • Capabilities never refresh. support() is read with ref.read in build, and nothing rebuilds the screen reputation." until it is reopened. Codex P2, open.
  • No living contract. There is still no contract for reputation portability in specs/004-mostro-p2p-client/contracts/, and none records that sign_reputation_rebind now accepts an npub. Same finding as on #675.
  • Misleading error text. reputationInvalidIdentity says "That is not a valid npub." although hex is accepted, and it is the fallback for any error from signing the rebind.
  • Rebind output has no automation id. _Copyable(text: _rebind!) carries none, despite "all attached" in the body.
  • _Copyable:
    • the IconButton has no tooltip or semantic label;
    • there is no "Copied" feedback, and Clipboard.setData is not awaited;
    • the value is cut to 2 lines and cannot be selected.
  • Tests:
    • the rebind test's validation lives in the fake (startsWith('npub1')), so it does not exercise the real check;
    • PublicKey::parse in sign_reputation_rebind has no Rust test for hex, npub and garbage input;
    • "privacy mode turns both directions off" asserts only the export button;
    • the DS-A11Y-4 test (320 dp, 2× text, German) is missing. The behaviour already holds (probe 2), but no test pins it.
  • Row placement: the Settings row sits in the Network group, and reputation is not a network setting.

@grunch
grunch force-pushed the feat/reputation-import-ui branch from 0a49c13 to 8844a09 Compare October 14, 2026 20:31
Base automatically changed from feat/reputation-import-ui to main October 14, 2026 21:02
@grunch

grunch commented Oct 15, 2026

Copy link
Copy Markdown
Member Author

Superseded by #827. That PR carries this branch's two commits rebased onto main, now that #675 and #676 are merged, plus the fixes for this review. I opened a new PR instead of force-pushing over this one.

@Catrya, here is where each of your findings landed:

Blocking

  1. Rebind dead end: fixed. Export takes the authorization in an optional field and sends it as rebind.
  2. NIP-19 in Dart: fixed. npub.dart is deleted, and the npub comes from Rust's reputation_identity_npub, which now takes hex or npub.
  3. No confirmation before signing the rebind: fixed. A MostroDialog shows the target npub and what signing hands over. Nothing is signed on Cancel.
  4. No end-to-end run: still open. We are setting up a local run next: mostrod main with the import trust list, a test lnp2pBot, and this build. The Manual testing in feat(reputation): add the reputation settings screen #827 is written as pending, not as results.
  5. Stack cannot merge: fixed. feat(reputation): add the reputation settings screen #827 is based on main, the duplicate commit is gone, the conflicts are resolved, and the core pin comes from main (0.17.x).

Non-blocking

  • Capabilities refresh: fixed (reread every 2 s).
  • Living contract: fixed (reputation_transfer.md § Reputation screen).
  • Misleading error text: fixed in all six locales.
  • Rebind output automation id: fixed (reputation.rebind.result).
  • _Copyable: fixed. It has a tooltip and a snackbar, it awaits setData, and the value is selectable.
  • Tests:
    • validation now goes through the API seam, which is Rust in production, and is no longer a startsWith in the fake;
    • Rust tests cover hex, npub and garbage input;
    • privacy mode checks all three actions;
    • DS-A11Y-4 is added.
  • Row placement: kept under Network. Import and export act on the active node, like the node and relay rows above it. Happy to move it if you see it differently.

@grunch grunch closed this Oct 15, 2026
@grunch
grunch deleted the feat/reputation-settings-ui branch October 15, 2026 17:28
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.

2 participants