Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
0521ef0
fix(withdraw): pre-select only a locally-signable withdrawal key
lklimek Jul 10, 2026
e795129
docs(claude): correct secret-storage note on identity-key encryption
lklimek Jul 9, 2026
ecaa46f
feat(masternodes): add page-nav model with two-scope selection (A1)
lklimek Jul 9, 2026
736c3bc
feat(masternodes): generalize breadcrumb into page-aware global switc…
lklimek Jul 9, 2026
eeb65e3
feat(masternodes): render global switcher on root screens + shared ap…
lklimek Jul 9, 2026
b336dd8
feat(masternodes): load-time key encryption plumbing (B0)
lklimek Jul 9, 2026
d8e3c55
chore(masternodes): drop ephemeral review ID from A3 reconciliation c…
lklimek Jul 9, 2026
5386d82
feat(masternodes): FR-6 resolution-layer boundary + masternode access…
lklimek Jul 9, 2026
d3e6b05
feat(masternodes): register Expert-gated Masternodes root tab (B2)
lklimek Jul 9, 2026
a47542d
feat(masternodes): empty state + card grid + card body (B3)
lklimek Jul 9, 2026
94309ff
feat(masternodes): dedicated load form + ProTxHash validator (B4)
lklimek Jul 9, 2026
1b8d2bf
feat(masternodes): detail view — header, actions, keys, remove (B5a)
lklimek Jul 9, 2026
daa474c
feat(masternodes): Testnet Fill-Random on the load form (B6)
lklimek Jul 9, 2026
3a9e583
feat(masternodes): inline DPNS voting + missing-voter prompt (B5b)
lklimek Jul 9, 2026
18caa36
feat(masternodes): page-scoped nav pill — FR-6 boundary in code (B7)
lklimek Jul 9, 2026
dc6747e
test(masternodes): cross-cutting integration coverage (B8)
lklimek Jul 9, 2026
1e27cd9
docs(user-stories): catalog the Masternodes tab, retire the legacy lo…
lklimek Jul 9, 2026
9a73dc2
docs(masternodes): commit final design docs (DOC-002)
lklimek Jul 9, 2026
dc6cd92
docs(masternodes): trim oversized module docs, catalog global-nav swi…
lklimek Jul 9, 2026
fe9b2be
fix(masternodes): guard identity load against silent overwrite (QA-00…
lklimek Jul 9, 2026
f0f0c84
fix(masternodes): key routing, network-switch reset, live refresh
lklimek Jul 9, 2026
3271149
fix(masternodes): SHOULD-FIX pass + QA-token cleanup + offline tests
lklimek Jul 9, 2026
5826b85
fix(dashpay): close FR-6 boundary bypass via DashPay identity selecto…
lklimek Jul 9, 2026
61a6fc9
test(masternodes): Marvin punch-list — in-flight guard + execution tests
lklimek Jul 9, 2026
6bf8291
docs(changelog): add Masternodes tab and global nav switcher (DOC-005)
lklimek Jul 9, 2026
80fd1d2
fix(masternodes): default GUI build broken — masternode_input feature…
lklimek Jul 9, 2026
b2ac420
docs(masternodes): correct global-nav coverage claim (F-003)
lklimek Jul 9, 2026
7a69b92
fix(masternodes): Fable final round — Tier-2 merge seal, load gate, P…
lklimek Jul 9, 2026
3be88f0
fix(masternodes): add load-form back link + remove object pill from b…
lklimek Jul 9, 2026
14b07cf
fix(masternodes): reject load when selected node type mismatches on-c…
lklimek Jul 9, 2026
908744f
fix(masternodes): surface a visible warning when node type is unverified
lklimek Jul 9, 2026
7508da5
revert(masternodes): drop Fix #3 node-type validation entirely
lklimek Jul 9, 2026
ce7096d
fix(masternodes): adapt retargeted tab to platform-wallet rewrite APIs
lklimek Jul 10, 2026
8377889
merge(withdraw): pre-select only a locally-signable withdrawal key
lklimek Jul 10, 2026
279d383
test(withdraw): screen-level kittest coverage for default_withdrawal_…
lklimek Jul 10, 2026
2edbc18
fix(withdraw): skip wallet resolution when no signable key exists
lklimek Jul 10, 2026
a37c78d
docs(masternodes): add user story for network-switch reset behavior
lklimek Jul 10, 2026
e235f88
test(withdraw): flip banner-leak test to a regression lock (2edbc18e)
lklimek Jul 10, 2026
c15173b
merge(withdraw): add screen-level kittest coverage + fix raw-error-ba…
lklimek Jul 10, 2026
a7cf0a4
fix(mcp): stop det-cli double-prefixing the HTTP bearer token
lklimek Jul 10, 2026
ad558d5
fix(masternodes): share Expert Mode flag app-wide; node-specific load…
lklimek Jul 10, 2026
9646771
merge(mcp): fix det-cli headless double-Bearer auth bug
lklimek Jul 10, 2026
c81db44
merge(masternodes): fix Expert Mode nav sync + node-specific load error
lklimek Jul 10, 2026
7a2df9b
feat(user-role): introduce UserRole + composable FeatureGate checks (…
lklimek Jul 10, 2026
2965731
docs(ai-design): add persona/capability-gating design doc
lklimek Jul 10, 2026
79914cf
feat(user-role): reclassify dev-mode callsites onto role/capability g…
Claudius-Maginificent Jul 10, 2026
116cc9c
fix(user-role): stop settings loss on k/v read failure; gate shielded…
lklimek Jul 13, 2026
fc3317c
merge(user-role): sync PR #879 with base branch (docs/platform-wallet…
lklimek Jul 13, 2026
f34d8d2
refactor(user-role): encapsulate the shared role atomic behind UserRo…
lklimek Jul 13, 2026
60fd091
docs: drop the stale DEVELOPER_MODE interface-mode claims
lklimek Jul 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
including pasting a raw shielded address in hex form — there are just fewer
screens to navigate between.

- **Expert mode replaced by three interface levels**: the single Expert-mode
toggle is now a three-way choice — **Default view**, **Detailed view**, and
**Developer tools** — set from Network Settings or during first-time setup.
Detailed view unlocks everything the old Expert mode did (account
breakdowns, address tables, masternode tools); a few of the most advanced
actions (state-transition signing overrides, proceeding without a locally
held key, clearing Platform addresses) now require the new Developer tools
level. Until you pick a level, the app starts in Detailed view, so nothing
the old Expert mode showed you is hidden. The choice is remembered and can
be changed anytime, and it is made in the app — the `DEVELOPER_MODE` entry
in `.env` no longer has any effect on it. See
[User Roles](docs/user-roles.md) for details.

### Known Limitations

- **Single-key wallets — send and balance refresh not available**: importing a
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ When the application runs for the first time, it creates an application director
| - | - | - | - |
| `DASH_EVO_TOOL_ACCESSIBILITY` | `1` / unset | unset | Force-enable accessibility support. Activates AccessKit eagerly so the UI element tree is populated every frame and (on macOS) forces the platform accessibility adapter to initialize. Without this flag, accessibility still works normally — VoiceOver and other assistive technologies trigger AccessKit's lazy activation automatically. This flag is needed for tools that query the accessibility tree without registering as assistive technology clients (e.g. AXUIElement-based automation like Peekaboo). |

The interface mode (Default view / Detailed view / Developer tools) is not an
environment variable — it is chosen in the app and stored with your settings.
See [docs/user-roles.md](docs/user-roles.md), which also covers the obsolete
`DEVELOPER_MODE` entry older configurations may still contain.

## Contributing

Contributions are welcome! See the [Contributing Guide](CONTRIBUTING.md) for details.
Expand Down
426 changes: 426 additions & 0 deletions docs/ai-design/2026-07-10-persona-capability-gating/design.md

Large diffs are not rendered by default.

70 changes: 70 additions & 0 deletions docs/user-roles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# User Roles (Interface Mode)

Dash Evo Tool adapts its interface to how you use it. Instead of a single
"Expert mode" on/off switch, the app offers three interface modes, each
revealing more advanced controls than the one before it:

| Mode | What it shows |
| - | - |
| **Default view** | Your balance, send and receive, and usernames. |
| **Detailed view** | Adds account details, address tables, and masternode tools. |
| **Developer tools** | Adds raw protocol data, Devnet, and signing overrides. |

Each mode includes everything the one before it does — Developer tools has
everything Detailed view has, plus more. **Detailed view** is the starting
point until you pick a mode yourself; switch down to Default view for a
simpler screen, or up to Developer tools when you need raw protocol data.

## Where to set it

The three modes are chosen the same way, with the same descriptions, in two
places:

- **Network Settings** — open the network chooser screen. An **Interface
mode** card sits above Advanced Settings, always visible (it is not tucked
inside a collapsed section).
- **Welcome screen** — on first launch, the onboarding screen shows a
"Choose your experience level" row with the same three options before you
create or load a wallet.

Picking a mode in either place updates the other — there is only one active
mode, shared across the whole app.

**This choice is reversible.** You are not locking yourself into a mode by
picking one at onboarding: open **Network Settings** at any time and change
it from the **Interface mode** card. The app applies the change immediately,
with no restart required.

## Upgrading from an older install

Earlier versions had a **Developer mode** on/off switch instead of the three
interface modes. If you are upgrading, and you have not picked an interface
mode yet, the app starts you in **Detailed view** — the mode that matches the
account details, address tables and masternode tools those builds exposed.
Nothing you had is hidden, and you can move to another mode at any time from
**Network Settings**.

The interface mode is chosen in the app, not in a file: it is stored with your
other settings, and it is the same on every network.

## Migration note: obsolete `.env` entries

Older configurations may still contain a global `DEVELOPER_MODE=true|false`
entry, or per-network entries such as `MAINNET_developer_mode=true`. Neither has
any effect on the interface mode — that is chosen in the app and stored with your
settings. New installs no longer get a `DEVELOPER_MODE` line at all.

Leave the leftover entries or delete them, as you prefer. The one place
`DEVELOPER_MODE` is still consulted is the one-time database upgrade that moved
existing installs onto the built-in light client (SPV): an install that had both
`DEVELOPER_MODE=true` and a Dash Core RPC password configured keeps talking to
its local Dash Core node. That upgrade runs once, and only for installs that
predate it.

The application directory holding `.env` is:

| Operating System | Path |
| - | - |
| macOS | `~/Library/Application Support/Dash-Evo-Tool/.env` |
| Windows | `C:\Users\<User>\AppData\Roaming\Dash-Evo-Tool\config\.env` |
| Linux | `~/.config/dash-evo-tool/.env` |
39 changes: 20 additions & 19 deletions docs/user-stories.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,12 +204,12 @@ As a user, I want to see clear tabs for Dash Core, Platform, and Shielded so tha
- Empty accounts display "(empty)" indicator.
- Switching tabs is instant with no data reload.

### WAL-022: View system accounts in developer mode [Implemented]
**Persona:** Jordan
### WAL-022: View system accounts in the Detailed view [Implemented]
**Persona:** Priya, Jordan

As a developer, I want a System tab that reveals all internal account categories (Identity Registration, CoinJoin, Provider keys, etc.) so that I can inspect low-level wallet structure without cluttering the default view.
As a power user, I want a System tab that reveals all internal account categories (Identity Registration, CoinJoin, Provider keys, etc.) so that I can inspect low-level wallet structure without cluttering the default view.

- System tab appears only when developer mode is enabled.
- System tab appears only at the Power role (Detailed view) or above.
- Each system account category is shown as a collapsible section.
- Section headers display address count and balance.

Expand Down Expand Up @@ -1050,20 +1050,21 @@ As a user, I want to choose between light, dark, or auto theme so that the app m
- Light, dark, and system-auto options.
- Theme change applied immediately.

### NET-005: Toggle developer mode [Implemented]
### NET-005: Unlock advanced features by interface mode [Implemented]
**Persona:** Priya, Jordan

As a user, I want to enable developer mode so that I can access advanced features like address tables, refresh controls, and debug tools.
As a user, I want a higher interface mode to reveal advanced features like address tables, refresh controls, and debug tools, so that complexity stays out of my way until I ask for it.

- Toggles visibility of advanced UI elements.
- Detailed view and Developer tools each add capabilities on top of the mode below them.
- Feature availability is monotonic: anything a lower mode can do, a higher mode can too.

### NET-006: Select user mode [Gap]
**Persona:** Alex, Priya
### NET-006: Select interface mode [Implemented]
**Persona:** Alex, Priya, Jordan

As a user, I want to choose between Beginner and Advanced mode so that the interface matches my experience level.
As a user, I want to choose between Default view, Detailed view, and Developer tools so that the interface matches my experience level.

- Beginner mode hides complexity; Advanced mode shows full detail.
- `UserMode` enum and database persistence exist but no UI control is implemented.
- Same three choices and descriptions on the Network Settings "Interface mode" card and the Welcome screen onboarding row.
- Choice persists and applies immediately, and can be changed again at any time.

### NET-007: Granular refresh controls [Implemented]
**Persona:** Priya
Expand Down Expand Up @@ -1136,7 +1137,7 @@ As an everyday user, I want to install and use Dash Evo Tool without having to r

- Fresh install connects to the Dash network via the built-in SPV light client with zero configuration.
- The user sees sync progress and status clearly; the default everyday-user UI avoids mentions of SPV, RPC, or nodes.
- Technical/protocol terminology may appear in Expert mode or advanced settings, where Dash Core RPC remains available as an opt-in for users who do run a local node.
- Technical/protocol terminology may appear in Detailed view, Developer tools, or advanced settings, where Dash Core RPC remains available as an opt-in for users who do run a local node.

---

Expand Down Expand Up @@ -1254,13 +1255,13 @@ As Alex, setting up a social profile to unlock DashPay contacts is clearly optio
- Settings tab hosts the social-profile block where display name and avatar can be edited; identities without a profile continue to use payments and usernames untouched.
- Home tab renders a `Set up your social profile` entry in the onboarding checklist with a skip affordance — opting in is never forced.

### IDH-005: Developer bulk identity creation [Gap]
**Persona:** Jordan
### IDH-005: Bulk identity creation [Gap]
**Persona:** Priya, Jordan

As Jordan in Developer Mode, I have a single entry point to create many test identities without leaving the Identities section.
As a power user, I have a single entry point to create many test identities without leaving the Identities section.

- Onboarding screen surfaces a Developer-mode footer mentioning `Create multiple test identities` / `Load identity by ID` as plain text.
- Planned (follow-up): wire those footer items to the existing `AddNewIdentityScreen` bulk path and dev-mode identity-picker dropdown entries.
- Onboarding screen surfaces a footer, shown at the Power role or above, mentioning `Create multiple test identities` / `Load identity by ID` as plain text.
- Planned (follow-up): wire those footer items to the existing `AddNewIdentityScreen` bulk path and the Power-role identity-picker dropdown entries.

### IDH-006: Unified activity timeline [Gap]
**Persona:** All
Expand Down Expand Up @@ -1289,7 +1290,7 @@ As a masternode operator, I want a card list of my loaded masternodes showing ty

- Each card shows a shortened ProTxHash (or alias as heading), a Masternode/Evonode type badge, voter-identity readiness ("Voting ready" / "No voting key"), a compact Voting/Owner/Payout key-status indicator, a DPNS-voting status line, and an identity status dot with a text label.
- An empty state explains what a masternode identity is for and offers a primary "Load a masternode" action when none are loaded.
- The Masternodes tab and its nav entry are visible only with Expert Mode enabled; turning Expert Mode off while the tab is active falls back to the Identities screen.
- The Masternodes tab and its nav entry are visible only at the Detailed view interface mode or above; dropping below Detailed view while the tab is active falls back to the Identities screen.

### MN-003: Open a masternode and vote [Implemented]
**Persona:** Priya
Expand Down
36 changes: 22 additions & 14 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::backend_task::error::TaskError;
use crate::backend_task::{BackendTask, BackendTaskSuccessResult};
use crate::context::AppContext;
use crate::context::connection_status::{ConnectionStatus, OverallConnectionState};
use crate::context::feature_gate::FeatureGate;
use crate::context::migration_status::MigrationStep;
use crate::database::Database;
use crate::model::settings::AppSettings;
Expand Down Expand Up @@ -563,15 +564,11 @@ impl AppState {

let saved_network = settings.network;

// App-global Expert Mode flag: read once from config and shared into
// every per-network context (including any created later by a network
// switch), so a live toggle is observed everywhere without a restart.
let developer_mode = Arc::new(std::sync::atomic::AtomicBool::new(
crate::config::Config::load_from(&data_dir)
.ok()
.and_then(|c| c.developer_mode)
.unwrap_or(false),
));
// App-global user role, shared into every per-network context (including
// any created later by a network switch) so a live change is observed
// everywhere without a restart. Seeded below from `get_app_settings()`
// once the active context exists — the single persisted source of truth.
let user_role = crate::model::user_role::UserRoleCell::default();

// Build a helper to create AppContext for a given network.
let make_context = |network: Network| -> Option<Arc<AppContext>> {
Expand All @@ -584,7 +581,7 @@ impl AppState {
ctx.clone(),
Arc::clone(&app_kv),
Arc::clone(&secret_store),
Arc::clone(&developer_mode),
user_role.clone(),
)
};

Expand Down Expand Up @@ -636,6 +633,17 @@ impl AppState {
.expect("invariant: chosen_network was just taken from network_contexts")
.clone();

// Seed the shared role cell from AppSettings — the single source of truth.
// `get_app_settings` resolves an account that never chose a role to
// `UserRole::WHEN_UNSET`. `set_user_role` publishes the value to every
// context holding the cell and syncs animation gating.
active_context.set_user_role(
active_context
.get_app_settings()
.user_role
.unwrap_or(crate::model::user_role::UserRole::WHEN_UNSET),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🔴 Blocking: A transient settings read still elevates the runtime role for the session

get_app_settings() now correctly returns an uncached fallback on error, but this boot call immediately publishes that fallback. The error fallback is with_default_user_role(AppSettings::default()), so its role is Power. If the persisted role is Everyday and this read transiently fails, the shared UserRoleCell becomes Power; after the store recovers, later settings reads return Everyday, but nothing reseeds the cell, so UI and feature gates remain at Power until restart. The MCP boot path has the same sequence. This leaves the runtime role inconsistent with the recovered settings and can expose Power-only operations after a transient read failure.

Keep the least-privileged pre-seed role on read errors, or make the boot seed distinguish a successful/absent read from an error instead of publishing the fallback. Please add a regression that stores Everyday, fails the boot role read once, recovers the store, and asserts the runtime role is not left at Power.

);
Comment on lines +640 to +645

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

💬 Nitpick: Dead .unwrap_or(UserRole::WHEN_UNSET) fallback after the settings fix

I traced every path through get_app_settings() (settings_db.rs:115-146): the success path, the first-run/absent-blob path, and the error path all now funnel through with_default_user_role, which does settings.user_role.get_or_insert(UserRole::WHEN_UNSET) unconditionally. That guarantees AppSettings::user_role is Some(_) for every value get_app_settings() can return, making .unwrap_or(crate::model::user_role::UserRole::WHEN_UNSET) here — and the identical one at src/mcp/server.rs:394-399 — provably unreachable. Not a functional bug (the fallback value matches the guaranteed one), but it's a duplicated decision: if with_default_user_role's default ever changes, these two callsites silently keep the stale value instead of surfacing the mismatch.

source: ['sonnet5-rust-quality']


// load fonts
ctx.set_fonts(crate::bundled::fonts().expect("failed to load fonts"));

Expand Down Expand Up @@ -1057,12 +1065,12 @@ impl AppState {
}

pub fn active_root_screen_mut(&mut self) -> &mut Screen {
// Live de-gating (§10.11): if Expert Mode flipped off while the
// Live de-gating (§10.11): if the role dropped below Power while the
// Masternodes tab was active, fall back to the neutral Identities tab so
// the Expert-gated screen is never shown without its gate. Identities is
// always registered, so the subsequent lookup cannot fail.
// the gated screen is never shown without its gate. Identities is always
// registered, so the subsequent lookup cannot fail.
if self.selected_main_screen == RootScreenType::RootScreenMasternodes
&& !self.current_app_context().is_developer_mode()
&& !FeatureGate::Masternodes.is_available(self.current_app_context())
{
self.selected_main_screen = RootScreenType::RootScreenIdentities;
}
Expand Down
2 changes: 1 addition & 1 deletion src/backend_task/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ mod send_payment_unsupported_options {
egui::Context::default(),
app_kv,
secret_store,
std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
crate::model::user_role::UserRoleCell::default(),
)
.expect("AppContext")
}
Expand Down
10 changes: 5 additions & 5 deletions src/backend_task/identity/load_identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,7 @@ mod tests {
egui::Context::default(),
app_kv,
secret_store,
std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
crate::model::user_role::UserRoleCell::default(),
)
.expect("offline testnet AppContext::new");
let (tx, _rx) = tokio::sync::mpsc::channel::<TaskResult>(32);
Expand Down Expand Up @@ -954,7 +954,7 @@ mod tests {
egui::Context::default(),
app_kv,
secret_store,
std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
crate::model::user_role::UserRoleCell::default(),
)
.expect("offline testnet AppContext::new");
let (tx, _rx) = tokio::sync::mpsc::channel::<TaskResult>(32);
Expand Down Expand Up @@ -1038,7 +1038,7 @@ mod tests {
egui::Context::default(),
app_kv,
secret_store,
std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
crate::model::user_role::UserRoleCell::default(),
)
.expect("offline testnet AppContext::new");
// The scoped merge prompt asks for the node's object password once.
Expand Down Expand Up @@ -1149,7 +1149,7 @@ mod tests {
egui::Context::default(),
app_kv,
secret_store,
std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
crate::model::user_role::UserRoleCell::default(),
)
.expect("offline testnet AppContext::new");
// No prompt installed: the default NullSecretPrompt fails closed.
Expand Down Expand Up @@ -1222,7 +1222,7 @@ mod tests {
egui::Context::default(),
app_kv,
secret_store,
std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
crate::model::user_role::UserRoleCell::default(),
)
.expect("offline testnet AppContext::new");
let (tx, _rx) = tokio::sync::mpsc::channel::<TaskResult>(32);
Expand Down
4 changes: 2 additions & 2 deletions src/backend_task/identity/protect_identity_keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ mod tests {
egui::Context::default(),
app_kv,
secret_store,
std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
crate::model::user_role::UserRoleCell::default(),
)
.expect("offline testnet AppContext::new");
let (tx, _rx) = tokio::sync::mpsc::channel::<TaskResult>(32);
Expand Down Expand Up @@ -792,7 +792,7 @@ mod tests {
egui::Context::default(),
app_kv,
secret_store,
std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
crate::model::user_role::UserRoleCell::default(),
)
.expect("offline testnet AppContext::new");
let (tx, _rx) = tokio::sync::mpsc::channel::<TaskResult>(32);
Expand Down
2 changes: 1 addition & 1 deletion src/backend_task/migration/finish_unwire.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2880,7 +2880,7 @@ mod tests {
egui::Context::default(),
app_kv,
secret_store,
std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
crate::model::user_role::UserRoleCell::default(),
)
.expect("AppContext")
}
Expand Down
10 changes: 5 additions & 5 deletions src/backend_task/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -624,10 +624,10 @@ impl AppContext {
let egui_ctx = self.egui_ctx().clone();
let app_kv = self.app_kv();
let secret_store = self.secret_store();
// Share the app-global Expert Mode flag so the freshly-switched
// context observes the same value (and live toggles) as the rest
// of the app — never a fresh per-context flag.
let developer_mode = self.developer_mode_handle();
// Share the app-global role cell so the freshly-switched context
// observes the same value (and live changes) as the rest of the
// app — never a fresh per-context cell.
let user_role = self.user_role_cell();
let new_ctx = tokio::task::block_in_place(|| {
AppContext::new(
data_dir,
Expand All @@ -638,7 +638,7 @@ impl AppContext {
egui_ctx,
app_kv,
secret_store,
developer_mode,
user_role,
)
})
.ok_or(TaskError::NetworkContextCreationFailed { network })?;
Expand Down
2 changes: 1 addition & 1 deletion src/backend_task/wallet/generate_receive_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ mod tests {
egui::Context::default(),
app_kv,
secret_store,
std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
crate::model::user_role::UserRoleCell::default(),
)
.expect("offline testnet AppContext::new");

Expand Down
Loading
Loading