Files
ObsidianDragon/docs/codebase-audit-2026-04-27.md
dan_s d684db446e Refactor app services and stabilize refresh/UI flows
- Add refresh scheduler and network refresh service boundaries for typed
  refresh results, ordered RPC collectors, applicators, and price parsing.
- Add daemon lifecycle and wallet security workflow helpers while preserving
  App-owned command RPC, decrypt, cancellation, and UI handoff behavior.
- Split balance, console, mining, amount formatting, and async task logic into
  focused modules with expanded Phase 4 test coverage.
- Fix market price loading by triggering price refresh immediately, avoiding
  queue-pressure drops, tracking loading/error state, and adding translations.
- Polish send, explorer, peers, settings, theme/schema, and related tab UI.
- Replace checked-in generated language headers with build-generated resources.
- Document the cleanup audit, UI static-state guidance, and architecture updates.
2026-04-29 12:47:57 -05:00

57 KiB

Codebase Cleanup Audit

Current as of 2026-04-27 for ObsidianDragon 1.2.0-rc1.

Scope

This audit covers architecture, threading, UI/layout code, build/resource integration, RPC/daemon behavior, and general cleanup opportunities. It is a static maintainability audit, not a runtime performance profile or security penetration test.

Evidence was gathered from source-tree scans, largest-file metrics, targeted code inspection, and focused architecture/UI/build review passes.

Snapshot

  • src/ contains about 77,639 handwritten C++ source/header lines, excluding generated language outputs.
  • Generated language headers previously accounted for about 34,675 source-tree lines under src/embedded/lang_*.h; Phase 4 moved those outputs to build/generated/embedded/.
  • Largest handwritten files are concentrated in UI and application orchestration:
    • src/ui/windows/balance_tab.cpp: 3,434 lines
    • src/app.cpp: 3,097 lines
    • src/ui/windows/mining_tab.cpp: 2,544 lines
    • src/ui/pages/settings_page.cpp: 2,092 lines
    • src/main.cpp: 2,014 lines
    • src/app_security.cpp: 1,918 lines
    • src/app_network.cpp: 1,912 lines
    • src/ui/windows/console_tab.cpp: 1,572 lines
    • src/app_wizard.cpp: 1,378 lines
    • src/daemon/embedded_daemon.cpp: 1,213 lines

Executive Summary

The codebase has a solid functional foundation: RPC work is already separated from UI-thread callbacks, the schema-driven UI system is useful, and embedded resource handling is well established. The largest opportunities are mostly structural rather than feature bugs.

The main cleanup theme is ownership. App currently coordinates lifecycle, rendering, daemon state, RPC refreshes, wallet security, dialogs, shutdown, mining, and first-run flow. That centralization makes features easy to wire in the short term, but it also pushes thread lifetime, error propagation, UI state, and background polling into a few very large files.

The second theme is consistency. Dialogs mostly use Material overlay helpers, but some still use raw ImGui modals. Many UI dimensions are schema-driven, but dialog/button/icon sizes still have scattered literals. RPC auth exists in more than one place, generated resources live partly in source and partly in build output, and error handling ranges from user-visible reporting to silent catch (...) blocks.

Implementation Status

Phase 1 Completed On 2026-04-27

  • Replaced time-seeded std::rand() RPC credential generation in src/rpc/connection.cpp with libsodium-backed random generation.
  • Replaced direct UI system() explorer/about launch calls with util::Platform::openUrl().
  • Reworked util::Platform::openUrl() and openFolder() so macOS/Linux launchers use posix_spawnp() arguments instead of shell-built command strings, and folder creation uses std::filesystem::create_directories().
  • Added URL scheme validation for platform URL opens.
  • Added CMake visibility for missing xxd and Python theme expansion dependencies.
  • Added xxd and Python checks/package coverage to setup.sh.
  • Added shared App::sendStopCommandSafely() logging helper and routed repeated daemon stop RPC calls through it.
  • Confirmed libs/incbin.h is already represented in THIRD_PARTY_LICENSES, so no additional license entry was needed.
  • Verified with staged builds after logical batches, ending with cd build && make -j$(nproc) successfully linking bin/ObsidianDragon.

Remaining high-priority follow-ups from this audit include the larger App/security/refresh service boundaries and tests around the newly centralized runtime behavior.

Phase 2 Completed On 2026-04-27

  • Added schema-backed dialog layout tokens in res/themes/ui.toml and central accessors in src/ui/layout.h for common dialog widths, form width, action width/gap, max height ratio, and compact bottom alignment.
  • Registered the dialog UI schema section in src/ui/schema/ui_schema.cpp so those tokens are available through the existing schema cache.
  • Extended material::BeginOverlayDialog() with an optional ID suffix and added shared overlay action/footer helpers in src/ui/material/draw_helpers.h.
  • Migrated address book Add/Edit address dialogs in src/ui/windows/address_book_dialog.cpp from raw ImGui::BeginPopupModal() usage to material::BeginOverlayDialog() with one shared form/action renderer.
  • Added src/ui/material/project_icons.h as the wallet icon registry and moved pickaxe-specific font rendering behind that helper.
  • Kept AddressLabelDialog::drawIconByName() and iconGlyphForName() as compatibility wrappers for existing call sites.
  • Verified with cd build && make -j$(nproc) successfully linking bin/ObsidianDragon; diagnostics were clean on Phase 2 touched files, git diff --check passed, and scans found no remaining Add/Edit raw modal usage or local address-label icon arrays.

Phase 3 Completed On 2026-04-27

  • Added src/util/async_task_manager.h/.cpp as a named task owner with cancellation tokens, completed-task reaping, and join-on-shutdown behavior.
  • Routed App-owned daemon maintenance, wizard daemon stop/check, encryption daemon restart, and decrypt restart/import background work through AsyncTaskManager instead of detached App threads.
  • Kept Bootstrap's worker joinable so its existing destructor cancellation can join the download/extract thread instead of losing ownership through detach().
  • Added src/daemon/daemon_controller.h/.cpp as the first daemon ownership boundary; it now owns EmbeddedDaemon, syncs settings into the daemon, and centralizes start/stop calls while App keeps a non-owning bridge pointer for low-churn follow-up migration.
  • Extended rpc::ConnectionConfig with auth-source tracking and use_tls, parsed rpctls/rpcssl-style config flags, and centralized .cookie auth retry construction in Connection::buildCookieAuthConfig().
  • Updated main, fast-lane, temporary stop, wizard stop, and decrypt-import RPC clients to pass the TLS flag to RPCClient.
  • Added a one-per-session runtime warning and Settings-page warning when a non-localhost RPC host is configured without TLS.
  • Verified after each logical batch with cmake .. && make -j$(nproc) or make -j$(nproc) successfully linking bin/ObsidianDragon; diagnostics were clean on Phase 3 touched files, git diff --check passed, and scans found no remaining App-owned detached background tasks or App-level manual .cookie fallback.

