Skip to content

feat(portfolio): surface degraded/stale chains instead of silent $0#240

Merged
BitHighlander merged 2 commits into
developfrom
feat/portfolio-fault-tolerance
Jun 11, 2026
Merged

feat(portfolio): surface degraded/stale chains instead of silent $0#240
BitHighlander merged 2 commits into
developfrom
feat/portfolio-fault-tolerance

Conversation

@BitHighlander

Copy link
Copy Markdown
Collaborator

Problem

The vault discarded Pioneer's portfolio meta, so a chain that failed its fresh fetch (node down/timeout) or served cache older than 5 min showed $0 or a stale value with no way to distinguish "data unavailable" from "genuinely empty." No retry, no staleness signal.

Pioneer already emits the needed signal — verified live on api.keepkey.info (server release/v1.3.115): POST /portfolio returns meta.{degraded, degradedCount, failures[], staleChains[]}. The vault just threw it away in unwrapPortfolioEntries.

What changed

Backend (src/bun/index.ts)

  • unwrapPortfolioResponse keeps meta per chunk; mergeMetas combines it across chunks.
  • After each getBalances, push degraded + stale chain display-names to the webview via the existing pioneer-error channel — severity: 'warning' for soft faults, 'none' to clear. Degraded = server meta.failures ∪ chains whose pubkeys all landed in a failed chunk.

Schema (src/shared/rpc-schema.ts)

  • Extend pioneer-error with optional severity + degradedChains/staleChains/staleMinutes. Missing severity stays a hard error — back-compatible with existing senders.

Frontend (Dashboard.tsx)

  • Amber soft-fault banner with its own state, separate from the rose hard-error banner (hard error supersedes). "Sync now" + "Dismiss".
  • Window-focus refetch when idle > 5 min.
  • Degraded-chain background retry with 30/60/120/300s backoff; resets on a clean fetch.

i18n — English strings only; other 14 languages fall back via fallbackLng: 'en'.

Scope notes

  • Reuses the existing banner channel rather than adding a parallel portfolio-fault event (one source of truth for "balance data problem").
  • The retry triggers live in the webview, not the Bun process (the handoff placed window listeners in src/bun — Bun has no window).
  • Dropped the handoff's onlyCaips targeted refetch (full refresh is ~5-6s — not worth the added branching) and the rest-pioneer.ts change (that's the external SDK surface, unrelated). Per-row stale indicators deferred.

Test plan

  • tsc --noEmit clean (verified locally — 0 source errors)
  • Degraded banner names the chain when a node is down (e.g. Optimism) instead of silent $0
  • Stale banner shows "last synced N minutes ago" on >5min cache
  • "Sync now" forces a refresh and clears the banner on success
  • Idle >5min, refocus window → background refresh fires
  • Degraded chains auto-retry on backoff ([Dashboard] Retrying degraded chains in console)

The vault discarded Pioneer's portfolio `meta`, so a chain that failed its
fresh fetch (node down/timeout) or served cache >5min old showed $0 / a stale
value with no way to tell "unavailable" from "genuinely empty."

- bun: preserve `meta` per chunk (unwrapPortfolioResponse) and merge across
  chunks (mergeMetas); after each fetch, push degraded + stale chain names to
  the webview through the existing pioneer-error channel (severity warning/none)
- schema: extend pioneer-error with optional severity + degraded/stale fields
  (missing severity stays a hard error — back-compatible)
- Dashboard: amber soft-fault banner (separate state from the hard-error
  banner, which supersedes it); window-focus refetch when idle >5min;
  degraded-chain background retry with 30/60/120/300s backoff
- i18n: English strings only; other languages fall back via fallbackLng

Server meta is live on api.keepkey.info (pioneer release/v1.3.115).
…ften degraded copy

- Banner rendered only the degraded branch; a response with both degraded
  and stale chains silently dropped the stale warning. Render both lines.
- lastFetchAttemptRef seeded at 0 made the first focus event read as
  'idle > 5 min' and force-refresh on startup. Seed with mount time.
- degradedDesc promised 'last known values', but vault-side chunk
  failures can render as zeroes. Soften the copy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.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.

1 participant