Skip to content

build: add Flatpak packaging and CI workflow#589

Merged
lklimek merged 21 commits into
v1.0-devfrom
feat/flatpak
Feb 18, 2026
Merged

build: add Flatpak packaging and CI workflow#589
lklimek merged 21 commits into
v1.0-devfrom
feat/flatpak

Conversation

@lklimek
Copy link
Copy Markdown
Contributor

@lklimek lklimek commented Feb 17, 2026

Summary

Replace Snap packaging with Flatpak for Linux distribution:

  • Remove snap/snapcraft.yaml (226 lines of complex, hard-to-maintain Snap config)
  • Add Flatpak build manifest, desktop entry, and AppStream metadata
  • Add GitHub Actions workflow for automated Flatpak builds (x86_64 + aarch64)
  • Set app_id on ViewportBuilder to match Flatpak/Wayland StartupWMClass
  • Update README with Flatpak install/uninstall instructions, rename "Installation" to "Build from Source"
  • No application logic changes — app continues to work in SPV mode by default

User Story

Imagine you are a Linux user who wants to try Dash Evo Tool without compiling Rust from source or installing system-wide dependencies. You download a single .flatpak bundle from the GitHub release page, install it with one command, and launch the app from your desktop's application menu. The app runs sandboxed with minimal permissions, stores its configuration inside the Flatpak sandbox automatically, and receives updates the same way. If you ever want to remove it, a single flatpak uninstall cleans everything up.

Changes

File Purpose
flatpak/org.dash.DashEvoTool.yml Flatpak build manifest (freedesktop 25.08 runtime, rust-stable + llvm21 SDK extensions)
flatpak/org.dash.DashEvoTool.desktop Desktop entry for app launchers
flatpak/org.dash.DashEvoTool.metainfo.xml AppStream metadata for software centers
.github/workflows/flatpak.yml CI workflow: matrix build (x86_64/aarch64), Cargo + Flatpak caching, release attachment
snap/snapcraft.yaml Deleted — replaced by Flatpak
src/main.rs Set with_app_id("org.dash.DashEvoTool") on viewport builder
README.md Add Flatpak install section, clarify build-from-source prerequisites
.gitignore Add Flatpak build artifact exclusions

CI workflow details

  • Triggers: push to **flatpak** branches, release (published), workflow_dispatch
  • Matrix: parallel x86_64 (ubuntu-latest) and aarch64 (ubuntu-24.04-arm) builds
  • Cargo caching: Cargo registry and target directories are bind-mounted into the Flatpak build sandbox and cached per architecture, with cleanup of incremental and registry/src artifacts (similar to Swatinem/rust-cache)
  • Flatpak caching: .flatpak-builder state cached per arch, keyed on manifest + lockfile
  • Concurrency: new push cancels in-progress builds for the same ref
  • Security: all GitHub Actions pinned to commit SHAs
  • On release: automatically attaches .flatpak bundles to the existing GitHub release
  • Artifacts: 14-day retention for build artifacts on non-release triggers

Flatpak sandbox permissions

Follows least-privilege principle:

  • --share=ipc — X11 shared memory
  • --share=network — blockchain communication
  • --socket=wayland + --socket=fallback-x11 — Wayland-first display
  • --device=dri — GPU acceleration for egui rendering
  • No filesystem access (Flatpak redirects XDG_CONFIG_HOME to sandbox automatically)

Build & install locally

# Build
flatpak-builder --user --force-clean --repo=flatpak-repo build-dir flatpak/org.dash.DashEvoTool.yml

# Bundle
flatpak build-bundle flatpak-repo dash-evo-tool-linux-x86_64.flatpak org.dash.DashEvoTool

# Install
flatpak --user install dash-evo-tool-linux-x86_64.flatpak

Known limitations

  • Build requires network access for cargo deps (--share=network in build-args) — cargo vendoring needed for Flathub submission
  • protoc module is x86_64-only in manifest; CI patches URL/sha256 for aarch64 via sed
  • No screenshots in metainfo yet (required for Flathub listing)

lklimek and others added 3 commits February 17, 2026 09:25
Add Flatpak build manifest, desktop entry, AppStream metadata, and
GitHub Actions workflow for building and distributing Flatpak bundles.
Uses freedesktop 25.08 runtime with rust-stable and llvm21 extensions.
No application source code changes required - works in SPV mode by default.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Pin GitHub Actions to commit SHAs for supply chain security
- Upgrade softprops/action-gh-release from v1 to v2.2.2
- Remove redundant --socket=x11 (fallback-x11 suffices)
- Remove duplicate tag trigger preventing double builds on release
- Remove duplicate env vars inherited from top-level build-options
- Add Flatpak build artifacts to .gitignore
- Add bugtracker URL to AppStream metainfo
- Remove deprecated <categories> from metainfo (use .desktop instead)
- Add Terminal=false and Keywords to desktop entry
- Add disk space check after SDK install in CI
- Rename artifact to include architecture suffix

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

coderabbitai Bot commented Feb 17, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Flatpak packaging and CI: new Flatpak manifest, desktop/metainfo, Flatpak build GitHub Actions workflow for x86_64/aarch64, .gitignore updates for Flatpak artifacts, removal of Snap packaging, README install/build changes, and a small runtime change setting egui app_id in src/main.rs.

Changes

Cohort / File(s) Summary
Flatpak manifests & desktop assets
flatpak/org.dash.DashEvoTool.yml, flatpak/org.dash.DashEvoTool.metainfo.xml, flatpak/org.dash.DashEvoTool.desktop
Adds Flatpak YAML manifest (modules: protoc, libsodium, dash-evo-tool), metainfo XML, and desktop entry. Declares runtime/sdk, finish-args, build modules, installs binary and desktop assets to /app/bin and /app/share.
CI: GitHub Actions workflow
.github/workflows/flatpak.yml
New "Build Flatpak" workflow with an architecture matrix (x86_64, aarch64): checks out code, installs Flatpak tooling, configures Flathub, installs SDKs/runtimes, patches manifest per-arch (protoc artifact), caches builder state, builds repo, creates .flatpak bundle, uploads artifact, and conditionally attaches to a release.
Removed Snap packaging
snap/snapcraft.yaml
Removes Snapcraft manifest and all parts/override-build logic previously used for snap packaging.
Docs / README
README.md
Adds "Install via Flatpak (Linux)" and a "Build from Source" section; updates TOC and prerequisites to reflect Flatpak and source-build workflows.
Ignore patterns
.gitignore
Adds Flatpak build ignores: *.flatpak, .flatpak-builder/, flatpak-repo/, build-dir/.
Runtime minor change
src/main.rs
Sets explicit egui viewport app_id to org.dash.DashEvoTool when starting the UI.

Sequence Diagram(s)

sequenceDiagram
    participant GH as "GitHub Actions"
    participant Runner as "Runner\n(arch-specific)"
    participant Flatpak as "flatpak-builder"
    participant Flathub as "Flathub Remote"
    participant Artifacts as "GitHub Artifacts / Release"

    GH->>Runner: trigger job (push/release, arch matrix)
    Runner->>Runner: checkout repo
    Runner->>Runner: install Flatpak tooling & SDKs
    Runner->>Flathub: add/configure Flathub remote
    Runner->>Runner: patch manifest for target arch (protoc)
    Runner->>Flatpak: build flatpak-repo
    Flatpak->>Runner: produce bundle (.flatpak)
    Runner->>Artifacts: upload artifact
    alt release event
        Runner->>Artifacts: attach bundle to release
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped from snaps into flatpak light,
Manifests, icons, and CI taking flight,
Builders hum and bundles gleam,
Artifacts tucked in a release-stream,
A carrot-powered CI delight 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'build: add Flatpak packaging and CI workflow' clearly and concisely summarizes the main changes: adding Flatpak packaging files and a GitHub Actions CI workflow.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/flatpak

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.

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: 5

🧹 Nitpick comments (3)
.github/workflows/flatpak.yml (1)

55-63: Consider failing fast if there's insufficient disk space after SDK install.

The disk space is printed after cleanup and after SDK install, but only for informational purposes. A Rust + Flatpak build can easily consume 10+ GB. Consider adding a threshold check that fails the job early rather than letting it fail mid-build with a cryptic "no space left on device" error.

Example threshold check
          echo "=== Disk space after SDK install ==="
          df -h
+          AVAIL_GB=$(df --output=avail -BG / | tail -1 | tr -d ' G')
+          if [ "$AVAIL_GB" -lt 10 ]; then
+            echo "::error::Insufficient disk space: ${AVAIL_GB}GB available, need at least 10GB"
+            exit 1
+          fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/flatpak.yml around lines 55 - 63, The "Install Flatpak SDK
and runtime" step currently only prints disk usage; add a bash check after the
SDK install that reads the available space (e.g., using df --output=avail -k /
or similar), compares it to a configurable threshold in KB (for example
15_000_000 KB ≈ 15GB), and exits non-zero with a clear error log if available
space is below the threshold so the job fails fast; update the step to emit a
helpful message including the checked mount, available KB, threshold KB, and
exit 1 when insufficient.
flatpak/org.dash.DashEvoTool.yml (1)

40-49: protoc module is x86_64-only — document or guard this.

The prebuilt protoc binary is hardcoded to linux-x86_64. This is noted as a known limitation in the PR, but adding a comment in the manifest would help future maintainers understand the constraint and what to change for aarch64 support.

Add a note
   # Module 1: Install protobuf compiler (required for building dash-sdk)