Phase 4 Completed On 2026-04-27

  • Added src/services/refresh_scheduler.h/.cpp and moved refresh interval/timer policy out of App, while leaving RPC refresh bodies behavior-preserving in the existing app/network methods.
  • Replaced App refresh timer fields with services::RefreshScheduler, including page-specific refresh policy, wallet mutation refresh marking, transaction-age throttling, OPID polling cadence, price refresh cadence, and fast mining/rescan ticks.
  • Moved generated language headers from tracked src/embedded/lang_*.h files into ${CMAKE_BINARY_DIR}/generated/embedded/, keeping the existing embedded/lang_*.h include strings working through the generated include directory.
  • Added CTest infrastructure and ObsidianDragonTests with focused coverage for connection config parsing, cookie fallback and plaintext/TLS checks, payment URI parsing, fixed amount formatting, spendable wallet address filtering, and scheduler behavior.
  • Added src/util/amount_format.h/.cpp for shared fixed-decimal amount formatting and used it in transaction send payload construction.
  • Added pure spendability helpers in src/data/wallet_state.h/.cpp and routed Send-tab source address selection through them.
  • Split low-risk helpers out of large UI tabs: balance helper formatting/drawing code, mining formatting/estimate/thread helpers, and the console RPC command reference registry.
  • Verified focused tests with cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure, then verified the UI split batch with cd build && cmake .. && make -j$(nproc) successfully linking bin/ObsidianDragon.

Phase 5 Completed On 2026-04-27

  • Added src/services/wallet_security_controller.h/.cpp and moved deferred wizard encryption/PIN state, connection retry throttling, PIN validation, and secure clearing behind a wallet-security boundary while keeping the existing RPC/UI behavior in App.
  • Added src/services/network_refresh_service.h/.cpp as the fuller refresh boundary around RefreshScheduler, named refresh jobs, in-flight job guards, and explicit queue-pressure skips for core, address, transaction, mining, and peer refreshes.
  • Extended DaemonController with shutdown/external-daemon policy decisions and routed App::beginShutdown() through that boundary.
  • Grouped Settings-page static UI state into SettingsPageState and first-run wizard static UI state into WizardUiState, preserving existing file-local behavior while making state ownership explicit.
  • Continued low-risk renderer splitting with console layout helpers, balance recent-transaction visual/amount helpers, and mining active-state/thread clamp helpers.
  • Expanded focused tests for daemon shutdown policy, wallet security transitions, network refresh job guards, renderer helpers, and generated resource fallback behavior.
  • Documented the src/config/version.h policy: it remains committed source for editor/release tooling compatibility, and CMake regenerates it from src/config/version.h.in during configure.
  • Verified Phase 5 batches with repeated cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure runs.

Phase 6 Completed On 2026-04-27

  • Expanded WalletSecurityController with mockable RPC and secure-vault gateways for deferred encryption, unlock, export, import, key classification, import messaging, decrypt export naming, and secure handoff behavior.
  • Routed wallet encryption, deferred encryption retry, unlock, and key import/export classification through the wallet-security service boundary while preserving existing App-visible behavior.
  • Extended DaemonController with lifecycle decisions for manual restart, rescan, blockchain-data deletion, and bootstrap daemon stop sequencing, then routed the corresponding App flows through those decisions.
  • Promoted NetworkRefreshService to dispatch-ticket ownership with queue-depth telemetry, queue-pressure skips, in-flight skips, completion stats, cancellation, and stale-callback detection for named refresh jobs.
  • Continued renderer splitting with stable modules for recent transaction presentation, pool-worker default selection, and console output filtering.
  • Removed Settings-page compatibility aliases so the page now uses SettingsPageState fields directly.
  • Expanded focused integration-style tests with mock wallet-security RPC/vault collaborators, daemon lifecycle policy checks, refresh dispatch telemetry/stale-callback edges, and UI helper coverage.
  • Verified with cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure.

Phase 7 completed on 2026-04-27:

  • DaemonController now owns lifecycle execution ordering for restart, rescan, blockchain-data deletion, and bootstrap stops through mockable LifecycleRuntime and LifecycleTaskContext collaborators.
  • App removed the temporary non-owning embedded_daemon_ bridge; daemon state/output reads now route through DaemonController wrappers or explicit controller access.
  • Added WalletSecurityWorkflow for decrypt/export/import dialog phase, step, import-active state, and wallet file planning, so the dialog state can be tested without constructing App.
  • NetworkRefreshService now owns enqueue/callback wrapping for named refresh jobs, including queue-depth sampling, queue-pressure skips, stale callback suppression, and UI-thread callback handoff.
  • Core, address, transaction, mining, peer, and encryption refresh jobs now use the service enqueue wrapper instead of app-local dispatch-ticket boilerplate.
  • Split additional UI helpers into balance_address_list, mining_benchmark, and console_input_model, with focused tests for filtering/sorting, benchmark state estimates, history navigation, and autocomplete.
  • Expanded ObsidianDragonTests with mock daemon lifecycle execution tests, wallet workflow tests, refresh enqueue/stale-callback tests, and the new UI module coverage.
  • Verified after each logical batch with cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure.

Phase 8 completed on 2026-04-27:

  • Added WalletSecurityWorkflowExecutor with mockable RPC, import, file, daemon, and secure-vault cleanup gateways; decrypt wallet unlock/export/backup/restart/import/cleanup sequencing now runs through that executor instead of nested App-local orchestration.
  • Completed named refresh enqueue coverage by routing price refresh through NetworkRefreshService::enqueue(Job::Price) and connect-time getinfo/getwalletinfo prefetch through Job::ConnectionInit.
  • Added daemon lifecycle collaborators for async/immediate task contexts and blockchain-data cleanup, reducing App-specific lifecycle runtime code where the extracted pieces are directly testable.
  • Continued large-view reduction with balance address-row layout/USD helpers, mining benchmark transition and pool saved/default helpers, and console command parse/result classification helpers.
  • Expanded ObsidianDragonTests for workflow executor edges, ConnectionInit enqueue coverage, daemon lifecycle adapters, and the new UI helper/model behavior.
  • Verified each logical Phase 8 batch with cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure.

