Skip to content

Fleet glance: runway on agent cards + low-funds alert in the sidebar - #111

Merged
ALLiDoizCode merged 2 commits into
mainfrom
sandcastle/issue-76
Aug 4, 2026
Merged

Fleet glance: runway on agent cards + low-funds alert in the sidebar#111
ALLiDoizCode merged 2 commits into
mainfrom
sandcastle/issue-76

Conversation

@toon-backlog-bot

Copy link
Copy Markdown

Adds fleet runway estimates to agent cards and a low-funds alert in the sidebar so operators can spot agents at risk of running out of funds at a glance.

Part of #76

This PR was produced by the sandcastle agent:implement runner and is awaiting human review.

🤖 Generated with Claude Code

claude added 2 commits August 4, 2026 04:02
… (buzz#76)

Task: buzz#76, part of the agent-fleet-money epic (toon-meta#261 decision
9/10). #80 (buzz#106) shipped the Money tab's Network spend block and its
NetworkSpendState/agentNetworkFlow runway derivation; this ticket reuses
that seam for the fleet-wide glance — runway badges on AgentIdentityCard
in the Agents grid, starving agents sorted to the top, and a low-funds
SidebarCompactActionCard, per the issue's own explicit instruction to
consume #80's wiring rather than re-derive it.

Key decisions:
- New desktop/src/features/agents/lib/agentFleetRunway.ts: pure derivation
  from NetworkSpendState to a critical/warning badge, using days-of-runway
  thresholds (1 day critical, 3 days warning) rather than absolute USDC, so
  a threshold survives a pricing change. `unavailable`/`relay`/`pending`
  states and self-funding agents map to no badge — never a fabricated
  figure. `sortByFleetRunway` is a stable sort so starving agents rise to
  the top without reshuffling ties; `countLowFundsAgents` backs the
  sidebar alert's count.
- Respected the issue's two documented live constraints rather than
  working around them: only the identity this desktop process itself pays
  as (account index 0) has a live channel read today — no toon-clientd
  spawn/lifecycle exists for managed agents (buzz#79's ADR 0006) and no
  account-index lookup is exposed to the frontend. `useAgentFleetRunwayBadges`
  gates on `pubkey === currentPubkey` per agent, so almost every managed
  agent honestly reads "no badge" today; the badge/sort/alert plumbing is
  fully wired and unit-tested (including the "demonstrated against a
  deliberately drained agent" AC, via a synthetic depleted NetworkFlowRead)
  and activates the moment the per-agent reads follow-up ticket lands.
- AgentIdentityCard's statusBadge slot already carries "Configuration
  missing"/"Restart required" warnings; factored the ternary (previously
  duplicated across AgentPersonaCard/StandaloneAgentCard) into one
  AgentStatusBadge helper that adds the runway badge as a third, lowest-
  priority case — same slot, no layout change, per the AC.
- SidebarLowFundsCard follows SidebarRelayConnectionCard's
  SidebarCompactActionCard idiom (icon, title, one action, dismissible,
  role=alert, click-through to the fleet via onSelectAgents) but is fully
  self-contained (own dismiss state, own visibility predicate in
  sidebarLowFundsCardVisibility.ts) so AppSidebar.tsx — already at 997/1000
  lines against the file-size ratchet — only grows by 2 lines.
- Did not add the optional SidebarMenuBadge count on the Agents nav item;
  the issue marks it optional and current data would always read zero.

Files changed: desktop/src/features/agents/lib/{agentFleetRunway,
useAgentFleetRunwayBadges}.ts (new, +test), desktop/src/features/agents/ui/
UnifiedAgentsSection.tsx, desktop/src/features/sidebar/ui/{SidebarLowFundsCard,
sidebarLowFundsCardVisibility}.ts (new, +test), AppSidebar.tsx.

Verified: fmt-check, desktop-tauri-fmt-check, clippy (workspace,
-D warnings), test-unit (864 tests), desktop-check (incl. file-size
ratchet — AppSidebar.tsx now 999/1000 lines), desktop-test (4293 passed),
desktop-build, web-check, web-build — all green.

Blockers/notes for next iteration: the badge/sort/alert are inert for
real managed agents until the per-agent reads follow-up ticket lands
(toon-clientd lifecycle + account-index lookup exposed to the frontend,
tracked separately per the issue's own comments) — this is the documented,
expected state, not a bug. Optional SidebarMenuBadge count on the Agents
nav item left undone (explicitly optional in the issue).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude Sonnet 5 <noreply@anthropic.com>
Array.sort's comparator was calling badgeOf() on every comparison
(O(n log n) times). For the Agents grid's group sort, badgeOf calls
pickProfileAgent(), which does its own internal sort — so the same
group's profile agent was recomputed repeatedly instead of once.
Decorate-sort-undecorate evaluates badgeOf per item up front instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude Sonnet 5 <noreply@anthropic.com>
@ALLiDoizCode
ALLiDoizCode merged commit 9cca5f4 into main Aug 4, 2026
22 checks passed
@ALLiDoizCode
ALLiDoizCode deleted the sandcastle/issue-76 branch August 4, 2026 04:43
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>
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.

2 participants