+  # NOTE: Uses x86_64 binary. For aarch64, replace with protoc-25.2-linux-aarch_64.zip and update sha256.
   - name: protoc
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@flatpak/org.dash.DashEvoTool.yml` around lines 40 - 49, The manifest
currently installs a prebuilt protoc binary that is linux-x86_64-only (see the
module named "protoc" and its build-commands that unzip
protoc-25.2-linux-x86_64.zip); add a clear inline comment in the manifest above
the "name: protoc" entry stating this architecture limitation and what to change
for aarch64 (e.g., use an aarch64 release or build from source), and optionally
add a guard/conditional around the "sources" or "build-commands" (based on
Flatpak variables or an ARCH check) to skip or replace the x86_64 package when
building on aarch64 so maintainers know how to handle non-x86_64 builds.
flatpak/org.dash.DashEvoTool.metainfo.xml (1)

32-34: Keep release metadata in sync with actual releases.

The hardcoded development release entry is fine for now, but this will need to be updated (or automated) when tagging actual releases. AppStream validators expect the latest <release> version to match the distributed version and the date to be accurate.

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

In `@flatpak/org.dash.DashEvoTool.metainfo.xml` around lines 32 - 34, The
<releases> block currently contains a hardcoded <release version="1.0.0-dev"
date="2026-02-17" type="development" /> entry which will diverge from actual
distributed versions and fails AppStream validators; update the <release>
element to match the real release version and current date before publishing or
automate population during your release pipeline (e.g., replace the version/date
in org.dash.DashEvoTool.metainfo.xml from the git tag or CI build metadata),
ensuring the latest <release> entry always equals the shipped package version
and accurate release date.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/flatpak.yml:
- Around line 65-67: The workflow step "Build Flatpak" currently uses the flag
--repo=repo which creates a top-level repo/ directory that doesn't match
.gitignore (which ignores flatpak-repo/); update the repo path to a more
specific name (e.g., --repo=flatpak-repo) in the Build Flatpak step of
.github/workflows/flatpak.yml and also update any references/comments in
flatpak/org.dash.DashEvoTool.yml to use the same flatpak-repo name so the
generated directory matches .gitignore, or alternatively add repo/ to .gitignore
if you prefer keeping --repo=repo.

In `@flatpak/org.dash.DashEvoTool.desktop`:
- Line 10: The ViewportBuilder configuration doesn't set an explicit app_id so
the WM_CLASS reported by the window doesn't match the desktop file; update the
ViewportBuilder used to construct the viewport (the viewport field in
src/main.rs where egui::ViewportBuilder::default() is called) to call
with_app_id("org.dash.DashEvoTool") (i.e.
egui::ViewportBuilder::default().with_icon(icon_data).with_app_id("org.dash.DashEvoTool")),
ensuring the app_id matches StartupWMClass in org.dash.DashEvoTool.desktop;
alternatively, if you prefer the opposite approach, change StartupWMClass to
match the actual reported class, but prefer adding with_app_id to
ViewportBuilder.

In `@flatpak/org.dash.DashEvoTool.yml`:
- Line 1: The file header "# Flatpak manifest for Dash Evo Tool" uses CRLF line
endings; convert this YAML to Unix-style LF endings (replace CRLF -> LF) — e.g.,
run dos2unix on the manifest or change your editor's EOL setting and save — then
recommit; optionally add a .gitattributes entry (e.g., for *.yml set text
eol=lf) to prevent future CRLF commits so org.dash.DashEvoTool.yml stays
LF-only.
- Around line 70-71: Update the build command under build-commands to run cargo
with the locked flag so dependency versions are fixed: replace the current
"cargo build --release" invocation with "cargo build --release --locked" in the
YAML entry to ensure reproducible, audited builds.
- Around line 63-69: The skip list in the dir entry (the mapping with "path: .."
and "skip:") is missing the actual output directory name used by CI (repo/);
update the skip array to include "repo" (or "repo/") so the repo output
directory is not copied into the build context, or alternatively rename the
CI/workflow output to match the existing "flatpak-repo" entry — adjust the
"skip" values in that dir block accordingly.

---

Nitpick comments:
In @.github/workflows/flatpak.yml:
- Around line 55-63: The "Install Flatpak SDK and runtime" step currently only
prints disk usage; add a bash check after the SDK install that reads the
available space (e.g., using df --output=avail -k / or similar), compares it to
a configurable threshold in KB (for example 15_000_000 KB ≈ 15GB), and exits
non-zero with a clear error log if available space is below the threshold so the
job fails fast; update the step to emit a helpful message including the checked
mount, available KB, threshold KB, and exit 1 when insufficient.

In `@flatpak/org.dash.DashEvoTool.metainfo.xml`:
- Around line 32-34: The <releases> block currently contains a hardcoded
<release version="1.0.0-dev" date="2026-02-17" type="development" /> entry which
will diverge from actual distributed versions and fails AppStream validators;
update the <release> element to match the real release version and current date
before publishing or automate population during your release pipeline (e.g.,
replace the version/date in org.dash.DashEvoTool.metainfo.xml from the git tag
or CI build metadata), ensuring the latest <release> entry always equals the
shipped package version and accurate release date.

In `@flatpak/org.dash.DashEvoTool.yml`:
- Around line 40-49: The manifest currently installs a prebuilt protoc binary
that is linux-x86_64-only (see the module named "protoc" and its build-commands
that unzip protoc-25.2-linux-x86_64.zip); add a clear inline comment in the
manifest above the "name: protoc" entry stating this architecture limitation and
what to change for aarch64 (e.g., use an aarch64 release or build from source),
and optionally add a guard/conditional around the "sources" or "build-commands"
(based on Flatpak variables or an ARCH check) to skip or replace the x86_64
package when building on aarch64 so maintainers know how to handle non-x86_64
builds.

Comment thread .github/workflows/flatpak.yml Outdated
Comment thread flatpak/org.dash.DashEvoTool.desktop Outdated
Comment thread flatpak/org.dash.DashEvoTool.yml Outdated
Comment thread flatpak/org.dash.DashEvoTool.yml Outdated
Comment thread flatpak/org.dash.DashEvoTool.yml Outdated
lklimek and others added 8 commits February 17, 2026 11:26
- Remove "Free disk space" step from flatpak and release workflows
- Remove Windows and macOS builds from release workflow
- Use "ubuntu" runner image instead of pinned versions
- Clean up unused matrix.ext references

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace release-creating job with attach-to-release (only on release event)
- Add 14-day retention for build artifacts
- On tag push or workflow_dispatch, only upload artifacts (no release)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The release workflow changes were out of scope for the Flatpak PR.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix CRLF line endings in Flatpak manifest (convert to LF)
- Set app_id on ViewportBuilder to match desktop StartupWMClass
- Use --locked flag for reproducible cargo builds in Flatpak
- Rename --repo=repo to --repo=flatpak-repo to match .gitignore
- Add architecture note for protoc x86_64 binary

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a dedicated section for installing via Flatpak on Linux,
clarify that prerequisites are only needed for building from source,
and rename "Installation" to "Build from Source" for clarity.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use reverse-DNS format org.dash.DashEvoTool to match the
Wayland app_id set via ViewportBuilder::with_app_id().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Single * doesn't match path separators in GitHub Actions branch filters.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add matrix strategy for parallel x86_64 and aarch64 builds
- Patch protoc URL/sha256 per architecture at build time
- Cache .flatpak-builder directory keyed on arch + manifest + lockfile
- Pin actions/cache to SHA

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

🧹 Nitpick comments (1)
README.md (1)

39-41: Consider noting the aarch64 bundle and clarifying the install command.

The PR adds both x86_64 and aarch64 Flatpak builds, but only the x86_64 bundle filename is shown here. Also, for users unfamiliar with Flatpak, it may help to note that flatpak install on a local .flatpak file may prompt for the --user flag or require the freedesktop runtime to already be installed.

Suggested improvement
-``` shell
-flatpak install dash-evo-tool-linux-x86_64.flatpak
-```
+``` shell
+# For x86_64:
+flatpak install dash-evo-tool-linux-x86_64.flatpak
+# For aarch64:
+flatpak install dash-evo-tool-linux-aarch64.flatpak
+```
+
+> **Note**: If prompted, allow installation of the required runtime dependencies. You may also use `--user` to install for the current user only.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` around lines 39 - 41, Update the Flatpak install example to
include both bundle filenames and a short note about potential
runtime/permission prompts: add the aarch64 bundle name
dash-evo-tool-linux-aarch64.flatpak alongside dash-evo-tool-linux-x86_64.flatpak
in the code block and append a brief note that users may be prompted to install
runtime dependencies or may need to add the --user flag to install for the
current user only.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@README.md`:
- Around line 39-41: Update the Flatpak install example to include both bundle
filenames and a short note about potential runtime/permission prompts: add the
aarch64 bundle name dash-evo-tool-linux-aarch64.flatpak alongside
dash-evo-tool-linux-x86_64.flatpak in the code block and append a brief note
that users may be prompted to install runtime dependencies or may need to add
the --user flag to install for the current user only.

Flatpak builder validates desktop files and rejects CRLF line endings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

🧹 Nitpick comments (2)
flatpak/org.dash.DashEvoTool.metainfo.xml (2)

32-34: Development release entry is fine for now; remember to update before stable release.

The 1.0.0-dev development release is appropriate for the current stage. When cutting a stable release, update the version, date, and change type to "stable", and add a <description> with release notes — Flathub validators expect release descriptions for stable releases.

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

In `@flatpak/org.dash.DashEvoTool.metainfo.xml` around lines 32 - 34, The current
<release> element uses version="1.0.0-dev" and type="development" which is
correct for now; before cutting a stable release, update the <release> element's
attributes: set version to the stable version string (e.g., "1.0.0"), update the
date to the release date, change type="stable", and add a <description> child
containing the release notes so Flathub validators accept the stable release.

1-35: Flathub will require <screenshots> before publishing.

The PR description notes this as a known limitation. Just flagging that appstreamcli validate will emit a warning/error for the missing <screenshots> element, and Flathub submission requires at least one screenshot. Consider adding a placeholder TODO comment or tracking this in an issue.

Would you like me to open an issue to track adding screenshots to the metainfo?

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

In `@flatpak/org.dash.DashEvoTool.metainfo.xml` around lines 1 - 35, The AppStream
metadata is missing the required <screenshots> element which causes appstreamcli
validation warnings and will block Flathub submission; add a <screenshots> block
to the metainfo XML for the component id org.dash.DashEvoTool (e.g., include at
least one <screenshot type="default"> entry pointing to a packaged image or a
placeholder), or if screenshots cannot be provided now, add a clear TODO comment
in the file and create/link an issue tracking adding screenshots for release
version 1.0.0-dev so the missing element is addressed before Flathub submission.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@flatpak/org.dash.DashEvoTool.metainfo.xml`:
- Around line 32-34: The current <release> element uses version="1.0.0-dev" and
type="development" which is correct for now; before cutting a stable release,
update the <release> element's attributes: set version to the stable version
string (e.g., "1.0.0"), update the date to the release date, change
type="stable", and add a <description> child containing the release notes so
Flathub validators accept the stable release.
- Around line 1-35: The AppStream metadata is missing the required <screenshots>
element which causes appstreamcli validation warnings and will block Flathub
submission; add a <screenshots> block to the metainfo XML for the component id
org.dash.DashEvoTool (e.g., include at least one <screenshot type="default">
entry pointing to a packaged image or a placeholder), or if screenshots cannot
be provided now, add a clear TODO comment in the file and create/link an issue
tracking adding screenshots for release version 1.0.0-dev so the missing element
is addressed before Flathub submission.

Add concurrency group keyed on git ref so a new push cancels
any running build for the same branch or release.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
lklimek and others added 3 commits February 17, 2026 13:13
- Remove unnecessary --filesystem=xdg-config/dash-evo-tool:create
  (Flatpak already redirects XDG_CONFIG_HOME to sandbox)
- Add categories and keywords to AppStream metainfo for discoverability
- Update README with both x86_64/aarch64 install commands, uninstall
  instructions, and Flatpak data path note
- Clarify aarch64 comment in manifest to reference CI sed patching

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Step-level permissions are not valid in GitHub Actions. Move
contents: write to the job level where it is needed for release
attachment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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

🧹 Nitpick comments (3)
.github/workflows/flatpak.yml (2)

34-35: Consider increasing timeout-minutes for cold-cache builds.

A full Rust build (including all dependencies) inside the Flatpak sandbox can easily exceed 20 minutes, especially on aarch64 runners. If the .flatpak-builder cache misses, this timeout may be hit before the build completes. A value of 60–90 minutes would be safer for initial or cache-miss builds.

Suggested change
     runs-on: ${{ matrix.runs-on }}
-    timeout-minutes: 20
+    timeout-minutes: 90
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/flatpak.yml around lines 34 - 35, The workflow's
timeout-minutes setting is too low for cold-cache Rust builds; update the
timeout-minutes value under the job definition (the "timeout-minutes" key) from
20 to a larger value (e.g., 60 or 90) so full Flatpak/Rust builds on
slow/aarch64 runners don't abort on cache misses; modify the timeout-minutes
entry in the .github/workflows/flatpak.yml workflow to the chosen higher value.

60-63: Sed-based manifest patching is fragile — consider a templating approach.

If the protoc version, filename, or SHA in the manifest is ever updated, the sed replacements will silently fail to match and the build will use the wrong (x86_64) binary on aarch64. Consider adding a verification step after patching, or using yq / envsubst for more robust substitution.

Add a post-patch verification step
      - name: Verify manifest was patched
        if: matrix.arch != 'x86_64'
        run: |
          grep -q "${{ matrix.protoc-zip }}" flatpak/org.dash.DashEvoTool.yml || \
            { echo "ERROR: manifest was not patched for ${{ matrix.arch }}"; exit 1; }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/flatpak.yml around lines 60 - 63, The current "Patch
manifest for architecture" step uses brittle sed replacements against
flatpak/org.dash.DashEvoTool.yml; replace this with a robust templating or
substitution approach (e.g., use yq or envsubst to set the protoc filename and
sha) and add a post-patch verification to fail the job if the manifest still
contains the old x86_64 filename or SHA; specifically update the step that
references matrix.protoc-zip and matrix.protoc-sha256 to perform structured YAML
substitution and then verify (grep or yq query) that
flatpak/org.dash.DashEvoTool.yml contains ${{ matrix.protoc-zip }} and ${{
matrix.protoc-sha256 }} (exit non‑zero if not) so the build cannot silently
continue with the wrong binary.
README.md (1)

35-61: Clear and well-structured Flatpak installation section.

Instructions cover install, run, and uninstall with architecture-specific commands. The data path note and SPV mode clarification are helpful for users transitioning from native builds.

One small thing: for bundle installs, users may want to pass --user to avoid requiring root permissions:

flatpak install --user dash-evo-tool-linux-x86_64.flatpak

This is optional since flatpak install typically prompts for the installation type.

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

In `@README.md` around lines 35 - 61, Update the Flatpak install instructions
under the "## Install via Flatpak (Linux)" section to note the optional --user
flag for bundle installs to avoid requiring root (e.g., mention users can run
flatpak install --user dash-evo-tool-linux-ARCH.flatpak); modify the install
command examples (the x86_64 and aarch64 lines) or add a short sentence after
them clarifying the --user option and that installation type may be prompted by
flatpak.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/flatpak.yml:
- Around line 87-95: The step "Attach bundle to release" uses a step-level
permissions key (permissions: contents: write) which is invalid and causes
uploads to fail; remove the step-level permissions and instead grant write
access at the job level that contains the step (or create a new job, e.g.
"attach-release", with permissions: contents: write that depends on the build
job and downloads artifacts before running the softprops/action-gh-release
step). Locate the step named "Attach bundle to release" and either move the
permissions block up to its job's permissions or implement the separate job that
runs softprops/action-gh-release with job-level permissions set to contents:
write.

