Skip to content

chore: let Claude write manual test scenarios for PRs#634

Merged
lklimek merged 3 commits into
v1.0-devfrom
chore/move-doc-to-docs
Feb 24, 2026
Merged

chore: let Claude write manual test scenarios for PRs#634
lklimek merged 3 commits into
v1.0-devfrom
chore/move-doc-to-docs

Conversation

@lklimek
Copy link
Copy Markdown
Contributor

@lklimek lklimek commented Feb 24, 2026

As discussed, we will get some manual test scenarios into each PR.

Summary

  • Move doc/COMPONENT_DESIGN_PATTERN.md into docs/ and remove the empty doc/ directory
  • Update all references in CLAUDE.md and src/ui/components/component_trait.rs
  • Add requirement in CLAUDE.md for manual test scenarios before PRs (using qa-engineer agent)

Test plan

  • Non-functional change (docs reorganization and CI/dev process update) — no manual test scenario needed.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation
    • Updated documentation references and directory structure for improved organization.
    • Added comprehensive manual testing guidance including test scenarios and workflow information.
    • Enhanced CI configuration guidance with additional usage examples and command references.

lklimek and others added 2 commits February 24, 2026 09:24
Consolidate documentation under a single docs/ directory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 24, 2026

📝 Walkthrough

Walkthrough

Documentation updates across the codebase: CLAUDE.md expanded with manual test guidance, extended CI examples, and directory path corrections from doc/ to docs/. Component trait documentation references also updated to match the new documentation structure.

Changes

Cohort / File(s) Summary
Documentation References
CLAUDE.md
Added manual test guidance section with test scenarios and placement guidance. Extended CI documentation with additional safe-cargo.sh usage examples. Updated multiple references from doc/COMPONENT_DESIGN_PATTERN.md to docs/COMPONENT_DESIGN_PATTERN.md. Updated docs/ai-design description to include manual testing scenarios.
Component Trait Documentation
src/ui/components/component_trait.rs
Updated public trait documentation comments to reference docs/COMPONENT_DESIGN_PATTERN.md instead of doc/COMPONENT_DESIGN_PATTERN.md.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Poem

🐰✨ Docs reorganized with care so fine,
From doc/ to docs/ paths now align,
Manual tests guide the way so bright,
CI wisdom shared, architecture in sight,
A tidy warren of knowledge, oh what delight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title mentions adding manual test scenarios but the primary changes are consolidating doc/ into docs/ and updating documentation references. Update title to reflect the main change: 'chore: consolidate doc/ into docs/ and update documentation references' or similar.
✅ Passed checks (2 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/move-doc-to-docs

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.

@lklimek lklimek changed the title chore: consolidate doc/ into docs/ and add manual test requirement chore: let Claude write manual test scenarios for PRs Feb 24, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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`:
- Around line 40-45: Fix the minor grammar issue and make the agent name
consistent in the "Manual test scenarios" section: reword the first sentence to
"You MUST identify manual tests needed for the changes and write manual test
scenarios; use the claudius:qa-engineer agent if available." and replace any
occurrence of "qa-engineer" alone with "claudius:qa-engineer"; also tighten the
skip instruction to read "Skip the manual test file only for non-functional
changes (CI, docs, formatting, pure refactoring) — state why in the PR
description." to improve clarity while editing the CLAUDE.md section header
"Manual test scenarios".

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 435c98b and a48c04c.

📒 Files selected for processing (3)
  • CLAUDE.md
  • docs/COMPONENT_DESIGN_PATTERN.md
  • src/ui/components/component_trait.rs

Comment thread CLAUDE.md
Comment on lines +40 to +45
### Manual test scenarios

You MUST identify manual tests needed for the changes and write a manual test scenarios. Use the `claudius:qa-engineer` agent if available.
Skip the manual test file only for non-functional changes (CI, docs, formatting, pure refactoring) — state why in the PR description.
Put tests in docs directory, as described in "Documentation" section below. Reference the file in the PR description under "Test plan".
Before creating a PR, re-review test scenarios and update them if needed.
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 | 🟡 Minor

Tighten manual-test wording + agent name consistency.

Minor doc polish: the sentence has a grammar error, and the agent name doesn’t match the PR objective (“qa-engineer”). Consider aligning the name and fixing the wording.

📝 Suggested edit
-You MUST identify manual tests needed for the changes and write a manual test scenarios. Use the `claudius:qa-engineer` agent if available.
+You MUST identify manual tests needed for the changes and write manual test scenarios. Use the `qa-engineer` agent if available.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLAUDE.md` around lines 40 - 45, Fix the minor grammar issue and make the
agent name consistent in the "Manual test scenarios" section: reword the first
sentence to "You MUST identify manual tests needed for the changes and write
manual test scenarios; use the claudius:qa-engineer agent if available." and
replace any occurrence of "qa-engineer" alone with "claudius:qa-engineer"; also
tighten the skip instruction to read "Skip the manual test file only for
non-functional changes (CI, docs, formatting, pure refactoring) — state why in
the PR description." to improve clarity while editing the CLAUDE.md section
header "Manual test scenarios".

@lklimek lklimek enabled auto-merge (squash) February 24, 2026 08:50
@lklimek lklimek disabled auto-merge February 24, 2026 08:50
@lklimek lklimek merged commit 8404cdd into v1.0-dev Feb 24, 2026
3 checks passed
@lklimek lklimek deleted the chore/move-doc-to-docs branch February 24, 2026 08:50
lklimek added a commit that referenced this pull request Feb 24, 2026
* chore: move doc/ contents into docs/ and update references

Consolidate documentation under a single docs/ directory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: CLAUDE.md should write manual test scenarios for PRs

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
@thepastaclaw
Copy link
Copy Markdown
Collaborator

Addressed the CodeRabbit grammar feedback ("write a manual test scenarios" → "write manual test scenarios") in branch fix/claude-md-grammar. Will open a follow-up PR.

The agent name claudius:qa-engineer is already correct as-is.

lklimek added a commit that referenced this pull request Mar 26, 2026
* more work

* more work

* fix(spv): zero out stale per-address balances during reconciliation (#627)

During SPV reconciliation, per_address_sum only contains addresses with
current UTXOs. Addresses whose funds were fully spent never had their
balance reset to zero, causing the address table to display stale
non-zero balances even though UTXO count correctly showed 0.

Now explicitly zeroes address_balances for any known address absent from
the refreshed UTXO map before applying current sums.

Closes #571

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix: handle malformed YAML gracefully in load_testnet_nodes_from_yml (#613)

* fix: handle malformed YAML gracefully in load_testnet_nodes_from_yml

Replace .expect() with match expression to avoid panicking when
.testnet_nodes.yml contains malformed YAML. Instead, logs the error
with tracing::error and returns None, allowing the application to
continue without crashing.

Closes #557

Co-authored-by: lklimek <lklimek@users.noreply.github.com>

* fix: propagate YAML parse errors to UI and remove unwrap calls

- Change load_testnet_nodes_from_yml to return Result<Option<TestnetNodes>, String>
  so parse errors display in the UI error banner instead of only logging
- Use explicit type annotation on serde_yaml_ng::from_str::<TestnetNodes>
- Replace .unwrap() with .and_then() in fill_random_hpmn/fill_random_masternode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: lklimek <lklimek@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* chore: let Claude write manual test scenarios for PRs (#634)

* chore: move doc/ contents into docs/ and update references

Consolidate documentation under a single docs/ directory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: CLAUDE.md should write manual test scenarios for PRs

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* build(flatpak): use only-arches for dynamic protoc architecture selection (#603)

* build(flatpak): use only-arches for dynamic protoc architecture selection

Replace the fragile sed-based CI patching of the Flatpak manifest with
Flatpak's native `only-arches` source selector. The protoc module now
declares both x86_64 and aarch64 sources inline, and build-commands use
a glob pattern (`protoc-*.zip`) so no per-arch fixup is needed.

Changes:
- flatpak manifest: add aarch64 protoc source with `only-arches`,
  use glob in unzip commands, remove stale CI-patching comment
- CI workflow: remove `protoc-zip`/`protoc-sha256` matrix variables
  and the "Patch manifest for architecture" step

https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG

* fix(flatpak): use dest-filename for deterministic protoc extraction

Use dest-filename to normalize both arch-specific protoc zips to a
common name, avoiding glob expansion in build-commands. This ensures
the unzip target is deterministic regardless of shell behavior in the
Flatpak sandbox.

https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG

* build: update platform to b445b6f0 and remove rust-dashcore patches

Update dashpay/platform dependency from d6f4eb9a to b445b6f0e0bd4863
(3.0.1 → 3.1.0-dev.1). Remove the [patch] section that pinned
rust-dashcore crates to a separate rev, as the new platform commit
resolves them correctly on its own.

https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG

---------

Co-authored-by: Claude <noreply@anthropic.com>

* fix(ci): remove local path patches, use git deps for platform crates

The [patch] section referenced ../platform local paths that don't exist
in CI. Since dash-sdk already depends on the feat/zk branch, all
transitive platform crates resolve correctly without patches. Also fixes
a formatting issue in address_table.rs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(test): store wallet in DB before registering addresses

The remove_utxos tests were failing because `register_test_address`
inserts into `wallet_addresses` which has a foreign key constraint on
`wallet(seed_hash)`. Added the missing `store_wallet` call so the
parent row exists before inserting child address rows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(build): restore shielded module declaration removed during merge

The `pub mod shielded;` declaration was accidentally removed from
`src/model/wallet/mod.rs` during the v1.0-dev merge, causing build
failures since the shielded.rs file still exists.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(test): restore store_wallet calls lost in merge (#663)

* Initial plan

* fix(test): restore store_wallet calls lost in merge

Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com>

* Merge remote-tracking branch 'origin/v1.0-dev' into zk-extract/all-merged

Resolve conflicts in Cargo.toml (keep feat/zk branch), Cargo.lock
(regenerate with pinned platform rev 4d7b9be5), and
backend_task/mod.rs (combine TaskError wrapping with ShieldedTask).

Fix post-merge integration issues:
- SPV manager: remove stale .await on subscribe methods, add
  command_receiver channel for updated DashSpvClient::run() API
- send_screen: update SendStatus::WaitingForResult to unit variant
- network_chooser_screen: handle new SyncState::Initializing variant

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: update platform dependency to 3.1-dev branch

Migrate from feat/zk to 3.1-dev branch of dashpay/platform,
adapting to breaking API changes in dash-spv, key-wallet, and dpp:

- FeeLevel removed; use FeeRate::normal() directly
- DashSpvClientInterface/Command removed; use DashSpvClient directly
- SyncState::Initializing removed; replaced with WaitForEvents
- NetworkExt trait inlined into Network impl
- OrchardProver now requires wrapper struct around ProvingKey
- OrchardAddress::from_raw_bytes now returns Result
- Builder functions gain fee/platform_version params
- NullifierSyncConfig API uses NullifierSyncCheckpoint
- WalletManager.create_unsigned_payment_transaction removed;
  use TransactionBuilder directly
- Work around Send lifetime issues with spawn_blocking

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: migrate shielded module from Result<T, String> to typed TaskError

Replace all Result<T, String> error patterns in the shielded pool module
with typed TaskError variants, aligning with the codebase-wide typed error
migration (PR #739).

New TaskError variants: ShieldedNoUnspentNotes, ShieldedInsufficientBalance,
PlatformAddressNotFound, ShieldedMerkleWitnessUnavailable,
ShieldedTransitionBuildFailed, ShieldedBroadcastFailed,
ShieldedInvalidRecipientAddress, ShieldedAssetLockTimeout,
ShieldedSyncFailed, ShieldedTreeUpdateFailed, ShieldedNullifierSyncFailed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): prevent settings password row from clipping right edge

Reserve width for Save and Auto Update buttons so the password input
doesn't consume all available space, pushing buttons off-screen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(db): consolidate migrations v28-v32 into v33

Resolves version numbering collision between zk and v1.0-dev branches:
the zk branch used v28 for shielded tables while v1.0-dev used v28 for
contacts. After merging, users migrating from either branch could end up
with missing tables depending on which version their DB was at.

v33 runs all sub-migrations idempotently in one step, ensuring all
tables exist regardless of prior migration history.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: pin platform dependency to zk-fixes revision

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): address PR review — fresh schema, error propagation, rename coverage

- propagate SQL error in add_nullifier_sync_timestamp_column instead of
  swallowing it with unwrap_or(false)
- add shielded_notes and shielded_wallet_meta to rename_network_dash_to_mainnet

Note: Fix 1 (last_nullifier_sync_timestamp in CREATE TABLE) was already
present on this branch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): add foreign key constraints to shielded tables

shielded_notes and shielded_wallet_meta both had wallet_seed_hash columns
with no FK constraint. Added FOREIGN KEY (wallet_seed_hash) REFERENCES
wallet(seed_hash) ON DELETE CASCADE to both, matching the pattern used by
all other per-wallet tables in the codebase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): address PR #789 review — doc comments, migration default

- Split misplaced doc comment: `add_wallet_transaction_status_column`
  now has its own Migration 30 doc; `rename_network_dash_to_mainnet`
  gets its own Migration 29 doc.
- Add inline comment explaining why the migration uses DEFAULT 2
  (Confirmed) while fresh CREATE TABLE in wallet.rs uses DEFAULT 0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): remove duplicate shielded methods after v1.0-dev merge

On zk-fixes, shielded DB methods live in shielded.rs. The v1.0-dev
backport inlined them in initialization.rs (no shielded.rs on that
branch). Merging v1.0-dev back caused E0592 duplicate definitions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: simplify shielded helpers comment in initialization.rs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(test): remove duplicate wallet store in register_test_address

register_test_address called db.store_wallet redundantly — callers
already store the wallet before calling it, causing UNIQUE constraint
violations when tests run in parallel on CI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(mcp): resolve async lifetime errors for Rust 2024 edition

Use spawn_blocking + block_on in dispatch_task to avoid Send bound
issues with platform SDK types (DataContract/Sdk references across
await points). Same pattern already used by AppState::handle_backend_task.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: fix mcp dispatch

* doc: remove obsolete manual teting docs

---------

Co-authored-by: Lukasz Klimek <842586+lklimek@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: lklimek <lklimek@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
lklimek added a commit that referenced this pull request Mar 26, 2026
… improvements (#786)

* more work

* more work

* fix(spv): zero out stale per-address balances during reconciliation (#627)

During SPV reconciliation, per_address_sum only contains addresses with
current UTXOs. Addresses whose funds were fully spent never had their
balance reset to zero, causing the address table to display stale
non-zero balances even though UTXO count correctly showed 0.

Now explicitly zeroes address_balances for any known address absent from
the refreshed UTXO map before applying current sums.

Closes #571

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix: handle malformed YAML gracefully in load_testnet_nodes_from_yml (#613)

* fix: handle malformed YAML gracefully in load_testnet_nodes_from_yml

Replace .expect() with match expression to avoid panicking when
.testnet_nodes.yml contains malformed YAML. Instead, logs the error
with tracing::error and returns None, allowing the application to
continue without crashing.

Closes #557

Co-authored-by: lklimek <lklimek@users.noreply.github.com>

* fix: propagate YAML parse errors to UI and remove unwrap calls

- Change load_testnet_nodes_from_yml to return Result<Option<TestnetNodes>, String>
  so parse errors display in the UI error banner instead of only logging
- Use explicit type annotation on serde_yaml_ng::from_str::<TestnetNodes>
- Replace .unwrap() with .and_then() in fill_random_hpmn/fill_random_masternode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: lklimek <lklimek@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* chore: let Claude write manual test scenarios for PRs (#634)

* chore: move doc/ contents into docs/ and update references

Consolidate documentation under a single docs/ directory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: CLAUDE.md should write manual test scenarios for PRs

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* build(flatpak): use only-arches for dynamic protoc architecture selection (#603)

* build(flatpak): use only-arches for dynamic protoc architecture selection

Replace the fragile sed-based CI patching of the Flatpak manifest with
Flatpak's native `only-arches` source selector. The protoc module now
declares both x86_64 and aarch64 sources inline, and build-commands use
a glob pattern (`protoc-*.zip`) so no per-arch fixup is needed.

Changes:
- flatpak manifest: add aarch64 protoc source with `only-arches`,
  use glob in unzip commands, remove stale CI-patching comment
- CI workflow: remove `protoc-zip`/`protoc-sha256` matrix variables
  and the "Patch manifest for architecture" step

https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG

* fix(flatpak): use dest-filename for deterministic protoc extraction

Use dest-filename to normalize both arch-specific protoc zips to a
common name, avoiding glob expansion in build-commands. This ensures
the unzip target is deterministic regardless of shell behavior in the
Flatpak sandbox.

https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG

* build: update platform to b445b6f0 and remove rust-dashcore patches

Update dashpay/platform dependency from d6f4eb9a to b445b6f0e0bd4863
(3.0.1 → 3.1.0-dev.1). Remove the [patch] section that pinned
rust-dashcore crates to a separate rev, as the new platform commit
resolves them correctly on its own.

https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG

---------

Co-authored-by: Claude <noreply@anthropic.com>

* fix(ci): remove local path patches, use git deps for platform crates

The [patch] section referenced ../platform local paths that don't exist
in CI. Since dash-sdk already depends on the feat/zk branch, all
transitive platform crates resolve correctly without patches. Also fixes
a formatting issue in address_table.rs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(test): store wallet in DB before registering addresses

The remove_utxos tests were failing because `register_test_address`
inserts into `wallet_addresses` which has a foreign key constraint on
`wallet(seed_hash)`. Added the missing `store_wallet` call so the
parent row exists before inserting child address rows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(build): restore shielded module declaration removed during merge

The `pub mod shielded;` declaration was accidentally removed from
`src/model/wallet/mod.rs` during the v1.0-dev merge, causing build
failures since the shielded.rs file still exists.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(test): restore store_wallet calls lost in merge (#663)

* Initial plan

* fix(test): restore store_wallet calls lost in merge

Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com>

* Merge remote-tracking branch 'origin/v1.0-dev' into zk-extract/all-merged

Resolve conflicts in Cargo.toml (keep feat/zk branch), Cargo.lock
(regenerate with pinned platform rev 4d7b9be5), and
backend_task/mod.rs (combine TaskError wrapping with ShieldedTask).

Fix post-merge integration issues:
- SPV manager: remove stale .await on subscribe methods, add
  command_receiver channel for updated DashSpvClient::run() API
- send_screen: update SendStatus::WaitingForResult to unit variant
- network_chooser_screen: handle new SyncState::Initializing variant

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: update platform dependency to 3.1-dev branch

Migrate from feat/zk to 3.1-dev branch of dashpay/platform,
adapting to breaking API changes in dash-spv, key-wallet, and dpp:

- FeeLevel removed; use FeeRate::normal() directly
- DashSpvClientInterface/Command removed; use DashSpvClient directly
- SyncState::Initializing removed; replaced with WaitForEvents
- NetworkExt trait inlined into Network impl
- OrchardProver now requires wrapper struct around ProvingKey
- OrchardAddress::from_raw_bytes now returns Result
- Builder functions gain fee/platform_version params
- NullifierSyncConfig API uses NullifierSyncCheckpoint
- WalletManager.create_unsigned_payment_transaction removed;
  use TransactionBuilder directly
- Work around Send lifetime issues with spawn_blocking

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: migrate shielded module from Result<T, String> to typed TaskError

Replace all Result<T, String> error patterns in the shielded pool module
with typed TaskError variants, aligning with the codebase-wide typed error
migration (PR #739).

New TaskError variants: ShieldedNoUnspentNotes, ShieldedInsufficientBalance,
PlatformAddressNotFound, ShieldedMerkleWitnessUnavailable,
ShieldedTransitionBuildFailed, ShieldedBroadcastFailed,
ShieldedInvalidRecipientAddress, ShieldedAssetLockTimeout,
ShieldedSyncFailed, ShieldedTreeUpdateFailed, ShieldedNullifierSyncFailed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): prevent settings password row from clipping right edge

Reserve width for Save and Auto Update buttons so the password input
doesn't consume all available space, pushing buttons off-screen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(rpc): include host:port in connection-refused errors and always show details

Add CoreRpcConnectionFailed variant to TaskError that includes the
configured address in the user-facing message.  Connection-refused
errors are now detected via is_rpc_connection_error() and enriched
with host:port at every RPC call site where the URL is known
(AppContext::rpc_error_with_url helper).

Details panel is now shown for all RPC-related errors regardless of
developer mode, so users can always see the technical information
they need to diagnose connectivity issues.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): save RPC password for active network instead of hardcoded Regtest

The Network Chooser screen had three bugs related to RPC password handling:

1. Password input was initialized from Regtest config only, ignoring the
   current network selection.
2. Password UI was hidden for all networks except Regtest, even though
   Mainnet/Testnet/Devnet also use RPC mode.
3. Save logic was hardcoded to update Regtest config and triggered a
   SwitchNetwork(Regtest) action, which disconnected the active network's
   ZMQ listener unnecessarily.

Now the password input shows for any network in RPC mode, reads/writes
the correct network config, reinits the RPC client in-place without
triggering a network switch, and reloads the stored password when the
user switches network tabs. The "Auto Update" (dashmate) button remains
Regtest-only since dashmate is only relevant for local networks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): ensure funding method dropdown fits all items without scrollbar

Add explicit .height(200.0) to the funding method ComboBox in both
top_up_identity_screen and add_new_identity_screen. The add_enabled_ui
wrappers inflate item height via frame overhead, causing the popup to
clip to a single row at the default height.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(error): show actionable message for insufficient identity balance

IdentityInsufficientBalanceError from the SDK now maps to a dedicated
TaskError::IdentityInsufficientBalance variant instead of falling through
to "An unexpected error occurred." The user sees the available and required
credit amounts along with a clear "top up your identity" action.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): clear stale error banners when saving RPC password

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use network-compatible comparison for platform address lookups

Regtest and Testnet share the `tdash` bech32m prefix, but
`PlatformAddress::from_bech32m_string()` always returns `Network::Testnet`
for `tdash` addresses. The strict `!=` comparison in the fund-platform
dialog rejected valid Regtest addresses with "Address network mismatch".

- Make `networks_address_compatible()` `pub(crate)` in `model::wallet`
  so all modules can reuse the canonical check
- Remove the duplicate private copy in `backend_task::core` and import
  from the single source
- Replace `network != self.app_context.network` in `dialogs.rs` with
  `networks_address_compatible()` so Testnet/Devnet/Regtest addresses
  are accepted interchangeably

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(error): add actionable messages for shielded fee and pool-size errors

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(error): add actionable message for shielded anchor mismatch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): auto-resync notes and retry on anchor mismatch

When unshield_credits, shielded_transfer, or shielded_withdrawal fails
with ShieldedAnchorMismatch (stale commitment tree witnesses), automatically
sync notes once to update the tree and retry the operation. Only one resync
attempt is made — if the retry also fails, the error propagates as-is.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): ensure shielded tables exist and log DB errors during init

Three changes to fix empty shielded balance after app restart:

1. Defensive table creation in initialize(): after migrations complete,
   ensure shielded_notes and shielded_wallet_meta tables exist even if
   the DB version was already past v29/v30 from a prior build. Both
   methods use CREATE TABLE IF NOT EXISTS, so this is safe.

2. Log DB errors during shielded init: change silent if-let-Ok pattern
   to match/Err with tracing::warn, so missing-table errors are visible
   instead of silently producing empty note lists.

3. Safety net resync: when the commitment tree has been synced but no
   unspent notes were loaded from DB, clear the tree and reset
   last_synced_index to 0 so the auto-sync rediscovers all notes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): consolidate migrations v28-v32 into v33

Resolves version numbering collision between zk and v1.0-dev branches:
the zk branch used v28 for shielded tables while v1.0-dev used v28 for
contacts. After merging, users migrating from either branch could end up
with missing tables depending on which version their DB was at.

v33 runs all sub-migrations idempotently in one step, ensuring all
tables exist regardless of prior migration history.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): consolidate migrations v28-v32 into v33

Resolves version numbering collision between zk and v1.0-dev branches:
the zk branch used v28 for shielded tables while v1.0-dev used v28 for
contacts. After merging, users migrating from either branch could end up
with missing tables depending on which version their DB was at.

v33 runs all sub-migrations idempotently in one step, ensuring all
tables exist regardless of prior migration history.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: pin platform dependency to zk-fixes revision

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): address PR review — fresh schema, error propagation, rename coverage