Phase 9 completed on 2026-04-27:

  • Added typed refresh result models, JSON parsers, and service-owned apply contracts on NetworkRefreshService for connection info, wallet encryption, core balance/sync, mining, peers, and price refreshes.
  • Routed App refresh callbacks through those contracts for connect-time prefetch, warmup info application, core refresh, encryption refresh, mining refresh, peer refresh, and price refresh while preserving the existing RPC call ordering.
  • Expanded ObsidianDragonTests with focused refresh result parsing/application coverage for balance/sync state, connection metadata, wallet lock state, mining history, peer lists, banned peers, and price history.
  • Documented remaining intentional process-wide ImGui state and legacy compatibility wrappers in docs/ui-static-state.md, and linked that policy from docs/codebase-overview.md.
  • Verified the Phase 9 implementation batch with cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure.

Phase 10 completed on 2026-04-27:

  • Added NetworkRefreshService::AddressRefreshResult and routed address snapshot application through applyAddressRefreshResult() while preserving App-owned RPC collection and dirty-flag behavior.
  • Added service-owned transaction view-cache models plus TransactionRefreshResult/TransactionCacheUpdate so transaction refresh callbacks now apply WalletState::transactions, last_tx_update, last_tx_block_height_, viewtx_cache_, send_txids_, and confirmed transaction caches through one cache-update contract.
  • Moved z-viewtransaction output parsing and outgoing-send enrichment into NetworkRefreshService helpers, leaving only the RPC call order and per-cycle throttling in App.
  • Evaluated the remaining decrypt workflow orchestration and kept the nested UI step handoffs App-owned because those boundaries still carry progress updates, worker-thread callback ordering, and shutdown/token cancellation semantics.
  • Did not touch high-churn tab/dialog statics in this phase; docs/ui-static-state.md remains the policy for future behavior-adjacent UI state changes.
  • Expanded ObsidianDragonTests with address/transaction applicator coverage, view-transaction enrichment/cache-update tests, stale callback cancellation coverage, additional remote/TLS config parsing, and shutdown-cancellation lifecycle coverage.
  • Verified the Phase 10 implementation batch with cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure.

Phase 11 completed on 2026-04-27:

  • Revisited the decrypt restart/import flow and kept the remaining UI handoff choreography App-owned because each boundary still corresponds to an explicit progress step, cancellation check, or background import transition. No partial executor move was made.
  • Moved additional address refresh construction into NetworkRefreshService helpers for shielded-address validation results, transparent address parsing, and unspent-output balance application while preserving the existing daemon RPC call order in App::refreshAddressData().
  • Moved transaction pre-refresh snapshot construction and list parsing helpers into NetworkRefreshService, including shielded address snapshots, fully enriched txid snapshots, transparent transaction parsing, shielded receive parsing, and final transaction sorting. App::refreshTransactionData() still owns RPC call order and per-cycle z_viewtransaction throttling.
  • Expanded ObsidianDragonTests with focused coverage for the new address/transaction snapshot helpers, worker callback ordering across independent refresh jobs, and reconnect-style stale transaction callbacks.
  • Did not migrate additional tab/dialog statics because Phase 11 did not touch those views for behavior changes.
  • Verified with cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure.

Phase 12 completed on 2026-04-27:

  • Added a shared ordered mock refresh RPC fixture in ObsidianDragonTests so service-level refresh collectors can assert exact daemon method ordering and parameters.
  • Introduced NetworkRefreshService::RefreshRpcGateway plus collectAddressRefreshResult() and collectTransactionRefreshResult(); address and transaction refresh RPC collection now lives behind the service boundary while preserving the same daemon call order and per-cycle z_viewtransaction cap.
  • Routed App::refreshAddressData() and App::refreshTransactionData() through a small RPCClient adapter for those collectors, keeping App focused on enqueueing and applying the returned results.
  • Expanded refresh lifecycle coverage for ordered callbacks, reconnect-style stale transaction callbacks, and collector ordering around address validation, z-balance fallback, shielded receive polling, cached viewtransaction entries, fresh z_viewtransaction, and gettransaction enrichment.
  • Revisited decrypt restart/import orchestration again and made no extra move because the remaining boundaries are still the explicit progress/cancellation/import handoff points.
  • Did not migrate additional tab/dialog statics because Phase 12 did not touch those views for behavior changes.
  • Verified with cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure.

Phase 13 completed on 2026-04-27:

  • Added service-owned core and peer refresh collectors behind NetworkRefreshService::RefreshRpcGateway, moving the fixed z_gettotalbalance/getblockchaininfo and getpeerinfo/listbanned RPC bodies out of App.
  • Extended the ordered mock RPC fixture tests to assert the exact core and peer daemon call order, empty parameter arrays, parsed result values, and partial-failure behavior where the second RPC still runs after the first fails.
  • Kept warmup getinfo, mining refresh, connection init, and price fetch orchestration App-owned because those paths either have UI status handoffs, cadence-specific behavior, or non-RPC HTTP/callback details that were not part of this small collector move.
  • Did not add new reconnect/shutdown lifecycle tests because Phase 13 did not change async cancellation, daemon restart, or worker lifecycle ownership.
  • Revisited decrypt restart/import orchestration and high-churn UI state migrations by scope only; no behavior work touched those complete progress/cancellation or tab/dialog seams.
  • Verified with cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure.

Phase 14 completed on 2026-04-27:

  • Added NetworkRefreshService::collectMiningRefreshResult() behind RefreshRpcGateway; the service now owns the ordered getlocalsolps plus optional getmininginfo RPC collection while App still owns the fast/slow cadence decision and daemon-memory snapshot.
  • Added NetworkRefreshService::collectConnectionInitResult() so the initial getinfo then getwalletinfo prefetch is service-owned while App::onConnected() keeps the high-priority enqueue and encryption_state_prefetched_ lock-screen timing flag.
  • Extended ordered mock RPC tests for mining slow ticks, mining fast-only ticks, mining partial failure, connection-init success, and connection-init wallet-info prefetch after getinfo failure.
  • Did not add reconnect/shutdown lifecycle tests because Phase 14 did not change worker callback ownership, shutdown, reconnect, daemon restart, or cancellation behavior.
  • Did not touch decrypt orchestration or high-churn UI state because no complete behavior-preserving step in those areas was part of the batch.
  • Verified with cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure.

