Skip to content

feat(ui): show the account-wide picture while paired, and only then - #116

Merged
GeiserX merged 3 commits into
mainfrom
feat/show-the-fleet-picture-when-paired
Aug 6, 2026
Merged

feat(ui): show the account-wide picture while paired, and only then#116
GeiserX merged 3 commits into
mainfrom
feat/show-the-fleet-picture-when-paired

Conversation

@GeiserX

@GeiserX GeiserX commented Aug 6, 2026

Copy link
Copy Markdown
Owner

The other half of CashPilot-Desktop-xjr. #115 hands the server the history this machine collected alone; this shows the result.

What

The heartbeat response has always carried the server's account-level figures for the platforms this machine runs. Nothing read them. The dashboard now renders them as "Across your CashPilot account", below the local numbers rather than replacing them.

Unlinking

Removing the pairing removes the panel and leaves exactly the local numbers as before. That is the behaviour the whole design exists for, and it needs no restore step — the history was copied upstream, never moved, so there is only a view to switch back.

Two independent barriers enforce it: stopUpstream drops the cached figures, and fleetView refuses when no server is configured. Both are tested separately, and the second one exists because a negative control found it untested — deleting the config check left every other test passing, since clearing the cache already covered them. A barrier nothing exercises is a barrier the next person deletes as dead code.

Two rules the rendering is careful about

  • A platform with no reading renders as , never 0.00. No reading usually means a collector that does not exist yet, or credentials nobody entered. Showing zero reports a loss that did not happen — most convincingly to the user whose collector is broken, who is exactly the person who must not be told everything is fine. The platforms with no reading are named underneath, so a total that looks low is explicable rather than mysterious.
  • A platform on more than one machine is marked shared. Earnings are collected per platform from the provider; if two machines run the same service the provider reports one balance and nothing can split it. So the panel is labelled as the account's, not this machine's.

A heartbeat that reports nothing leaves the last figures alone rather than blanking them — one bad minute on the server is not evidence the account earned nothing.

Verification

  • go build, go vet, gofmt clean; go test -race ./... green.
  • Frontend typechecked with real tsc 5.9.3 in Docker (npx tsc silently installs a bogus tsc@2.0.4 that checks nothing and exits 0). Proven by control: injecting const _bad: number = fleet.serverUrl gives TS2322 and exit 2, so the checker is genuinely running. vite build succeeds.
  • Negative controls, each failing the test that claims to catch it: not clearing the cache on unlink, dropping the not-paired guard, and treating a silent heartbeat as "blank the figures".
  • Round-trip test that fleet serialises as null (not omitted, not {}) when there is nothing to show — the frontend branches on it.

Known gap, filed rather than hidden

frontend/ has no test runner at all, so the null-vs-zero rule is proven up to the JSON boundary and no further. Filed as its own bead with the concrete first step (main.ts is a single 1700-line file with import-time side effects, so nothing in it can be imported by a harness today) and an explicit warning not to write tests that assert on the source text.

Summary by CodeRabbit

  • New Features

    • Paired devices now display account-wide earnings alongside local earnings.
    • Shared platforms are clearly labeled, with unavailable readings shown as .
    • Dashboard reports include server timestamps and indicate platforms excluded from totals.
    • Unlinked devices continue to show only local earnings.
  • Bug Fixes

    • Preserved previously reported fleet data when new reports are unavailable.
    • Cleared account-wide data after unlinking.

The other half of CashPilot-Desktop-xjr. Pairing already hands the server
the history this machine collected alone; this shows the result.

The heartbeat response has always carried the server's account-level
figures for the platforms this machine runs. Nothing read them. The
dashboard now renders them as 'Across your CashPilot account', below the
local numbers rather than replacing them.

Unlinking removes the panel and leaves exactly the local numbers as
before. That is the behaviour the whole design exists for, and it needs
no restore step: the history was COPIED upstream, never moved, so there
is only a view to switch back. Two independent barriers enforce it --
stopUpstream drops the cached figures, and fleetView refuses when no
server is configured.

Two rules the rendering is careful about:

* A platform with no reading renders as an em dash, NEVER 0.00. No
  reading usually means a collector that does not exist yet, or
  credentials nobody entered. Showing zero reports a loss that did not
  happen, and it does so most convincingly to the user whose collector
  is broken -- exactly the person who must not be told all is well. The
  platforms with no reading are named underneath, so a total that looks
  low is explicable rather than mysterious.
* A platform on more than one machine is marked shared. Earnings are
  collected per PLATFORM from the provider; if two machines run the same
  service the provider reports one balance and nothing can split it. The
  panel is therefore labelled as the account's, not this machine's.