- propagate SQL error in add_nullifier_sync_timestamp_column instead of
  swallowing it with unwrap_or(false)
- add shielded_notes and shielded_wallet_meta to rename_network_dash_to_mainnet

Note: Fix 1 (last_nullifier_sync_timestamp in CREATE TABLE) was already
present on this branch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): address PR review — fresh schema, error propagation, rename coverage

- propagate SQL error in add_nullifier_sync_timestamp_column instead of
  swallowing it with unwrap_or(false)
- add shielded_notes and shielded_wallet_meta to rename_network_dash_to_mainnet

Note: Fix 1 (last_nullifier_sync_timestamp in CREATE TABLE) was already
present on this branch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): add foreign key constraints to shielded tables

shielded_notes and shielded_wallet_meta both had wallet_seed_hash columns
with no FK constraint. Added FOREIGN KEY (wallet_seed_hash) REFERENCES
wallet(seed_hash) ON DELETE CASCADE to both, matching the pattern used by
all other per-wallet tables in the codebase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): add foreign key constraints to shielded tables

shielded_notes and shielded_wallet_meta both had wallet_seed_hash columns
with no FK constraint. Added FOREIGN KEY (wallet_seed_hash) REFERENCES
wallet(seed_hash) ON DELETE CASCADE to both, matching the pattern used by
all other per-wallet tables in the codebase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): remove duplicate shielded methods after v1.0-dev merge

On zk-fixes, shielded DB methods live in shielded.rs. The v1.0-dev
backport inlined them in initialization.rs (no shielded.rs on that
branch). Merging v1.0-dev back caused E0592 duplicate definitions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(test): remove duplicate wallet store in register_test_address

register_test_address called db.store_wallet redundantly — callers
already store the wallet before calling it, causing UNIQUE constraint
violations when tests run in parallel on CI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(shielded): address PR review — error propagation, retry helper, safety net

- Propagate DB error from get_unspent_shielded_notes instead of swallowing
- Extract with_anchor_retry() helper to deduplicate ~27 lines across
  shielded_transfer_task, unshield_credits_task, shielded_withdrawal_task
- Fix safety net false positive: check all notes (spent + unspent) to
  distinguish "never had notes" from "all spent"
- Propagate error from clear_commitment_tree_tables instead of discarding

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(error): address PR review — Amount formatting, Display completeness

- Add Amount::dash_from_credits() constructor to model/amount.rs
- Replace manual format_credits_as_dash() logic with Amount::dash_from_credits().to_string(),
  so credit amounts use Amount's Display (with DASH unit, trimmed trailing zeros)
- Update ShieldedFeeExceedsBalance #[error] message: remove redundant "Dash" literal
  (Amount's Display already includes the "DASH" unit suffix)
- Update format_credits_as_dash tests to match new output format ("1 DASH", "2.5 DASH", etc.)
- Remove special-casing that showed with_details() for all RPC errors unconditionally:
  all required user-facing info is already in each variant's Display string;
  technical details are now shown only in developer mode (aligns with CLAUDE.md §error-messages rule 4)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(rpc): address PR review — context fallback, success banner, error source

- Guard in-memory config update + reinit behind a check that the target
  network's AppContext actually exists; skip both when it doesn't so we
  don't accidentally overwrite mainnet config via the fallback path.
- Show success banner only when reinit succeeds; show a warning when it
  fails so the user knows the connection may not reflect the new password.
- Make CoreRpcConnectionFailed.source Option<dashcore_rpc::Error> so
  chain_lock_rpc_error can pass None instead of fabricating a fake
  ConnectionRefused error from a borrowed reference.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: simplify shielded helpers comment in initialization.rs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): address PR #789 review — doc comments, migration default

- Split misplaced doc comment: `add_wallet_transaction_status_column`
  now has its own Migration 30 doc; `rename_network_dash_to_mainnet`
  gets its own Migration 29 doc.
- Add inline comment explaining why the migration uses DEFAULT 2
  (Confirmed) while fresh CREATE TABLE in wallet.rs uses DEFAULT 0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): remove duplicate shielded methods after v1.0-dev merge

On zk-fixes, shielded DB methods live in shielded.rs. The v1.0-dev
backport inlined them in initialization.rs (no shielded.rs on that
branch). Merging v1.0-dev back caused E0592 duplicate definitions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: simplify shielded helpers comment in initialization.rs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore(db): document migration DEFAULT 2 vs fresh DEFAULT 0

Existing transactions predate status tracking and are assumed
confirmed (DEFAULT 2). Fresh installs use DEFAULT 0 (Unconfirmed).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(wallet): address review — zero-balance detection, status warning

Add `spv_balance_known: bool` to `Wallet` to distinguish a synced
zero-balance wallet from an unsynced one. `spv_confirmed_balance()`
now returns `None` only before the first SPV report, and `Some(0)`
after SPV confirms an empty wallet.

Add `tracing::warn!` in `TransactionStatus::from_u8` when an unknown
discriminant is encountered, avoiding silent data coercion in a
financial context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(error): address review — typed BIP32 source, migration error variant

Replace WalletKeyDerivationFailed { detail: String } with a typed
#[source] field (Box<dyn Error + Send + Sync>) so the error chain is
preserved and Display/Debug separation is explicit. Update all callsites
in wallet/mod.rs and identity/mod.rs accordingly.

Replace the semantically wrong InvalidParameterName map_err in
rename_network_dash_to_mainnet with a plain ? — execute() already
returns rusqlite::Result so no conversion is needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test(db): add v33 consolidated migration regression tests

Two test scenarios verify the v33 consolidated migration that merges
sub-migrations v28-v32 (core_wallet_name, shielded tables, contacts,
wallet transaction status, network rename):

1. Fresh install creates all v33 tables/columns directly via create_tables()
2. Upgrade from v27 runs the full migration path and produces identical schema

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): show warning when config save fails instead of success

When config.save() fails, stop early with a warning banner instead
of continuing to reinit and showing a misleading success message.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(error): consolidate format_credits_as_dash and remove jargon from user messages

- fee_estimation::format_credits_as_dash now delegates to Amount::dash_from_credits()
  instead of doing its own f64 math with fixed 8 decimals; output is now trimmed
  (e.g. "1 DASH" instead of "1.00000000 DASH")
- Remove private format_credits_as_dash from error.rs; import the pub version from
  fee_estimation instead
- IdentityInsufficientBalance: show amounts in DASH rather than raw credit integers
- ShieldedAnchorMismatch: replace "out of sync" with plain-language retry guidance
- ShieldedFeeExceedsBalance: remove "shielded balance" / "shield more credits" jargon
- ShieldedInsufficientPoolNotes: remove count interpolations and ZK pool jargon
- Update tests throughout to match new message content and format

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(test): remove duplicate wallet store in register_test_address

register_test_address called db.store_wallet redundantly — callers
already store the wallet before calling it, causing UNIQUE constraint
violations when tests run in parallel on CI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(core): log chain lock RPC errors that aren't auth/connection failures

Silent swallowing of errors like "Unable to find any ChainLock" made it
impossible to diagnose why the active network showed as Disconnected.
Now warns via tracing when chain_lock_rpc_error returns None.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ui): surface chain lock RPC errors in Networks tab

Previously, non-auth/non-connection RPC errors (e.g. "Unable to find
any ChainLock") were silently swallowed — the UI showed "Disconnected"
with no explanation. Now the error message is carried through the
ChainLocks result, stored in ConnectionStatus.rpc_last_error, and
displayed as "Error: ..." in both developer and non-developer RPC
status labels on the Networks tab.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(ui): unified AddressInput component with autocomplete (#787)

* feat(ui): add unified AddressInput component with autocomplete

Introduce a reusable AddressInput component that handles text input with
real-time address type detection, autocomplete from wallet data, balance
display, type filtering, and network-aware validation. Supports Core,
Platform, Shielded, and Identity address kinds.

New files:
- src/model/address.rs: AddressKind enum and ValidatedAddress enum
- src/ui/components/address_input.rs: full component with 33 unit tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): integrate AddressInput component into send and unshield screens

Replace inline address parsing and validation with the unified
AddressInput component across 3 proof-of-concept sites:

- UnshieldCreditsScreen: full migration, removes local Destination enum
  and parse_destination(), gains autocomplete and type-restricted input
- WalletSendScreen simple mode: full migration, removes AddressType enum,
  replaces detect_address_type/is_shielded_address with AddressKind-based
  detection, eliminates double-parsing in send handlers
- WalletSendScreen advanced mode: minimal migration, updates type
  detection to use AddressKind, keeps CoreAddressInput/PlatformAddressInput
  structs unchanged

Net reduction of ~47 lines per screen. All send flows preserved.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test(address-input): fix misleading test and add missing coverage

The existing `disabled_type_rejected_with_correct_error` test was testing
empty input (which returns no error) rather than actually verifying type
restriction. Fixed the test and added coverage for:
- Selection-only mode rejection of manual input
- Identity validation with valid/invalid identifiers
- Truncate boundary at exactly 16/17 characters
- Empty input in selection-only and restricted-type modes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): address QA findings for AddressInput component

QA-001: Extract duplicated address detection logic from send_screen.rs
into AddressKind::detect() on the model type. Both send_screen and
address_input now delegate to the single canonical implementation.

QA-002: Fix autocomplete "...and N more" count using unfiltered total.
filtered_entries() now returns the pre-truncation match count so the
overflow label shows the correct number of remaining matches.

QA-003: Add minimum length check (>= 60 chars) to shielded address
validation. Previously any string with the correct prefix was accepted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): address bot review findings for AddressInput component

- Validate shielded addresses via OrchardAddress::from_bech32m_string()
  instead of prefix+length check only
- Use char-aware truncation in truncate_address() to prevent panics
  on multi-byte UTF-8 input (DPNS labels, emoji)
- Reset AddressInput when source selection changes in send screen,
  and configure allowed destination kinds per source type
- Store bech32m string in ValidatedAddress::Platform variant so
  to_address_string() returns canonical encoding instead of debug hex

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): address remaining review findings for AddressInput component

- Fix manual entry not propagating validated address on blur (HIGH)
- Fix selected_from_autocomplete causing repeated change signals
- Remove protocol jargon from unshield screen messages
- Reject mixed-case bech32m platform addresses per BIP-350
- Use per-instance ComboBox ID to prevent state collision
- Clear cached_detection after autocomplete selection
- Fix doc comments and reuse filtered_entries() computation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): support multi-wallet autocomplete in AddressInput

Replace with_wallet/set_wallet (single wallet) with with_wallets/set_wallets
(slice of wallets). When multiple wallets are loaded, each autocomplete entry
is prefixed with the wallet alias so the user can tell which wallet owns the
address.

send_screen.rs now passes all loaded wallets to AddressInput instead of only
the selected one.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): restore missing "Show zero-balance addresses" checkbox

The checkbox was accidentally dropped during a branch merge. Restored
the horizontal layout with heading on the left and checkbox
right-aligned, matching the v1.0-dev implementation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): improve AddressInput autocomplete UX

- Fix click selection: keep popup rendered when text field loses focus
  so the click handler fires (was gated on has_focus only)
- Show dropdown on focus with any input length (removed 3-char minimum)
- Add type suffix (Core), (Platform), (Identity), (Shielded) to
  dropdown entries when multiple address types are enabled