Phase 15 completed on 2026-04-27:

  • Added NetworkRefreshService::WarmupPollResult and collectWarmupPollResult() behind RefreshRpcGateway; the service now owns the single warmup getinfo call and preserves either parsed connection info or the raw RPC error string.
  • Routed the warmup branch of App::refreshCoreData() through the new collector while keeping UI status translation, daemon block-height decoration, connection_status_, and the refreshData() transition App-owned.
  • Extended ordered mock RPC tests for warmup success and warmup failure so the getinfo call, empty params, parsed ready state, and error propagation are directly covered.
  • Reviewed price HTTP fetching and kept it App-owned because the current worker callback, libcurl setup, HTTP-status handling, logging, and parse/apply handoff are clearer in one place; only JSON response parsing remains service-owned.
  • Left command-style RPC actions App-owned because Phase 15 did not include behavior changes for send, address creation, mining toggles, ban operations, or import/export commands.
  • Did not add reconnect/shutdown lifecycle tests because worker callback ownership, shutdown, reconnect, daemon restart, and cancellation behavior did not change.
  • Did not touch decrypt orchestration or high-churn UI state because no complete behavior-preserving step in those areas was part of the batch.
  • Verified with cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure.

Phase 16 completed on 2026-04-27:

  • Added NetworkRefreshService::PriceHttpResponse, PriceHttpResult, and parsePriceHttpResponse() so libcurl transport status, HTTP status, parse success, and failure messages are represented as a focused service-owned boundary.
  • Kept libcurl initialization, request execution, worker callback ownership, successful price logging, and UI-thread price application in App::refreshPrice().
  • Expanded focused tests for successful price HTTP parsing, HTTP non-200 failures, transport failures, and unrecognized response bodies.
  • Kept command-style RPC actions App-owned because Phase 16 did not include a complete behavior change for send, address creation, mining toggles, ban operations, or import/export commands.
  • Did not add reconnect/shutdown lifecycle tests because callback ownership, cancellation, shutdown, reconnect, and daemon restart behavior did not change.
  • Did not touch decrypt orchestration or high-churn UI state because no behavior work touched those workflows.
  • Verified with cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure.

Phase 17 completed on 2026-04-27:

  • Reviewed command-style RPC actions for send, address creation, mining toggles, ban operations, and key import/export; kept them App-owned because no complete behavior change created a focused extraction boundary with tests.
  • Reviewed lifecycle, reconnect, shutdown, and daemon restart ownership; no new lifecycle tests were added because callback ownership, cancellation, shutdown, reconnect, and daemon restart behavior did not change.
  • Reviewed decrypt and high-churn UI-state seams by scope; no changes were made because Phase 17 did not directly touch those workflows for behavior.
  • Kept refresh and price boundaries stable because the current service collectors/result helpers already cover the testable seams introduced in prior phases, and no new smaller behavior seam appeared.
  • Verified the stable-boundary decision with cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure.

Phase 18 completed on 2026-04-27:

  • Re-reviewed the remaining command-style RPC actions (setgenerate, setban, new address creation, key import, send) and kept them App-owned because Phase 18 did not include a real behavior change that supplied a focused extraction boundary and tests.
  • Re-reviewed reconnect, shutdown, daemon restart, and callback ownership; no lifecycle tests were added because those ownership and cancellation behaviors did not change.
  • Re-reviewed decrypt and high-churn UI-state seams by scope; no code changes were made because those workflows were not directly touched for behavior.
  • Preserved the stable refresh and price boundaries introduced in prior phases because no new behavior made a smaller tested seam useful.
  • Verified the feature-driven cleanup decision with cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure.

Phase 19 completed on 2026-04-27:

  • Treated the audit as feature-scoped maintenance rather than an extraction-only phase and made no source changes because no real behavior change created a new focused test seam.
  • Re-reviewed command-style RPC actions (setgenerate, setban, new address creation, key import, send) and kept them App-owned because their current boundaries are command-specific and tied to immediate UI/app-state updates.
  • Re-reviewed lifecycle, reconnect, shutdown, daemon restart, decrypt, and high-churn UI-state seams; no tests or migrations were added because those behaviors did not change directly.
  • Preserved the stable refresh and price service boundaries because the existing collectors/result helpers already cover the testable seams created by earlier work.
  • Verified the maintenance decision with cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure.

Phase 20 completed on 2026-04-27:

  • Moved the cleanup audit into maintenance mode and avoided additional extraction-only work because no concrete behavior change created a smaller tested seam.
  • Kept command-style RPC actions App-owned until a complete command workflow change supplies focused tests.
  • Added no lifecycle, decrypt, or UI-state tests because callback ownership, cancellation, daemon restart, decrypt flow, and UI state behavior did not change directly.
  • Preserved stable refresh and price service boundaries because no new behavior exposed a better tested boundary.
  • Verified maintenance mode with cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure.

Phase 21 completed on 2026-04-27:

  • Sustained maintenance mode and avoided extraction-only work because no concrete feature change created a focused tested seam.
  • Kept command-style RPC actions App-owned until a complete command workflow change supplies focused tests.
  • Added no lifecycle, decrypt, or UI-state coverage because callback ownership, cancellation, daemon restart, decrypt flow, and UI state behavior did not change directly.
  • Preserved stable refresh and price service boundaries because no new behavior exposed a smaller tested seam.
  • Verified sustained maintenance mode with cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure.

Phase 22 completed on 2026-04-27:

  • Continued sustained maintenance mode and avoided extraction-only work because no concrete feature change created a focused tested seam.
  • Kept command-style RPC actions App-owned until a complete command workflow change supplies focused tests.
  • Added no lifecycle, decrypt, or UI-state coverage because callback ownership, cancellation, daemon restart, decrypt flow, and UI state behavior did not change directly.
  • Preserved stable refresh and price service boundaries because no new behavior exposed a smaller tested seam.
  • Verified the maintenance checkpoint with cd build && cmake .. && make -j$(nproc) && ctest --output-on-failure.

Remaining Phase 23 follow-ups:

  • Continue sustained maintenance mode and avoid extraction-only phases unless concrete feature work creates a focused tested seam.
  • Keep command-style RPC actions App-owned until a complete command workflow change supplies focused tests.
  • Add lifecycle, decrypt, or UI-state coverage only when directly changing callback ownership, cancellation, daemon restart, decrypt flow, or UI state behavior.
  • Preserve stable refresh and price service boundaries unless new behavior exposes a smaller tested seam.

High-Priority Findings

1. App Is Carrying Too Many Responsibilities