---

Nitpick comments:
In @.github/workflows/flatpak.yml:
- Around line 34-35: The workflow's timeout-minutes setting is too low for
cold-cache Rust builds; update the timeout-minutes value under the job
definition (the "timeout-minutes" key) from 20 to a larger value (e.g., 60 or
90) so full Flatpak/Rust builds on slow/aarch64 runners don't abort on cache
misses; modify the timeout-minutes entry in the .github/workflows/flatpak.yml
workflow to the chosen higher value.
- Around line 60-63: The current "Patch manifest for architecture" step uses
brittle sed replacements against flatpak/org.dash.DashEvoTool.yml; replace this
with a robust templating or substitution approach (e.g., use yq or envsubst to
set the protoc filename and sha) and add a post-patch verification to fail the
job if the manifest still contains the old x86_64 filename or SHA; specifically
update the step that references matrix.protoc-zip and matrix.protoc-sha256 to
perform structured YAML substitution and then verify (grep or yq query) that
flatpak/org.dash.DashEvoTool.yml contains ${{ matrix.protoc-zip }} and ${{
matrix.protoc-sha256 }} (exit non‑zero if not) so the build cannot silently
continue with the wrong binary.

In `@README.md`:
- Around line 35-61: Update the Flatpak install instructions under the "##
Install via Flatpak (Linux)" section to note the optional --user flag for bundle
installs to avoid requiring root (e.g., mention users can run flatpak install
--user dash-evo-tool-linux-ARCH.flatpak); modify the install command examples
(the x86_64 and aarch64 lines) or add a short sentence after them clarifying the
--user option and that installation type may be prompted by flatpak.

Comment thread .github/workflows/flatpak.yml
lklimek and others added 3 commits February 17, 2026 13:39
Bind-mount host-side cargo-cache and cargo-target directories into the
Flatpak build sandbox so CARGO_HOME and target/ persist across builds.
Uses split restore/save with cleanup of incremental and registry/src
(similar to Swatinem/rust-cache).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous sed matched --share=network in both finish-args and
build-args, corrupting finish-args. Use a sed range to only target
the build-args section.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@lklimek lklimek changed the title build: add Flatpak packaging and CI workflow build: add Flatpak packaging and CI workflow (test) Feb 17, 2026
@lklimek lklimek changed the title build: add Flatpak packaging and CI workflow (test) build: add Flatpak packaging and CI workflow Feb 17, 2026
@PastaPastaPasta
Copy link
Copy Markdown
Member

PR #589 Review: build: add Flatpak packaging and CI workflow

Author: lklimek | Status: All CI checks pass | +307 / -230 lines

Overview

Replaces the existing Snap packaging with Flatpak for Linux distribution. Adds a Flatpak build
manifest, desktop entry, AppStream metadata, a GitHub Actions CI workflow (x86_64 + aarch64), and
minor app changes (with_app_id). Clean removal of the old Snap config.

Strengths

  • Well-structured PR — excellent description with rationale, user story, change table, and known
    limitations
  • Security best practices — all GitHub Actions pinned to commit SHAs, top-level permissions:
    contents: read with scoped contents: write only on the job that needs it
  • Minimal app code change — only with_app_id() added to main.rs, no application logic touched
  • Least-privilege Flatpak sandbox — no --filesystem access, Wayland-first with X11 fallback, only
    the permissions actually needed
  • Good caching strategy — separate Cargo and Flatpak builder caches keyed by arch + lockfile,
    with incremental/src cleanup before saving
  • Concurrency control — cancels in-progress builds for the same ref

Issues & Suggestions

Medium

  1. protoc build-commands reference hardcoded x86_64 filename
    (flatpak/org.dash.DashEvoTool.yml:46-48)
    The unzip commands inside build-commands hardcode protoc-25.2-linux-x86_64.zip as the archive
    name. The CI sed step patches the url and sha256 for aarch64, but the build-commands unzip lines
    also reference that filename. This works because flatpak-builder downloads the file and renames
    it, but it's fragile and confusing — worth adding a comment or using a wildcard (protoc-*.zip).
  2. timeout-minutes: 20 may be tight for cold cache — The aarch64 build took ~14 minutes (with
    cache). A cold build (new Cargo.lock, no cache) could exceed 20 minutes. Consider bumping to
    30-40 minutes for safety, especially since Rust compile times are unpredictable.
  3. contents: write on push branches — The job has contents: write always, but it only needs write
    for the release event (to attach assets). On push/dispatch triggers this is an unnecessary
    permission escalation. Consider:
    permissions:
    contents: ${{ github.event_name == 'release' && 'write' || 'read' }}

Low / Nits

  1. Metainfo block is not part of the AppStream spec (metainfo.xml:19-23) —
    in AppStream metainfo.xml is valid, but BlockchainTools is not a standard
    freedesktop category. This won't break anything but could generate warnings from appstreamcli
    validate. Standard alternatives: Finance, Network, Utility.
  2. Hardcoded placeholder release in metainfo (metainfo.xml:48) — version="1.0.0-dev"
    date="2026-02-17" is a snapshot. When actual releases happen, this will need to be updated (or
    generated). Consider adding a comment noting this needs to be kept in sync, or automating it in
    CI.
  3. --locked in build-commands (org.dash.DashEvoTool.yml:70) — cargo build --release --locked
    requires Cargo.lock to be committed and up-to-date. This is good practice for reproducible
    builds, but make sure Cargo.lock is always committed (it is, based on .gitignore not excluding
    it).
  4. Snap deletion removes dash-qt bundling — The old Snap bundled dash-qt (Dash Core wallet
    binary). The new Flatpak does not. This is intentional (SPV mode), but worth confirming that no
    users relied on the snap-bundled dash-qt.
  5. No retention-days on artifact upload — The PR description mentions 14-day retention, but the
    workflow doesn't set retention-days on upload-artifact. It will use the org/repo default (which
    may differ). Consider being explicit:
    retention-days: 14

Correctness

  • The with_app_id("org.dash.DashEvoTool") in main.rs matches the Flatpak app-id, desktop
    StartupWMClass, and metainfo — all consistent.
  • Icon path assets/DET_LOGO.png exists and is referenced correctly.
  • The sed for bind-mount injection in CI looks correct but is somewhat brittle. If the YAML
    structure of build-args changes, it could silently fail.

Verdict