- Validate immediately on paste (text >3 chars) instead of requiring
  blur first, so "Fund Platform Address" button activates without
  needing to click away

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): make entire autocomplete row clickable in AddressInput

Previously only the address label was clickable — clicking the balance
text on the right side of the row did nothing. Now the click handler
uses the horizontal row response, so the entire row triggers selection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): fix autocomplete click handling and format balances with 4dp

- Capture selectable_label click response instead of discarding it;
  combine with row-level click for full-row clickability
- Format all DASH balances in dropdown with exactly 4 decimal places

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): use single interaction rect for full-row clickable autocomplete

Replace selectable_label + horizontal layout with allocate_exact_size
and manual painting — no child widgets steal clicks, the entire row
(address, balance, dead space) is clickable with hover feedback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(ui): add wallet address autocomplete to unshield screen

Populate AddressInput with wallet addresses via .with_wallets() so
users can select a destination from the dropdown instead of manually
entering addresses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(error): handle asset lock and shielded insufficient funds errors with user-friendly messages

Add two new TaskError variants to replace raw SDK errors with actionable,
jargon-free messages:

- AssetLockOutPointInsufficientBalance: shown when an asset lock outpoint
  has been partially consumed and lacks credits for the operation. Extracts
  credits_left/credits_required and displays DASH amounts.

- ShieldedAddressInsufficientFunds: shown when a shielded broadcast fails
  because the address balance is too low. Detected in shielded_broadcast_error()
  before falling through to ShieldedBroadcastFailed.

Both follow the IdentityInsufficientBalance pattern with format_credits_as_dash.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): use AmountInput component for all amount inputs in wallet screens

Replace raw text_edit_singleline + custom parse_amount_*() methods with
the AmountInput component in 4 shielded screens (unshield, shield,
shield-from-asset-lock, shielded-send). This fixes a bug where entering
"1" would send 1 credit instead of 1 DASH because the raw input had
ambiguous parsing. Also adds "Amount (DASH):" label to SendScreen's
AmountInput and removes its redundant manual label.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add UI components reference and teach CLAUDE.md to use it

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): use AddressInput component in Mine dialog for core address selection

Replace the manual ComboBox address selector in the Mine Blocks dialog
with the unified AddressInput component, configured for Core-only
addresses with selection-only mode from the current wallet.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): trigger shielded balance refresh after all shielding operations

After Shield, Shield from Core, Send Private, Unshield, and Send Dash
(with shielded source) complete successfully, automatically dispatch a
SyncNotes task so the shielded tab shows updated balances without
manual refresh.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: demote cookie auth fallback log to trace level

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): prevent transaction list showing wrong wallet data

Add defensive checks in render_transactions_section to prevent
cross-wallet transaction leakage:

1. Verify the selected wallet Arc matches the canonical one in
   app_context.wallets (guards against stale references).

2. Filter displayed transactions to only those with at least one
   output matching the wallet's known addresses (prevents showing
   transactions from other wallets that leaked in via a
   non-wallet-scoped RPC endpoint).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): auto-show zero-balance addresses when wallet is empty

When all addresses have zero balance and there are fewer than 5
addresses total, bypass the zero-balance filter so new/empty wallets
show their addresses instead of a blank list. The checkbox remains
functional for wallets with balances.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): trigger shielded sync on wallet refresh and wallet switch

The wallet Refresh button now dispatches a SyncNotes task alongside the
core wallet refresh when the shielded wallet has been initialized.
Switching HD wallets also triggers a full refresh (core + shielded) on
the next frame for unlocked wallets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(log): add diagnostic logging for shielded transfer operations

Add tracing::info! and tracing::debug! logs throughout the shielded
transfer pipeline to enable post-mortem diagnosis when balances don't
update after broadcast. Key additions:

- bundle.rs: log amount, input notes, change before building each
  shielded operation (transfer, shield, unshield, withdrawal,
  shield-from-asset-lock); log broadcast success with note that
  balance updates after next block
- sync.rs: warn when next_start_index is 0 (full rescan); log
  post-sync balance with unspent note count
- context/shielded.rs: log note spend marking with before/after
  unspent counts in with_anchor_retry
- shielded_send_screen.rs: log task result variant received and
  post-transfer sync completion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): improve shielded transfer UX messaging for balance update delays

After a shielded transfer, the sender's change notes and the recipient's
new notes won't appear until the next block is mined and synced. Users
see their balance temporarily drop to 0 and think the transfer failed.

Add informational messages on all three shielded transfer screens
(ShieldedSendScreen, UnshieldCreditsScreen, WalletSendScreen) explaining
that balances will update after the next block is confirmed. For
shielded-to-shielded transfers, also note that the recipient needs to
sync after the next block.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): scope commitment tree per wallet for multi-wallet correctness

Each wallet's ClientPersistentCommitmentTree now uses its own dedicated
SQLite file under <data_dir>/shielded/<hex>.db instead of sharing the
main database's global commitment_tree_* tables. This prevents wallets
from stepping on each other's Merkle tree state, which caused invalid
witnesses and silently rejected transactions.

Changes:
- SHLD-001: Per-wallet commitment tree DB files via ClientPersistentCommitmentTree::open()
- SHLD-002: clear_commitment_tree_for_wallet() scoped to a single wallet
- SHLD-007: Safety-net resync guard now logs wallet seed hash before clearing
- Migration v34: drops orphaned global commitment_tree_* tables
- clear_network_data() deletes per-wallet tree files for the target network

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): prevent permanent state leak on sync_notes failure in with_anchor_retry

When sync_notes() failed after an anchor mismatch, the early `?` return
skipped re-inserting the shielded state back into the HashMap, permanently
orphaning it until app restart. Now the sync result is captured without
early return, ensuring the state is always re-inserted regardless of
success or failure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): apply password change for current session even when config save fails

The in-memory config update and SDK reinit only ran on the save-success
path, silently discarding the password change on save failure despite
the banner promising session-level application. Now the in-memory update
and reinit run unconditionally; only the banner text varies across the
four (save x reinit) outcome combinations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(error): use dedicated TaskError variants for non-build wallet errors

reload_utxos() and recalculate_affected_address_balances() errors were
routed through shielded_build_error() which pattern-matches for build-
specific patterns like "AnchorMismatch". These are wallet operations,
not shielded builds. Added WalletUtxoReloadFailed and
WalletBalanceRecalculationFailed variants with appropriate user-facing
messages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(error): preserve error chain in CoreRpcConnectionFailed

chain_lock_rpc_error() took the RPC error by reference and created
CoreRpcConnectionFailed with source: None, dropping all diagnostic
info. Added a detail: Option<String> field to the variant to carry
the formatted error. Boxed the source field to keep the enum size
under the clippy threshold. Updated all constructors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(test): rename test_v33_migration_fresh_install to match DB version 34

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* revert(shielded): use shared main DB for commitment tree instead of per-wallet files

Reverts the per-wallet SQLite file approach (63ce0e1). Multiple wallets
share the same commitment_tree_* tables in the main database. This is
accepted behavior until the SDK adds proper wallet-scoping support
(dashpay/grovedb#653).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): harden anchor retry, commitment tree clearing, and state guard

SEC-002: Verify shielded balance after anchor retry sync before retrying
the operation — prevents doomed retries with zero balance.

SEC-003: Handle missing commitment tree tables in clear_commitment_tree_tables
(grovedb creates them lazily, so DELETEs fail on fresh installs).

Fix clear_network_data to log-and-continue when commitment tree clearing
fails — these tables are optional and shouldn't block network data reset.

Add SEC-001 INTENTIONAL annotation for panic safety in with_anchor_retry.

Document shield_from_asset_lock_task: anchor retry is not applicable since
it only reads the payment address and doesn't use the commitment tree.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(core): sanitize RPC errors and simplify CoreRpcConnectionFailed

SEC-006: Store user-friendly message instead of raw RPC error string
in network status when chain lock query fails with a non-auth,
non-connection error.

CODE-002: Remove redundant detail field from CoreRpcConnectionFailed --
format diagnostic info into the url field instead, keeping source for
the error chain.

Add SEC-005 INTENTIONAL annotation for RPC errors in status tooltip.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): config permissions, address detection, and per-frame caching

SEC-007: Set config file permissions to 0600 on Unix after save (contains
RPC credentials).

CODE-003: Deduplicate Amount::dash_from_duffs by delegating to
dash_from_credits.

CODE-006: Remove unused network parameter from AddressKind::detect --
detection is format-based, network validation happens separately.

CODE-004/CODE-005: Cache filtered transaction indices per wallet to
avoid rebuilding HashSet and filtering on every frame. Invalidated
on wallet switch and refresh.

CODE-008: Reset AddressInput widgets on network switch so they pick up
the new network for validation. Add invalidate_address_input methods
to SendScreen, UnshieldCreditsScreen, and WalletsBalancesScreen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(logging): correct log levels per coding best practices

- debug! -> trace!: "state transition built, broadcasting" messages in
  bundle.rs (5 occurrences) — primary-path step-by-step progress
- trace! -> debug!: cookie auth fallback in core/mod.rs and context/mod.rs
  (2 occurrences) — secondary/fallback execution path, not primary flow
- info! -> debug!: anchor mismatch retry in context/shielded.rs — error
  handling branch, not a business event
- info! -> debug!: "marked N note(s) spent" in context/shielded.rs —
  internal bookkeeping, not a user-visible business event
- info! -> debug!: post-transfer sync complete in shielded_send_screen.rs —
  internal plumbing at screen level

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(mcp): resolve async lifetime errors for Rust 2024 edition

Use spawn_blocking + block_on in dispatch_task to avoid Send bound
issues with platform SDK types (DataContract/Sdk references across
await points). Same pattern already used by AppState::handle_backend_task.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: fix mcp dispatch

* doc: remove obsolete manual teting docs

---------

Co-authored-by: Quantum Explorer <quantum@dash.org>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: lklimek <lklimek@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
lklimek added a commit that referenced this pull request Mar 26, 2026
* more work

* more work

* fix(spv): zero out stale per-address balances during reconciliation (#627)

During SPV reconciliation, per_address_sum only contains addresses with
current UTXOs. Addresses whose funds were fully spent never had their
balance reset to zero, causing the address table to display stale
non-zero balances even though UTXO count correctly showed 0.

Now explicitly zeroes address_balances for any known address absent from
the refreshed UTXO map before applying current sums.

Closes #571

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix: handle malformed YAML gracefully in load_testnet_nodes_from_yml (#613)

* fix: handle malformed YAML gracefully in load_testnet_nodes_from_yml

Replace .expect() with match expression to avoid panicking when
.testnet_nodes.yml contains malformed YAML. Instead, logs the error
with tracing::error and returns None, allowing the application to
continue without crashing.

Closes #557

Co-authored-by: lklimek <lklimek@users.noreply.github.com>

* fix: propagate YAML parse errors to UI and remove unwrap calls

- Change load_testnet_nodes_from_yml to return Result<Option<TestnetNodes>, String>
  so parse errors display in the UI error banner instead of only logging
- Use explicit type annotation on serde_yaml_ng::from_str::<TestnetNodes>
- Replace .unwrap() with .and_then() in fill_random_hpmn/fill_random_masternode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: lklimek <lklimek@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* chore: let Claude write manual test scenarios for PRs (#634)

* chore: move doc/ contents into docs/ and update references

Consolidate documentation under a single docs/ directory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: CLAUDE.md should write manual test scenarios for PRs

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* build(flatpak): use only-arches for dynamic protoc architecture selection (#603)

* build(flatpak): use only-arches for dynamic protoc architecture selection

Replace the fragile sed-based CI patching of the Flatpak manifest with
Flatpak's native `only-arches` source selector. The protoc module now
declares both x86_64 and aarch64 sources inline, and build-commands use
a glob pattern (`protoc-*.zip`) so no per-arch fixup is needed.

Changes:
- flatpak manifest: add aarch64 protoc source with `only-arches`,
  use glob in unzip commands, remove stale CI-patching comment
- CI workflow: remove `protoc-zip`/`protoc-sha256` matrix variables
  and the "Patch manifest for architecture" step

https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG

* fix(flatpak): use dest-filename for deterministic protoc extraction

Use dest-filename to normalize both arch-specific protoc zips to a
common name, avoiding glob expansion in build-commands. This ensures
the unzip target is deterministic regardless of shell behavior in the
Flatpak sandbox.

https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG

* build: update platform to b445b6f0 and remove rust-dashcore patches

Update dashpay/platform dependency from d6f4eb9a to b445b6f0e0bd4863
(3.0.1 → 3.1.0-dev.1). Remove the [patch] section that pinned
rust-dashcore crates to a separate rev, as the new platform commit
resolves them correctly on its own.

https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG

---------

Co-authored-by: Claude <noreply@anthropic.com>

* fix(ci): remove local path patches, use git deps for platform crates

The [patch] section referenced ../platform local paths that don't exist
in CI. Since dash-sdk already depends on the feat/zk branch, all
transitive platform crates resolve correctly without patches. Also fixes
a formatting issue in address_table.rs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(test): store wallet in DB before registering addresses

The remove_utxos tests were failing because `register_test_address`
inserts into `wallet_addresses` which has a foreign key constraint on
`wallet(seed_hash)`. Added the missing `store_wallet` call so the
parent row exists before inserting child address rows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(build): restore shielded module declaration removed during merge

The `pub mod shielded;` declaration was accidentally removed from
`src/model/wallet/mod.rs` during the v1.0-dev merge, causing build
failures since the shielded.rs file still exists.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(test): restore store_wallet calls lost in merge (#663)

* Initial plan

* fix(test): restore store_wallet calls lost in merge

Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com>

* Merge remote-tracking branch 'origin/v1.0-dev' into zk-extract/all-merged

Resolve conflicts in Cargo.toml (keep feat/zk branch), Cargo.lock
(regenerate with pinned platform rev 4d7b9be5), and
backend_task/mod.rs (combine TaskError wrapping with ShieldedTask).

Fix post-merge integration issues:
- SPV manager: remove stale .await on subscribe methods, add
  command_receiver channel for updated DashSpvClient::run() API
- send_screen: update SendStatus::WaitingForResult to unit variant
- network_chooser_screen: handle new SyncState::Initializing variant

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: update platform dependency to 3.1-dev branch

Migrate from feat/zk to 3.1-dev branch of dashpay/platform,
adapting to breaking API changes in dash-spv, key-wallet, and dpp:

- FeeLevel removed; use FeeRate::normal() directly
- DashSpvClientInterface/Command removed; use DashSpvClient directly
- SyncState::Initializing removed; replaced with WaitForEvents
- NetworkExt trait inlined into Network impl
- OrchardProver now requires wrapper struct around ProvingKey
- OrchardAddress::from_raw_bytes now returns Result
- Builder functions gain fee/platform_version params
- NullifierSyncConfig API uses NullifierSyncCheckpoint
- WalletManager.create_unsigned_payment_transaction removed;
  use TransactionBuilder directly
- Work around Send lifetime issues with spawn_blocking

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: migrate shielded module from Result<T, String> to typed TaskError

Replace all Result<T, String> error patterns in the shielded pool module
with typed TaskError variants, aligning with the codebase-wide typed error
migration (PR #739).

New TaskError variants: ShieldedNoUnspentNotes, ShieldedInsufficientBalance,
PlatformAddressNotFound, ShieldedMerkleWitnessUnavailable,
ShieldedTransitionBuildFailed, ShieldedBroadcastFailed,
ShieldedInvalidRecipientAddress, ShieldedAssetLockTimeout,
ShieldedSyncFailed, ShieldedTreeUpdateFailed, ShieldedNullifierSyncFailed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): prevent settings password row from clipping right edge

Reserve width for Save and Auto Update buttons so the password input
doesn't consume all available space, pushing buttons off-screen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(rpc): include host:port in connection-refused errors and always show details

Add CoreRpcConnectionFailed variant to TaskError that includes the
configured address in the user-facing message.  Connection-refused
errors are now detected via is_rpc_connection_error() and enriched
with host:port at every RPC call site where the URL is known
(AppContext::rpc_error_with_url helper).

Details panel is now shown for all RPC-related errors regardless of
developer mode, so users can always see the technical information
they need to diagnose connectivity issues.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): save RPC password for active network instead of hardcoded Regtest

The Network Chooser screen had three bugs related to RPC password handling:

1. Password input was initialized from Regtest config only, ignoring the
   current network selection.
2. Password UI was hidden for all networks except Regtest, even though
   Mainnet/Testnet/Devnet also use RPC mode.
3. Save logic was hardcoded to update Regtest config and triggered a
   SwitchNetwork(Regtest) action, which disconnected the active network's
   ZMQ listener unnecessarily.

Now the password input shows for any network in RPC mode, reads/writes
the correct network config, reinits the RPC client in-place without
triggering a network switch, and reloads the stored password when the
user switches network tabs. The "Auto Update" (dashmate) button remains
Regtest-only since dashmate is only relevant for local networks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): ensure funding method dropdown fits all items without scrollbar

Add explicit .height(200.0) to the funding method ComboBox in both
top_up_identity_screen and add_new_identity_screen. The add_enabled_ui
wrappers inflate item height via frame overhead, causing the popup to
clip to a single row at the default height.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(error): show actionable message for insufficient identity balance

IdentityInsufficientBalanceError from the SDK now maps to a dedicated
TaskError::IdentityInsufficientBalance variant instead of falling through
to "An unexpected error occurred." The user sees the available and required
credit amounts along with a clear "top up your identity" action.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): clear stale error banners when saving RPC password

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use network-compatible comparison for platform address lookups

Regtest and Testnet share the `tdash` bech32m prefix, but
`PlatformAddress::from_bech32m_string()` always returns `Network::Testnet`
for `tdash` addresses. The strict `!=` comparison in the fund-platform
dialog rejected valid Regtest addresses with "Address network mismatch".

- Make `networks_address_compatible()` `pub(crate)` in `model::wallet`
  so all modules can reuse the canonical check
- Remove the duplicate private copy in `backend_task::core` and import
  from the single source
- Replace `network != self.app_context.network` in `dialogs.rs` with
  `networks_address_compatible()` so Testnet/Devnet/Regtest addresses
  are accepted interchangeably

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(error): add actionable messages for shielded fee and pool-size errors

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(error): add actionable message for shielded anchor mismatch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): auto-resync notes and retry on anchor mismatch

When unshield_credits, shielded_transfer, or shielded_withdrawal fails
with ShieldedAnchorMismatch (stale commitment tree witnesses), automatically
sync notes once to update the tree and retry the operation. Only one resync
attempt is made — if the retry also fails, the error propagates as-is.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): ensure shielded tables exist and log DB errors during init