Evidence:

  • src/app.h, src/app.cpp, src/app_network.cpp, src/app_security.cpp, and src/app_wizard.cpp form a broad partial-class style application layer.
  • src/app.cpp is 3,141 lines, with lifecycle, rendering, daemon/RPC startup, shutdown, modal rendering, and restart flows.
  • src/app_network.cpp is 1,926 lines and contains many refresh, polling, send, mining, market, peer, and address metadata paths.
  • src/app_security.cpp is 1,819 lines and combines wallet encryption, PIN handling, secure vault, import/export, and restart flows.

Why it matters:

  • Changes in one workflow can easily affect unrelated lifecycle or UI behavior.
  • It is hard to unit-test isolated flows without instantiating most of the app.
  • Shutdown and async callback ownership are difficult to reason about because many subsystems capture or mutate App state directly.

Recommended cleanup:

  • Extract a DaemonController for embedded/external daemon ownership, restart, port ownership, and shutdown sequencing.
  • Extract a WalletSecurityController for PIN, encryption, secure vault, and key import/export flow state.
  • Extract a NetworkRefreshService or WalletRefreshScheduler for balance/address/transaction/mining/market polling.
  • Keep App focused on initialization, top-level navigation, frame dispatch, and service composition.

Suggested first slice:

  • Completed in Phase 3: add the first DaemonController boundary around EmbeddedDaemon ownership, settings sync, and start/stop behavior. Follow-up work should move more restart/shutdown sequencing and external-daemon policy into the controller.

2. Background Thread Lifetime Is Not Centrally Owned

Evidence:

  • Detached threads appear in src/app.cpp, src/app_security.cpp, src/app_wizard.cpp, src/util/bootstrap.cpp, and src/main.cpp.
  • Several lambdas capture this and then call back after waits or daemon operations.
  • Managed thread members also exist (shutdown_thread_, daemon_restart_thread_, wizard_stop_thread_, monitor threads), but ownership patterns vary by subsystem.

Why it matters:

  • Detached work can outlive the App object or UI state it touches.
  • Shutdown order is harder to guarantee.
  • Cancellation is inconsistent, especially across daemon restart, wallet import, bootstrap download, and PIN unlock flows.

Recommended cleanup:

  • Introduce an AsyncTaskManager owned by App, with named tasks, cancellation flags/tokens, and join-on-shutdown behavior.
  • Replace new detached threads with submitted tasks and explicit cancellation.
  • Require background callbacks to hop through the existing UI callback queue before touching UI state.
  • Add shutdown assertions or logging for tasks that fail to stop promptly.

Suggested first slice:

  • Completed in Phase 3: convert App-owned daemon restart/import/wizard background work to AsyncTaskManager and keep Bootstrap's worker joinable. Follow-up work should move remaining subsystem monitor/watchdog policies behind explicit owners where useful.

3. RPC Credential Generation And Remote RPC Handling Need Hardening

Evidence:

  • Before Phase 1, src/rpc/connection.cpp generated default rpcuser/rpcpassword with std::srand(std::time(nullptr)) and std::rand().
  • Phase 1 replaced that path with libsodium-backed random generation.
  • Before Phase 3, src/rpc/rpc_client.cpp always constructed an http://host:port/ URL.
  • Before Phase 3, .cookie auth handling appeared in src/rpc/connection.cpp and also as a 401 fallback in src/app_network.cpp.

Why it matters:

  • std::rand() is not appropriate for credentials.
  • HTTP is acceptable for strict localhost daemon RPC, but remote hosts can expose credentials unless the UI explicitly warns or supports TLS.
  • Duplicate auth fallback paths make it harder to reason about precedence and failures.

Recommended cleanup:

  • Completed in Phase 1: generate RPC credentials with libsodium-backed random generation.
  • Completed in Phase 3: centralize .cookie retry construction in Connection after config-password failure.
  • Completed in Phase 3: add a use_tls transport flag to ConnectionConfig and pass it through RPCClient connections.
  • Completed in Phase 3: warn at runtime and in Settings when a non-localhost RPC host uses plaintext HTTP.

Suggested first slice:

  • Completed in Phase 1 and Phase 3: replace std::rand() credential generation, centralize cookie fallback, and add remote plaintext/TLS handling.

4. Shell Launching Uses system() In UI And Platform Paths

Evidence:

  • Before Phase 1, src/util/platform.cpp used system() for URL/folder opening and directory creation on some platforms.
  • Before Phase 1, src/ui/windows/transactions_tab.cpp, src/ui/windows/transaction_details_dialog.cpp, and src/ui/windows/about_dialog.cpp called system() directly.
  • Phase 1 routed these UI calls through util::Platform::openUrl() and removed shell-built launcher strings from Platform on macOS/Linux.

Why it matters:

  • Shell invocation increases quoting and command-injection risk.
  • Direct calls bypass the existing platform abstraction.
  • Behavior varies by shell and desktop environment.

Recommended cleanup:

  • Route all URL/file/folder launch behavior through Platform helpers.
  • Replace shell strings with platform APIs where possible: ShellExecute on Windows, open or xdg-open via fork/exec or posix_spawn on Unix-like systems.
  • Validate URL schemes before opening external links.
  • Remove direct system() calls from UI windows.

Suggested first slice:

  • Completed in Phase 1: update About, transaction details, and transaction tab actions to use Platform::openUrl() and safer platform launcher behavior.

Medium-Priority Findings

5. Silent And Broad Exception Handling Is Common

Evidence:

  • Empty or near-empty catch (...) blocks appear in src/app.cpp, src/app_network.cpp, src/app_security.cpp, src/app_wizard.cpp, src/rpc/rpc_worker.cpp, src/rpc/rpc_client.cpp, src/main.cpp, and several UI files.
  • Repeated try { rpc_->call("stop"); } catch (...) {} patterns appear in daemon stop flows.

Why it matters:

  • Failures vanish from logs and UI, making support and diagnosis difficult.
  • Some swallowed failures are acceptable during best-effort shutdown, but the code does not consistently explain that intent.
  • Repeated patterns invite copy-paste drift.

Recommended cleanup:

  • Add small helpers such as stopDaemonSafely(context) and parseFloatOrDefault(value, fallback, context).
  • Log best-effort failures with enough context to debug without spamming normal shutdown.
  • Prefer typed catches where the thrown type is known.
  • For RPC work, propagate categorized failures to the notification system when they affect user-visible state.

Suggested first slice:

  • Completed in Phase 1: replace repeated daemon stop catch blocks with a single helper that logs at debug/verbose level.

6. UI Dialogs Have Mixed Modal Systems And Repeated Layout Literals