This is a well-executed packaging change. The CI workflow is solid and follows security best
practices. The main concern is the permission scope on non-release triggers (#3) and the tight
timeout (#2). The rest are minor polish items. Looks good to merge after considering the medium
items.

Comment thread flatpak/org.dash.DashEvoTool.metainfo.xml Outdated
Comment thread flatpak/org.dash.DashEvoTool.metainfo.xml Outdated
Comment thread flatpak/org.dash.DashEvoTool.metainfo.xml Outdated
@lklimek lklimek merged commit 1a249ba into v1.0-dev Feb 18, 2026
6 checks passed
@lklimek lklimek deleted the feat/flatpak branch February 18, 2026 17:17
lklimek pushed a commit that referenced this pull request Feb 18, 2026
Preserve the new documentation structure (split README, CONTRIBUTING.md,
docs/ directory) while incorporating new Flatpak install instructions
from upstream PR #589. Upstream build/prerequisites content is already
covered by CONTRIBUTING.md so it was dropped in favor of our structure.

https://claude.ai/code/session_01AH9RuArd3yn2NEGwUcZqCC
lklimek added a commit that referenced this pull request Feb 18, 2026
* build: remove snap version

* build: add Flatpak packaging and CI workflow

Add Flatpak build manifest, desktop entry, AppStream metadata, and
GitHub Actions workflow for building and distributing Flatpak bundles.
Uses freedesktop 25.08 runtime with rust-stable and llvm21 extensions.
No application source code changes required - works in SPV mode by default.

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

* build: address review findings for Flatpak packaging

- Pin GitHub Actions to commit SHAs for supply chain security
- Upgrade softprops/action-gh-release from v1 to v2.2.2
- Remove redundant --socket=x11 (fallback-x11 suffices)
- Remove duplicate tag trigger preventing double builds on release
- Remove duplicate env vars inherited from top-level build-options
- Add Flatpak build artifacts to .gitignore
- Add bugtracker URL to AppStream metainfo
- Remove deprecated <categories> from metainfo (use .desktop instead)
- Add Terminal=false and Keywords to desktop entry
- Add disk space check after SDK install in CI
- Rename artifact to include architecture suffix

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

* build: simplify CI workflows for Linux-only releases

- Remove "Free disk space" step from flatpak and release workflows
- Remove Windows and macOS builds from release workflow
- Use "ubuntu" runner image instead of pinned versions
- Clean up unused matrix.ext references

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

* build: attach to existing releases instead of creating new ones

- Replace release-creating job with attach-to-release (only on release event)
- Add 14-day retention for build artifacts
- On tag push or workflow_dispatch, only upload artifacts (no release)

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

* revert: restore release.yml to original v1.0-dev version

The release workflow changes were out of scope for the Flatpak PR.

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

* fix: address CodeRabbit review comments

- Fix CRLF line endings in Flatpak manifest (convert to LF)
- Set app_id on ViewportBuilder to match desktop StartupWMClass
- Use --locked flag for reproducible cargo builds in Flatpak
- Rename --repo=repo to --repo=flatpak-repo to match .gitignore
- Add architecture note for protoc x86_64 binary

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

* docs: add Flatpak install instructions to README

Add a dedicated section for installing via Flatpak on Linux,
clarify that prerequisites are only needed for building from source,
and rename "Installation" to "Build from Source" for clarity.

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

* fix: match StartupWMClass to Flatpak app_id

Use reverse-DNS format org.dash.DashEvoTool to match the
Wayland app_id set via ViewportBuilder::with_app_id().

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

* fix: use ** glob for branch trigger to match feat/flatpak

Single * doesn't match path separators in GitHub Actions branch filters.

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

* feat: add aarch64 Flatpak build and caching to CI

- Add matrix strategy for parallel x86_64 and aarch64 builds
- Patch protoc URL/sha256 per architecture at build time
- Cache .flatpak-builder directory keyed on arch + manifest + lockfile
- Pin actions/cache to SHA

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

* fix: convert desktop and metainfo files to LF line endings

Flatpak builder validates desktop files and rejects CRLF line endings.

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

* build: cancel in-progress Flatpak builds on new push

Add concurrency group keyed on git ref so a new push cancels
any running build for the same branch or release.

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

* fix: address PR review findings for Flatpak packaging

- Remove unnecessary --filesystem=xdg-config/dash-evo-tool:create
  (Flatpak already redirects XDG_CONFIG_HOME to sandbox)
- Add categories and keywords to AppStream metainfo for discoverability
- Update README with both x86_64/aarch64 install commands, uninstall
  instructions, and Flatpak data path note
- Clarify aarch64 comment in manifest to reference CI sed patching

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

* chore: workflow timeout and perms

* fix: move permissions to job level in Flatpak workflow

Step-level permissions are not valid in GitHub Actions. Move
contents: write to the job level where it is needed for release
attachment.

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

* build: cache Cargo registry and target in Flatpak CI

Bind-mount host-side cargo-cache and cargo-target directories into the
Flatpak build sandbox so CARGO_HOME and target/ persist across builds.
Uses split restore/save with cleanup of incremental and registry/src
(similar to Swatinem/rust-cache).

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

* fix: scope cargo cache bind-mount sed to build-args only

The previous sed matched --share=network in both finish-args and
build-args, corrupting finish-args. Use a sed range to only target
the build-args section.

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

* Apply suggestions from code review

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
lklimek added a commit that referenced this pull request Feb 23, 2026
…601)

* docs: add unified message display design documents

Add UX specification, technical architecture, and HTML mockup for the
MessageBanner component that will replace the ~50 ad-hoc error/message
display implementations across screens with a single reusable component.

Key design decisions:
- Per-screen MessageBanner with show()/set_message() API
- All colors via DashColors (zero hardcoded Color32 values)
- 4 severity levels: Error, Warning, Success, Info
- Auto-dismiss for Success/Info (5s), persistent for Error/Warning
- Follows Component Design Pattern conventions (private fields, builder, show)
- No changes to BackendTask/TaskResult/AppState architecture

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

* feat: add MessageType::Warning

* chore: initial implementation

* chore: docs

* chore: self review

* refactor(context): replace RwLock<Sdk> with ArcSwap<Sdk> (#600)

* refactor(context): replace RwLock<Sdk> with ArcSwap<Sdk>

Sdk is internally thread-safe (Arc, ArcSwapOption, atomics) and all
methods take &self. The RwLock was adding unnecessary contention across
backend tasks.

Using ArcSwap instead of plain Sdk because reinit_core_client_and_sdk()
needs to atomically swap the entire Sdk instance when config changes.
ArcSwap provides lock-free reads with atomic swap for the rare write.

Suggested-by: lklimek

* fix: address CodeRabbit review findings for ArcSwap migration

- Fix import ordering: move arc_swap::ArcSwap before crossbeam_channel
- Remove redundant SDK loads in load_identity_from_wallet, register_dpns_name,
  and load_identity — use the sdk parameter already passed to these functions
- Fix stale TODO referencing removed sdk.read().unwrap() API
- Rename sdk_guard → sdk in transfer, withdraw_from_identity, and
  refresh_loaded_identities_dpns_names (no longer lock guards)
- Pass &sdk to run_platform_info_task from dispatch site instead of
  reloading internally
- Fix leftover sdk.write() call in context_provider.rs (RwLock remnant)
- Add missing Color32 import in wallets dialogs

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

* refactor: address remaining CodeRabbit review feedback on ArcSwap migration

- Move SDK load outside for loop in refresh_loaded_identities_dpns_names.rs
  so it's loaded once for the batch instead of on each iteration
- Update stale TODO comment in default_platform_version() to reflect that
  this is a free function with no sdk access

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

* refactor: consolidate double read-lock on spv_context_provider

Clone the SPV provider in a single lock acquisition, then bind app
context on the clone instead of locking twice.

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

---------

Co-authored-by: PastaClaw <thepastaclaw@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: remove unused Insight API and show_in_ui config fields (#597)

* refactor: remove unused Insight API references

The `insight_api_url` field in `NetworkConfig` and its associated
`insight_api_uri()` method were never used in production code (both
marked `#[allow(dead_code)]`). Remove the field, method, config
entries, env example lines, and related tests.

https://claude.ai/code/session_01HWPmCJHT8KTZGP9bFiksjn

* refactor: remove unused `show_in_ui` field from NetworkConfig

The `show_in_ui` field was defined on `NetworkConfig` and serialized in
`save()`, but never read by any production code to control network
visibility. Remove the field, its serialization, env example lines,
and test references.

https://claude.ai/code/session_01HWPmCJHT8KTZGP9bFiksjn

* fix: add missing `Color32` import in wallet dialogs

https://claude.ai/code/session_01HWPmCJHT8KTZGP9bFiksjn

---------

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

* build: add Flatpak packaging and CI workflow (#589)

* build: remove snap version

* build: add Flatpak packaging and CI workflow

Add Flatpak build manifest, desktop entry, AppStream metadata, and
GitHub Actions workflow for building and distributing Flatpak bundles.
Uses freedesktop 25.08 runtime with rust-stable and llvm21 extensions.
No application source code changes required - works in SPV mode by default.

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

* build: address review findings for Flatpak packaging

- Pin GitHub Actions to commit SHAs for supply chain security
- Upgrade softprops/action-gh-release from v1 to v2.2.2
- Remove redundant --socket=x11 (fallback-x11 suffices)
- Remove duplicate tag trigger preventing double builds on release
- Remove duplicate env vars inherited from top-level build-options
- Add Flatpak build artifacts to .gitignore
- Add bugtracker URL to AppStream metainfo
- Remove deprecated <categories> from metainfo (use .desktop instead)
- Add Terminal=false and Keywords to desktop entry
- Add disk space check after SDK install in CI
- Rename artifact to include architecture suffix

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

* build: simplify CI workflows for Linux-only releases

- Remove "Free disk space" step from flatpak and release workflows
- Remove Windows and macOS builds from release workflow
- Use "ubuntu" runner image instead of pinned versions
- Clean up unused matrix.ext references

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

* build: attach to existing releases instead of creating new ones

- Replace release-creating job with attach-to-release (only on release event)
- Add 14-day retention for build artifacts
- On tag push or workflow_dispatch, only upload artifacts (no release)

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

* revert: restore release.yml to original v1.0-dev version

The release workflow changes were out of scope for the Flatpak PR.

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

* fix: address CodeRabbit review comments

- Fix CRLF line endings in Flatpak manifest (convert to LF)
- Set app_id on ViewportBuilder to match desktop StartupWMClass
- Use --locked flag for reproducible cargo builds in Flatpak
- Rename --repo=repo to --repo=flatpak-repo to match .gitignore
- Add architecture note for protoc x86_64 binary

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

* docs: add Flatpak install instructions to README

Add a dedicated section for installing via Flatpak on Linux,
clarify that prerequisites are only needed for building from source,
and rename "Installation" to "Build from Source" for clarity.

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

* fix: match StartupWMClass to Flatpak app_id

Use reverse-DNS format org.dash.DashEvoTool to match the
Wayland app_id set via ViewportBuilder::with_app_id().

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

* fix: use ** glob for branch trigger to match feat/flatpak

Single * doesn't match path separators in GitHub Actions branch filters.

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

* feat: add aarch64 Flatpak build and caching to CI

- Add matrix strategy for parallel x86_64 and aarch64 builds
- Patch protoc URL/sha256 per architecture at build time
- Cache .flatpak-builder directory keyed on arch + manifest + lockfile
- Pin actions/cache to SHA

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

* fix: convert desktop and metainfo files to LF line endings

Flatpak builder validates desktop files and rejects CRLF line endings.

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

* build: cancel in-progress Flatpak builds on new push

Add concurrency group keyed on git ref so a new push cancels
any running build for the same branch or release.

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

* fix: address PR review findings for Flatpak packaging

- Remove unnecessary --filesystem=xdg-config/dash-evo-tool:create
  (Flatpak already redirects XDG_CONFIG_HOME to sandbox)
- Add categories and keywords to AppStream metainfo for discoverability
- Update README with both x86_64/aarch64 install commands, uninstall
  instructions, and Flatpak data path note
- Clarify aarch64 comment in manifest to reference CI sed patching

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

* chore: workflow timeout and perms

* fix: move permissions to job level in Flatpak workflow

Step-level permissions are not valid in GitHub Actions. Move
contents: write to the job level where it is needed for release
attachment.

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

* build: cache Cargo registry and target in Flatpak CI

Bind-mount host-side cargo-cache and cargo-target directories into the
Flatpak build sandbox so CARGO_HOME and target/ persist across builds.
Uses split restore/save with cleanup of incremental and registry/src
(similar to Swatinem/rust-cache).

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

* fix: scope cargo cache bind-mount sed to build-args only

The previous sed matched --share=network in both finish-args and
build-args, corrupting finish-args. Use a sed range to only target
the build-args section.

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

* Apply suggestions from code review

---------

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

* chore: fix build

* Update src/app.rs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* chore: peer review

* feat: add collapsible details and suggestion support to MessageBanner

Enhance BannerState with optional details (collapsible technical info)
and suggestion (inline recovery hint) fields. BannerHandle gains
with_details() and with_suggestion() builder methods and is now Clone.
The details section renders collapsed by default with a "Show details"
toggle and scrollable monospace content area.

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

* fix: correct MessageBanner dedup, replace_global reset, and doc drift

- set_global now updates message_type on deduplicated banners
- replace_global resets details/suggestion/details_expanded on replacement
- Fix doc comment referencing set_text instead of set_message
- Update architecture.md BannerState struct with details/suggestion fields
- Update architecture.md render_banner signature and BannerHandle method table

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

* feat: log everything that is displayed by the MessageBanner

* fix(ui): address PR #601 review items 12-16 for MessageBanner

Add BannerState::new() and reset_to() helpers to eliminate duplicated
field initialization across set_message, set_global, and replace_global.
Fix dedup paths that forgot to update created_at/auto_dismiss_after/
show_elapsed (items 13-14). Fix concurrent refresh showing premature
success by tracking pending_refresh_count (item 15). Remove unreachable
match arm and tighten wildcard to guard on identity refresh tasks only
(item 16). Update architecture docs to reflect current code (item 12).

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

* fix(ui): clarify display_message contract for Success/Info in MintTokensScreen

Add explicit comment documenting that Success/Info types require no
local state change since the global banner handles display. Addresses
PR #601 review comment about asymmetric handling.

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

* fix(ui): use distinct icon for Error vs Warning banners

Error banners now show ❌ (cross mark) instead of ⚠ (warning sign),
so colorblind users can distinguish severity by shape, not just color.

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

* fix(test): update banner test to expect new Error icon

The Error icon changed from ⚠ to ❌ in the previous commit.
Update test_banner_renders_all_types to match.

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

* Update docs/ai-design/2026-02-17-unified-messages/architecture.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Pasta Lil Claw <pasta+claw@dashboost.org>
Co-authored-by: PastaClaw <thepastaclaw@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
lklimek added a commit that referenced this pull request Mar 3, 2026
* docs: add unified message display design documents

Add UX specification, technical architecture, and HTML mockup for the
MessageBanner component that will replace the ~50 ad-hoc error/message
display implementations across screens with a single reusable component.

Key design decisions:
- Per-screen MessageBanner with show()/set_message() API
- All colors via DashColors (zero hardcoded Color32 values)
- 4 severity levels: Error, Warning, Success, Info
- Auto-dismiss for Success/Info (5s), persistent for Error/Warning
- Follows Component Design Pattern conventions (private fields, builder, show)
- No changes to BackendTask/TaskResult/AppState architecture

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

* feat: add MessageType::Warning

* chore: initial implementation

* chore: docs

* chore: self review

* refactor(context): replace RwLock<Sdk> with ArcSwap<Sdk> (#600)

* refactor(context): replace RwLock<Sdk> with ArcSwap<Sdk>

Sdk is internally thread-safe (Arc, ArcSwapOption, atomics) and all
methods take &self. The RwLock was adding unnecessary contention across
backend tasks.

Using ArcSwap instead of plain Sdk because reinit_core_client_and_sdk()
needs to atomically swap the entire Sdk instance when config changes.
ArcSwap provides lock-free reads with atomic swap for the rare write.

Suggested-by: lklimek

* fix: address CodeRabbit review findings for ArcSwap migration

- Fix import ordering: move arc_swap::ArcSwap before crossbeam_channel
- Remove redundant SDK loads in load_identity_from_wallet, register_dpns_name,
  and load_identity — use the sdk parameter already passed to these functions
- Fix stale TODO referencing removed sdk.read().unwrap() API
- Rename sdk_guard → sdk in transfer, withdraw_from_identity, and
  refresh_loaded_identities_dpns_names (no longer lock guards)
- Pass &sdk to run_platform_info_task from dispatch site instead of
  reloading internally
- Fix leftover sdk.write() call in context_provider.rs (RwLock remnant)
- Add missing Color32 import in wallets dialogs

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

* refactor: address remaining CodeRabbit review feedback on ArcSwap migration

- Move SDK load outside for loop in refresh_loaded_identities_dpns_names.rs
  so it's loaded once for the batch instead of on each iteration
- Update stale TODO comment in default_platform_version() to reflect that
  this is a free function with no sdk access

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

* refactor: consolidate double read-lock on spv_context_provider

Clone the SPV provider in a single lock acquisition, then bind app
context on the clone instead of locking twice.

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

---------

Co-authored-by: PastaClaw <thepastaclaw@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: remove unused Insight API and show_in_ui config fields (#597)

* refactor: remove unused Insight API references

The `insight_api_url` field in `NetworkConfig` and its associated
`insight_api_uri()` method were never used in production code (both
marked `#[allow(dead_code)]`). Remove the field, method, config
entries, env example lines, and related tests.

https://claude.ai/code/session_01HWPmCJHT8KTZGP9bFiksjn

* refactor: remove unused `show_in_ui` field from NetworkConfig

The `show_in_ui` field was defined on `NetworkConfig` and serialized in
`save()`, but never read by any production code to control network
visibility. Remove the field, its serialization, env example lines,
and test references.

https://claude.ai/code/session_01HWPmCJHT8KTZGP9bFiksjn

* fix: add missing `Color32` import in wallet dialogs

https://claude.ai/code/session_01HWPmCJHT8KTZGP9bFiksjn

---------

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

* build: add Flatpak packaging and CI workflow (#589)

* build: remove snap version

* build: add Flatpak packaging and CI workflow

Add Flatpak build manifest, desktop entry, AppStream metadata, and
GitHub Actions workflow for building and distributing Flatpak bundles.
Uses freedesktop 25.08 runtime with rust-stable and llvm21 extensions.
No application source code changes required - works in SPV mode by default.

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

* build: address review findings for Flatpak packaging

- Pin GitHub Actions to commit SHAs for supply chain security
- Upgrade softprops/action-gh-release from v1 to v2.2.2
- Remove redundant --socket=x11 (fallback-x11 suffices)
- Remove duplicate tag trigger preventing double builds on release
- Remove duplicate env vars inherited from top-level build-options
- Add Flatpak build artifacts to .gitignore
- Add bugtracker URL to AppStream metainfo
- Remove deprecated <categories> from metainfo (use .desktop instead)
- Add Terminal=false and Keywords to desktop entry
- Add disk space check after SDK install in CI
- Rename artifact to include architecture suffix

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

* build: simplify CI workflows for Linux-only releases

- Remove "Free disk space" step from flatpak and release workflows
- Remove Windows and macOS builds from release workflow
- Use "ubuntu" runner image instead of pinned versions
- Clean up unused matrix.ext references

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

* build: attach to existing releases instead of creating new ones

- Replace release-creating job with attach-to-release (only on release event)
- Add 14-day retention for build artifacts
- On tag push or workflow_dispatch, only upload artifacts (no release)

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

* revert: restore release.yml to original v1.0-dev version

The release workflow changes were out of scope for the Flatpak PR.

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

* fix: address CodeRabbit review comments

- Fix CRLF line endings in Flatpak manifest (convert to LF)
- Set app_id on ViewportBuilder to match desktop StartupWMClass
- Use --locked flag for reproducible cargo builds in Flatpak
- Rename --repo=repo to --repo=flatpak-repo to match .gitignore
- Add architecture note for protoc x86_64 binary

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

* docs: add Flatpak install instructions to README

Add a dedicated section for installing via Flatpak on Linux,
clarify that prerequisites are only needed for building from source,
and rename "Installation" to "Build from Source" for clarity.

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

* fix: match StartupWMClass to Flatpak app_id

Use reverse-DNS format org.dash.DashEvoTool to match the
Wayland app_id set via ViewportBuilder::with_app_id().

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

* fix: use ** glob for branch trigger to match feat/flatpak

Single * doesn't match path separators in GitHub Actions branch filters.

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

* feat: add aarch64 Flatpak build and caching to CI

- Add matrix strategy for parallel x86_64 and aarch64 builds
- Patch protoc URL/sha256 per architecture at build time
- Cache .flatpak-builder directory keyed on arch + manifest + lockfile
- Pin actions/cache to SHA

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

* fix: convert desktop and metainfo files to LF line endings

Flatpak builder validates desktop files and rejects CRLF line endings.

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

* build: cancel in-progress Flatpak builds on new push

Add concurrency group keyed on git ref so a new push cancels
any running build for the same branch or release.

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

* fix: address PR review findings for Flatpak packaging

- Remove unnecessary --filesystem=xdg-config/dash-evo-tool:create
  (Flatpak already redirects XDG_CONFIG_HOME to sandbox)
- Add categories and keywords to AppStream metainfo for discoverability
- Update README with both x86_64/aarch64 install commands, uninstall
  instructions, and Flatpak data path note
- Clarify aarch64 comment in manifest to reference CI sed patching

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

* chore: workflow timeout and perms

* fix: move permissions to job level in Flatpak workflow

Step-level permissions are not valid in GitHub Actions. Move
contents: write to the job level where it is needed for release
attachment.

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

* build: cache Cargo registry and target in Flatpak CI

Bind-mount host-side cargo-cache and cargo-target directories into the
Flatpak build sandbox so CARGO_HOME and target/ persist across builds.
Uses split restore/save with cleanup of incremental and registry/src
(similar to Swatinem/rust-cache).

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

* fix: scope cargo cache bind-mount sed to build-args only

The previous sed matched --share=network in both finish-args and
build-args, corrupting finish-args. Use a sed range to only target
the build-args section.

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

* Apply suggestions from code review

---------

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

* chore: fix build

* chore: use new error handling everywhere - not self reviewed

* chore: use message banner to show progress

* fix: start elapsed counter at 1s instead of 0s

Aligns elapsed display with the countdown timer which already adds 1
to avoid showing "0s" immediately.

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

* chore: rabbit review

* Update src/app.rs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* chore: peer review

* chore: fix build errors

* fix(ui): make MessageBanner::set_global truly idempotent

set_global() no longer resets timestamps, auto-dismiss timer, or
logged flag when a banner with identical text already exists. This
makes it safe to call every frame without log spam or timer restarts.

Cherry-picked from origin/fix/spv-peer-timeout (08e3b3b).

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

* fix(ui): address code review findings R01, R03, R06, R09, R10

- R01: Replace expect("No key selected") with graceful match + error
  banner in 11 token screens to prevent panics on missing signing key
- R03: Remove dead backend_message field from AddExistingIdentityScreen
- R06: Replace is_some() + unwrap() with idiomatic if-let-Some pattern
  in 10 token screens; use is_some_and() in structs.rs
- R09: Add use imports for MessageBanner in 5 dashpay screens, replacing
  22 fully-qualified crate::ui::components::MessageBanner:: calls
- R10: Replace custom_dash_qt_error_message inline rendering with
  MessageBanner::set_global in network_chooser_screen

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

* fix(ui): address review findings SEC-08, SEC-09, RUST-015, SEC-05, SEC-07

- SEC-08: Restore safe if-let-Some pattern in WithdrawalScreen::refresh()
  to prevent double unwrap() panic on DB error or deleted identity
- SEC-09: Restore original DB lookup in SendPaymentScreen::load_contact_info()
  replacing hardcoded "alice.dash" mock data
- RUST-015: Revert unimplemented!() back to ui.label() in
  update_token_config MarketplaceTradeMode arm
- SEC-05: Add success banners for contact request accept/reject in
  ContactRequests::display_task_result
- SEC-07: Add MessageBanner::clear_all_global() and call it from
  AppState::change_network() to prevent stale banners leaking across
  network switches

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

* docs: update coding conventions and message display guidance

Add fallible constructor rule (Result<Self, ...> when they can fail),
rename section to "General rules", and document MessageBanner
idempotency (no guard needed for set_global).

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

* fix(ui): replace expect/panic with graceful error handling (SEC-10)

Replace all expect() calls in token screen constructors and confirmation
handlers with MessageBanner error display. Constructors handle errors
internally and return Self with degraded state, keeping create_screen()
clean. refresh() methods now show errors via MessageBanner instead of
tracing-only logging.

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

* refactor(ui): accept impl Display/Debug in MessageBanner API

Change MessageBanner public methods to accept `impl Display` for
message text and `impl Debug` for details, instead of `&str`. Remove
needless `&format!(...)` borrows across 27 call sites.

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

* fix(ui): remove error Failed to get best chain lock for mainnet, testnet, devnet, and local

Fixes #633

* feat(ui): add automatic connection status banners

Display persistent MessageBanner notifications based on network
connection state transitions. Mode-aware messages guide users toward
the right recovery action (RPC vs SPV).

- Disconnected (RPC): "Disconnected — check that Dash Core is running"
- Disconnected (SPV): "Disconnected — check your internet connection"
- Syncing (RPC): "Syncing with Dash Core…"
- Syncing (SPV): "SPV sync in progress…"
- Synced: banner cleared

Uses Option<OverallConnectionState> for change detection, with None
as initial/post-network-switch sentinel to force first evaluation.

Closes #667 (partial — action links deferred to follow-up)

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

* fix(ui): pass TaskError directly to with_details() to avoid double-formatting

The previous code used `format!("{err:?}")` which produced a String, then
`with_details()` applied `{:#?}` again — wrapping the output in quotes and
escaping inner characters. Passing `&err` directly lets Debug format once.

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

* fix(ui): correct copy-paste error messages in token screens

Replace "Burning" error messages that were copy-pasted from burn screen
into freeze, destroy, and resume token screens with contextually correct
messages.

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

* fix(ui): restore lost success/error messages in 5 screens

Replace display_message() calls with MessageBanner::set_global() in
screens where display_message() is now a side-effect-only handler and
no longer displays messages directly.

Affected screens: create_asset_lock_screen, wallets_screen (MineBlocks),
address_table (export error), profile_screen (validation), contact_details.

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

* fix(ui): replace unwrap/expect with graceful error handling

Replace double unwrap in transfer_screen refresh() with unwrap_or_else
+ MessageBanner error display, matching the pattern from withdraw_screen.

SEC-002 tokens_screen skipped: the .expect() calls are only on
compile-time embedded image data (include_bytes!) which is safe.

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

* refactor(ui): migrate masternode_list_diff_screen to global MessageBanner

Replace ~15 local ui_state.message assignments and custom
render_message_banner() with MessageBanner::set_global() via the
display_message() trait method. Remove the message field from UiState
and the unused Color32 import.

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

* fix(ui): improve banner eviction logging and atomics

- Upgrade BANNER_KEY_COUNTER from Relaxed to SeqCst ordering for
  future-proofing against multi-threaded usage
- Log evicted banners at warn level in set_global() and replace_global()
- Add comment explaining why show_global() always writes back

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

* chore: remove resolved TODO.md

All items tracked in the unified message display TODO have been
addressed or moved to the review findings.

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

* docs(ui): add INTENTIONAL markers and API documentation

- Document why with_details() accepts Debug (not Display): structured
  error context is more useful in diagnostic details panes
- Document replace_global() fallback-to-add behavior as intentional
- Add INTENTIONAL(SEC-003) marker for developer mode error details
- Add INTENTIONAL(SEC-004) marker for BannerHandle Send+Sync safety

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

* refactor(ui): extract shared token validation and fix ordering

- Add validate_signing_key() helper in tokens/mod.rs to eliminate
  duplicated signing key validation across 12 token screens
- Move signing key validation BEFORE WaitingForResult state transition
  so users see immediate errors instead of loading spinner then error
- Replace is_err()/unwrap() anti-pattern with idiomatic let-else blocks
  in freeze, mint, transfer, destroy_frozen_funds, unfreeze screens

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

* refactor(ui): return Result from get_selected_wallet

Replace &mut Option<String> error out-parameter with idiomatic
Result<Option<Arc<RwLock<Wallet>>>, String>. Update 26+ callsites
across identity, token, DashPay, and contract screens.

Callsite patterns: unwrap_or_else with MessageBanner for user-visible
errors, unwrap_or(None) where errors were previously silently ignored.

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

* fix(ui): resolve duplicate imports and clippy warnings

Remove duplicate MessageBanner imports in create_asset_lock_screen and
wallets_screen/mod. Fix needless_borrows_for_generic_args clippy lints
in profile_screen, transfer_screen, and wallets_screen.

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

* style: reorder imports in masternode_list_diff_screen

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

* fix(ui): address review findings from MessageBanner migration audit

Apply 13 triaged review fixes plus 1 bug fix across 22 files:

- Remove dead error state fields (backend_message, error_message, Error variant)
- Replace .expect() panics with graceful fallback + MessageBanner in token screens
- Fix missing MessageBanner::show_global() on contracts documents screen
- Migrate DocumentActionScreen inline errors to MessageBanner
- Replace unwrap_or(None) with error-reporting fallback in DashPay screens
- Fix replace_global idempotency and use relaxed atomic ordering in banner
- Extract shared set_error_banner helper for 8 token screens
- Restore correct Some(0) wallet index semantics
- Document BannerHandle lifecycle in CLAUDE.md

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

* fix: validate token description length before sending to Platform (#530)

* fix: validate token description length before sending to Platform

Descriptions must be either empty or 3-100 characters long.

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

* fix(ui): validate token description by char count, not byte length

String::len() counts UTF-8 bytes, causing multi-byte characters
(CJK, emoji) to be miscounted against the 3–100 limit. Switch to
chars().count() and update all UI labels to surface the minimum.

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Lukasz Klimek <842586+lklimek@users.noreply.github.com>

* refactor(ui): consolidate banner extension traits into message_banner

Move BannerHandleExt and ResultBannerExt from banner_ext.rs into
message_banner.rs where they belong. Merge take_and_clear() into
OptionBannerExt trait (impl for Option<BannerHandle>) alongside
or_show_error() for Option. Remove the separate banner_ext module
and Clearable helper trait for simplicity.

Apply review findings: DRY patterns (take_and_clear, or_show_error,
load_identities_with_banner), fix .expect() panics in constructors,
restore known_identities in refresh(), narrow pub field visibility,
add ScreenLike doc comments, and update CLAUDE.md conventions.

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

* doc(tmp): review guide for pr 604

* fix(ui): address review findings from grumpy-review iteration 1

- Replace .expect() panics in TransferTokensScreen and ClaimTokensScreen
  constructors with graceful degradation via Option<QualifiedIdentity>
  and MessageBanner error display (PROJ-001 HIGH)
- Fix CLAUDE.md referencing non-existent BannerHandleExt trait name,
  corrected to OptionBannerExt (PROJ-002 MEDIUM)
- Update set_global to preserve message_type when same text appears
  with different severity (RUST-001 MEDIUM)
- Standardize display_message to handle both Error and Warning across
  all 11 token screens (RUST-002 MEDIUM)
- Replace 21 manual take().clear() patterns with take_and_clear()
  across 6 files (RUST-003 MEDIUM)
- Remove unused OptionBannerExt::or_show_error method (RUST-004 MEDIUM)
- Migrate update_token_config from old error_message pattern to
  set_error_banner closure (RUST-005 MEDIUM)

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

* perf(ui): replace per-frame QualifiedIdentity clone with borrow

Use .as_ref() instead of .clone() in the ui() identity guard of
TransferTokensScreen and ClaimTokensScreen. QualifiedIdentity
(Identity + KeyStorage + BTreeMap + Vec) was being cloned 60x/sec;
now only borrowed for display, with clones deferred to button-click
paths that actually need ownership.

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

* feat(ui): add OptionResultExt::or_show_error for Option<T>

Mirrors ResultBannerExt::or_show_error but for Option<T>: if None,
displays a global error banner with the given message. Enables
concise patterns like:
  identities.first().cloned().or_show_error(ctx, "No identities loaded")

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

* fix(ui): address review findings from grumpy-review iteration 2

- Standardize display_message to handle both Error and Warning across
  13 non-token screens that were missed in iteration 1 (PROJ-001 MEDIUM)
- Replace .expect() panic in AddKeyScreen::refresh() with graceful
  or_show_error() + unwrap_or_default() (PROJ-002 MEDIUM)
- Rename OptionResultExt to OptionBannerShowExt to avoid confusion
  with ResultBannerExt (RUST-001 MEDIUM)

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

* fix(ui): handle Warning in add_new_identity_screen display_message

Missed in the previous sweep — standardize display_message to handle
both Error and Warning, matching all other screens.

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

* fix(ui): standardize display_message side-effect patterns across screens

Guard side effects with Error|Warning match, use take_and_clear(),
and remove redundant MessageBanner::set_global() call in 4 screens.

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

* chore(deps): update dashpay/platform to rev 570e3af0

Adapt to breaking changes in rust-dashcore (a05d256f → 2824e52a):

- Replace removed FeeLevel enum with FeeRate::normal() direct calls
- Replace removed WalletManager::create_unsigned_payment_transaction()
  with TransactionBuilder + WalletManager::get_change_address()
- Replace removed DashSpvClientInterface/DashSpvClientCommand with
  direct Arc<SpvClient> for quorum lookups via get_quorum_at_height()
- Replace removed start()+monitor_network() with client.run(token)
- Add .await to now-async subscribe_sync/network/progress methods
- Replace removed SyncState::Initializing with SyncState::WaitForEvents

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

* chore: log backtrace on panic

* fix: panic in asset locks

* fix(ui): address PR #604 review comments (CMT-001, CMT-002, CMT-003)

- Fix QR scanner form reset matching wrong result type (CMT-001)
- Remove dangerous identity fallback in token transfer screen (CMT-002)
- Add fee-aware validation before credit transfers (CMT-003)

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

* refactor(spv): replace AsyncRwLock with ArcSwapOption for SPV client reference

The SPV client reference only needs atomic set/clear (on start/stop) and
wait-free reads (quorum lookups). ArcSwapOption is a better fit than
AsyncRwLock<Option<Arc<...>>> — no lock contention, no async in blocking
context, and consistent with how AppContext already uses ArcSwap for the SDK.

Also fixes stale doc comment referencing removed DashSpvClientInterface.

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

* fix(ui): address PR #604 review comments iteration 2 (transfer_tokens_screen)

- Remove duplicate conflicting banner on missing identity (CMT-003)
- Use generic banner messages with with_details() for errors (CMT-002)
- Fix refresh to match specific token by contract+position (CMT-001)
- Document error banner pattern in CLAUDE.md

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

* doc: review guide updated

* fix(ui): apply grumpy-review triage fixes — core infrastructure

Address findings from code review triage (SEC-002, RUST-003, RUST-012,
PROJ-005): preserve error context in payment tx builder, add INTENTIONAL
comment for Debug-formatting accepted risk, remove dead import in SPV
manager, fix CLAUDE.md error banner documentation, and add OptionBannerExt
replace/replace_with_elapsed convenience methods.

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

* fix(ui): apply grumpy-review triage fixes — screen-level changes

RUST-010: Add wallet_open_attempted guard to ~30 screens preventing
per-frame try_open_wallet_no_password calls. Guard resets on wallet
or identity selection change.

PROJ-001: Migrate register/update contract and document action screens
from BroadcastStatus::Broadcasting(u64) to refresh_banner with elapsed
time display.

RUST-001: Surface database errors via .or_show_error() in contacts_list
instead of silently swallowing with let _ =.

RUST-005: tokens_screen display_message only clears operation_banner on
Error/Warning, preserving it on Success/Info.

RUST-007: Remove dead Error(String, Instant) variant from
TransitionBroadcastStatus in transition_visualizer_screen.

RUST-009: Make identity Optional in claim_tokens_screen with degraded
state handling when identity not found locally.

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

* fix(ui): handle OverallConnectionState::Error in connection banner

Cover the new Error variant added by #650 (SPV sync error state) in the
connection banner match arm. Shows an error banner directing users to the
connection status tooltip for details.

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

* build: update Cargo.lock

* build: reduce dependency debuginfo to fix <unknown> panic backtraces

std's backtrace symbolizer (gimli) silently fails to resolve symbols
when parsing ~790MB of DWARF debug sections in our 1.1GB debug binary,
producing backtraces full of <unknown> frames.

Set `debug = "line-tables-only"` for dependency crates while keeping
full debuginfo for our own code. This cuts DWARF from 790MB to ~354MB
and binary size from 1.1GB to 600MB, letting gimli actually resolve
panic backtraces.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Pasta Lil Claw <pasta+claw@dashboost.org>
Co-authored-by: PastaClaw <thepastaclaw@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Paul DeLucia <69597248+pauldelucia@users.noreply.github.com>
lklimek added a commit that referenced this pull request Mar 18, 2026
* docs: add unified message display design documents

Add UX specification, technical architecture, and HTML mockup for the
MessageBanner component that will replace the ~50 ad-hoc error/message
display implementations across screens with a single reusable component.

Key design decisions:
- Per-screen MessageBanner with show()/set_message() API
- All colors via DashColors (zero hardcoded Color32 values)
- 4 severity levels: Error, Warning, Success, Info
- Auto-dismiss for Success/Info (5s), persistent for Error/Warning
- Follows Component Design Pattern conventions (private fields, builder, show)
- No changes to BackendTask/TaskResult/AppState architecture

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

* feat: add MessageType::Warning

* chore: initial implementation

* chore: docs

* chore: self review

* refactor(context): replace RwLock<Sdk> with ArcSwap<Sdk> (#600)

* refactor(context): replace RwLock<Sdk> with ArcSwap<Sdk>

Sdk is internally thread-safe (Arc, ArcSwapOption, atomics) and all
methods take &self. The RwLock was adding unnecessary contention across
backend tasks.

Using ArcSwap instead of plain Sdk because reinit_core_client_and_sdk()
needs to atomically swap the entire Sdk instance when config changes.
ArcSwap provides lock-free reads with atomic swap for the rare write.

Suggested-by: lklimek

* fix: address CodeRabbit review findings for ArcSwap migration

- Fix import ordering: move arc_swap::ArcSwap before crossbeam_channel
- Remove redundant SDK loads in load_identity_from_wallet, register_dpns_name,
  and load_identity — use the sdk parameter already passed to these functions
- Fix stale TODO referencing removed sdk.read().unwrap() API
- Rename sdk_guard → sdk in transfer, withdraw_from_identity, and
  refresh_loaded_identities_dpns_names (no longer lock guards)
- Pass &sdk to run_platform_info_task from dispatch site instead of
  reloading internally
- Fix leftover sdk.write() call in context_provider.rs (RwLock remnant)
- Add missing Color32 import in wallets dialogs

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

* refactor: address remaining CodeRabbit review feedback on ArcSwap migration

- Move SDK load outside for loop in refresh_loaded_identities_dpns_names.rs
  so it's loaded once for the batch instead of on each iteration
- Update stale TODO comment in default_platform_version() to reflect that
  this is a free function with no sdk access

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

* refactor: consolidate double read-lock on spv_context_provider

Clone the SPV provider in a single lock acquisition, then bind app
context on the clone instead of locking twice.

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

---------

Co-authored-by: PastaClaw <thepastaclaw@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: remove unused Insight API and show_in_ui config fields (#597)

* refactor: remove unused Insight API references

The `insight_api_url` field in `NetworkConfig` and its associated
`insight_api_uri()` method were never used in production code (both
marked `#[allow(dead_code)]`). Remove the field, method, config
entries, env example lines, and related tests.

https://claude.ai/code/session_01HWPmCJHT8KTZGP9bFiksjn

* refactor: remove unused `show_in_ui` field from NetworkConfig

The `show_in_ui` field was defined on `NetworkConfig` and serialized in
`save()`, but never read by any production code to control network
visibility. Remove the field, its serialization, env example lines,
and test references.

https://claude.ai/code/session_01HWPmCJHT8KTZGP9bFiksjn

* fix: add missing `Color32` import in wallet dialogs

https://claude.ai/code/session_01HWPmCJHT8KTZGP9bFiksjn

---------

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

* build: add Flatpak packaging and CI workflow (#589)

* build: remove snap version

* build: add Flatpak packaging and CI workflow

Add Flatpak build manifest, desktop entry, AppStream metadata, and
GitHub Actions workflow for building and distributing Flatpak bundles.
Uses freedesktop 25.08 runtime with rust-stable and llvm21 extensions.
No application source code changes required - works in SPV mode by default.

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

* build: address review findings for Flatpak packaging

- Pin GitHub Actions to commit SHAs for supply chain security
- Upgrade softprops/action-gh-release from v1 to v2.2.2
- Remove redundant --socket=x11 (fallback-x11 suffices)
- Remove duplicate tag trigger preventing double builds on release
- Remove duplicate env vars inherited from top-level build-options
- Add Flatpak build artifacts to .gitignore
- Add bugtracker URL to AppStream metainfo
- Remove deprecated <categories> from metainfo (use .desktop instead)
- Add Terminal=false and Keywords to desktop entry
- Add disk space check after SDK install in CI
- Rename artifact to include architecture suffix

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

* build: simplify CI workflows for Linux-only releases

- Remove "Free disk space" step from flatpak and release workflows
- Remove Windows and macOS builds from release workflow
- Use "ubuntu" runner image instead of pinned versions
- Clean up unused matrix.ext references

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

* build: attach to existing releases instead of creating new ones

- Replace release-creating job with attach-to-release (only on release event)
- Add 14-day retention for build artifacts
- On tag push or workflow_dispatch, only upload artifacts (no release)

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

* revert: restore release.yml to original v1.0-dev version

The release workflow changes were out of scope for the Flatpak PR.

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

* fix: address CodeRabbit review comments

- Fix CRLF line endings in Flatpak manifest (convert to LF)
- Set app_id on ViewportBuilder to match desktop StartupWMClass
- Use --locked flag for reproducible cargo builds in Flatpak
- Rename --repo=repo to --repo=flatpak-repo to match .gitignore
- Add architecture note for protoc x86_64 binary

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

* docs: add Flatpak install instructions to README

Add a dedicated section for installing via Flatpak on Linux,
clarify that prerequisites are only needed for building from source,
and rename "Installation" to "Build from Source" for clarity.

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

* fix: match StartupWMClass to Flatpak app_id

Use reverse-DNS format org.dash.DashEvoTool to match the
Wayland app_id set via ViewportBuilder::with_app_id().

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

* fix: use ** glob for branch trigger to match feat/flatpak

Single * doesn't match path separators in GitHub Actions branch filters.

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

* feat: add aarch64 Flatpak build and caching to CI

- Add matrix strategy for parallel x86_64 and aarch64 builds
- Patch protoc URL/sha256 per architecture at build time
- Cache .flatpak-builder directory keyed on arch + manifest + lockfile
- Pin actions/cache to SHA

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

* fix: convert desktop and metainfo files to LF line endings

Flatpak builder validates desktop files and rejects CRLF line endings.

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

* build: cancel in-progress Flatpak builds on new push

Add concurrency group keyed on git ref so a new push cancels
any running build for the same branch or release.

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

* fix: address PR review findings for Flatpak packaging

- Remove unnecessary --filesystem=xdg-config/dash-evo-tool:create
  (Flatpak already redirects XDG_CONFIG_HOME to sandbox)
- Add categories and keywords to AppStream metainfo for discoverability
- Update README with both x86_64/aarch64 install commands, uninstall
  instructions, and Flatpak data path note
- Clarify aarch64 comment in manifest to reference CI sed patching

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

* chore: workflow timeout and perms

* fix: move permissions to job level in Flatpak workflow

Step-level permissions are not valid in GitHub Actions. Move
contents: write to the job level where it is needed for release
attachment.

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

* build: cache Cargo registry and target in Flatpak CI

Bind-mount host-side cargo-cache and cargo-target directories into the
Flatpak build sandbox so CARGO_HOME and target/ persist across builds.
Uses split restore/save with cleanup of incremental and registry/src
(similar to Swatinem/rust-cache).

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

* fix: scope cargo cache bind-mount sed to build-args only

The previous sed matched --share=network in both finish-args and
build-args, corrupting finish-args. Use a sed range to only target
the build-args section.

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

* Apply suggestions from code review

---------

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

* chore: fix build

* chore: use new error handling everywhere - not self reviewed

* chore: use message banner to show progress

* fix: start elapsed counter at 1s instead of 0s

Aligns elapsed display with the countdown timer which already adds 1
to avoid showing "0s" immediately.

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

* chore: rabbit review

* Update src/app.rs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* chore: peer review

* chore: fix build errors

* fix(ui): make MessageBanner::set_global truly idempotent

set_global() no longer resets timestamps, auto-dismiss timer, or
logged flag when a banner with identical text already exists. This
makes it safe to call every frame without log spam or timer restarts.

Cherry-picked from origin/fix/spv-peer-timeout (08e3b3b).

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

* fix(ui): address code review findings R01, R03, R06, R09, R10

- R01: Replace expect("No key selected") with graceful match + error
  banner in 11 token screens to prevent panics on missing signing key
- R03: Remove dead backend_message field from AddExistingIdentityScreen
- R06: Replace is_some() + unwrap() with idiomatic if-let-Some pattern
  in 10 token screens; use is_some_and() in structs.rs
- R09: Add use imports for MessageBanner in 5 dashpay screens, replacing
  22 fully-qualified crate::ui::components::MessageBanner:: calls
- R10: Replace custom_dash_qt_error_message inline rendering with
  MessageBanner::set_global in network_chooser_screen

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

* fix(ui): address review findings SEC-08, SEC-09, RUST-015, SEC-05, SEC-07

- SEC-08: Restore safe if-let-Some pattern in WithdrawalScreen::refresh()
  to prevent double unwrap() panic on DB error or deleted identity
- SEC-09: Restore original DB lookup in SendPaymentScreen::load_contact_info()
  replacing hardcoded "alice.dash" mock data
- RUST-015: Revert unimplemented!() back to ui.label() in
  update_token_config MarketplaceTradeMode arm
- SEC-05: Add success banners for contact request accept/reject in
  ContactRequests::display_task_result
- SEC-07: Add MessageBanner::clear_all_global() and call it from
  AppState::change_network() to prevent stale banners leaking across
  network switches

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

* docs: update coding conventions and message display guidance

Add fallible constructor rule (Result<Self, ...> when they can fail),
rename section to "General rules", and document MessageBanner
idempotency (no guard needed for set_global).

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

* fix(ui): replace expect/panic with graceful error handling (SEC-10)

Replace all expect() calls in token screen constructors and confirmation
handlers with MessageBanner error display. Constructors handle errors
internally and return Self with degraded state, keeping create_screen()
clean. refresh() methods now show errors via MessageBanner instead of
tracing-only logging.

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

* refactor(ui): accept impl Display/Debug in MessageBanner API

Change MessageBanner public methods to accept `impl Display` for
message text and `impl Debug` for details, instead of `&str`. Remove
needless `&format!(...)` borrows across 27 call sites.

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

* fix(ui): remove error Failed to get best chain lock for mainnet, testnet, devnet, and local

Fixes #633

* feat(ui): add automatic connection status banners

Display persistent MessageBanner notifications based on network
connection state transitions. Mode-aware messages guide users toward
the right recovery action (RPC vs SPV).

- Disconnected (RPC): "Disconnected — check that Dash Core is running"
- Disconnected (SPV): "Disconnected — check your internet connection"
- Syncing (RPC): "Syncing with Dash Core…"
- Syncing (SPV): "SPV sync in progress…"
- Synced: banner cleared

Uses Option<OverallConnectionState> for change detection, with None
as initial/post-network-switch sentinel to force first evaluation.

Closes #667 (partial — action links deferred to follow-up)

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

* fix(ui): pass TaskError directly to with_details() to avoid double-formatting

The previous code used `format!("{err:?}")` which produced a String, then
`with_details()` applied `{:#?}` again — wrapping the output in quotes and
escaping inner characters. Passing `&err` directly lets Debug format once.

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

* fix(ui): correct copy-paste error messages in token screens

Replace "Burning" error messages that were copy-pasted from burn screen
into freeze, destroy, and resume token screens with contextually correct
messages.

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

* fix(ui): restore lost success/error messages in 5 screens

Replace display_message() calls with MessageBanner::set_global() in
screens where display_message() is now a side-effect-only handler and
no longer displays messages directly.

Affected screens: create_asset_lock_screen, wallets_screen (MineBlocks),
address_table (export error), profile_screen (validation), contact_details.

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

* fix(ui): replace unwrap/expect with graceful error handling

Replace double unwrap in transfer_screen refresh() with unwrap_or_else
+ MessageBanner error display, matching the pattern from withdraw_screen.

SEC-002 tokens_screen skipped: the .expect() calls are only on
compile-time embedded image data (include_bytes!) which is safe.

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

* refactor(ui): migrate masternode_list_diff_screen to global MessageBanner

Replace ~15 local ui_state.message assignments and custom
render_message_banner() with MessageBanner::set_global() via the
display_message() trait method. Remove the message field from UiState
and the unused Color32 import.

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

* fix(ui): improve banner eviction logging and atomics

- Upgrade BANNER_KEY_COUNTER from Relaxed to SeqCst ordering for
  future-proofing against multi-threaded usage
- Log evicted banners at warn level in set_global() and replace_global()
- Add comment explaining why show_global() always writes back

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

* chore: remove resolved TODO.md

All items tracked in the unified message display TODO have been
addressed or moved to the review findings.

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

* docs(ui): add INTENTIONAL markers and API documentation

- Document why with_details() accepts Debug (not Display): structured
  error context is more useful in diagnostic details panes
- Document replace_global() fallback-to-add behavior as intentional
- Add INTENTIONAL(SEC-003) marker for developer mode error details
- Add INTENTIONAL(SEC-004) marker for BannerHandle Send+Sync safety

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

* refactor(ui): extract shared token validation and fix ordering

- Add validate_signing_key() helper in tokens/mod.rs to eliminate
  duplicated signing key validation across 12 token screens
- Move signing key validation BEFORE WaitingForResult state transition
  so users see immediate errors instead of loading spinner then error
- Replace is_err()/unwrap() anti-pattern with idiomatic let-else blocks
  in freeze, mint, transfer, destroy_frozen_funds, unfreeze screens

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

* refactor(ui): return Result from get_selected_wallet

Replace &mut Option<String> error out-parameter with idiomatic
Result<Option<Arc<RwLock<Wallet>>>, String>. Update 26+ callsites
across identity, token, DashPay, and contract screens.

Callsite patterns: unwrap_or_else with MessageBanner for user-visible
errors, unwrap_or(None) where errors were previously silently ignored.

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

* fix(ui): resolve duplicate imports and clippy warnings

Remove duplicate MessageBanner imports in create_asset_lock_screen and
wallets_screen/mod. Fix needless_borrows_for_generic_args clippy lints
in profile_screen, transfer_screen, and wallets_screen.

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

* style: reorder imports in masternode_list_diff_screen

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

* fix(ui): address review findings from MessageBanner migration audit

Apply 13 triaged review fixes plus 1 bug fix across 22 files:

- Remove dead error state fields (backend_message, error_message, Error variant)
- Replace .expect() panics with graceful fallback + MessageBanner in token screens
- Fix missing MessageBanner::show_global() on contracts documents screen
- Migrate DocumentActionScreen inline errors to MessageBanner
- Replace unwrap_or(None) with error-reporting fallback in DashPay screens
- Fix replace_global idempotency and use relaxed atomic ordering in banner
- Extract shared set_error_banner helper for 8 token screens
- Restore correct Some(0) wallet index semantics
- Document BannerHandle lifecycle in CLAUDE.md

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

* fix: validate token description length before sending to Platform (#530)

* fix: validate token description length before sending to Platform

Descriptions must be either empty or 3-100 characters long.

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

* fix(ui): validate token description by char count, not byte length

String::len() counts UTF-8 bytes, causing multi-byte characters
(CJK, emoji) to be miscounted against the 3–100 limit. Switch to
chars().count() and update all UI labels to surface the minimum.

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Lukasz Klimek <842586+lklimek@users.noreply.github.com>

* refactor(ui): consolidate banner extension traits into message_banner

Move BannerHandleExt and ResultBannerExt from banner_ext.rs into
message_banner.rs where they belong. Merge take_and_clear() into
OptionBannerExt trait (impl for Option<BannerHandle>) alongside
or_show_error() for Option. Remove the separate banner_ext module
and Clearable helper trait for simplicity.

Apply review findings: DRY patterns (take_and_clear, or_show_error,
load_identities_with_banner), fix .expect() panics in constructors,
restore known_identities in refresh(), narrow pub field visibility,
add ScreenLike doc comments, and update CLAUDE.md conventions.

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

* doc(tmp): review guide for pr 604

* fix(ui): address review findings from grumpy-review iteration 1

- Replace .expect() panics in TransferTokensScreen and ClaimTokensScreen
  constructors with graceful degradation via Option<QualifiedIdentity>
  and MessageBanner error display (PROJ-001 HIGH)
- Fix CLAUDE.md referencing non-existent BannerHandleExt trait name,
  corrected to OptionBannerExt (PROJ-002 MEDIUM)
- Update set_global to preserve message_type when same text appears
  with different severity (RUST-001 MEDIUM)
- Standardize display_message to handle both Error and Warning across
  all 11 token screens (RUST-002 MEDIUM)
- Replace 21 manual take().clear() patterns with take_and_clear()
  across 6 files (RUST-003 MEDIUM)
- Remove unused OptionBannerExt::or_show_error method (RUST-004 MEDIUM)
- Migrate update_token_config from old error_message pattern to
  set_error_banner closure (RUST-005 MEDIUM)

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

* perf(ui): replace per-frame QualifiedIdentity clone with borrow

Use .as_ref() instead of .clone() in the ui() identity guard of
TransferTokensScreen and ClaimTokensScreen. QualifiedIdentity
(Identity + KeyStorage + BTreeMap + Vec) was being cloned 60x/sec;
now only borrowed for display, with clones deferred to button-click
paths that actually need ownership.

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

* feat(ui): add OptionResultExt::or_show_error for Option<T>

Mirrors ResultBannerExt::or_show_error but for Option<T>: if None,
displays a global error banner with the given message. Enables
concise patterns like:
  identities.first().cloned().or_show_error(ctx, "No identities loaded")

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

* fix(ui): address review findings from grumpy-review iteration 2

- Standardize display_message to handle both Error and Warning across
  13 non-token screens that were missed in iteration 1 (PROJ-001 MEDIUM)
- Replace .expect() panic in AddKeyScreen::refresh() with graceful
  or_show_error() + unwrap_or_default() (PROJ-002 MEDIUM)
- Rename OptionResultExt to OptionBannerShowExt to avoid confusion
  with ResultBannerExt (RUST-001 MEDIUM)

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

* fix(ui): handle Warning in add_new_identity_screen display_message

Missed in the previous sweep — standardize display_message to handle
both Error and Warning, matching all other screens.

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

* fix(ui): standardize display_message side-effect patterns across screens

Guard side effects with Error|Warning match, use take_and_clear(),
and remove redundant MessageBanner::set_global() call in 4 screens.

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

* chore(deps): update dashpay/platform to rev 570e3af0

Adapt to breaking changes in rust-dashcore (a05d256f → 2824e52a):

- Replace removed FeeLevel enum with FeeRate::normal() direct calls
- Replace removed WalletManager::create_unsigned_payment_transaction()
  with TransactionBuilder + WalletManager::get_change_address()
- Replace removed DashSpvClientInterface/DashSpvClientCommand with
  direct Arc<SpvClient> for quorum lookups via get_quorum_at_height()
- Replace removed start()+monitor_network() with client.run(token)
- Add .await to now-async subscribe_sync/network/progress methods
- Replace removed SyncState::Initializing with SyncState::WaitForEvents

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

* chore: log backtrace on panic

* fix: panic in asset locks

* fix(ui): address PR #604 review comments (CMT-001, CMT-002, CMT-003)

- Fix QR scanner form reset matching wrong result type (CMT-001)
- Remove dangerous identity fallback in token transfer screen (CMT-002)
- Add fee-aware validation before credit transfers (CMT-003)

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

* refactor(spv): replace AsyncRwLock with ArcSwapOption for SPV client reference

The SPV client reference only needs atomic set/clear (on start/stop) and
wait-free reads (quorum lookups). ArcSwapOption is a better fit than
AsyncRwLock<Option<Arc<...>>> — no lock contention, no async in blocking
context, and consistent with how AppContext already uses ArcSwap for the SDK.

Also fixes stale doc comment referencing removed DashSpvClientInterface.

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

* fix(ui): address PR #604 review comments iteration 2 (transfer_tokens_screen)

- Remove duplicate conflicting banner on missing identity (CMT-003)
- Use generic banner messages with with_details() for errors (CMT-002)
- Fix refresh to match specific token by contract+position (CMT-001)
- Document error banner pattern in CLAUDE.md

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

* [claudesquad] update from 'testing research' on 02 Mar 26 13:25 CET (paused)

* test: add backend E2E test harness and SPV wallet test

Add test-only accessors (db(), wallets()) on AppContext gated behind
cfg(test/testing), fix compilation errors in the backend-e2e test
(private field access, unused import), and apply nightly rustfmt.

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

* feat: add DASH_EVO_DATA_DIR env var to override app data directory

Allows tests and CI to redirect all app data (database, SPV chain
state, .env config) to a temp directory. The backend-e2e test harness
now uses this to achieve full data isolation.

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

* test: add backend E2E test framework with shared state and funded wallets

Evolve the prototype backend E2E test harness into a reusable framework:

- LazyLock shared BackendTestContext with persistent workdir, SPV, and
  framework wallet (funded via E2E_WALLET_MNEMONIC or testnet faucet)
- Task runner wrapper, polling wait helpers, faucet HTTP client
- Identity key derivation helpers for wallet-funded registration
- Six test scenarios: SPV wallet, identity create, identity withdraw,
  send/receive funds, fetch contracts, register DPNS name
- Move default_identity_key_specs() from UI to backend_task::identity
  (domain logic, not UI concern) and make IdentityKeys fields pub
- Add dashpay_contract_id() test accessor to AppContext

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

* fix(test): use tokio::sync::OnceCell instead of LazyLock for async init

LazyLock triggers synchronously inside the #[tokio::test] runtime,
causing "Cannot start a runtime from within a runtime" when the init
function calls block_on(). Switch to tokio::sync::OnceCell with an
async init() method so shared state initialization runs cooperatively
within the existing tokio runtime.

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

* fix(test): handle persistent DB and SPV balance sync in E2E harness

- Wait for SPV to sync existing wallet balance before checking if
  faucet funding is needed (pre-funded wallets need time to discover
  on-chain UTXOs)
- Handle "already imported" error gracefully when framework wallet
  exists in persistent DB from a previous run

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

* fix(test): wait for spendable balance and retry sends in E2E harness

The SPV wallet reports total balance (including unconfirmed) but only
confirmed/IS-locked UTXOs are available for transaction building. This
caused "Insufficient funds" errors when tests tried to spend immediately
after receiving funds.

- Add wait_for_spendable_balance() that checks confirmed_balance_duffs()
  and triggers reconcile_spv_wallets() on each poll iteration
- Add retry logic (5 attempts, 10s backoff) to create_funded_test_wallet()
  for sends that fail with InsufficientFunds
- Wait for framework wallet change output to become spendable after each
  send so subsequent calls don't fail
- Add wait_for_spendable_balance() before identity registration in all
  identity/DPNS tests
- Add send_with_retry() helper in send_funds test
- Add developer-facing README.md for the test framework

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

* fix(test): use tokio-shared-rt for shared runtime and sweep orphaned wallets

Replace per-test tokio runtimes with tokio-shared-rt's global shared runtime
to prevent SPV background tasks from dying between test functions. Add
automatic orphaned wallet fund recovery during setup — wallets persist in DB,
so on next run the harness sweeps funds back to the framework wallet.

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

* fix: handle OverallConnectionState::Error in connection banner

Semantic merge conflict from v1.0-dev: PR #650 added an Error variant to
OverallConnectionState, which our connection banner match didn't cover.
Show an error banner when SPV sync enters the error state.

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

* docs(test): improve backend-e2e documentation and funding UX

- Add backend E2E section to CLAUDE.md pointing to the full README
- Document .env file handling (project root vs workdir) and precedence
- Fix test attribute in README: tokio::test → tokio_shared_rt::test(shared)
- Update init sequence to reflect current code (spendable wait, orphan sweep)
- Document automatic cleanup-on-init of orphaned test wallets
- Raise minimum balance threshold from 1 to 10 tDASH
- Always panic with receive address when faucet fails and balance is below
  minimum (previously only panicked on zero balance)
- Show receive address in spendable-balance timeout warning

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

* fix: patch SPV UTXO spendability flags before coin selection

Upstream key-wallet-manager (rust-dashcore) never sets is_confirmed or
is_instantlocked on UTXOs, but CoinSelector requires one of them.
This caused "No UTXOs available for selection" errors despite having
balance. Workaround infers status from block inclusion (height > 0 →
confirmed, height == 0 → IS-locked).

Ref: dashpay/rust-dashcore#514

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

* test: harden backend E2E tests with retry logic and calibrated amounts

- Increase funding amounts to avoid insufficient-funds flakes
- Add 3-attempt retry for identity registration (chain height sync)
- Retry on "No UTXOs" in send_with_retry alongside "Insufficient"
- Wait for spendable balance in create_funded_test_wallet before return
- Add CI workflow for backend E2E tests

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

* Update test command in backend E2E workflow

* ci: merge backend-e2e workflow into tests workflow (#727)

* Initial plan

* ci: merge backend-e2e workflow into tests workflow as an additional step

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

* test: add cleanup_only noop test to sweep orphaned wallets

Add a standalone test that triggers BackendTestContext initialization,
which runs cleanup_test_wallets() as its final step. This can be run
as a dedicated CI step after the E2E suite to sweep orphaned wallet
funds back to the framework wallet.

Run with:
  cargo test --test backend-e2e --all-features -- --ignored --nocapture cleanup_only

Co-authored-by: lklimek <lklimek@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add cleanup step for E2E test wallets

Added a cleanup step for E2E test wallets in the workflow.

* Simplify E2E test workflow conditions

Removed conditional checks for E2E_WALLET_MNEMONIC in test steps.

---------

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

* fix: address PR #673 review comments from triage

Production code:
- Extract patch_utxo_spendability_flags() helper to deduplicate
  workaround in estimate_fallback_amount and build_unsigned_payment_tx
- Add IdentityKeys::new() constructor
- Make wallet+address persistence atomic via store_wallet_with_addresses()
- Add pending_wallet_selection after wallet creation

Test code:
- Add DPNS registration retry for identity propagation delay
- Use u64 hex for DPNS name uniqueness (CMT-023)
- Calibrate test funding amounts per reviewer feedback
- Add fragility note on string-match wallet detection (CMT-006)
- Add TODO for identity fund withdrawal in cleanup (CMT-032)
- Add INTENTIONAL comment for bounded channel design (CMT-017)
- Add balance assertion for return leg in send_funds test
- Log reconcile_spv_wallets errors in wait helpers
- Use is_ok_and for clarity in spv_wallet test

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

* refactor: remove UTXO spendability workaround, update platform dep

Remove `patch_utxo_spendability_flags()` that faked IS-locked status on
mempool UTXOs. Wait for upstream fix (dashpay/rust-dashcore#514) to
properly set is_confirmed/is_instantlocked flags on UTXOs.

Also:
- Update dashpay/platform rev to aa86b74f7e2
- Adapt to upstream API: FeeLevel→FeeRate, remove NetworkExt import
- Fix retry to catch "No UTXOs" errors in test harness

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

* refactor: deduplicate default_identity_key_specs

Move the single canonical copy to backend_task::identity::mod and have
the UI screen import it, eliminating ~240 lines of duplicated function
and tests.

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

* ci: temporarily disable backend E2E tests in CI

The backend E2E tests need updates after the TaskError migration
(#739) changed AppContext field visibility. Commenting out the
CI steps until the tests are adapted.

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

* refactor: type-safe wallet creation and identity key visibility

- Restrict IdentityKeys fields to pub(crate) to prevent private key
  exposure outside the crate
- Change register_wallet() to return TaskError instead of String, using
  proper rusqlite error matching via is_unique_constraint_violation()
  and a new WalletAlreadyImported variant
- Change Wallet::new_from_seed() to accept Option<&Secret> for password
  instead of Option<&str>, keeping sensitive data in the Secret wrapper
- Change Wallet::new_from_seed() to return TaskError instead of String,
  with a new WalletKeyDerivationFailed variant for derivation errors
- Move build_identity_registration() and get_receive_address() from
  test helpers to production code in src/backend_task/identity/mod.rs
- Extract is_unique_constraint_violation() to src/database/mod.rs as a
  shared pub(crate) utility, removing the duplicate in import_mnemonic_screen
- Update all callers in add_new_wallet_screen and import_mnemonic_screen

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

* test: address PR #673 review comments for E2E test framework

- Move framework modules (harness, task_runner, wait, funding, cleanup,
  identity_helpers) into tests/backend-e2e/framework/ subdirectory
- Make E2E_WALLET_MNEMONIC required (panic with instructions if unset)
- Remove auto-faucet from initialization flow (keep as helper)
- Remove retry loops in identity_create and identity_withdraw tests
- Remove unnecessary wait_for_spendable_balance calls (already done
  by create_funded_test_wallet)
- Replace all println!/eprintln! with tracing macros
- Initialize tracing subscriber in harness init
- Add "No spendable funds" and "spendable" to send retry conditions
- Remove stale "other agent" NOTE comments from identity_helpers
- Consolidate funding logic (harness delegates to funding module)
- Update README for required mnemonic and new directory structure

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

* fix: reconcile production and test code after type-safe refactor

Adapt test framework to production API changes:
- Use IdentityKeys::new() constructor (fields now pub(crate))
- Match TaskError::WalletAlreadyImported variant instead of string
- Allow dead_code on faucet helpers (kept but not auto-called)

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

* refactor: thread data_dir as explicit parameter to eliminate env var dependency

Add `data_dir: PathBuf` field to `AppContext` and thread it through
`Config::load_from()`, `Config::save()`, `SpvManager::new()`,
`start_dash_qt()`, and `create_dash_core_config_if_not_exists()`.

This enables E2E tests to specify their data directory without mutating
process-wide environment variables, making parallel test execution safe.

The `DASH_EVO_DATA_DIR` env var is still checked in production via
`app_user_data_dir_path()`, but the resolved path is threaded through
as a value rather than re-read from env on every call.

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

* fix(test): simplify funded wallet creation and make spendable balance check reliable

- Remove retry loop from create_funded_test_wallet; wait for full
  amount_duffs instead of 1 duff in spendable balance check
- Add Wallet::spv_confirmed_balance() that returns None when SPV
  hasn't synced yet (no max_balance fallback)
- Use spv_confirmed_balance() in wait_for_spendable_balance so
  the wait never gets false positives from the fallback
- Remove --test-threads=1 requirement from README (unsafe set_var
  was the only reason; data_dir is now threaded explicitly)

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

* fix: remove send retry loop, handle PRIMARY KEY constraints, isolate SPV test dirs

- Remove send_with_retry() from send_funds.rs; use
  wait_for_spendable_balance before each send instead
- Add SQLITE_CONSTRAINT_PRIMARYKEY (1555) to uniqueness check
  alongside SQLITE_CONSTRAINT_UNIQUE (2067)
- Use tempfile::TempDir in SPV tests instead of fixed /tmp/spv-test
  path to prevent state leaks and support concurrent test runs

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Pasta Lil Claw <pasta+claw@dashboost.org>
Co-authored-by: PastaClaw <thepastaclaw@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Paul DeLucia <69597248+pauldelucia@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: lklimek <lklimek@users.noreply.github.com>
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.

3 participants