Three changes to fix empty shielded balance after app restart:

1. Defensive table creation in initialize(): after migrations complete,
   ensure shielded_notes and shielded_wallet_meta tables exist even if
   the DB version was already past v29/v30 from a prior build. Both
   methods use CREATE TABLE IF NOT EXISTS, so this is safe.

2. Log DB errors during shielded init: change silent if-let-Ok pattern
   to match/Err with tracing::warn, so missing-table errors are visible
   instead of silently producing empty note lists.

3. Safety net resync: when the commitment tree has been synced but no
   unspent notes were loaded from DB, clear the tree and reset
   last_synced_index to 0 so the auto-sync rediscovers all notes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): consolidate migrations v28-v32 into v33

Resolves version numbering collision between zk and v1.0-dev branches:
the zk branch used v28 for shielded tables while v1.0-dev used v28 for
contacts. After merging, users migrating from either branch could end up
with missing tables depending on which version their DB was at.

v33 runs all sub-migrations idempotently in one step, ensuring all
tables exist regardless of prior migration history.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): consolidate migrations v28-v32 into v33

Resolves version numbering collision between zk and v1.0-dev branches:
the zk branch used v28 for shielded tables while v1.0-dev used v28 for
contacts. After merging, users migrating from either branch could end up
with missing tables depending on which version their DB was at.

v33 runs all sub-migrations idempotently in one step, ensuring all
tables exist regardless of prior migration history.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: pin platform dependency to zk-fixes revision

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): address PR review — fresh schema, error propagation, rename coverage

- propagate SQL error in add_nullifier_sync_timestamp_column instead of
  swallowing it with unwrap_or(false)
- add shielded_notes and shielded_wallet_meta to rename_network_dash_to_mainnet

Note: Fix 1 (last_nullifier_sync_timestamp in CREATE TABLE) was already
present on this branch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): address PR review — fresh schema, error propagation, rename coverage

- propagate SQL error in add_nullifier_sync_timestamp_column instead of
  swallowing it with unwrap_or(false)
- add shielded_notes and shielded_wallet_meta to rename_network_dash_to_mainnet

Note: Fix 1 (last_nullifier_sync_timestamp in CREATE TABLE) was already
present on this branch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): add foreign key constraints to shielded tables

shielded_notes and shielded_wallet_meta both had wallet_seed_hash columns
with no FK constraint. Added FOREIGN KEY (wallet_seed_hash) REFERENCES
wallet(seed_hash) ON DELETE CASCADE to both, matching the pattern used by
all other per-wallet tables in the codebase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): add foreign key constraints to shielded tables

shielded_notes and shielded_wallet_meta both had wallet_seed_hash columns
with no FK constraint. Added FOREIGN KEY (wallet_seed_hash) REFERENCES
wallet(seed_hash) ON DELETE CASCADE to both, matching the pattern used by
all other per-wallet tables in the codebase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): remove duplicate shielded methods after v1.0-dev merge

On zk-fixes, shielded DB methods live in shielded.rs. The v1.0-dev
backport inlined them in initialization.rs (no shielded.rs on that
branch). Merging v1.0-dev back caused E0592 duplicate definitions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(test): remove duplicate wallet store in register_test_address

register_test_address called db.store_wallet redundantly — callers
already store the wallet before calling it, causing UNIQUE constraint
violations when tests run in parallel on CI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(shielded): address PR review — error propagation, retry helper, safety net

- Propagate DB error from get_unspent_shielded_notes instead of swallowing
- Extract with_anchor_retry() helper to deduplicate ~27 lines across
  shielded_transfer_task, unshield_credits_task, shielded_withdrawal_task
- Fix safety net false positive: check all notes (spent + unspent) to
  distinguish "never had notes" from "all spent"
- Propagate error from clear_commitment_tree_tables instead of discarding

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(error): address PR review — Amount formatting, Display completeness

- Add Amount::dash_from_credits() constructor to model/amount.rs
- Replace manual format_credits_as_dash() logic with Amount::dash_from_credits().to_string(),
  so credit amounts use Amount's Display (with DASH unit, trimmed trailing zeros)
- Update ShieldedFeeExceedsBalance #[error] message: remove redundant "Dash" literal
  (Amount's Display already includes the "DASH" unit suffix)
- Update format_credits_as_dash tests to match new output format ("1 DASH", "2.5 DASH", etc.)
- Remove special-casing that showed with_details() for all RPC errors unconditionally:
  all required user-facing info is already in each variant's Display string;
  technical details are now shown only in developer mode (aligns with CLAUDE.md §error-messages rule 4)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(rpc): address PR review — context fallback, success banner, error source

- Guard in-memory config update + reinit behind a check that the target
  network's AppContext actually exists; skip both when it doesn't so we
  don't accidentally overwrite mainnet config via the fallback path.
- Show success banner only when reinit succeeds; show a warning when it
  fails so the user knows the connection may not reflect the new password.
- Make CoreRpcConnectionFailed.source Option<dashcore_rpc::Error> so
  chain_lock_rpc_error can pass None instead of fabricating a fake
  ConnectionRefused error from a borrowed reference.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: simplify shielded helpers comment in initialization.rs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): address PR #789 review — doc comments, migration default

- Split misplaced doc comment: `add_wallet_transaction_status_column`
  now has its own Migration 30 doc; `rename_network_dash_to_mainnet`
  gets its own Migration 29 doc.
- Add inline comment explaining why the migration uses DEFAULT 2
  (Confirmed) while fresh CREATE TABLE in wallet.rs uses DEFAULT 0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): remove duplicate shielded methods after v1.0-dev merge

On zk-fixes, shielded DB methods live in shielded.rs. The v1.0-dev
backport inlined them in initialization.rs (no shielded.rs on that
branch). Merging v1.0-dev back caused E0592 duplicate definitions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: simplify shielded helpers comment in initialization.rs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore(db): document migration DEFAULT 2 vs fresh DEFAULT 0

Existing transactions predate status tracking and are assumed
confirmed (DEFAULT 2). Fresh installs use DEFAULT 0 (Unconfirmed).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(wallet): address review — zero-balance detection, status warning

Add `spv_balance_known: bool` to `Wallet` to distinguish a synced
zero-balance wallet from an unsynced one. `spv_confirmed_balance()`
now returns `None` only before the first SPV report, and `Some(0)`
after SPV confirms an empty wallet.

Add `tracing::warn!` in `TransactionStatus::from_u8` when an unknown
discriminant is encountered, avoiding silent data coercion in a
financial context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(error): address review — typed BIP32 source, migration error variant

Replace WalletKeyDerivationFailed { detail: String } with a typed
#[source] field (Box<dyn Error + Send + Sync>) so the error chain is
preserved and Display/Debug separation is explicit. Update all callsites
in wallet/mod.rs and identity/mod.rs accordingly.

Replace the semantically wrong InvalidParameterName map_err in
rename_network_dash_to_mainnet with a plain ? — execute() already
returns rusqlite::Result so no conversion is needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test(db): add v33 consolidated migration regression tests

Two test scenarios verify the v33 consolidated migration that merges
sub-migrations v28-v32 (core_wallet_name, shielded tables, contacts,
wallet transaction status, network rename):

1. Fresh install creates all v33 tables/columns directly via create_tables()
2. Upgrade from v27 runs the full migration path and produces identical schema

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): show warning when config save fails instead of success

When config.save() fails, stop early with a warning banner instead
of continuing to reinit and showing a misleading success message.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(error): consolidate format_credits_as_dash and remove jargon from user messages

- fee_estimation::format_credits_as_dash now delegates to Amount::dash_from_credits()
  instead of doing its own f64 math with fixed 8 decimals; output is now trimmed
  (e.g. "1 DASH" instead of "1.00000000 DASH")
- Remove private format_credits_as_dash from error.rs; import the pub version from
  fee_estimation instead
- IdentityInsufficientBalance: show amounts in DASH rather than raw credit integers
- ShieldedAnchorMismatch: replace "out of sync" with plain-language retry guidance
- ShieldedFeeExceedsBalance: remove "shielded balance" / "shield more credits" jargon
- ShieldedInsufficientPoolNotes: remove count interpolations and ZK pool jargon
- Update tests throughout to match new message content and format

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(test): remove duplicate wallet store in register_test_address

register_test_address called db.store_wallet redundantly — callers
already store the wallet before calling it, causing UNIQUE constraint
violations when tests run in parallel on CI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(core): log chain lock RPC errors that aren't auth/connection failures

Silent swallowing of errors like "Unable to find any ChainLock" made it
impossible to diagnose why the active network showed as Disconnected.
Now warns via tracing when chain_lock_rpc_error returns None.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ui): surface chain lock RPC errors in Networks tab

Previously, non-auth/non-connection RPC errors (e.g. "Unable to find
any ChainLock") were silently swallowed — the UI showed "Disconnected"
with no explanation. Now the error message is carried through the
ChainLocks result, stored in ConnectionStatus.rpc_last_error, and
displayed as "Error: ..." in both developer and non-developer RPC
status labels on the Networks tab.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(ui): unified AddressInput component with autocomplete (#787)

* feat(ui): add unified AddressInput component with autocomplete

Introduce a reusable AddressInput component that handles text input with
real-time address type detection, autocomplete from wallet data, balance
display, type filtering, and network-aware validation. Supports Core,
Platform, Shielded, and Identity address kinds.

New files:
- src/model/address.rs: AddressKind enum and ValidatedAddress enum
- src/ui/components/address_input.rs: full component with 33 unit tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): integrate AddressInput component into send and unshield screens

Replace inline address parsing and validation with the unified
AddressInput component across 3 proof-of-concept sites:

- UnshieldCreditsScreen: full migration, removes local Destination enum
  and parse_destination(), gains autocomplete and type-restricted input
- WalletSendScreen simple mode: full migration, removes AddressType enum,
  replaces detect_address_type/is_shielded_address with AddressKind-based
  detection, eliminates double-parsing in send handlers
- WalletSendScreen advanced mode: minimal migration, updates type
  detection to use AddressKind, keeps CoreAddressInput/PlatformAddressInput
  structs unchanged

Net reduction of ~47 lines per screen. All send flows preserved.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test(address-input): fix misleading test and add missing coverage

The existing `disabled_type_rejected_with_correct_error` test was testing
empty input (which returns no error) rather than actually verifying type
restriction. Fixed the test and added coverage for:
- Selection-only mode rejection of manual input
- Identity validation with valid/invalid identifiers
- Truncate boundary at exactly 16/17 characters
- Empty input in selection-only and restricted-type modes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): address QA findings for AddressInput component

QA-001: Extract duplicated address detection logic from send_screen.rs
into AddressKind::detect() on the model type. Both send_screen and
address_input now delegate to the single canonical implementation.

QA-002: Fix autocomplete "...and N more" count using unfiltered total.
filtered_entries() now returns the pre-truncation match count so the
overflow label shows the correct number of remaining matches.

QA-003: Add minimum length check (>= 60 chars) to shielded address
validation. Previously any string with the correct prefix was accepted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): address bot review findings for AddressInput component

- Validate shielded addresses via OrchardAddress::from_bech32m_string()
  instead of prefix+length check only
- Use char-aware truncation in truncate_address() to prevent panics
  on multi-byte UTF-8 input (DPNS labels, emoji)
- Reset AddressInput when source selection changes in send screen,
  and configure allowed destination kinds per source type
- Store bech32m string in ValidatedAddress::Platform variant so
  to_address_string() returns canonical encoding instead of debug hex

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): address remaining review findings for AddressInput component

- Fix manual entry not propagating validated address on blur (HIGH)
- Fix selected_from_autocomplete causing repeated change signals
- Remove protocol jargon from unshield screen messages
- Reject mixed-case bech32m platform addresses per BIP-350
- Use per-instance ComboBox ID to prevent state collision
- Clear cached_detection after autocomplete selection
- Fix doc comments and reuse filtered_entries() computation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): support multi-wallet autocomplete in AddressInput

Replace with_wallet/set_wallet (single wallet) with with_wallets/set_wallets
(slice of wallets). When multiple wallets are loaded, each autocomplete entry
is prefixed with the wallet alias so the user can tell which wallet owns the
address.

send_screen.rs now passes all loaded wallets to AddressInput instead of only
the selected one.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): restore missing "Show zero-balance addresses" checkbox

The checkbox was accidentally dropped during a branch merge. Restored
the horizontal layout with heading on the left and checkbox
right-aligned, matching the v1.0-dev implementation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): improve AddressInput autocomplete UX

- Fix click selection: keep popup rendered when text field loses focus
  so the click handler fires (was gated on has_focus only)
- Show dropdown on focus with any input length (removed 3-char minimum)
- Add type suffix (Core), (Platform), (Identity), (Shielded) to
  dropdown entries when multiple address types are enabled
- Validate immediately on paste (text >3 chars) instead of requiring
  blur first, so "Fund Platform Address" button activates without
  needing to click away

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): make entire autocomplete row clickable in AddressInput

Previously only the address label was clickable — clicking the balance
text on the right side of the row did nothing. Now the click handler
uses the horizontal row response, so the entire row triggers selection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): fix autocomplete click handling and format balances with 4dp

- Capture selectable_label click response instead of discarding it;
  combine with row-level click for full-row clickability
- Format all DASH balances in dropdown with exactly 4 decimal places

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): use single interaction rect for full-row clickable autocomplete

Replace selectable_label + horizontal layout with allocate_exact_size
and manual painting — no child widgets steal clicks, the entire row
(address, balance, dead space) is clickable with hover feedback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(ui): add wallet address autocomplete to unshield screen

Populate AddressInput with wallet addresses via .with_wallets() so
users can select a destination from the dropdown instead of manually
entering addresses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(error): handle asset lock and shielded insufficient funds errors with user-friendly messages

Add two new TaskError variants to replace raw SDK errors with actionable,
jargon-free messages:

- AssetLockOutPointInsufficientBalance: shown when an asset lock outpoint
  has been partially consumed and lacks credits for the operation. Extracts
  credits_left/credits_required and displays DASH amounts.

- ShieldedAddressInsufficientFunds: shown when a shielded broadcast fails
  because the address balance is too low. Detected in shielded_broadcast_error()
  before falling through to ShieldedBroadcastFailed.

Both follow the IdentityInsufficientBalance pattern with format_credits_as_dash.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): use AmountInput component for all amount inputs in wallet screens

Replace raw text_edit_singleline + custom parse_amount_*() methods with
the AmountInput component in 4 shielded screens (unshield, shield,
shield-from-asset-lock, shielded-send). This fixes a bug where entering
"1" would send 1 credit instead of 1 DASH because the raw input had
ambiguous parsing. Also adds "Amount (DASH):" label to SendScreen's
AmountInput and removes its redundant manual label.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add UI components reference and teach CLAUDE.md to use it

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): redesign wallet screen information architecture

Replace the old Balances|Shielded top-level tabs and account dropdown
with a unified layout that better matches how users think about their
wallet:

- Balance section with collapsible breakdown (Core/Platform split)
- Dev Tools expandable button replaces scattered dev-mode controls
- Transaction History shown as collapsible section (visible in all modes)
- Accounts & Addresses use tabs instead of a dropdown selector
- Shielded view moved from top-level tab to an account tab
- Asset Locks restricted to the Dash Core tab only
- "Main Account" renamed to "Dash Core" throughout
- Fee column added to transaction table (dev mode only)
- "Get Test Dash" button opens testnet faucet in dev tools

Tab visibility follows progressive disclosure: default mode shows only
Dash Core, Platform, and Shielded tabs. Developer mode reveals all
account types that have addresses.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): use AddressInput component in Mine dialog for core address selection

Replace the manual ComboBox address selector in the Mine Blocks dialog
with the unified AddressInput component, configured for Core-only
addresses with selection-only mode from the current wallet.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): trigger shielded balance refresh after all shielding operations

After Shield, Shield from Core, Send Private, Unshield, and Send Dash
(with shielded source) complete successfully, automatically dispatch a
SyncNotes task so the shielded tab shows updated balances without
manual refresh.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: demote cookie auth fallback log to trace level

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): prevent transaction list showing wrong wallet data

Add defensive checks in render_transactions_section to prevent
cross-wallet transaction leakage:

1. Verify the selected wallet Arc matches the canonical one in
   app_context.wallets (guards against stale references).

2. Filter displayed transactions to only those with at least one
   output matching the wallet's known addresses (prevents showing
   transactions from other wallets that leaked in via a
   non-wallet-scoped RPC endpoint).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): auto-show zero-balance addresses when wallet is empty

When all addresses have zero balance and there are fewer than 5
addresses total, bypass the zero-balance filter so new/empty wallets
show their addresses instead of a blank list. The checkbox remains
functional for wallets with balances.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): address PR review — shielded balance, dev tools layout

1. Include shielded balance in the collapsible balance breakdown
2. Add shielded balance to the total wallet balance everywhere
3. Right-align the Dev Tools button in the action row
4. Always show downward arrow on Dev Tools button
5. Dev Tools opens as a vertical dropdown popup instead of
   a horizontal inline row

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): rename transaction heading, add explorer link, merge zk-fixes

