Skip to content

feat(profiles): env-gated discovery + event-driven dropdown health dots#48

Merged
8144225309 merged 1 commit into
mainfrom
feat/profile-discovery-health-160
May 28, 2026
Merged

feat(profiles): env-gated discovery + event-driven dropdown health dots#48
8144225309 merged 1 commit into
mainfrom
feat/profile-discovery-health-160

Conversation

@8144225309

Copy link
Copy Markdown
Owner

Background

The post-#46 orchestrator audit surfaced three related issues with the wallet's profile system:

  1. The dropdown's per-profile red/green health dots are almost always stale — the existing probe loop only fires while nodeInfo.error is truthy (i.e., when you're already broken).
  2. Auto-discovery on auth pollutes the cwd-relative node-profiles.json by adding every local CLN node it can find, breaking isolation for the demo (8 profiles instead of the intended 2 after a single restart).
  3. checkAllHealth probes all profiles in parallel — fine at small counts, but a connection burst with many profiles.

This PR addresses all three, opt-in by env var where appropriate.

Changes

A — Event-driven dropdown health dots

No continuous polling. Per the design discussion, dots refresh only on:

  • Profile switchNodePicker.handleSwitchNode fires NodesService.healthCheck() in the post-switch background refresh.
  • Rescan for Nodes buttonNodePicker.handleDiscover fires it after the rescan completes.

The slice's setProfileHealth now merges by profileId instead of replacing the whole array. Partial probes (e.g. one capped by WALLET_HEALTH_PROBE_MAX) don't clear previously-known colors, and a profile that went red stays red until the next good probe answers from that profileId — the sticky-red behavior you asked for. Entries are never auto-removed.

B — Env-gated discovery + standardized profile-file path

Two new env vars:

Var Default Effect
WALLET_AUTODISCOVER (unset / true) Set to false to skip the auth-time local-socket scan entirely — the wallet uses only the profiles in the file you handed it
WALLET_PROFILES_FILE ./node-profiles.json (cwd-relative, backward compat) Absolute path override; lets isolated/test runs point at a deterministic fixture instead of relying on cwd

Production: both unset → current friendly auto-discovery, current file location.
Isolated demo / orchestrator: both set → frozen, deterministic profile list every restart.

D — Sequential, capped probe in checkAllHealth

Promise.allSettled(profiles.map(probe)) → sequential for…await loop with two new env vars:

Var Default Effect
WALLET_HEALTH_PROBE_DELAY_MS 200 Delay between probes
WALLET_HEALTH_PROBE_MAX 64 Hard cap on profiles probed per scan; excess gets a logger.warn

One commando connection at a time. Worst case at 64 profiles, 200ms delay, 5s probe timeout ≈ a sequential scan that takes a few seconds — acceptable since it only fires on switch / explicit refresh.

Validation

  • CI build/eslint/test on this PR.
  • Post-deploy on the demo: set WALLET_AUTODISCOVER=false + WALLET_PROFILES_FILE=/root/ss-walletdemo/node-profiles.json in env.sh, restart, confirm dropdown returns to just the 2 demo profiles. Switch between them and watch the dots flip.
  • Playwright orchestrator pass (sequenced after deploy) to repeat the cross-profile click-through that exposed R8.36: rendezvousSlice reducers + selectors tests #157 and verify the new sticky-red + event-driven dot semantics.

Files changed

File Why
apps/backend/source/service/node-profiles.service.ts WALLET_PROFILES_FILE env override
apps/backend/source/service/node-manager.service.ts WALLET_AUTODISCOVER=false early-out + sequential capped checkAllHealth
apps/frontend/src/components/ui/NodePicker/NodePicker.tsx healthCheck calls in both handlers + setProfileHealth import
apps/frontend/src/store/nodesSlice.tsx merging setProfileHealth reducer

Three changes addressing the post-#46 audit findings on the profile system
(tracked as bug #159 and design ticket #160).

A) Event-driven health dots in the dropdown
- NodePicker.handleSwitchNode fires NodesService.healthCheck() in the
  post-switch background refresh.
- NodePicker.handleDiscover (Rescan for Nodes button) fires it after the
  scan completes.
- nodesSlice.setProfileHealth now MERGES results by profileId instead of
  replacing the whole array. A partial probe -- one capped by
  WALLET_HEALTH_PROBE_MAX, or a switch that probed a subset -- no longer
  clears previously-known dot colors. Sticky-red: a node that went red
  stays red until the next successful probe answers from that profileId.
- No continuous polling added; dots refresh ONLY on profile switch or on
  Rescan button click. Entries are never auto-removed.

B) Env-gated auto-discovery + standardized profile-file path
- node-profiles.service.ts: PROFILES_FILE now defaults to ./node-profiles.json
  (unchanged behavior) but honors WALLET_PROFILES_FILE for an explicit
  absolute-path override. Isolated demos and orchestrator-driven test runs
  set this to a deterministic fixture path instead of relying on cwd.
- node-manager.service.ts: discoverNodes() bails immediately when
  WALLET_AUTODISCOVER=false. Production keeps the friendly auth-time scan;
  isolated/test runs get a frozen profile list.

D) Sequential capped probe in checkAllHealth
- Was Promise.allSettled(profiles.map(probe)) -- all profiles probed at
  once. Now: sequential loop with a configurable delay between probes
  (WALLET_HEALTH_PROBE_DELAY_MS, default 200ms) and a hard cap on
  attempts per scan (WALLET_HEALTH_PROBE_MAX, default 64).
- One commando connection at a time keeps the burst small. Profiles
  beyond the cap are skipped with a logger.warn line.

Together: predictable behavior, gentle probing, deterministic dot state
for the orchestrator + the isolated demo, no impact on production users
who don't set the new env vars.
@8144225309 8144225309 merged commit e629059 into main May 28, 2026
3 checks passed
@8144225309 8144225309 deleted the feat/profile-discovery-health-160 branch May 28, 2026 18:35
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.

1 participant