Evidence:

  • Most dialogs use Material::BeginOverlayDialog(), but src/ui/windows/address_book_dialog.cpp still uses raw ImGui::BeginPopupModal() for Add/Edit address modals.
  • Dialog widths such as 480.0f, 500.0f, 620.0f, and 660.0f appear across UI files.
  • Button widths and padding literals such as 140.0f, 160.0f, and 24.0f are repeated.
  • Icon font sizes are hardcoded in src/ui/material/typography.cpp as 14, 18, 24, and 40 px families.

Why it matters:

  • The schema system already solves this class of problem, but not all dimensions use it yet.
  • Modal behavior diverges when some dialogs bypass overlay scrim/input blocking helpers.
  • Theme and density changes require hunting through many files.

Recommended cleanup:

  • Add schema-backed dialog tokens under res/themes/ui.toml, such as globals.dialog.width-default, width-lg, min-width, and max-height-ratio.
  • Add schema-backed action widths under a shared button or globals.button-sizes section.
  • Move icon size selection into schema or a single icon token helper.
  • Migrate address book Add/Edit dialogs to BeginOverlayDialog() and extract one shared form renderer.

Suggested first slice:

  • Completed in Phase 2: refactor the address book Add/Edit dialog because it had duplicated modal form code and was still outside the overlay helper path.

7. UI State Uses Many Static Locals And File-Scoped Globals

Evidence:

  • src/ui/pages/settings_page.cpp has many sp_* static variables for page state.
  • src/app_wizard.cpp uses local statics for first-run appearance and daemon checks.
  • Theme/color/effect modules use static state for current theme and effect initialization.

Why it matters:

  • Static state is hard to reset in tests.
  • It makes multi-window or future session-reset behavior harder.
  • Initialization order and stale state can become subtle bugs after hot reloads or account switching.

Recommended cleanup:

  • Introduce SettingsPageState and WizardState structures owned by App or a UI state container.
  • Keep global/static state only for immutable constants or explicit process-wide singletons.
  • Add reset/apply methods for state structures to support theme reload and test setup.

Suggested first slice:

  • Move settings-page statics into a single struct without changing behavior. This is mostly mechanical and improves readability.

8. Build And Generated Resource Lifecycle Is Split Across Source, Build, And Scripts