- Rename "Transaction History" to "Dash Core Transaction History"
- Add "View" button on transactions for Mainnet/Testnet (opens Insight explorer)
- Merge zk-fixes (mine dialog, shielded refresh, wallet bug fixes, log level)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): trigger shielded sync on wallet refresh and wallet switch

The wallet Refresh button now dispatches a SyncNotes task alongside the
core wallet refresh when the shielded wallet has been initialized.
Switching HD wallets also triggers a full refresh (core + shielded) on
the next frame for unlocked wallets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(log): add diagnostic logging for shielded transfer operations

Add tracing::info! and tracing::debug! logs throughout the shielded
transfer pipeline to enable post-mortem diagnosis when balances don't
update after broadcast. Key additions:

- bundle.rs: log amount, input notes, change before building each
  shielded operation (transfer, shield, unshield, withdrawal,
  shield-from-asset-lock); log broadcast success with note that
  balance updates after next block
- sync.rs: warn when next_start_index is 0 (full rescan); log
  post-sync balance with unspent note count
- context/shielded.rs: log note spend marking with before/after
  unspent counts in with_anchor_retry
- shielded_send_screen.rs: log task result variant received and
  post-transfer sync completion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): improve Dev Tools dropdown layout and refresh mode cycling

Right-align popup content to match the Dev Tools button position.
Replace ComboBox with a cycle-on-click button to avoid nested popup
conflicts in egui, where the inner ComboBox dropdown would close the
parent popup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): fix shielded balance conversion, add tab balances, reorder sync status

- Fix shielded balance inflating total: divide credits by CREDITS_PER_DUFF
  before summing with duffs-denominated balances (was showing 5000 DASH
  instead of 5 DASH)
- Show balances in account tab labels: Dash Core (0.9980) | Platform (0.1000)
  | Shielded (5.0000), with (empty) for zero-balance tabs
- Move Sync Status section inside detail panel between balance breakdown
  and action buttons for better visual flow

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): collapsible sections, move tx history to Dash Core tab, restore account types

- Wrap Addresses, Transaction History, Asset Locks, and Shielded Notes
  in collapsible headers (default open, tx history default closed)
- Move Dash Core Transaction History from top-level into the Dash Core
  tab, between Addresses and Asset Locks
- In developer mode, show ALL account category tabs (Bip44, Platform,
  Bip32, CoinJoin, Identity Registration/System/Top-up/Invitation,
  Provider) even when no addresses exist for that type
- Add TODO for shielded tab layout redesign

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): improve shielded transfer UX messaging for balance update delays

After a shielded transfer, the sender's change notes and the recipient's
new notes won't appear until the next block is mined and synced. Users
see their balance temporarily drop to 0 and think the transfer failed.

Add informational messages on all three shielded transfer screens
(ShieldedSendScreen, UnshieldCreditsScreen, WalletSendScreen) explaining
that balances will update after the next block is confirmed. For
shielded-to-shielded transfers, also note that the recipient needs to
sync after the next block.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): consolidate dev-mode accounts into System tab, limit balance decimals

Replace all individual dev-mode-only account type tabs (Identity Registration,
Identity System, Identity Top-up, Identity Invitation, CoinJoin, Provider,
Legacy BIP32) with a single "System" tab. Inside the System tab, each account
type appears as a collapsible section (collapsed by default) showing address
count and balance.

Tab order: Dash Core | Platform | Shielded | System (dev-mode only, always last)

Additional changes:
- Limit tab balance display to max 4 decimal places (was 8)
- Rename "Dev Tools" button to "Tools"
- Simplify refresh mode button: single button with "Refresh mode: X" text,
  no separate label or arrow indicator
- Remove "Accounts & Addresses" section heading
- Style active tab with underline for visual distinction

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): scope commitment tree per wallet for multi-wallet correctness

Each wallet's ClientPersistentCommitmentTree now uses its own dedicated
SQLite file under <data_dir>/shielded/<hex>.db instead of sharing the
main database's global commitment_tree_* tables. This prevents wallets
from stepping on each other's Merkle tree state, which caused invalid
witnesses and silently rejected transactions.

Changes:
- SHLD-001: Per-wallet commitment tree DB files via ClientPersistentCommitmentTree::open()
- SHLD-002: clear_commitment_tree_for_wallet() scoped to a single wallet
- SHLD-007: Safety-net resync guard now logs wallet seed hash before clearing
- Migration v34: drops orphaned global commitment_tree_* tables
- clear_network_data() deletes per-wallet tree files for the target network

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): prevent permanent state leak on sync_notes failure in with_anchor_retry

When sync_notes() failed after an anchor mismatch, the early `?` return
skipped re-inserting the shielded state back into the HashMap, permanently
orphaning it until app restart. Now the sync result is captured without
early return, ensuring the state is always re-inserted regardless of
success or failure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): apply password change for current session even when config save fails

The in-memory config update and SDK reinit only ran on the save-success
path, silently discarding the password change on save failure despite
the banner promising session-level application. Now the in-memory update
and reinit run unconditionally; only the banner text varies across the
four (save x reinit) outcome combinations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(error): use dedicated TaskError variants for non-build wallet errors

reload_utxos() and recalculate_affected_address_balances() errors were
routed through shielded_build_error() which pattern-matches for build-
specific patterns like "AnchorMismatch". These are wallet operations,
not shielded builds. Added WalletUtxoReloadFailed and
WalletBalanceRecalculationFailed variants with appropriate user-facing
messages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(error): preserve error chain in CoreRpcConnectionFailed

chain_lock_rpc_error() took the RPC error by reference and created
CoreRpcConnectionFailed with source: None, dropping all diagnostic
info. Added a detail: Option<String> field to the variant to carry
the formatted error. Boxed the source field to keep the enum size
under the clippy threshold. Updated all constructors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): two-column wallet header layout, rename Tools to Advanced

Split the wallet detail panel header into left (name, total balance,
action buttons) and right (collapsible balance breakdown, sync status)
columns. Renamed the "Tools" dropdown to "Advanced" and moved it into
the action buttons row instead of right-aligning it separately.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(test): rename test_v33_migration_fresh_install to match DB version 34

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* revert(shielded): use shared main DB for commitment tree instead of per-wallet files

Reverts the per-wallet SQLite file approach (63ce0e18). Multiple wallets
share the same commitment_tree_* tables in the main database. This is
accepted behavior until the SDK adds proper wallet-scoping support
(dashpay/grovedb#653).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): fix stale clear_commitment_tree_for_wallet reference after merge

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): harden anchor retry, commitment tree clearing, and state guard

SEC-002: Verify shielded balance after anchor retry sync before retrying
the operation — prevents doomed retries with zero balance.

SEC-003: Handle missing commitment tree tables in clear_commitment_tree_tables
(grovedb creates them lazily, so DELETEs fail on fresh installs).

Fix clear_network_data to log-and-continue when commitment tree clearing
fails — these tables are optional and shouldn't block network data reset.

Add SEC-001 INTENTIONAL annotation for panic safety in with_anchor_retry.

Document shield_from_asset_lock_task: anchor retry is not applicable since
it only reads the payment address and doesn't use the commitment tree.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(core): sanitize RPC errors and simplify CoreRpcConnectionFailed

SEC-006: Store user-friendly message instead of raw RPC error string
in network status when chain lock query fails with a non-auth,
non-connection error.

CODE-002: Remove redundant detail field from CoreRpcConnectionFailed --
format diagnostic info into the url field instead, keeping source for
the error chain.

Add SEC-005 INTENTIONAL annotation for RPC errors in status tooltip.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): config permissions, address detection, and per-frame caching

SEC-007: Set config file permissions to 0600 on Unix after save (contains
RPC credentials).

CODE-003: Deduplicate Amount::dash_from_duffs by delegating to
dash_from_credits.

CODE-006: Remove unused network parameter from AddressKind::detect --
detection is format-based, network validation happens separately.

CODE-004/CODE-005: Cache filtered transaction indices per wallet to
avoid rebuilding HashSet and filtering on every frame. Invalidated
on wallet switch and refresh.

CODE-008: Reset AddressInput widgets on network switch so they pick up
the new network for validation. Add invalidate_address_input methods
to SendScreen, UnshieldCreditsScreen, and WalletsBalancesScreen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(logging): correct log levels per coding best practices

- debug! -> trace!: "state transition built, broadcasting" messages in
  bundle.rs (5 occurrences) — primary-path step-by-step progress
- trace! -> debug!: cookie auth fallback in core/mod.rs and context/mod.rs
  (2 occurrences) — secondary/fallback execution path, not primary flow
- info! -> debug!: anchor mismatch retry in context/shielded.rs — error
  handling branch, not a business event
- info! -> debug!: "marked N note(s) spent" in context/shielded.rs —
  internal bookkeeping, not a user-visible business event
- info! -> debug!: post-transfer sync complete in shielded_send_screen.rs —
  internal plumbing at screen level

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: minor ui fixes

* feat(ui): replace Advanced dropdown with inline right-aligned buttons

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): use allocate_ui_with_layout to right-align dev buttons

The right_to_left layout inside with_layout only got the remaining
space after left buttons, centering the dev buttons instead of
pushing them to the right edge. Using allocate_ui_with_layout with
the full remaining width forces the right-to-left block to span
the entire remaining area.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): eagerly initialize shielded balance on wallet screen open

The shielded tab balance only recalculated when the user clicked the
Shielded tab because ShieldedTabView was lazily initialized on tab
selection. This meant the wallet list and tab header showed zero balance
until the user manually visited the tab.

Two-layer fix:
- Backend: call initialize_shielded_wallet() in handle_wallet_unlocked()
  so persisted shielded balance is loaded into shielded_states at wallet
  load time, making it available to all UI screens immediately.
- UI: eagerly create ShieldedTabView on wallet screen construction and
  wallet switch, and tick() it every frame so the init/sync chain
  (InitializeShieldedWallet -> SyncNotes -> CheckNullifiers) runs even
  when the Shielded tab is not the active tab.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove stale balance_breakdown_expanded field from cherry-pick

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): move action buttons to full-width row below header columns

The button bar was inside the left column (55% width), so the
right-aligned dev buttons ended up in the middle of the screen.
Move render_action_buttons() below the two-column header so it
spans the full available width.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(ui): change sync status to bullet-point layout

Replace the wide horizontal Platform line (pipe-separated) with
individual bullet-point items: Core, Addresses, Notes, Nullifiers
each on their own line. Removes the Frame wrapper for a lighter
appearance.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(shielded): prevent double balance from redundant init + sync chain

When eager init (wallet_lifecycle) populates shielded_states and then
ShieldedTabView.tick() dispatches another InitializeShieldedWallet,
the idempotency check returns early but handle_result() chains
SyncNotes which can re-append notes already loaded from DB.

Fix: tick() now checks if shielded_states already contains the
wallet's state. If so, it adopts the cached balance and marks
itself initialized without dispatching a backend task, preventing
the redundant sync chain.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(shielded): move initialization entirely to backend, remove UI init path

The shielded wallet had two initialization paths: the backend
(handle_wallet_unlocked -> initialize_shielded_wallet) and the UI
(ShieldedTabView::tick dispatching InitializeShieldedWallet). This
created redundant init attempts and blurred the responsibility boundary.

Now:
- handle_wallet_unlocked initializes the shielded state synchronously,
  then queues SyncNotes -> CheckNullifiers as a background task
- ShieldedTabView reads its state from AppContext::shielded_states
  via refresh_from_backend_state(), never dispatching init itself
- The "Initialize Shielded Wallet" button and WalletUnlockPopup are
  removed from ShieldedTabView (wallet unlock already calls
  handle_wallet_unlocked which handles everything)
- Resync Notes (developer mode) still dispatches InitializeShieldedWallet
  as an explicit user action

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): deduplicate notes in sync to prevent double balance

When the commitment tree resets (empty/corrupt) but persisted notes
remain in the DB, initialize_shielded_wallet loads notes from DB
while last_synced_index falls to 0. The subsequent sync rescans
from position 0 and re-appends all decrypted notes — duplicating
the ones already in memory from DB, doubling the balance.

Fix: sync_notes() now checks if a note at the same position already
exists in shielded_state.notes before appending, preventing
duplicates regardless of the last_synced_index value.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(ui): add shielded diversified address table

Replace the single-address display with prev/next buttons in the
Shielded tab with a collapsible address table showing all generated
diversified addresses. Each row shows the index, truncated bech32m
address (click or Copy button to copy full address), and status
(Default for index 0). The section is collapsed by default in normal
mode and expanded in developer mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address review findings — duplicate controls, perf, tab visibility

- Remove duplicate sync status/buttons block in shielded_tab.rs
- Fix BIP44 accounts with index > 0 being invisible (regression)
- Fix early return in render_account_tabs blocking Shielded tab for new wallets
- Fix double-tick of ShieldedTabView per frame when tab is active
- Fix system_tab_sections O(n*m) perf by precomputing address counts
- Track queue_shielded_sync via subtasks for graceful shutdown
- Use HashSet for O(1) duplicate note check in sync (was O(n^2))
- Fix balance_breakdown default_open to respect developer mode
- Fix is_system_category to delegate to is_visible_in_default_mode
- Fix doc comment for system_tab_sections sort order claim
- Narrow #[allow(dead_code)] to specific field on AccountSummary

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): replace mutex unwrap() with graceful error handling in shielded tab

Replace .lock().unwrap() calls on shielded_states with .lock().ok()
patterns throughout ShieldedTabView to prevent UI panics on poisoned
mutex. Shows fallback messages or skips updates gracefully.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(mcp): resolve async lifetime errors for Rust 2024 edition

Use spawn_blocking + block_on in dispatch_task to avoid Send bound
issues with platform SDK types (DataContract/Sdk references across
await points). Same pattern already used by AppState::handle_backend_task.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: fix mcp dispatch

* doc: remove obsolete manual teting docs

* fix(ui): include zero-balance addresses in AddressInput wallet entries

Pull Core addresses from known_addresses (all derived) instead of
address_balances (only funded). Balance is looked up from
address_balances, defaulting to 0. Callers use with_balance_range(1..)
when they need funded-only addresses.

Fixes Mine dialog showing empty dropdown for fresh/zero-balance wallets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): sort AddressInput wallet entries alphabetically

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* revert: remove redundant sort in extract_wallet_entries

filtered_entries() already sorts alphabetically by display_label.
The sort added in extract_wallet_entries was redundant.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): clarify shielded network validation logic

Replace double-negation with explicit mainnet-class comparison.
Documents that testnet/devnet/local share the same HRP ("tdash1z")
and cannot be distinguished at the address level.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): include send-all transactions in wallet history

Check both outputs and inputs when filtering transactions. Build an
OutPoint→Address lookup from the wallet's own transactions to resolve
input addresses. Send-all transactions (no change output) were
previously dropped because no output matched known_addresses.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): use is_ours flag for transaction filtering

Simplify transaction filter to use the is_ours flag instead of
address matching. Fix SPV path to set is_ours=true for all wallet
transactions (upstream only sets it for sends). SPV history is
per-wallet, so all entries are ours by definition.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test(e2e): verify is_ours flag for SPV send and receive transactions

Sends funds between two wallets via SPV and asserts that both the
sender (negative net_amount) and receiver (positive net_amount) have
is_ours=true. Validates the fix to the SPV reconcile path that
overrides the upstream library's send-only is_ours logic.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(core): clear RPC error state on successful chain lock fetch

When get_best_chain_lock() succeeds on the active network, explicitly
clear any lingering RPC error in ConnectionStatus so the connection
indicator recovers after a transient outage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(model): move is_platform_address_string from UI helpers to model layer

AddressKind::detect() in the model layer depended on crate::ui::helpers
for platform address detection — wrong layering. Move the function to
src/model/address.rs and keep a re-export in helpers for existing callers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): add actionable guidance to address validation error messages

Each validation error now tells the user what to do, not just what went
wrong: "check for typos", "check you are using the correct network",
"use all lowercase". Messages that already had guidance are unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): invalidate address inputs on all screens during context switch

TransferScreen, AddressBalanceScreen, and ShieldedSendScreen retained
stale address text from the previous network after a context switch.
Add invalidate_address_input() to each and call it from change_context().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): clear password field on network switch when no config exists

When switching networks, if no config entry exists for the new network
(or the password is empty), the password input field now clears instead
of retaining the previous network's password.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): clear validated_destination in invalidate_address_input

WalletSendScreen and UnshieldCreditsScreen cleared the AddressInput
widget but left validated_destination with stale validation from the
previous network. Now both are cleared together.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): consume ShieldedNotesSynced to update shielded send screen state

After a shielded transfer, the post-transfer note sync result was only
logged but never used to update the UI. Now ShieldedNotesSynced updates
the balance display, clears the pending-update banner, and appends the
remaining balance to the success message.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: apply cargo +nightly fmt formatting

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(model): disambiguate Core vs Identity address detection by prefix

Core addresses on Dash start with X/Y (mainnet) or y/8/7 (testnet).
When the input starts with a known Core prefix, try Core first.
Otherwise try Identity first, since Identity IDs use the same Base58
alphabet but don't have Core address prefixes.

This fixes identity-only AddressInput mode rejecting valid Identity
IDs that happened to parse as Core addresses, and eliminates the
flaky test that had to skip Core-looking random identifiers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(core): show actual RPC error on Networks page instead of generic message

The Networks page is a debugging tool — users need the real error text
to diagnose Dash Core issues. Replace sanitized "RPC error — check
Dash Core status" with the actual error from dashcore_rpc.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(db): resolve deadlock in clear_network_data

clear_network_data() held self.conn.lock() for the transaction, then
called clear_commitment_tree_tables() which tried to acquire the same
mutex — classic non-reentrant mutex deadlock. UI froze on "Delete Data".

