Show earning in the Money tab — net flow, same channel - #97
Merged
Conversation
…ing (buzz#86) Task: #86, part of the agent-fleet-money epic (toon-meta#262 decision 9 / toon-meta#261 decision 4). Ships the pure net-flow domain logic the ticket's four acceptance criteria all reduce to — income beside spend, runway that accounts for income, the earning predicate for AgentIdentityCard, and low-funds-alert suppression. Key decisions: - New `desktop/src/features/profile/lib/agentNetworkFlow.ts`: models `spendable = deposit − owed + credited` (the connector's stated netting formula, one channel, never a second "earnings" pot) plus a runway derivation that discounts untrusted income. Sustained income (>= 3 samples in the trailing window) that covers burn yields a `self-funding` state with no depletion date; anything less degrades to burn-rate-alone runway — a single paid job must not flip the UI to "indefinite" and strand someone when the job stream dries up. `isEarning` (AgentIdentityCard badge) and `shouldSuppressLowFundsAlert` reuse the same trusted-income bar so the fleet glance and the alert never disagree about whether an agent pays for itself. - Followed the `paymentsOverview.ts` / `huddleFeeQuote.ts` idiom: pure, fully unit-tested (RGR — 8 new cases), no DOM/network, ready for a caller to wire once real data exists. - Not wired into any component. The two things this logic needs to render for real do not exist yet in this repo: the Network spend block itself (#80 — still a "Coming soon" placeholder, no balance/ burn-rate read of any kind) and a per-agent claim-state read (toon-client#494's `getClaimState()`, not present in this repo's pinned `@toon-protocol/client@0.25.1`). Per the issue's own Gotchas, income must come from the connector's claim state, never inferred by scanning NIP-90 job events client-side — so there is no honest workaround available in this repo's current state. Updated the Money tab's Network spend placeholder copy to state the net-flow model (income nets into the same balance) without fabricating a live read. Files changed: desktop/src/features/profile/lib/agentNetworkFlow.ts (new, +test), desktop/src/features/profile/ui/UserProfilePanelMoneyTab.tsx. Full local gate green: fmt-check, desktop-tauri-fmt-check, clippy, test-unit (864 tests), desktop-check, desktop-test (4209 passed), desktop-build, web-check, web-build. Blockers for next iteration: #80 (Network spend block: balance, burn-rate read via `onPaidWrite`, refill) must land first, and the per-agent claim-state read this ticket's income data depends on needs either a newer `@toon-protocol/client` with `getClaimState()` or the buzz-cli/relay-side plumbing to expose it to the desktop for an agent that is not the local wallet identity. Once either lands, `deriveNetworkRunway`/ `isEarning`/`shouldSuppressLowFundsAlert` are ready to consume it directly. Signed-off-by: Claude Sonnet 5 <noreply@anthropic.com>
…sertion The "single payment does not flip runway to indefinite" test built its finite-runway state from a read with incomeSampleCount: 1, but then asserted isEarning(NO_INCOME) — the untouched base fixture with a zero income rate, which trivially returns false regardless of the sample-count logic under test. Assert against the same read used to derive the state. Signed-off-by: Claude Sonnet 5 <noreply@anthropic.com>
|
Heads-up on a cross-epic collision, not a blocker for this PR's own correctness. This PR (buzz#86, earning epic toon-protocol/toon-meta#262) edits the Money tab, and so does buzz#80 (Network spend block, agent-fleet-money epic toon-protocol/toon-meta#261). #80 establishes the tab's two-block structure and the never-summed rule that earning then slots into, so the agreed sequencing is #80 lands first, then this PR rebases onto it. #80 is currently blocked on buzz#79. Flagging now so the layout decisions here are made knowing the Network spend block is arriving beside them — two independent takes on the same file's structure is the one thing a rebase cannot reconcile. |
ALLiDoizCode
pushed a commit
that referenced
this pull request
Aug 4, 2026
* RALPH: earning badge on AgentIdentityCard, close out net-flow Money tab (buzz#86) Task: #86, part of the agent-fleet-money epic (toon-meta#262 decision 9). Per the issue's own re-dispatch comment, AC1/AC2 (income beside spend, income-aware runway) landed in PR #97 (agentNetworkFlow.ts) and were wired to real data in #108/#106 (PR #113, #106). AC4 (suppress the low-funds alert for a self-funding agent) was already implemented and tested by buzz#76/PR #111 — `deriveAgentFleetRunwayBadge` returns no badge once `deriveNetworkRunway` concludes an agent is self-funding, which flows straight through `countLowFundsAgents`/`shouldShowSidebarLowFundsCard` — confirmed via the existing "self-funding agents show no badge" test, no code change needed. This lands the one true remaining gap: AC3, an earning indicator on `AgentIdentityCard` in the Agents grid. Key decisions: - New `isAgentFleetEarning(state: NetworkSpendState): boolean` (agentFleetRunway.ts) wraps `agentNetworkFlow.ts`'s `isEarning` behind the same `state.kind !== "quoted"` gate `deriveAgentFleetRunwayBadge` already uses, so the earning badge and the low-funds suppression share one trusted-income bar and can never disagree about whether an agent is self-funding. - New `useAgentFleetEarningBadges` hook mirrors `useAgentFleetRunwayBadges`'s `isSelf`-only gate exactly (same documented buzz#79 ADR 0006 gap — no per-agent channel read exists for any identity but the one this desktop process itself pays as). Kept as its own hook rather than folding into the existing runway hook: the runway hook's `Map<pubkey, AgentFleetRunwayBadge>` return type is consumed as-is by two call sites (`UnifiedAgentsSection.tsx`, `SidebarLowFundsCard.tsx`), and `SidebarLowFundsCard.tsx` has no need for earning state, so widening its shape would be churn for one consumer's benefit. - `AgentStatusBadge` (the card's single-warning slot, `UnifiedAgentsSection.tsx`) gets a third, lowest-priority branch: operational issues, then a low-funds runway warning, then "Earning" (success-variant badge, `TrendingUp` icon) — same slot, no layout change. The three are mutually exclusive in practice (self-funding agents never carry a runway badge), but priority order is defensive. Files changed: desktop/src/features/agents/lib/{agentFleetRunway.ts (+3 tests),useAgentFleetEarningBadges.ts (new)}, desktop/src/features/agents/ui/UnifiedAgentsSection.tsx. Verified: fmt-check, desktop-tauri-fmt-check, clippy (workspace, -D warnings), test-unit (864 tests), desktop-check, desktop-test (4334 passed), desktop-build, web-check, web-build — all green. Blockers/notes for next iteration: the earning badge is wired but inert against real data today — `networkSpendState.ts`'s `deriveNetworkSpendState` hardcodes `incomeRateBaseUnitsPerSec: 0`/`incomeSampleCount: 0` (no live inbound-payment event feed exists yet, only `networkSpendLiveStore.ts`'s outbound `onPaidWrite` — documented in agentNetworkFlow.ts's module doc since PR #113), so `isEarning`/`isAgentFleetEarning` cannot return true from a real read until that income-rate feed lands. This is the same documented, pre-existing gap AC1/AC2's runway already ships ahead of, not new scope this ticket introduces. Signed-off-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: toon-backlog-bot[bot] <294337193+toon-backlog-bot[bot]@users.noreply.github.com> * Dedupe fleet-status hooks: one useNetworkSpend read for badges + earning UnifiedAgentsSection called useAgentFleetRunwayBadges and the new useAgentFleetEarningBadges together, each independently calling useNetworkSpend(true)/useIdentityQuery and duplicating the same isSelf-matching loop — doubling the connector fetch on every mount. Fold both into useAgentFleetStatus, which computes runway badges and the earning set from a single self-spend read; useAgentFleetRunwayBadges becomes a thin wrapper so SidebarLowFundsCard is unaffected. Signed-off-by: toon-backlog-bot[bot] <294337193+toon-backlog-bot[bot]@users.noreply.github.com> --------- Signed-off-by: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: toon-backlog-bot[bot] <294337193+toon-backlog-bot[bot]@users.noreply.github.com> Co-authored-by: toon-backlog-bot[bot] <294337193+toon-backlog-bot[bot]@users.noreply.github.com> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds net-flow/runway domain logic to the desktop Money tab so earning is derived from same-channel net flow rather than raw incoming payments.
Part of #86
This PR was produced by the sandcastle
agent:implementrunner and is awaiting human review.🤖 Generated with Claude Code