Skip to content

feat(usage): unify subscription quota readings across federated instances - #6114

Merged
atomantic merged 1 commit into
mainfrom
cos/task-mtlxw03u/agent-61956436
Sep 3, 2026
Merged

feat(usage): unify subscription quota readings across federated instances#6114
atomantic merged 1 commit into
mainfrom
cos/task-mtlxw03u/agent-61956436

Conversation

@atomantic

Copy link
Copy Markdown
Owner

Summary

Subscription usage cards said "Local sessions only — does not include other devices or claude.ai." But a subscription is one account across every federated instance, and PortOS already federates usage data — the other devices are right there. Each install could only read its own local CLI's quota panel, so every card was a partial view of a shared allowance.

Each instance's last quota reading now rides the usage sync category alongside its usage digest, and the cards are unified before they render.

Two merge rules, because the halves of a card mean different things:

  • limits (the meters) are account-wide — every machine reads the same server-side allowance, just at a different moment. The freshest reading per limit key wins; summing them would multiply one allowance by the number of machines that looked at it.
  • activity (requests/sessions) is per-machine — exactly what the provider's caption is about — so those sum.
  • metrics[] stays local: its values are prose ("3 renders · 24h"), not addends.

Also:

  • A card this machine could not read (logged-out CLI, scrape in flight) is filled from a peer that read the same account.
  • Only families this install has enabled get a card — no meter for a plan the viewer can't spend.
  • Instances marked API billed via the existing Across Instances toggle are excluded; they meter a different account.
  • A single-machine install is unchanged, caption included — with nothing to combine, the honest note is that no other instance has reported yet.
  • The card header gains an "N instances" pill naming the contributors and when each read.

Mechanics: the readings live in an in-memory SWR cache (a reading costs a 10-20s CLI/TUI spawn) which can't be federated — it dies with the process, and the category's checksum keys on file fingerprints. services/providerQuotaShare.js persists them to data/provider-quotas.json, added to USAGE_CHECKSUM_PATHS and folded into the entry's capturedAt so a quota refresh with no new AI runs still advances the slot a peer pulls. The write is skipped when a card's claim is unchanged, so a page poll doesn't hand peers a new slot that says nothing new.

Nothing here reads a provider — the AI Provider Usage Policy holds; this only records and forwards what a user-triggered reading already produced. Peer payloads are rebuilt to the wire shape on arrival, same recursion-depth reason as the usage digest.

ADR amended: docs/decisions/2026-09-01-federated-usage-metrics.md.

Test plan

  • server/lib/fleetQuotas.test.js (new, 11 cases) — sanitization drops unmergeable input and strips unknown fields; freshest-meter-wins vs summed-activity; local reading holds a tie; peer-only window appended; an unreadable local card filled from a peer; no card invented for a disabled family; note wording and name-list collapse.
  • server/services/peerUsage.test.js (+5 cases) — readings publish under this instance and advance the LWW stamp; an unchanged claim is not re-recorded; a narrowed read merges rather than retiring skipped families; peer payloads rebuilt to the wire shape; API-billed instances excluded.
  • client/src/pages/UsagePage.test.jsx (+2 cases) — the fleet pill names contributors; absent on a single-machine install.
  • Full suites green: server 38617 passed | 14 skipped, client 10208 passed | 2 skipped. Lint clean.
  • Reviewed by agy (Gemini 3.8 Flash, low effort): NO FINDINGS.

https://claude.ai/code/session_016Co1zj9L1cEMNmudHWZGaN

…nces

A subscription is one account across every federated install, but each
machine could only read the quota panel of its own local CLI — so every
subscription card was a partial view captioned "Local sessions only —
does not include other devices or claude.ai." The federation already
carries this user's other devices.

Each instance's last quota reading now rides the `usage` sync category
alongside its usage digest, and the cards are unified before they render:
account-wide meters take the freshest reading per limit key (summing them
would multiply one allowance), while per-machine activity counts sum. A
card this machine could not read — a logged-out CLI, a scrape in flight —
is filled from a peer that read the same account. Instances marked as
paying API rates are excluded, since they meter a different account, and
a single-machine install is unchanged, caption included.

The Usage page card now says how many instances it combined and names
them, replacing the CLI's local-only wording.

Claude-Session: https://claude.ai/code/session_016Co1zj9L1cEMNmudHWZGaN
@atomantic
atomantic merged commit 1d051ff into main Sep 3, 2026
7 checks passed
@atomantic
atomantic deleted the cos/task-mtlxw03u/agent-61956436 branch September 3, 2026 20:10
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