Fix: scope the connection lock so it's released before the commitment
tree clearing acquires it again.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): replace mutex unwrap() with graceful .ok() in shielded_sync_task

The shielded_states mutex lock used .unwrap() which would panic on a
poisoned mutex. Replace with .ok()? to return None gracefully.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): replace RwLock unwrap() with graceful error handling in shielded tab

Replace wallets.read().unwrap() with .read().ok() and show an error
label instead of panicking on a poisoned RwLock.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(spv): add debug log when overriding is_ours for receive transactions

Log at debug level when SPV reports is_ours=false for a receive
transaction (net_amount >= 0) to detect upstream API behavior changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): log warning on note value divergence during position dedup

Use a HashMap<position, value> instead of HashSet<position> so we can
detect when a re-synced note at an existing position has a different
value — indicating potential data integrity issues.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* perf(model): avoid allocation in is_platform_address_string

Replace to_lowercase() with eq_ignore_ascii_case() for the HRP prefix
check. The HRP constants are ASCII-only so this is safe and avoids a
heap allocation on every call.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(ui): remove unused label field and is_key_only from AccountSummary

The label field was constructed but never read outside the builder.
is_key_only() had no callers. Remove both instead of suppressing
dead_code warnings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(ui): avoid cloning full AccountTab enum in tab content match

Extract only the category data (clone of AccountCategory + index) before
the match instead of cloning the entire enum. This avoids unnecessary
allocation for the Shielded and System variants.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): clear validated_address on network switch in mine dialog

invalidate_address_inputs() cleared the AddressInput widget but left
the validated_address stale, which could reference an address from the
previous network.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(model): move truncate_address to model layer and document ASCII precondition

Move truncate_address from address_input.rs and shielded_tab.rs into
src/model/address.rs as a parameterized public function. Both callers
now delegate to the shared implementation with their own prefix/suffix
lengths.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs(shielded): document why spawn_blocking trampoline is needed in queue_shielded_sync

The spawn_blocking(block_on(...)) pattern is required because async
methods on Arc<Self> produce non-'static futures due to a known Rust
compiler limitation (rust-lang/rust#100013). Document this to prevent
future removal attempts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: apply nightly rustfmt formatting

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(wallet): bootstrap platform addresses on wallet creation

bootstrap_wallet_addresses only ran when known_addresses was empty,
but new_from_seed already derives one Core address. This meant
platform payment addresses were never bootstrapped for new wallets
— only populated later by network sync (which returns nothing for
fresh wallets with no on-chain activity).

Now checks for the presence of PlatformPayment addresses in
watched_addresses and runs bootstrap if missing. Bootstrap functions
are idempotent (add_address_if_not_exists).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): show bootstrapped platform addresses in AddressInput

Platform addresses were only populated from platform_address_info
(network sync results). Fresh wallets with no on-chain activity had
empty platform_address_info, so no platform addresses appeared in
the Send screen autocomplete.

Now derives platform addresses from watched_addresses (which contains
all bootstrapped platform payment addresses), with balance looked up
from platform_address_info (defaulting to 0).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(ui): pass account filter directly to render_address_table

Remove the `selected_account` field that was mutated as a side-effect
during rendering, causing the last-rendered collapsible section in the
System tab to overwrite previous sections' state. Now each render call
receives its `(AccountCategory, Option<u32>)` as a direct parameter.

Additionally, `system_tab_sections` now builds per-(category, index)
pair so multi-index accounts show correct per-index balance and address
counts instead of aggregating across all indices.

Fixes CODE-005, CODE-011, PROJ-004.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* perf(shielded): move wallet initialization to background thread

ZIP32 key derivation and DB reads in `initialize_shielded_wallet` ran
synchronously on the UI thread during `handle_wallet_unlocked()`.
Move the call to a `spawn_blocking` task tracked by `subtasks` so the
UI remains responsive. The shielded tab already handles the
"initializing" state gracefully via its `is_initialized` check.

Fixes CODE-010.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test(spv): add unit tests for is_ours override logic

Extract the SPV is_ours override into a named function and add four
unit tests covering: outgoing already-ours, incoming not-ours (the
main override case), zero-amount edge case, and outgoing not-ours.

A comment explains why bloom filter false positive testing requires
mocking the SPV layer and is out of scope for unit tests.

Fixes PROJ-002.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs(user-stories): add stories for wallet tab redesign

Add WAL-021 through WAL-024 covering the tab-based navigation,
developer-mode System tab, collapsible transaction history, and
collapsible balance breakdown sections introduced in PR #791.

Fixes PROJ-007.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: apply nightly rustfmt formatting

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): distinguish change addresses in AddressInput autocomplete

Core addresses now show "(change)" suffix for BIP44 change addresses
(m/44'/5'/0'/1/x). New with_exclude_change(true) builder method
filters them out — send inputs should not display change addresses
since users don't share them with others.

Change detection uses the existing DerivationPathHelpers::is_bip44_change
trait method from the wallet…
lklimek added a commit that referenced this pull request Mar 26, 2026
…798)

* more work

* more work

* fix(spv): zero out stale per-address balances during reconciliation (#627)

During SPV reconciliation, per_address_sum only contains addresses with
current UTXOs. Addresses whose funds were fully spent never had their
balance reset to zero, causing the address table to display stale
non-zero balances even though UTXO count correctly showed 0.

Now explicitly zeroes address_balances for any known address absent from
the refreshed UTXO map before applying current sums.

Closes #571

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix: handle malformed YAML gracefully in load_testnet_nodes_from_yml (#613)

* fix: handle malformed YAML gracefully in load_testnet_nodes_from_yml

Replace .expect() with match expression to avoid panicking when
.testnet_nodes.yml contains malformed YAML. Instead, logs the error
with tracing::error and returns None, allowing the application to
continue without crashing.

Closes #557

Co-authored-by: lklimek <lklimek@users.noreply.github.com>

* fix: propagate YAML parse errors to UI and remove unwrap calls

- Change load_testnet_nodes_from_yml to return Result<Option<TestnetNodes>, String>
  so parse errors display in the UI error banner instead of only logging
- Use explicit type annotation on serde_yaml_ng::from_str::<TestnetNodes>
- Replace .unwrap() with .and_then() in fill_random_hpmn/fill_random_masternode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: lklimek <lklimek@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* chore: let Claude write manual test scenarios for PRs (#634)

* chore: move doc/ contents into docs/ and update references

Consolidate documentation under a single docs/ directory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: CLAUDE.md should write manual test scenarios for PRs

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* build(flatpak): use only-arches for dynamic protoc architecture selection (#603)

* build(flatpak): use only-arches for dynamic protoc architecture selection

Replace the fragile sed-based CI patching of the Flatpak manifest with
Flatpak's native `only-arches` source selector. The protoc module now
declares both x86_64 and aarch64 sources inline, and build-commands use
a glob pattern (`protoc-*.zip`) so no per-arch fixup is needed.

Changes:
- flatpak manifest: add aarch64 protoc source with `only-arches`,
  use glob in unzip commands, remove stale CI-patching comment
- CI workflow: remove `protoc-zip`/`protoc-sha256` matrix variables
  and the "Patch manifest for architecture" step

https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG

* fix(flatpak): use dest-filename for deterministic protoc extraction

Use dest-filename to normalize both arch-specific protoc zips to a
common name, avoiding glob expansion in build-commands. This ensures
the unzip target is deterministic regardless of shell behavior in the
Flatpak sandbox.

https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG

* build: update platform to b445b6f0 and remove rust-dashcore patches

Update dashpay/platform dependency from d6f4eb9a to b445b6f0e0bd4863
(3.0.1 → 3.1.0-dev.1). Remove the [patch] section that pinned
rust-dashcore crates to a separate rev, as the new platform commit
resolves them correctly on its own.

https://claude.ai/code/session_015AD2pCWoJdV2VDydcqFHPG

---------

Co-authored-by: Claude <noreply@anthropic.com>

* fix(ci): remove local path patches, use git deps for platform crates

The [patch] section referenced ../platform local paths that don't exist
in CI. Since dash-sdk already depends on the feat/zk branch, all
transitive platform crates resolve correctly without patches. Also fixes
a formatting issue in address_table.rs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(test): store wallet in DB before registering addresses

The remove_utxos tests were failing because `register_test_address`
inserts into `wallet_addresses` which has a foreign key constraint on
`wallet(seed_hash)`. Added the missing `store_wallet` call so the
parent row exists before inserting child address rows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(build): restore shielded module declaration removed during merge

The `pub mod shielded;` declaration was accidentally removed from
`src/model/wallet/mod.rs` during the v1.0-dev merge, causing build
failures since the shielded.rs file still exists.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(test): restore store_wallet calls lost in merge (#663)

* Initial plan

* fix(test): restore store_wallet calls lost in merge

Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com>

* Merge remote-tracking branch 'origin/v1.0-dev' into zk-extract/all-merged

Resolve conflicts in Cargo.toml (keep feat/zk branch), Cargo.lock
(regenerate with pinned platform rev 4d7b9be5), and
backend_task/mod.rs (combine TaskError wrapping with ShieldedTask).

Fix post-merge integration issues:
- SPV manager: remove stale .await on subscribe methods, add
  command_receiver channel for updated DashSpvClient::run() API
- send_screen: update SendStatus::WaitingForResult to unit variant
- network_chooser_screen: handle new SyncState::Initializing variant

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: update platform dependency to 3.1-dev branch

Migrate from feat/zk to 3.1-dev branch of dashpay/platform,
adapting to breaking API changes in dash-spv, key-wallet, and dpp:

- FeeLevel removed; use FeeRate::normal() directly
- DashSpvClientInterface/Command removed; use DashSpvClient directly
- SyncState::Initializing removed; replaced with WaitForEvents
- NetworkExt trait inlined into Network impl
- OrchardProver now requires wrapper struct around ProvingKey
- OrchardAddress::from_raw_bytes now returns Result
- Builder functions gain fee/platform_version params
- NullifierSyncConfig API uses NullifierSyncCheckpoint
- WalletManager.create_unsigned_payment_transaction removed;
  use TransactionBuilder directly
- Work around Send lifetime issues with spawn_blocking

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: migrate shielded module from Result<T, String> to typed TaskError

Replace all Result<T, String> error patterns in the shielded pool module
with typed TaskError variants, aligning with the codebase-wide typed error
migration (PR #739).

New TaskError variants: ShieldedNoUnspentNotes, ShieldedInsufficientBalance,
PlatformAddressNotFound, ShieldedMerkleWitnessUnavailable,
ShieldedTransitionBuildFailed, ShieldedBroadcastFailed,
ShieldedInvalidRecipientAddress, ShieldedAssetLockTimeout,
ShieldedSyncFailed, ShieldedTreeUpdateFailed, ShieldedNullifierSyncFailed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): prevent settings password row from clipping right edge

Reserve width for Save and Auto Update buttons so the password input
doesn't consume all available space, pushing buttons off-screen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(rpc): include host:port in connection-refused errors and always show details

Add CoreRpcConnectionFailed variant to TaskError that includes the
configured address in the user-facing message.  Connection-refused
errors are now detected via is_rpc_connection_error() and enriched
with host:port at every RPC call site where the URL is known
(AppContext::rpc_error_with_url helper).

Details panel is now shown for all RPC-related errors regardless of
developer mode, so users can always see the technical information
they need to diagnose connectivity issues.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): save RPC password for active network instead of hardcoded Regtest

The Network Chooser screen had three bugs related to RPC password handling:

1. Password input was initialized from Regtest config only, ignoring the
   current network selection.
2. Password UI was hidden for all networks except Regtest, even though
   Mainnet/Testnet/Devnet also use RPC mode.
3. Save logic was hardcoded to update Regtest config and triggered a
   SwitchNetwork(Regtest) action, which disconnected the active network's
   ZMQ listener unnecessarily.

Now the password input shows for any network in RPC mode, reads/writes
the correct network config, reinits the RPC client in-place without
triggering a network switch, and reloads the stored password when the
user switches network tabs. The "Auto Update" (dashmate) button remains
Regtest-only since dashmate is only relevant for local networks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): ensure funding method dropdown fits all items without scrollbar

Add explicit .height(200.0) to the funding method ComboBox in both
top_up_identity_screen and add_new_identity_screen. The add_enabled_ui
wrappers inflate item height via frame overhead, causing the popup to
clip to a single row at the default height.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(error): show actionable message for insufficient identity balance

IdentityInsufficientBalanceError from the SDK now maps to a dedicated
TaskError::IdentityInsufficientBalance variant instead of falling through
to "An unexpected error occurred." The user sees the available and required
credit amounts along with a clear "top up your identity" action.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): clear stale error banners when saving RPC password

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use network-compatible comparison for platform address lookups

Regtest and Testnet share the `tdash` bech32m prefix, but
`PlatformAddress::from_bech32m_string()` always returns `Network::Testnet`
for `tdash` addresses. The strict `!=` comparison in the fund-platform
dialog rejected valid Regtest addresses with "Address network mismatch".

- Make `networks_address_compatible()` `pub(crate)` in `model::wallet`
  so all modules can reuse the canonical check
- Remove the duplicate private copy in `backend_task::core` and import
  from the single source
- Replace `network != self.app_context.network` in `dialogs.rs` with
  `networks_address_compatible()` so Testnet/Devnet/Regtest addresses
  are accepted interchangeably

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(error): add actionable messages for shielded fee and pool-size errors

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(error): add actionable message for shielded anchor mismatch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): auto-resync notes and retry on anchor mismatch

When unshield_credits, shielded_transfer, or shielded_withdrawal fails
with ShieldedAnchorMismatch (stale commitment tree witnesses), automatically
sync notes once to update the tree and retry the operation. Only one resync
attempt is made — if the retry also fails, the error propagates as-is.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): ensure shielded tables exist and log DB errors during init

Three changes to fix empty shielded balance after app restart:

1. Defensive table creation in initialize(): after migrations complete,
   ensure shielded_notes and shielded_wallet_meta tables exist even if
   the DB version was already past v29/v30 from a prior build. Both
   methods use CREATE TABLE IF NOT EXISTS, so this is safe.

2. Log DB errors during shielded init: change silent if-let-Ok pattern
   to match/Err with tracing::warn, so missing-table errors are visible
   instead of silently producing empty note lists.

3. Safety net resync: when the commitment tree has been synced but no
   unspent notes were loaded from DB, clear the tree and reset
   last_synced_index to 0 so the auto-sync rediscovers all notes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): consolidate migrations v28-v32 into v33

Resolves version numbering collision between zk and v1.0-dev branches:
the zk branch used v28 for shielded tables while v1.0-dev used v28 for
contacts. After merging, users migrating from either branch could end up
with missing tables depending on which version their DB was at.

v33 runs all sub-migrations idempotently in one step, ensuring all
tables exist regardless of prior migration history.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): consolidate migrations v28-v32 into v33

Resolves version numbering collision between zk and v1.0-dev branches:
the zk branch used v28 for shielded tables while v1.0-dev used v28 for
contacts. After merging, users migrating from either branch could end up
with missing tables depending on which version their DB was at.

v33 runs all sub-migrations idempotently in one step, ensuring all
tables exist regardless of prior migration history.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: pin platform dependency to zk-fixes revision

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): address PR review — fresh schema, error propagation, rename coverage

- propagate SQL error in add_nullifier_sync_timestamp_column instead of
  swallowing it with unwrap_or(false)
- add shielded_notes and shielded_wallet_meta to rename_network_dash_to_mainnet

Note: Fix 1 (last_nullifier_sync_timestamp in CREATE TABLE) was already
present on this branch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): address PR review — fresh schema, error propagation, rename coverage

- propagate SQL error in add_nullifier_sync_timestamp_column instead of
  swallowing it with unwrap_or(false)
- add shielded_notes and shielded_wallet_meta to rename_network_dash_to_mainnet

Note: Fix 1 (last_nullifier_sync_timestamp in CREATE TABLE) was already
present on this branch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): add foreign key constraints to shielded tables

shielded_notes and shielded_wallet_meta both had wallet_seed_hash columns
with no FK constraint. Added FOREIGN KEY (wallet_seed_hash) REFERENCES
wallet(seed_hash) ON DELETE CASCADE to both, matching the pattern used by
all other per-wallet tables in the codebase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): add foreign key constraints to shielded tables

shielded_notes and shielded_wallet_meta both had wallet_seed_hash columns
with no FK constraint. Added FOREIGN KEY (wallet_seed_hash) REFERENCES
wallet(seed_hash) ON DELETE CASCADE to both, matching the pattern used by
all other per-wallet tables in the codebase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): remove duplicate shielded methods after v1.0-dev merge

On zk-fixes, shielded DB methods live in shielded.rs. The v1.0-dev
backport inlined them in initialization.rs (no shielded.rs on that
branch). Merging v1.0-dev back caused E0592 duplicate definitions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(test): remove duplicate wallet store in register_test_address

register_test_address called db.store_wallet redundantly — callers
already store the wallet before calling it, causing UNIQUE constraint
violations when tests run in parallel on CI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(shielded): address PR review — error propagation, retry helper, safety net

- Propagate DB error from get_unspent_shielded_notes instead of swallowing
- Extract with_anchor_retry() helper to deduplicate ~27 lines across
  shielded_transfer_task, unshield_credits_task, shielded_withdrawal_task
- Fix safety net false positive: check all notes (spent + unspent) to
  distinguish "never had notes" from "all spent"
- Propagate error from clear_commitment_tree_tables instead of discarding

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(error): address PR review — Amount formatting, Display completeness

- Add Amount::dash_from_credits() constructor to model/amount.rs
- Replace manual format_credits_as_dash() logic with Amount::dash_from_credits().to_string(),
  so credit amounts use Amount's Display (with DASH unit, trimmed trailing zeros)