Evidence:

  • src/config/version.h is generated from src/config/version.h.in but the generated file is present in source control.
  • CMake generates src/embedded/lang_*.h from res/lang/*.json with xxd.
  • setup.sh does not appear to check for xxd.
  • find_package(Python3 QUIET COMPONENTS Interpreter) is used for theme expansion; CMake falls back when Python is unavailable.
  • Font OBJECT_DEPENDS are listed manually in CMakeLists.txt.
  • src/resources/embedded_resources.cpp conditionally includes embedded_data.h, which is produced by release/build scripts for some platforms.

Why it matters:

  • Generated files under src/ make the source tree noisier and can cause stale diffs.
  • Missing tools fail late or silently reduce build output quality.
  • Resource embedding behavior differs by platform without one obvious matrix.

Recommended cleanup:

  • Generate language headers under build/generated/embedded/ and include that directory instead of writing under src/embedded/.
  • Decide whether src/config/version.h should be generated-only or committed, then enforce that decision with .gitignore and documentation.
  • Add explicit xxd checks to setup.sh and CMake configure output.
  • Make the Python theme-expansion fallback a warning, not a quiet behavior change.
  • Replace manual font OBJECT_DEPENDS with a generated list or CMake glob configured for the font directory.
  • Document the embedded resource matrix by platform and build type.

Suggested first slice:

  • Completed in Phase 1: add xxd detection and make missing Python theme expansion noisy.

9. Polling And Refresh Flow Would Benefit From A Scheduler Boundary

Evidence:

  • src/app_network.cpp coordinates many refresh paths and timings.
  • src/app.cpp also contains timed daemon/mining/rescan update logic.
  • src/rpc/rpc_worker.cpp already provides a worker queue, but refresh orchestration is still spread through app-level methods.

Why it matters:

  • Polling order, throttle behavior, and cancellation are difficult to audit.
  • Adding one more refresh path can accidentally affect responsiveness or RPC queue pressure.
  • Console fast-lane RPC is a good pattern, but regular refresh batches still need a clear scheduler owner.

Recommended cleanup:

  • Add a PollingScheduler or RefreshScheduler that owns timer intervals, dependencies, cancellation, and rate limiting.
  • Model refreshes as named jobs with last-run time, minimum interval, and in-flight state.
  • Keep RPCWorker as execution plumbing while moving scheduling decisions out of App.

Suggested first slice:

  • Completed in Phase 4: added RefreshScheduler for refresh timers, page intervals, transaction age throttling, OPID cadence, price refresh, and fast tick behavior.
  • Completed in Phase 7 and Phase 8: NetworkRefreshService now owns enqueue/callback wrapping for core, address, transaction, mining, peer, encryption, price, and connection-init jobs, including queue pressure and stale callback suppression.

10. Large UI Tabs Need Feature-Sliced Components

Evidence:

  • src/ui/windows/balance_tab.cpp is 3,562 lines.
  • src/ui/windows/mining_tab.cpp is 2,842 lines.
  • src/ui/windows/console_tab.cpp is 1,861 lines.
  • Several of these files combine state management, drawing, filtering, formatting, and modal coordination.

Why it matters:

  • UI changes become difficult to review because unrelated drawing and state logic share one file.
  • Reusable patterns stay local and get reimplemented elsewhere.
  • Testing smaller formatting/state helpers is harder when they are buried in render functions.

Recommended cleanup:

  • Split large tabs by feature area: panels, table/list renderers, local state structs, formatting helpers, and action handlers.
  • Keep ImGui draw calls close to the view but move data preparation and command decisions into smaller helpers.
  • Avoid introducing inheritance-heavy UI abstractions; simple namespaces and structs should be enough.

Suggested first slice:

  • Completed in Phase 4 through Phase 8: extracted low-risk balance helpers, balance address-list/recent-transaction models, mining benchmark/pool helpers, console command/input/output models, and additional address-row/mining/console execution helpers. Remaining work should continue moving draw-heavy action glue only when it lowers review/test cost.

Low-Priority And Quick-Win Findings

11. Test Infrastructure Is Missing Or Not Obvious

Before Phase 4, no test target was visible in the workspace snapshot. Phase 4 added a small CTest executable covering connection config parsing, .cookie fallback priority, plaintext/TLS detection, wallet state filtering, amount formatting, payment URI parsing, and scheduler behavior.

Recommended cleanup:

  • Completed in Phase 4: add a small CTest target with a lightweight assertion harness for pure utility/RPC/scheduler helpers.
  • Consider migrating to Catch2 or GoogleTest if the test suite grows beyond a few focused files.
  • Add mocks for RPCClient and daemon process state once service boundaries exist.

12. Translation Key Usage Is Inconsistent

TR() and TrId() coexist in places such as Settings. This is workable, but it increases typo risk and makes missing translations harder to audit.

Recommended cleanup:

  • Define constants for high-traffic translation keys.
  • Prefer one translation call style inside each module.
  • Add a script that compares referenced keys against res/lang/*.json.

13. Project Icon Handling Needs One Registry

Material icons are referenced directly in many files, while the pickaxe icon uses a special one-glyph font path. The special case is now documented in docs/codebase-overview.md, but a code-level registry would make future icon work cleaner.

Recommended cleanup:

  • Completed in Phase 2: add src/ui/material/project_icons.h for app-specific icon names.
  • Completed in Phase 2: encapsulate pickaxe rendering behind one helper, so feature code does not need to know which font supplies it.

14. Release/Portability Docs Can Be Closer To The Real Build

build.sh contains much more platform-specific behavior than the README currently explains. AppImage dependency bundling, macOS universal build defaults, Windows MinGW assumptions, embedded daemon/resource behavior, and third-party license sync deserve their own build notes.

Recommended cleanup:

  • Add a focused docs/build-and-release.md.
  • Document development build vs release packaging per platform.
  • Add a checklist for third-party license updates, including libs/incbin.h.

Suggested Roadmap

Phase 1: Small High-Value Fixes

  • Replace weak RPC credential generation in src/rpc/connection.cpp.
  • Route direct URL/open actions away from raw system() calls.
  • Add xxd checks and noisy Python theme-expansion warnings.
  • Confirm missing third-party license entry for INCBIN is not needed because THIRD_PARTY_LICENSES already includes it.
  • Extract repeated daemon stop try/catch blocks into a helper with contextual logging.

Phase 2: UI Consistency Pass

  • Move common dialog sizes and action button widths into res/themes/ui.toml or existing layout helpers.
  • Migrate address book Add/Edit dialogs to BeginOverlayDialog().
  • Add a shared dialog content/footer helper.
  • Add an icon registry and hide the pickaxe font special case behind it.

Phase 3: Ownership And Runtime Boundaries

  • Introduce AsyncTaskManager for background work.
  • Extract DaemonController from App.
  • Consolidate RPC auth and connection fallback behavior.
  • Add warning/TLS configuration for non-localhost RPC.

Phase 4: Larger Maintainability Work

  • Extract NetworkRefreshService or RefreshScheduler.
  • Split balance_tab.cpp, mining_tab.cpp, and console_tab.cpp into smaller feature files where safe.
  • Move generated language headers into build/generated.
  • Add focused unit tests for connection config, URI parsing, amount formatting, wallet address spendability filtering, and scheduler behavior.

Phase 5: Service Boundaries And Test Depth

  • Move wallet encryption, PIN, secure vault, key import/export, and restart flow state toward a WalletSecurityController.
  • Move more daemon restart/shutdown policy and external-daemon behavior from App into DaemonController.
  • Evolve RefreshScheduler into a fuller network refresh service by modeling named refresh jobs, in-flight state, and RPC queue pressure explicitly.
  • Split larger UI feature renderers after the low-risk helper extractions, starting with balance address/recent transaction panels and mining benchmark/pool panels.
  • Move settings/wizard static UI state into explicit state structs.
  • Add deeper tests around daemon/RPC service boundaries, scheduler edge cases, wallet security state transitions, and generated resource behavior.
  • Decide and document whether src/config/version.h is committed source or generated-only output.

Phase 6: Deeper Service Extraction And Integration Tests

  • Move wallet-security RPC orchestration, secure-vault handoffs, key import/export state, and daemon restart sequencing behind service interfaces that can be exercised without constructing the full UI App.
  • Move daemon restart/rescan/bootstrap shutdown sequencing and external-daemon ownership policy into DaemonController with mockable collaborators.
  • Promote NetworkRefreshService from refresh policy/guard owner to dispatcher owner for named RPC jobs, queue-pressure telemetry, and stale-callback handling.
  • Split large UI files into stable feature modules, especially balance address-list/recent-transaction rendering, mining benchmark/pool rendering, and console output/input rendering.
  • Replace remaining compatibility aliases around extracted UI state with direct state-struct use.
  • Add integration tests with mock RPC/daemon collaborators for wallet security, daemon lifecycle, and refresh dispatch edges.

Phase 7: Service-Owned Execution And UI Module Finish

  • Move async daemon lifecycle execution, restart delays, rescan flags, blockchain-data deletion, bootstrap stops, and external-daemon ownership enforcement deeper into DaemonController with mock daemon/filesystem/task collaborators.
  • Extract wallet decrypt/export/import dialog workflow state and secure-vault restart handoffs into a wallet-security workflow service that can be tested without constructing App.
  • Promote NetworkRefreshService from dispatch-ticket/telemetry owner to the enqueue/callback wrapper for named RPC refresh jobs, including stale callback suppression and queue-pressure reporting at the service boundary.
  • Continue splitting balance address-list rendering, mining benchmark controls, and console input/history/autocomplete into stable modules with tests.
  • Remove temporary App compatibility bridges around daemon ownership and any remaining extracted UI state once direct service/state use is complete.

Phase 8: Workflow Executors And Large-View Reduction

  • Move wallet decrypt import/restart RPC orchestration out of App's nested lambdas into a workflow executor with mock RPC/daemon/vault/file collaborators.
  • Split concrete daemon lifecycle runtime responsibilities into smaller adapters only where it lowers App coupling without adding indirection for its own sake.
  • Complete named refresh enqueue coverage for price/market refresh and connect-time one-off polling.
  • Continue feature-slicing large ImGui views by extracting address-row, mining benchmark/pool, and console command execution helper/model seams.
  • Add focused tests around the new workflow, refresh, daemon lifecycle, and UI helper seams.

Phase 9: Typed Refresh Results And State Cleanup

  • Evolve refresh jobs toward typed result models and service-owned result application contracts for refresh paths that still require complex app-local snapshots.
  • Review remaining static UI state and compatibility glue, then move it to explicit state structs or document what is intentionally process-wide.
  • Reviewed draw-heavy ImGui bodies and deferred additional slicing to Phase 10 unless adjacent to a required behavior-preserving change.
  • Add focused tests around refresh result application, reconnect/stale-callback edges, and any state-struct migrations.

Phase 10: Transaction Refresh And State Follow-Through

  • Evaluate typed address/transaction refresh result models and cache-update applicators for the refresh paths that still build large App-local snapshots.
  • Reduce decrypt workflow orchestration in App further only where executor-owned operations can preserve progress reporting and cancellation behavior; Phase 10 evaluated the remaining chain and deferred extra movement because the current App handoffs are the progress/cancellation boundary.
  • Convert remaining high-churn tab/dialog statics to explicit state structs as those views are touched, following docs/ui-static-state.md; Phase 10 did not touch those views for behavior changes.
  • Add stronger reconnect/stale-callback, remote/TLS RPC, and shutdown-cancellation tests.

Phase 11: Workflow And UI State Hardening

  • Revisit decrypt restart/import orchestration for one complete executor-owned step if progress and cancellation can stay explicit.
  • Continue high-churn tab/dialog state-struct migrations only when those views are touched for real behavior changes.
  • Move more address/transaction RPC snapshot construction into testable helpers if it reduces App coupling without duplicating daemon call ordering.
  • Keep broadening refresh/RPC lifecycle tests around realistic worker callback ordering and reconnect races.

Phase 12: Integration Fixtures And Remaining Orchestration Edges

  • Add shared mock RPC fixtures only if they let tests assert daemon call ordering directly instead of duplicating production sequencing in test setup.
  • Move more refresh body construction out of App only where call ordering remains obvious and covered by tests.
  • Revisit decrypt restart/import orchestration only if a complete progress/cancellation step can be executor-owned end to end.
  • Continue UI state-struct migrations opportunistically when behavior work touches those tabs/dialogs.

Phase 13: Ordered Collectors And Lifecycle Coverage

  • Extend ordered mock RPC coverage to additional refresh collectors only when those collectors move beyond App-owned call bodies.
  • Keep refresh call ordering explicit in tests whenever daemon call sequencing moves into a service.
  • Broaden reconnect/shutdown lifecycle coverage around async refresh cancellation only when those flows are touched.
  • Continue decrypt and UI-state hardening opportunistically at complete, behavior-preserving seams.

Phase 14: Mining And Connection Edge Review

  • Move mining refresh collection only if the fast/slow polling cadence remains explicit and covered by ordered mock RPC tests.
  • Move connection-init prefetch only if lock-screen timing and getinfo/getwalletinfo ordering remain obvious and tested.
  • Add lifecycle coverage only when changing worker callback, shutdown, reconnect, or daemon restart ownership.
  • Continue decrypt and UI-state work only when a complete behavior-preserving step is touched.

Phase 15: Remaining App-Owned Edges

  • Review warmup status polling only if the UI status/progress handoff can stay explicit and covered.
  • Review price HTTP fetching only if network parsing, callback behavior, and failure handling become easier to test without hiding libcurl details.
  • Leave command-style RPC actions App-owned unless a complete behavior change creates a focused extraction boundary.
  • Add lifecycle, decrypt, or UI-state tests only when touching those behaviors directly.

Phase 16: Price And Command Boundary Review

  • Revisit price HTTP fetching only if libcurl status/error behavior can be represented as a focused testable boundary.
  • Keep send, address creation, mining toggle, ban, and import/export command RPC actions App-owned unless a complete behavior change justifies extraction.
  • Add reconnect/shutdown lifecycle coverage only when callback ownership or cancellation behavior changes.
  • Continue decrypt and UI-state work only when directly touching those workflows for behavior.

Phase 17: Command And Lifecycle Guardrails

  • Review command-style RPC actions only when a complete behavior change creates a focused extraction boundary.
  • Add reconnect/shutdown lifecycle coverage only when callback ownership, cancellation, shutdown, reconnect, or daemon restart behavior changes.
  • Continue decrypt and UI-state work only when directly touching those workflows for behavior.
  • Keep existing refresh and price boundaries stable unless new behavior creates a smaller tested seam.

Phase 18: Feature-Driven Cleanup Only

  • Move command-style RPC actions only when a real behavior change supplies a focused extraction boundary and tests.
  • Add lifecycle tests only when callback ownership, cancellation, shutdown, reconnect, or daemon restart behavior changes.
  • Continue decrypt and UI-state cleanup only when those workflows are touched for behavior.
  • Preserve stable refresh and price boundaries unless new behavior makes a smaller tested seam useful.

Phase 19: Feature-Scoped Maintenance

  • Prefer feature-scoped maintenance over additional extraction-only phases.
  • Move command-style RPC actions only with a complete behavior change and focused tests.
  • Add lifecycle/decrypt/UI-state coverage only when those behaviors change directly.
  • Preserve existing refresh and price service boundaries unless new behavior exposes a smaller tested seam.

Phase 20: Maintenance Mode

  • Convert the cleanup roadmap from extraction phases to maintenance-mode guidance.
  • Keep command-style RPC actions App-owned unless a complete feature change supplies focused tests.
  • Add lifecycle/decrypt/UI-state coverage only when those behaviors change directly.
  • Preserve stable refresh and price service boundaries unless new behavior exposes a smaller tested seam.

Phase 21: Sustained Maintenance Mode

  • Continue maintenance-mode cleanup only when concrete feature work creates a focused tested seam.
  • Keep command-style RPC actions App-owned unless a complete command workflow change supplies focused tests.
  • Add lifecycle/decrypt/UI-state coverage only when those behaviors change directly.
  • Preserve stable refresh and price service boundaries unless new behavior exposes a smaller tested seam.

Phase 22: Maintenance Checkpoint

  • Continue sustained maintenance mode and avoid extraction-only phases unless concrete feature work creates a focused tested seam.
  • Keep command-style RPC actions App-owned unless a complete command workflow change supplies focused tests.
  • Add lifecycle/decrypt/UI-state coverage only when those behaviors change directly.
  • Preserve stable refresh and price service boundaries unless new behavior exposes a smaller tested seam.

Phase 23: Maintenance Checkpoint

  • Continue sustained maintenance mode and avoid extraction-only phases unless concrete feature work creates a focused tested seam.
  • Keep command-style RPC actions App-owned unless a complete command workflow change supplies focused tests.
  • Add lifecycle/decrypt/UI-state coverage only when those behaviors change directly.
  • Preserve stable refresh and price service boundaries unless new behavior exposes a smaller tested seam.

Residual Risk

Several findings are maintainability risks rather than confirmed user-facing bugs. Remote/plaintext RPC handling now warns and has a TLS config path, and Phase 10 added focused parsing tests, but real daemon TLS setups still deserve manual validation.

The safest next step is Phase 23: continue sustained maintenance mode, preserving tested boundaries and tying future cleanup to concrete feature work.