A heartbeat that reports nothing leaves the last figures alone rather
than blanking them: one bad minute on the server is not evidence the
account earned nothing.

Refs: CashPilot-Desktop-xjr
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@GeiserX, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 42 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a8e9df41-1e6f-47af-bab3-08e082d9e849

📥 Commits

Reviewing files that changed from the base of the PR and between b7888c0 and f68d9fc.

📒 Files selected for processing (2)
  • upstream_client.go
  • upstream_fleetview_test.go
📝 Walkthrough

Walkthrough

The application now parses fleet earnings from heartbeat responses, caches valid reports, exposes them through AppState, and renders paired-server earnings in the dashboard. The UI shows shared platforms, unknown readings, missing platforms, totals, and report freshness.

Changes

Fleet earnings

Layer / File(s) Summary
Earnings response contract
internal/upstream/upstream.go, internal/upstream/earnings_test.go
Adds fleet earnings response types and parsing for absent, malformed, unknown, and explicit zero values.
Upstream fleet state
upstream_client.go, upstream_fleetview_test.go
Caches valid heartbeat earnings, clears cached data when pairing stops, preserves data across empty heartbeats, and maps results to FleetView.
AppState and dashboard rendering
app.go, frontend/src/wails.d.ts, frontend/src/main.ts, frontend/src/style.css, CHANGELOG.md
Exposes nullable fleet data through AppState and renders fleet totals, platform values, shared status, unknown readings, missing platforms, and report freshness.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main UI change: showing account-wide earnings only while the app is paired.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/show-the-fleet-picture-when-paired

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.

@codecov

codecov Bot commented Aug 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.37209% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.45%. Comparing base (0369f13) to head (f68d9fc).

Files with missing lines Patch % Lines
upstream_client.go 86.11% 2 Missing and 3 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #116      +/-   ##
==========================================
+ Coverage   74.24%   74.45%   +0.20%     
==========================================
  Files          18       18              
  Lines        3747     3785      +38     
==========================================
+ Hits         2782     2818      +36     
+ Misses        762      761       -1     
- Partials      203      206       +3     
Files with missing lines Coverage Δ
internal/upstream/upstream.go 84.61% <100.00%> (+2.39%) ⬆️
upstream_client.go 70.62% <86.11%> (+4.86%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…ns it

They lived in package main, exercised the function perfectly, and counted
for nothing: go test measures coverage per-package, so a call from
another package left internal/upstream reading 0% for ParseEarnings.
codecov/patch failed on it, which is the gate working.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@upstream_client.go`:
- Around line 136-142: Update stopUpstream so it first cancels the old upstream
loop and waits for done, then acquires a.upstream.mu before clearing
fleetEarnings and fleetEarningsAt; preserve the existing shutdown flow while
ensuring sendUpstream cannot repopulate the cache afterward. Add an interleaving
test covering a response completing during unlinking and verify the cache
remains cleared.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 80138b13-089d-425b-b362-10bc1513c8e0

📥 Commits

Reviewing files that changed from the base of the PR and between 0369f13 and b7888c0.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • app.go
  • frontend/src/main.ts
  • frontend/src/style.css
  • frontend/src/wails.d.ts
  • internal/upstream/earnings_test.go
  • internal/upstream/upstream.go
  • upstream_client.go
  • upstream_fleetview_test.go

Comment thread upstream_client.go
stopUpstream cleared the cached fleet figures and THEN cancelled the
loop. A heartbeat already returned from the server -- merely not yet
holding the mutex -- writes the old server's figures after the clear.

Re-pair to a DIFFERENT server and fleetView then presents those figures
under the new server's URL. That is worse than showing nothing: the
label makes the wrong number look authoritative.

Cancel and wait for the loop to exit first, then clear under the lock.
The clear now runs on both branches, including the commonest one of all
-- no loop running, which is a Desktop that was never paired or one
being stopped twice.

Driven deterministically rather than by racing and hoping: the test's
stand-in loop writes figures only once it OBSERVES the cancel, so the
write is guaranteed to land in the window the old ordering left open. A
test that merely raced would have passed on the broken code most of the
time. Proven by control -- restoring the old ordering fails it with the
old server's 99.0 still cached.

Reported by CodeRabbit on PR #116.
@GeiserX
GeiserX merged commit e8f63ac into main Aug 6, 2026
6 checks passed
@GeiserX
GeiserX deleted the feat/show-the-fleet-picture-when-paired branch August 6, 2026 09:05
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