- Update ShieldedFeeExceedsBalance #[error] message: remove redundant "Dash" literal
  (Amount's Display already includes the "DASH" unit suffix)
- Update format_credits_as_dash tests to match new output format ("1 DASH", "2.5 DASH", etc.)
- Remove special-casing that showed with_details() for all RPC errors unconditionally:
  all required user-facing info is already in each variant's Display string;
  technical details are now shown only in developer mode (aligns with CLAUDE.md §error-messages rule 4)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(rpc): address PR review — context fallback, success banner, error source

- Guard in-memory config update + reinit behind a check that the target
  network's AppContext actually exists; skip both when it doesn't so we
  don't accidentally overwrite mainnet config via the fallback path.
- Show success banner only when reinit succeeds; show a warning when it
  fails so the user knows the connection may not reflect the new password.
- Make CoreRpcConnectionFailed.source Option<dashcore_rpc::Error> so
  chain_lock_rpc_error can pass None instead of fabricating a fake
  ConnectionRefused error from a borrowed reference.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: simplify shielded helpers comment in initialization.rs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): address PR #789 review — doc comments, migration default

- Split misplaced doc comment: `add_wallet_transaction_status_column`
  now has its own Migration 30 doc; `rename_network_dash_to_mainnet`
  gets its own Migration 29 doc.
- Add inline comment explaining why the migration uses DEFAULT 2
  (Confirmed) while fresh CREATE TABLE in wallet.rs uses DEFAULT 0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(db): remove duplicate shielded methods after v1.0-dev merge

On zk-fixes, shielded DB methods live in shielded.rs. The v1.0-dev
backport inlined them in initialization.rs (no shielded.rs on that
branch). Merging v1.0-dev back caused E0592 duplicate definitions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: simplify shielded helpers comment in initialization.rs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore(db): document migration DEFAULT 2 vs fresh DEFAULT 0

Existing transactions predate status tracking and are assumed
confirmed (DEFAULT 2). Fresh installs use DEFAULT 0 (Unconfirmed).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(wallet): address review — zero-balance detection, status warning

Add `spv_balance_known: bool` to `Wallet` to distinguish a synced
zero-balance wallet from an unsynced one. `spv_confirmed_balance()`
now returns `None` only before the first SPV report, and `Some(0)`
after SPV confirms an empty wallet.

Add `tracing::warn!` in `TransactionStatus::from_u8` when an unknown
discriminant is encountered, avoiding silent data coercion in a
financial context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(error): address review — typed BIP32 source, migration error variant

Replace WalletKeyDerivationFailed { detail: String } with a typed
#[source] field (Box<dyn Error + Send + Sync>) so the error chain is
preserved and Display/Debug separation is explicit. Update all callsites
in wallet/mod.rs and identity/mod.rs accordingly.

Replace the semantically wrong InvalidParameterName map_err in
rename_network_dash_to_mainnet with a plain ? — execute() already
returns rusqlite::Result so no conversion is needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test(db): add v33 consolidated migration regression tests

Two test scenarios verify the v33 consolidated migration that merges
sub-migrations v28-v32 (core_wallet_name, shielded tables, contacts,
wallet transaction status, network rename):

1. Fresh install creates all v33 tables/columns directly via create_tables()
2. Upgrade from v27 runs the full migration path and produces identical schema

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): show warning when config save fails instead of success

When config.save() fails, stop early with a warning banner instead
of continuing to reinit and showing a misleading success message.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(error): consolidate format_credits_as_dash and remove jargon from user messages

- fee_estimation::format_credits_as_dash now delegates to Amount::dash_from_credits()
  instead of doing its own f64 math with fixed 8 decimals; output is now trimmed
  (e.g. "1 DASH" instead of "1.00000000 DASH")
- Remove private format_credits_as_dash from error.rs; import the pub version from
  fee_estimation instead
- IdentityInsufficientBalance: show amounts in DASH rather than raw credit integers
- ShieldedAnchorMismatch: replace "out of sync" with plain-language retry guidance
- ShieldedFeeExceedsBalance: remove "shielded balance" / "shield more credits" jargon
- ShieldedInsufficientPoolNotes: remove count interpolations and ZK pool jargon
- Update tests throughout to match new message content and format

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(test): remove duplicate wallet store in register_test_address

register_test_address called db.store_wallet redundantly — callers
already store the wallet before calling it, causing UNIQUE constraint
violations when tests run in parallel on CI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(core): log chain lock RPC errors that aren't auth/connection failures

Silent swallowing of errors like "Unable to find any ChainLock" made it
impossible to diagnose why the active network showed as Disconnected.
Now warns via tracing when chain_lock_rpc_error returns None.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ui): surface chain lock RPC errors in Networks tab

Previously, non-auth/non-connection RPC errors (e.g. "Unable to find
any ChainLock") were silently swallowed — the UI showed "Disconnected"
with no explanation. Now the error message is carried through the
ChainLocks result, stored in ConnectionStatus.rpc_last_error, and
displayed as "Error: ..." in both developer and non-developer RPC
status labels on the Networks tab.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(ui): unified AddressInput component with autocomplete (#787)

* feat(ui): add unified AddressInput component with autocomplete

Introduce a reusable AddressInput component that handles text input with
real-time address type detection, autocomplete from wallet data, balance
display, type filtering, and network-aware validation. Supports Core,
Platform, Shielded, and Identity address kinds.

New files:
- src/model/address.rs: AddressKind enum and ValidatedAddress enum
- src/ui/components/address_input.rs: full component with 33 unit tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): integrate AddressInput component into send and unshield screens

Replace inline address parsing and validation with the unified
AddressInput component across 3 proof-of-concept sites:

- UnshieldCreditsScreen: full migration, removes local Destination enum
  and parse_destination(), gains autocomplete and type-restricted input
- WalletSendScreen simple mode: full migration, removes AddressType enum,
  replaces detect_address_type/is_shielded_address with AddressKind-based
  detection, eliminates double-parsing in send handlers
- WalletSendScreen advanced mode: minimal migration, updates type
  detection to use AddressKind, keeps CoreAddressInput/PlatformAddressInput
  structs unchanged

Net reduction of ~47 lines per screen. All send flows preserved.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test(address-input): fix misleading test and add missing coverage

The existing `disabled_type_rejected_with_correct_error` test was testing
empty input (which returns no error) rather than actually verifying type
restriction. Fixed the test and added coverage for:
- Selection-only mode rejection of manual input
- Identity validation with valid/invalid identifiers
- Truncate boundary at exactly 16/17 characters
- Empty input in selection-only and restricted-type modes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): address QA findings for AddressInput component

QA-001: Extract duplicated address detection logic from send_screen.rs
into AddressKind::detect() on the model type. Both send_screen and
address_input now delegate to the single canonical implementation.

QA-002: Fix autocomplete "...and N more" count using unfiltered total.
filtered_entries() now returns the pre-truncation match count so the
overflow label shows the correct number of remaining matches.

QA-003: Add minimum length check (>= 60 chars) to shielded address
validation. Previously any string with the correct prefix was accepted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): address bot review findings for AddressInput component

- Validate shielded addresses via OrchardAddress::from_bech32m_string()
  instead of prefix+length check only
- Use char-aware truncation in truncate_address() to prevent panics
  on multi-byte UTF-8 input (DPNS labels, emoji)
- Reset AddressInput when source selection changes in send screen,
  and configure allowed destination kinds per source type
- Store bech32m string in ValidatedAddress::Platform variant so
  to_address_string() returns canonical encoding instead of debug hex

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): address remaining review findings for AddressInput component

- Fix manual entry not propagating validated address on blur (HIGH)
- Fix selected_from_autocomplete causing repeated change signals
- Remove protocol jargon from unshield screen messages
- Reject mixed-case bech32m platform addresses per BIP-350
- Use per-instance ComboBox ID to prevent state collision
- Clear cached_detection after autocomplete selection
- Fix doc comments and reuse filtered_entries() computation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): support multi-wallet autocomplete in AddressInput

Replace with_wallet/set_wallet (single wallet) with with_wallets/set_wallets
(slice of wallets). When multiple wallets are loaded, each autocomplete entry
is prefixed with the wallet alias so the user can tell which wallet owns the
address.

send_screen.rs now passes all loaded wallets to AddressInput instead of only
the selected one.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): restore missing "Show zero-balance addresses" checkbox

The checkbox was accidentally dropped during a branch merge. Restored
the horizontal layout with heading on the left and checkbox
right-aligned, matching the v1.0-dev implementation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): improve AddressInput autocomplete UX

- Fix click selection: keep popup rendered when text field loses focus
  so the click handler fires (was gated on has_focus only)
- Show dropdown on focus with any input length (removed 3-char minimum)
- Add type suffix (Core), (Platform), (Identity), (Shielded) to
  dropdown entries when multiple address types are enabled
- Validate immediately on paste (text >3 chars) instead of requiring
  blur first, so "Fund Platform Address" button activates without
  needing to click away

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): make entire autocomplete row clickable in AddressInput

Previously only the address label was clickable — clicking the balance
text on the right side of the row did nothing. Now the click handler
uses the horizontal row response, so the entire row triggers selection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): fix autocomplete click handling and format balances with 4dp

- Capture selectable_label click response instead of discarding it;
  combine with row-level click for full-row clickability
- Format all DASH balances in dropdown with exactly 4 decimal places

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): use single interaction rect for full-row clickable autocomplete

Replace selectable_label + horizontal layout with allocate_exact_size
and manual painting — no child widgets steal clicks, the entire row
(address, balance, dead space) is clickable with hover feedback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(ui): add wallet address autocomplete to unshield screen

Populate AddressInput with wallet addresses via .with_wallets() so
users can select a destination from the dropdown instead of manually
entering addresses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(error): handle asset lock and shielded insufficient funds errors with user-friendly messages

Add two new TaskError variants to replace raw SDK errors with actionable,
jargon-free messages:

- AssetLockOutPointInsufficientBalance: shown when an asset lock outpoint
  has been partially consumed and lacks credits for the operation. Extracts
  credits_left/credits_required and displays DASH amounts.

- ShieldedAddressInsufficientFunds: shown when a shielded broadcast fails
  because the address balance is too low. Detected in shielded_broadcast_error()
  before falling through to ShieldedBroadcastFailed.

Both follow the IdentityInsufficientBalance pattern with format_credits_as_dash.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): use AmountInput component for all amount inputs in wallet screens

Replace raw text_edit_singleline + custom parse_amount_*() methods with
the AmountInput component in 4 shielded screens (unshield, shield,
shield-from-asset-lock, shielded-send). This fixes a bug where entering
"1" would send 1 credit instead of 1 DASH because the raw input had
ambiguous parsing. Also adds "Amount (DASH):" label to SendScreen's
AmountInput and removes its redundant manual label.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add UI components reference and teach CLAUDE.md to use it

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): redesign wallet screen information architecture

Replace the old Balances|Shielded top-level tabs and account dropdown
with a unified layout that better matches how users think about their
wallet:

- Balance section with collapsible breakdown (Core/Platform split)
- Dev Tools expandable button replaces scattered dev-mode controls
- Transaction History shown as collapsible section (visible in all modes)
- Accounts & Addresses use tabs instead of a dropdown selector
- Shielded view moved from top-level tab to an account tab
- Asset Locks restricted to the Dash Core tab only
- "Main Account" renamed to "Dash Core" throughout
- Fee column added to transaction table (dev mode only)
- "Get Test Dash" button opens testnet faucet in dev tools

Tab visibility follows progressive disclosure: default mode shows only
Dash Core, Platform, and Shielded tabs. Developer mode reveals all
account types that have addresses.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): use AddressInput component in Mine dialog for core address selection

Replace the manual ComboBox address selector in the Mine Blocks dialog
with the unified AddressInput component, configured for Core-only
addresses with selection-only mode from the current wallet.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): trigger shielded balance refresh after all shielding operations

After Shield, Shield from Core, Send Private, Unshield, and Send Dash
(with shielded source) complete successfully, automatically dispatch a
SyncNotes task so the shielded tab shows updated balances without
manual refresh.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: demote cookie auth fallback log to trace level

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): prevent transaction list showing wrong wallet data

Add defensive checks in render_transactions_section to prevent
cross-wallet transaction leakage:

1. Verify the selected wallet Arc matches the canonical one in
   app_context.wallets (guards against stale references).

2. Filter displayed transactions to only those with at least one
   output matching the wallet's known addresses (prevents showing
   transactions from other wallets that leaked in via a
   non-wallet-scoped RPC endpoint).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): auto-show zero-balance addresses when wallet is empty

When all addresses have zero balance and there are fewer than 5
addresses total, bypass the zero-balance filter so new/empty wallets
show their addresses instead of a blank list. The checkbox remains
functional for wallets with balances.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): address PR review — shielded balance, dev tools layout

1. Include shielded balance in the collapsible balance breakdown
2. Add shielded balance to the total wallet balance everywhere
3. Right-align the Dev Tools button in the action row
4. Always show downward arrow on Dev Tools button
5. Dev Tools opens as a vertical dropdown popup instead of
   a horizontal inline row

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): rename transaction heading, add explorer link, merge zk-fixes

- Rename "Transaction History" to "Dash Core Transaction History"
- Add "View" button on transactions for Mainnet/Testnet (opens Insight explorer)
- Merge zk-fixes (mine dialog, shielded refresh, wallet bug fixes, log level)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): trigger shielded sync on wallet refresh and wallet switch

The wallet Refresh button now dispatches a SyncNotes task alongside the
core wallet refresh when the shielded wallet has been initialized.
Switching HD wallets also triggers a full refresh (core + shielded) on
the next frame for unlocked wallets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(log): add diagnostic logging for shielded transfer operations

Add tracing::info! and tracing::debug! logs throughout the shielded
transfer pipeline to enable post-mortem diagnosis when balances don't
update after broadcast. Key additions:

- bundle.rs: log amount, input notes, change before building each
  shielded operation (transfer, shield, unshield, withdrawal,
  shield-from-asset-lock); log broadcast success with note that
  balance updates after next block
- sync.rs: warn when next_start_index is 0 (full rescan); log
  post-sync balance with unspent note count
- context/shielded.rs: log note spend marking with before/after
  unspent counts in with_anchor_retry
- shielded_send_screen.rs: log task result variant received and
  post-transfer sync completion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): improve Dev Tools dropdown layout and refresh mode cycling

Right-align popup content to match the Dev Tools button position.
Replace ComboBox with a cycle-on-click button to avoid nested popup
conflicts in egui, where the inner ComboBox dropdown would close the
parent popup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): fix shielded balance conversion, add tab balances, reorder sync status

- Fix shielded balance inflating total: divide credits by CREDITS_PER_DUFF
  before summing with duffs-denominated balances (was showing 5000 DASH
  instead of 5 DASH)
- Show balances in account tab labels: Dash Core (0.9980) | Platform (0.1000)
  | Shielded (5.0000), with (empty) for zero-balance tabs
- Move Sync Status section inside detail panel between balance breakdown
  and action buttons for better visual flow

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): collapsible sections, move tx history to Dash Core tab, restore account types

- Wrap Addresses, Transaction History, Asset Locks, and Shielded Notes
  in collapsible headers (default open, tx history default closed)
- Move Dash Core Transaction History from top-level into the Dash Core
  tab, between Addresses and Asset Locks
- In developer mode, show ALL account category tabs (Bip44, Platform,
  Bip32, CoinJoin, Identity Registration/System/Top-up/Invitation,
  Provider) even when no addresses exist for that type
- Add TODO for shielded tab layout redesign

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): improve shielded transfer UX messaging for balance update delays

After a shielded transfer, the sender's change notes and the recipient's
new notes won't appear until the next block is mined and synced. Users
see their balance temporarily drop to 0 and think the transfer failed.

Add informational messages on all three shielded transfer screens
(ShieldedSendScreen, UnshieldCreditsScreen, WalletSendScreen) explaining
that balances will update after the next block is confirmed. For
shielded-to-shielded transfers, also note that the recipient needs to
sync after the next block.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): consolidate dev-mode accounts into System tab, limit balance decimals

Replace all individual dev-mode-only account type tabs (Identity Registration,
Identity System, Identity Top-up, Identity Invitation, CoinJoin, Provider,
Legacy BIP32) with a single "System" tab. Inside the System tab, each account
type appears as a collapsible section (collapsed by default) showing address
count and balance.

Tab order: Dash Core | Platform | Shielded | System (dev-mode only, always last)

Additional changes:
- Limit tab balance display to max 4 decimal places (was 8)
- Rename "Dev Tools" button to "Tools"
- Simplify refresh mode button: single button with "Refresh mode: X" text,
  no separate label or arrow indicator
- Remove "Accounts & Addresses" section heading
- Style active tab with underline for visual distinction

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): scope commitment tree per wallet for multi-wallet correctness

Each wallet's ClientPersistentCommitmentTree now uses its own dedicated
SQLite file under <data_dir>/shielded/<hex>.db instead of sharing the
main database's global commitment_tree_* tables. This prevents wallets
from stepping on each other's Merkle tree state, which caused invalid
witnesses and silently rejected transactions.

Changes:
- SHLD-001: Per-wallet commitment tree DB files via ClientPersistentCommitmentTree::open()
- SHLD-002: clear_commitment_tree_for_wallet() scoped to a single wallet
- SHLD-007: Safety-net resync guard now logs wallet seed hash before clearing
- Migration v34: drops orphaned global commitment_tree_* tables
- clear_network_data() deletes per-wallet tree files for the target network

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): prevent permanent state leak on sync_notes failure in with_anchor_retry

When sync_notes() failed after an anchor mismatch, the early `?` return
skipped re-inserting the shielded state back into the HashMap, permanently
orphaning it until app restart. Now the sync result is captured without
early return, ensuring the state is always re-inserted regardless of
success or failure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): apply password change for current session even when config save fails

The in-memory config update and SDK reinit only ran on the save-success
path, silently discarding the password change on save failure despite
the banner promising session-level application. Now the in-memory update
and reinit run unconditionally; only the banner text varies across the
four (save x reinit) outcome combinations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(error): use dedicated TaskError variants for non-build wallet errors

reload_utxos() and recalculate_affected_address_balances() errors were
routed through shielded_build_error() which pattern-matches for build-
specific patterns like "AnchorMismatch". These are wallet operations,
not shielded builds. Added WalletUtxoReloadFailed and
WalletBalanceRecalculationFailed variants with appropriate user-facing
messages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(error): preserve error chain in CoreRpcConnectionFailed

chain_lock_rpc_error() took the RPC error by reference and created
CoreRpcConnectionFailed with source: None, dropping all diagnostic
info. Added a detail: Option<String> field to the variant to carry
the formatted error. Boxed the source field to keep the enum size
under the clippy threshold. Updated all constructors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): two-column wallet header layout, rename Tools to Advanced

Split the wallet detail panel header into left (name, total balance,
action buttons) and right (collapsible balance breakdown, sync status)
columns. Renamed the "Tools" dropdown to "Advanced" and moved it into
the action buttons row instead of right-aligning it separately.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(test): rename test_v33_migration_fresh_install to match DB version 34

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* revert(shielded): use shared main DB for commitment tree instead of per-wallet files

Reverts the per-wallet SQLite file approach (63ce0e18). Multiple wallets
share the same commitment_tree_* tables in the main database. This is
accepted behavior until the SDK adds proper wallet-scoping support
(dashpay/grovedb#653).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): fix stale clear_commitment_tree_for_wallet reference after merge

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): harden anchor retry, commitment tree clearing, and state guard

SEC-002: Verify shielded balance after anchor retry sync before retrying
the operation — prevents doomed retries with zero balance.

SEC-003: Handle missing commitment tree tables in clear_commitment_tree_tables
(grovedb creates them lazily, so DELETEs fail on fresh installs).

Fix clear_network_data to log-and-continue when commitment tree clearing
fails — these tables are optional and shouldn't block network data reset.

Add SEC-001 INTENTIONAL annotation for panic safety in with_anchor_retry.

Document shield_from_asset_lock_task: anchor retry is not applicable since
it only reads the payment address and doesn't use the commitment tree.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(core): sanitize RPC errors and simplify CoreRpcConnectionFailed

SEC-006: Store user-friendly message instead of raw RPC error string
in network status when chain lock query fails with a non-auth,
non-connection error.

CODE-002: Remove redundant detail field from CoreRpcConnectionFailed --
format diagnostic info into the url field instead, keeping source for
the error chain.

Add SEC-005 INTENTIONAL annotation for RPC errors in status tooltip.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): config permissions, address detection, and per-frame caching

SEC-007: Set config file permissions to 0600 on Unix after save (contains
RPC credentials).

CODE-003: Deduplicate Amount::dash_from_duffs by delegating to
dash_from_credits.

CODE-006: Remove unused network parameter from AddressKind::detect --
detection is format-based, network validation happens separately.

CODE-004/CODE-005: Cache filtered transaction indices per wallet to
avoid rebuilding HashSet and filtering on every frame. Invalidated
on wallet switch and refresh.

CODE-008: Reset AddressInput widgets on network switch so they pick up
the new network for validation. Add invalidate_address_input methods
to SendScreen, UnshieldCreditsScreen, and WalletsBalancesScreen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(logging): correct log levels per coding best practices

- debug! -> trace!: "state transition built, broadcasting" messages in
  bundle.rs (5 occurrences) — primary-path step-by-step progress
- trace! -> debug!: cookie auth fallback in core/mod.rs and context/mod.rs
  (2 occurrences) — secondary/fallback execution path, not primary flow
- info! -> debug!: anchor mismatch retry in context/shielded.rs — error
  handling branch, not a business event
- info! -> debug!: "marked N note(s) spent" in context/shielded.rs —
  internal bookkeeping, not a user-visible business event
- info! -> debug!: post-transfer sync complete in shielded_send_screen.rs —
  internal plumbing at screen level

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: minor ui fixes

* feat(ui): replace Advanced dropdown with inline right-aligned buttons

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): use allocate_ui_with_layout to right-align dev buttons

The right_to_left layout inside with_layout only got the remaining
space after left buttons, centering the dev buttons instead of
pushing them to the right edge. Using allocate_ui_with_layout with
the full remaining width forces the right-to-left block to span
the entire remaining area.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): eagerly initialize shielded balance on wallet screen open

The shielded tab balance only recalculated when the user clicked the
Shielded tab because ShieldedTabView was lazily initialized on tab
selection. This meant the wallet list and tab header showed zero balance
until the user manually visited the tab.

Two-layer fix:
- Backend: call initialize_shielded_wallet() in handle_wallet_unlocked()
  so persisted shielded balance is loaded into shielded_states at wallet
  load time, making it available to all UI screens immediately.
- UI: eagerly create ShieldedTabView on wallet screen construction and
  wallet switch, and tick() it every frame so the init/sync chain
  (InitializeShieldedWallet -> SyncNotes -> CheckNullifiers) runs even
  when the Shielded tab is not the active tab.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove stale balance_breakdown_expanded field from cherry-pick

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): move action buttons to full-width row below header columns

The button bar was inside the left column (55% width), so the
right-aligned dev buttons ended up in the middle of the screen.
Move render_action_buttons() below the two-column header so it
spans the full available width.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(ui): change sync status to bullet-point layout

Replace the wide horizontal Platform line (pipe-separated) with
individual bullet-point items: Core, Addresses, Notes, Nullifiers
each on their own line. Removes the Frame wrapper for a lighter
appearance.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(shielded): prevent double balance from redundant init + sync chain

When eager init (wallet_lifecycle) populates shielded_states and then
ShieldedTabView.tick() dispatches another InitializeShieldedWallet,
the idempotency check returns early but handle_result() chains
SyncNotes which can re-append notes already loaded from DB.

Fix: tick() now checks if shielded_states already contains the
wallet's state. If so, it adopts the cached balance and marks
itself initialized without dispatching a backend task, preventing
the redundant sync chain.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(shielded): move initialization entirely to backend, remove UI init path

The shielded wallet had two initialization paths: the backend
(handle_wallet_unlocked -> initialize_shielded_wallet) and the UI
(ShieldedTabView::tick dispatching InitializeShieldedWallet). This
created redundant init attempts and blurred the responsibility boundary.

Now:
- handle_wallet_unlocked initializes the shielded state synchronously,
  then queues SyncNotes -> CheckNullifiers as a background task
- ShieldedTabView reads its state from AppContext::shielded_states
  via refresh_from_backend_state(), never dispatching init itself
- The "Initialize Shielded Wallet" button and WalletUnlockPopup are
  removed from ShieldedTabView (wallet unlock already calls
  handle_wallet_unlocked which handles everything)
- Resync Notes (developer mode) still dispatches InitializeShieldedWallet
  as an explicit user action

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): deduplicate notes in sync to prevent double balance

When the commitment tree resets (empty/corrupt) but persisted notes
remain in the DB, initialize_shielded_wallet loads notes from DB
while last_synced_index falls to 0. The subsequent sync rescans
from position 0 and re-appends all decrypted notes — duplicating
the ones already in memory from DB, doubling the balance.

Fix: sync_notes() now checks if a note at the same position already
exists in shielded_state.notes before appending, preventing
duplicates regardless of the last_synced_index value.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(ui): add shielded diversified address table

Replace the single-address display with prev/next buttons in the
Shielded tab with a collapsible address table showing all generated
diversified addresses. Each row shows the index, truncated bech32m
address (click or Copy button to copy full address), and status
(Default for index 0). The section is collapsed by default in normal
mode and expanded in developer mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address review findings — duplicate controls, perf, tab visibility

- Remove duplicate sync status/buttons block in shielded_tab.rs
- Fix BIP44 accounts with index > 0 being invisible (regression)
- Fix early return in render_account_tabs blocking Shielded tab for new wallets
- Fix double-tick of ShieldedTabView per frame when tab is active
- Fix system_tab_sections O(n*m) perf by precomputing address counts
- Track queue_shielded_sync via subtasks for graceful shutdown
- Use HashSet for O(1) duplicate note check in sync (was O(n^2))
- Fix balance_breakdown default_open to respect developer mode
- Fix is_system_category to delegate to is_visible_in_default_mode
- Fix doc comment for system_tab_sections sort order claim
- Narrow #[allow(dead_code)] to specific field on AccountSummary

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ui): replace mutex unwrap() with graceful error handling in shielded tab

Replace .lock().unwrap() calls on shielded_states with .lock().ok()
patterns throughout ShieldedTabView to prevent UI panics on poisoned
mutex. Shows fallback messages or skips updates gracefully.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(mcp): resolve async lifetime errors for Rust 2024 edition

Use spawn_blocking + block_on in dispatch_task to avoid Send bound
issues with platform SDK types (DataContract/Sdk references across
await points). Same pattern already used by AppState::handle_backend_task.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: fix mcp dispatch

* doc: remove obsolete manual teting docs

* fix(ui): include zero-balance addresses in AddressInput wallet entries

Pull Core addresses from known_addresses (all derived) instead of
address_balances (only funded). Balance is looked up from
address_balances, defaulting to 0. Callers use with_balance_range(1..)
when they need funded-only addresses.

Fixes Mine dialog showing empty dropdown for fresh/zero-balance wallets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): sort AddressInput wallet entries alphabetically

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* revert: remove redundant sort in extract_wallet_entries

filtered_entries() already sorts alphabetically by display_label.
The sort added in extract_wallet_entries was redundant.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): clarify shielded network validation logic

Replace double-negation with explicit mainnet-class comparison.
Documents that testnet/devnet/local share the same HRP ("tdash1z")
and cannot be distinguished at the address level.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): include send-all transactions in wallet history

Check both outputs and inputs when filtering transactions. Build an
OutPoint→Address lookup from the wallet's own transactions to resolve
input addresses. Send-all transactions (no change output) were
previously dropped because no output matched known_addresses.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): use is_ours flag for transaction filtering

Simplify transaction filter to use the is_ours flag instead of
address matching. Fix SPV path to set is_ours=true for all wallet
transactions (upstream only sets it for sends). SPV history is
per-wallet, so all entries are ours by definition.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test(e2e): verify is_ours flag for SPV send and receive transactions

Sends funds between two wallets via SPV and asserts that both the
sender (negative net_amount) and receiver (positive net_amount) have
is_ours=true. Validates the fix to the SPV reconcile path that
overrides the upstream library's send-only is_ours logic.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(core): clear RPC error state on successful chain lock fetch

When get_best_chain_lock() succeeds on the active network, explicitly
clear any lingering RPC error in ConnectionStatus so the connection
indicator recovers after a transient outage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(model): move is_platform_address_string from UI helpers to model layer

AddressKind::detect() in the model layer depended on crate::ui::helpers
for platform address detection — wrong layering. Move the function to
src/model/address.rs and keep a re-export in helpers for existing callers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): add actionable guidance to address validation error messages

Each validation error now tells the user what to do, not just what went
wrong: "check for typos", "check you are using the correct network",
"use all lowercase". Messages that already had guidance are unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): invalidate address inputs on all screens during context switch

TransferScreen, AddressBalanceScreen, and ShieldedSendScreen retained
stale address text from the previous network after a context switch.
Add invalidate_address_input() to each and call it from change_context().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): clear password field on network switch when no config exists

When switching networks, if no config entry exists for the new network
(or the password is empty), the password input field now clears instead
of retaining the previous network's password.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): clear validated_destination in invalidate_address_input

WalletSendScreen and UnshieldCreditsScreen cleared the AddressInput
widget but left validated_destination with stale validation from the
previous network. Now both are cleared together.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): consume ShieldedNotesSynced to update shielded send screen state

After a shielded transfer, the post-transfer note sync result was only
logged but never used to update the UI. Now ShieldedNotesSynced updates
the balance display, clears the pending-update banner, and appends the
remaining balance to the success message.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: apply cargo +nightly fmt formatting

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(model): disambiguate Core vs Identity address detection by prefix

Core addresses on Dash start with X/Y (mainnet) or y/8/7 (testnet).
When the input starts with a known Core prefix, try Core first.
Otherwise try Identity first, since Identity IDs use the same Base58
alphabet but don't have Core address prefixes.

This fixes identity-only AddressInput mode rejecting valid Identity
IDs that happened to parse as Core addresses, and eliminates the
flaky test that had to skip Core-looking random identifiers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(core): show actual RPC error on Networks page instead of generic message

The Networks page is a debugging tool — users need the real error text
to diagnose Dash Core issues. Replace sanitized "RPC error — check
Dash Core status" with the actual error from dashcore_rpc.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(db): resolve deadlock in clear_network_data

clear_network_data() held self.conn.lock() for the transaction, then
called clear_commitment_tree_tables() which tried to acquire the same
mutex — classic non-reentrant mutex deadlock. UI froze on "Delete Data".

Fix: scope the connection lock so it's released before the commitment
tree clearing acquires it again.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): replace mutex unwrap() with graceful .ok() in shielded_sync_task

The shielded_states mutex lock used .unwrap() which would panic on a
poisoned mutex. Replace with .ok()? to return None gracefully.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): replace RwLock unwrap() with graceful error handling in shielded tab

Replace wallets.read().unwrap() with .read().ok() and show an error
label instead of panicking on a poisoned RwLock.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(spv): add debug log when overriding is_ours for receive transactions

Log at debug level when SPV reports is_ours=false for a receive
transaction (net_amount >= 0) to detect upstream API behavior changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shielded): log warning on note value divergence during position dedup

Use a HashMap<position, value> instead of HashSet<position> so we can
detect when a re-synced note at an existing position has a different
value — indicating potential data integrity issues.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* perf(model): avoid allocation in is_platform_address_string

Replace to_lowercase() with eq_ignore_ascii_case() for the HRP prefix
check. The HRP constants are ASCII-only so this is safe and avoids a
heap allocation on every call.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(ui): remove unused label field and is_key_only from AccountSummary

The label field was constructed but never read outside the builder.
is_key_only() had no callers. Remove both instead of suppressing
dead_code warnings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(ui): avoid cloning full AccountTab enum in tab content match

Extract only the category data (clone of AccountCategory + index) before
the match instead of cloning the entire enum. This avoids unnecessary
allocation for the Shielded and System variants.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): clear validated_address on network switch in mine dialog

invalidate_address_inputs() cleared the AddressInput widget but left
the validated_address stale, which could reference an address from the
previous network.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(model): move truncate_address to model layer and document ASCII precondition

Move truncate_address from address_input.rs and shielded_tab.rs into
src/model/address.rs as a parameterized public function. Both callers
now delegate to the shared implementation with their own prefix/suffix
lengths.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs(shielded): document why spawn_blocking trampoline is needed in queue_shielded_sync

The spawn_blocking(block_on(...)) pattern is required because async
methods on Arc<Self> produce non-'static futures due to a known Rust
compiler limitation (rust-lang/rust#100013). Document this to prevent
future removal attempts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: apply nightly rustfmt formatting

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add unified send screen requirements

Full requirements for extending the Send screen to support all 14 viable
Source×Destination combinations plus 13 new MCP/CLI tools.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add unified send screen architecture

System layer traces for all 8 new combinations, code placement plan,
send_screen decomposition strategy, MCP tools architecture (13 tools
across 3 files), and 8-task work decomposition with dependency graph.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): add unified send screen with all 14 source-destination combinations

Wire 8 new send combinations into the Send screen routing:
- Core->Shielded, Core->Identity, Platform->Shielded, Platform->Identity
- Shielded->Core, Identity->Core, Identity->Platform, Identity->Identity

Add Identity as 4th source type with selector dropdown, progressive
disclosure (developer mode gates shielded, identities shown when loaded),
and display_task_result handlers for all new backend result types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(mcp): add 10 identity and shielded MCP tools

Add identity tools: topup (core/platform), transfer, withdraw, to-address.
Add shielded tools: shield-from-core, shield-from-platform, transfer,
unshield, withdraw. Add resolve::qualified_identity() helper and
validate_credits() for shared parameter validation.

Box QualifiedIdentity in SourceSelection::Identity to fix clippy
large_enum_variant warning.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add user stories and MCP tool reference for unified send

Add SND-007 through SND-013 user stories for new send combinations.
Add 10 new tools to MCP.md tool reference table.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): add unified send screen with all 14 source-destination combinations

Wire 8 new send combinations into the Send screen routing:
- Core->Shielded, Core->Identity, Platform->Shielded, Platform->Identity
- Shielded->Core, Identity->Core, Identity->Platform, Identity->Identity

Add Identity as 4th source type with selector dropdown, progressive
disclosure (developer mode gates shielded, identities shown when loaded),
and display_task_result handlers for all new backend result types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(mcp): add 10 identity and shielded MCP tools

Add identity tools: topup (core/platform), transfer, withdraw, to-address.
Add shielded tools: shield-from-core, shield-from-platform, transfer,
unshield, withdraw. Add resolve::qualified_identity() helper and
validate_credits() for shared parameter validation.

Box QualifiedIdentity in SourceSelection::Identity to fix clippy
large_enum_variant warning.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: resolve merge conflicts with v1.0-dev

Restore address_input.rs from v1.0-dev (includes change/system
address filtering). Fix render_address_table calls to pass
account_filter parameter.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address PR #798 review findings across send screen and MCP tools

Send screen (C01/C02/C03/C05):
- Validate shielded destination before dispatching shield routes
- Check highest-address platform balance for Platform->Shielded
- Subtract estimated fee from identity source available balance
- Improve identity-not-found error to be actionable without navigation

MCP tools (C09/C10/C15):
- Add INTENTIONAL comments for tools that skip SPV …
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