Skip to content

spec: AI credit usage in status bar (#10449)#10454

Open
lonexreb wants to merge 4 commits intowarpdotdev:masterfrom
lonexreb:spec/10449-credits-status-bar
Open

spec: AI credit usage in status bar (#10449)#10454
lonexreb wants to merge 4 commits intowarpdotdev:masterfrom
lonexreb:spec/10449-credits-status-bar

Conversation

@lonexreb
Copy link
Copy Markdown
Contributor

@lonexreb lonexreb commented May 8, 2026

Summary

Spec-only PR proposing an opt-in status-bar segment that shows AI
credit consumption for the current billing period (e.g., 89/2500),
mirroring the existing context-remaining segment so users can monitor
remaining credits at a glance without opening Settings.

Resolves intent of #10449.

What's in this PR

  • specs/GH10449/SPEC.md — full behavior contract, settings surface,
    acceptance criteria, tests, and open questions.

No code changes. This PR is for spec review only.

Highlights

  • Opt-in via status_bar.show_ai_credits (default false) so we
    don't surprise existing users with new chrome.
  • Reuses the Billing pane's fetch pipeline — no second network
    path. Refreshes within ≤2s of agent-turn completion.
  • Threshold colors (neutral / yellow / red at <80% / 80–94% /
    ≥95%) reuse existing status-bar tokens.
  • Workspace-scope aware — follows the same selector that drives
    the Billing pane.
  • Graceful degradation — stale tooltip on transient failures,
    hide segment after 30s of consecutive failures.

Related

Triage request

Requesting ready-to-implement once the open questions in the spec
are resolved (default-on for metered plans, unlimited-plan rendering,
glyph vs word).

Test plan

  • Spec reviewed by an Appearance / status-bar maintainer.
  • Spec reviewed by a Billing-pane maintainer to confirm the
    shared fetch pipeline assumption holds.
  • Open questions Q1–Q3 resolved before promoting to
    ready-to-implement.

@cla-bot cla-bot Bot added the cla-signed label May 8, 2026
@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 8, 2026

@lonexreb

I'm starting a first review of this spec-only pull request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

@github-actions github-actions Bot added the external-contributor Indicates that a PR has been opened by someone outside the Warp team. label May 8, 2026
Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

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

Overview

This spec proposes an opt-in status-bar segment for AI credit usage and covers display, settings, refresh, scope, click behavior, acceptance criteria, and tests. The main behavior is understandable, but several requirements are still ambiguous enough to produce inconsistent or inefficient implementations.

Concerns

  • The spec does not define a concrete mapping from the existing Billing & Usage state model to status-bar display states for unlimited, usage-based, add-on, enterprise, delinquent, restricted, and missing-allowance cases.
  • The refresh/subscription contract can still permit one polling loop per visible segment across tabs or windows, which conflicts with the stated no-extra-pipeline intent.
  • The implementation pointers do not match the local code structure, making the spec not ready for implementation handoff.

Security

  • The workspace-scope behavior needs to explicitly inherit Billing & Usage authorization and redaction rules before exposing credit usage in always-visible chrome.
  • The telemetry surface field should be constrained so it does not accidentally add usage totals, scope names, or workspace identifiers beyond existing billing instrumentation.

Verdict

Found: 0 critical, 4 important, 1 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Comment thread specs/GH10449/SPEC.md Outdated
the existing status-bar warning token).
- `≥95%` used → red / danger foreground (uses the existing
status-bar danger token).
- "Unlimited" plan: when the billing source returns
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] plan == "unlimited" or equivalent is not a concrete contract for existing billing states such as usage-based pricing, add-on credits, enterprise limited reporting, delinquent/restricted workspaces, or missing allowances. Add a mapping from Billing & Usage state to display, threshold, and hidden behavior.

Comment thread specs/GH10449/SPEC.md Outdated
for the active conversation. The segment subscribes to the same
agent-turn completion signal already used by the conversation
list.
- Background refresh: when the segment is visible, refresh on a
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] The refresh contract does not state whether polling is shared per billing scope or created per rendered segment. Specify a single shared cache/subscription across tabs and windows, plus a test that multiple visible segments do not create duplicate 60s fetch loops.

Comment thread specs/GH10449/SPEC.md

### B3. Scope (workspace selector)

- The segment follows the current workspace-billing-context
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] [SECURITY] The scope rule must explicitly inherit the Billing pane's authorization and redaction rules, not only its selector. Add behavior and tests for non-admin or restricted-workspace users so workspace credit usage is hidden whenever Billing & Usage would not show it.

Comment thread specs/GH10449/SPEC.md Outdated

## Implementation Pointers

- `app/src/status_bar/*.rs` — add a new segment struct/component
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] These implementation paths do not match the local architecture: app/src/status_bar/*.rs is absent, and Billing & Usage currently relies on settings/workspace/AI request usage models rather than an app/src/billing usage client. Update the pointers to the actual status-bar and billing-usage models before marking this ready to implement.

Comment thread specs/GH10449/SPEC.md Outdated

- No new telemetry events. The billing fetch reuses the existing
Billing-pane fetch path and its existing instrumentation.
- One existing event SHOULD gain a `surface` field if it does not
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 [SUGGESTION] [SECURITY] Define surface as a fixed low-cardinality enum and state that telemetry must not include used/allowance values, scope names, or workspace identifiers beyond what the existing billing event already records.

@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 8, 2026

Round-1 fixes for oz-for-oss review: (1) added Display State Matrix covering unlimited, usage-based, add-on, enterprise, delinquent, restricted, missing-allowance; (2) Subscription Model now requires a single shared, ref-counted subscription to the existing Billing & Usage state stream — segments are downstream observers, never poll independently; (3) Implementation Pointers updated to verified paths in the codebase; (4) Authorization & Redaction subsection inherits billing-pane authz, hides segment when permission absent, never crosses workspace scope; (5) telemetry constrained to click events only — no usage totals, scope names, or workspace identifiers in payload.

@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 8, 2026

/oz-review

@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 8, 2026

@lonexreb

I'm re-reviewing this spec-only pull request in response to a review request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

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

Overview

This spec proposes an opt-in AI credit usage status-bar segment and covers the main behavior, settings, telemetry, authorization, and testing surfaces.

Concerns

  • The display contract has conflicting requirements for missing usage/allowance data.
  • Add-on credit rendering is underspecified when multiple pools exist.
  • The required shared Billing & Usage state stream is not tied to a concrete existing model or refresh owner, which risks an implementation that invents a new pipeline while trying to avoid one.
  • The Open Questions section still leaves behavior that is already referenced by acceptance criteria unresolved.

Security

  • The synced setting controls always-visible billing data; the spec should justify global sync or make the privacy behavior explicit.

Verdict

Found: 0 critical, 4 important, 1 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Comment thread specs/GH10449/SPEC.md Outdated
- "Unlimited" plan: when the billing source returns
`plan == "unlimited"` (or equivalent), display `∞` glyph and
use default foreground regardless of `used`.
- Indeterminate state (used or allowance is null/missing while
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] B1 says any missing used or allowance value renders —/—, but B1a later says known used plus missing allowance renders <used>/—; split these cases explicitly so implementers do not choose different fallbacks.

Comment thread specs/GH10449/SPEC.md Outdated
|---|---|---|---|---|
| Unlimited plan | `∞` glyph (or "Unlimited" if locale lacks the glyph) | Default fg; thresholds NOT applied | "Unlimited plan · current monthly billing usage: $X" if a USD figure is exposed by the existing billing source, else "Unlimited plan" | Open Settings → Billing & Usage |
| Usage-based / metered | `<used>/<allowance>` (integers) | <80% default · 80–94% warning · ≥95% danger | "<scope> · Monthly allowance · resets <date>" | Open Settings → Billing & Usage |
| Add-on credits on top of base allowance | `<used>/<base>+<addon>` (e.g. `89/2500+500`) | Thresholds computed against `(base + addon_total)` | "<scope> · Base 2500 (resets <date>) · Add-on 500 (expires <date>) · Add-on 250 (expires <date>)" | Open Settings → Billing & Usage |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] Add-on rendering is ambiguous for multiple pools: specify whether +<addon> is the total granted, total remaining, currently available after expiration, or multiple per-pool values, and align the example with the tooltip's two add-ons.

Comment thread specs/GH10449/SPEC.md Outdated

### B2. Refresh & caching (subscription model)

- Source of truth: the same Billing & Usage state stream that
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] The current Billing pane appears to compose overview data from existing app models rather than a single Billing & Usage state stream; name the concrete source model(s), fields, and refresh owner the segment must observe, or this requirement is not implementable without creating the parallel abstraction it forbids.

Comment thread specs/GH10449/SPEC.md
- Key: `status_bar.show_ai_credits`
- Type: `bool`
- Default: `false` (opt-in)
- Sync: `SyncToCloud::Globally(RespectUserSyncSetting::Yes)`,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 [SUGGESTION] [SECURITY] Because this setting exposes billing usage in always-visible chrome, document why global sync is safe or make the setting device-local/private so enabling it on one synced device does not unexpectedly surface credit usage on another.

Comment thread specs/GH10449/SPEC.md
— no `used`, `allowance`, `scope_name`, `workspace_id`,
`plan_tier`, or `reset_at` fields.

## Open Questions
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] Resolve or remove the open questions before marking this ready to implement; Q1-Q4 duplicate decisions already made in B1/B1a/B5/A6/A9, so leaving them open makes the acceptance criteria non-final.

@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 8, 2026

Round-2 fixes: (1) data-state display table is now the single source of truth — covers loading/success/allowance-missing/used-missing/both-missing/stale/failed/unauthorized states with consistent display+color+tooltip per state; (2) multiple add-on pools render <used>/<base>+<addon_total> with full per-addon breakdown in tooltip; (3) Subscription Model now ties to the verified existing AIRequestUsageModel singleton owner — status-bar segment is a passive observer subscribing via existing API, never polls; codebase paths verified; (4) Open Questions resolved inline for any items referenced by acceptance criteria; (5) synced toggle is justified — only the on/off bool is synced, never billing data; per-device override available via existing mechanism.

@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 8, 2026

/oz-review

@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 8, 2026

@lonexreb

I'm re-reviewing this spec-only pull request in response to a review request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

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

Overview

This spec proposes an opt-in AI credit usage segment in the status bar and covers display states, settings, authorization, telemetry, and test expectations.

Concerns

  • The failure-state timing is internally contradictory: the behavior contract says 30s should show a stale state and only ≥5 min hides the segment, while acceptance criteria and tests require hiding after 30s.
  • Refresh/subscription ownership is ambiguous: the spec says the chip is a passive observer and adds no refresh trigger, but also says it subscribes to agent-turn completion and requires exactly one billing-state subscription.
  • Tooltip rendering for add-on pools exposes add-on names, amounts, and expirations without the same redaction/user-visible-data constraint applied to workspace identifiers.
  • Telemetry requirements conflict between an exact single click event and later fetch instrumentation that the segment observer contributes to.

Security

  • Add-on grant tooltip data needs an explicit redaction/display-name rule before implementation because it places billing details into status-bar chrome/tooltips that can appear in screenshares or recordings.

Verdict

Found: 1 critical, 4 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Comment thread specs/GH10449/SPEC.md Outdated
- A3. Clicking the segment opens Settings → Billing & Usage.
- A4. Switching workspace billing scope updates the segment to
the new scope's used/allowance within 2s.
- A5. Simulated 30s of consecutive fetch failures hides the
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚨 [CRITICAL] A5 contradicts B1a.i/B2: those sections say 30s shows stale and only ≥5 min hides. Choose one failure window and align A5/T4 so implementation/tests do not encode a different state machine.

Comment thread specs/GH10449/SPEC.md Outdated
invariant is structural, not advisory: spawning N status-bar
chips (in N windows / panes) attaches N subscribers to the SAME
singleton — there is no additional fetch loop per chip.
- Cache TTL: ≤2s after the most-recent agent-turn-completion
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] This says the segment subscribes to agent-turn completion while nearby text says it is a passive observer and adds no refresh trigger. Name the existing owner that refreshes AIRequestUsageModel within ≤2s, or explicitly allow one shared refresh trigger outside per-segment UI.

Comment thread specs/GH10449/SPEC.md Outdated
exact glyph/text and color token specified, including
delinquent, restricted, missing-allowance, enterprise-no-limit,
and add-on credit cases.
- A10. Single shared subscription: with N status-bar segments
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] A10 requires exactly one billing-state subscription, but B2 explicitly allows N chips to attach N subscribers to the singleton. Specify whether the invariant is one fetch pipeline/network request versus one UI subscriber, and update T7/T10 accordingly.

Comment thread specs/GH10449/SPEC.md
- Threshold computation: against `(base + addon_total)`. If
`addon_total` is 0 (all expired), the segment falls back to
the base-only `<used>/<allowance>` render.
- Tooltip lists EACH add-on on its own line, in the form
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] [SECURITY] Add-on names/amounts/expiry are billing data in always-visible chrome/tooltips, but redaction only covers workspace identifiers. Require add-on display names to be the same sanitized/user-visible labels used in Billing & Usage, or omit names when not already exposed.

Comment thread specs/GH10449/SPEC.md Outdated
pane-vs-segment attribution and never carries usage shape.
- The billing fetch itself reuses the existing Billing-pane fetch
path and its existing instrumentation. If that path already
has a `surface` field, the segment's attached observer
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] This conflicts with the earlier exactly ONE event/no segment-side telemetry requirement and A12. Either make fetch surface purely Billing-pane-owned instrumentation with no segment-owned emission, or include the allowed surface behavior in A12/non-goals so telemetry tests are implementable.

@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 8, 2026

Addressed round-N oz-for-oss review:

Critical — Failure-state timing contradiction: Aligned A5 and T4 with the B1a.i data-state table. Behavior is now: ≥30s without a successful update → Stale row (last value with stale dot); ≥5 min consecutive failures → Failed row (segment hidden); successful RequestUsageUpdated restores the live render. No more 30s vs 5 min conflict.

Refresh/subscription ownership: Clarified that the chip is a passive observer of FETCHES, not of model events. It MAY subscribe to AIRequestUsageModel::RequestUsageUpdated for redraw, but never to agent-turn completion or billing GraphQL directly. The agent-turn → refresh_request_usage_async chain stays entirely upstream. A10 reworded: 'single shared upstream' (one SingletonEntity, one fetch cadence, N model-event listeners for redraw).

Add-on tooltip redaction (security): Added explicit redaction rule (B1a.iii) — display name only, integer credit amount only, coarse user-facing date for expiry, fallback string 'Add-on credit' when display_name missing, omit row entirely if amount missing, applied identically to hover/keyboard/AT a11y tree, never copied into telemetry. Mirrors the workspace-identifier rule in B3a.

Telemetry conflict: Resolved by scoping ALL fetch instrumentation as upstream-owned and out of scope. The segment emits exactly one click event; no observer-side surface value, no contribution to existing fetch events. The 'attached observer contributes status_bar' wording is removed.

Commit: 18ff1b2

@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 8, 2026

/oz-review

@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 8, 2026

@lonexreb

I'm re-reviewing this spec-only pull request in response to a review request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

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

Overview

This spec proposes an opt-in status-bar chip for AI credit usage and covers most expected behavior, settings, tests, telemetry, and privacy constraints.

Concerns

  • The unlimited-plan row still allows a USD amount in the tooltip, which conflicts with the credit-count scope and creates an always-visible financial-data exposure.
  • Refresh ownership is ambiguous for workspace-scope changes: the spec says the segment refetches while also forbidding the segment from triggering fetches.
  • The stale/failed state machine is specified in terms of consecutive failures but is tied to last_update_time, which only records successful updates.
  • The subscription tests describe a single ref-counted billing subscription, contradicting the model-event observer pattern defined elsewhere in the spec.

Security

  • Remove or explicitly gate the unlimited-plan USD tooltip; the current wording surfaces financial data in always-visible chrome outside the sibling USD-spend spec.

Verdict

Found: 0 critical, 4 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Comment thread specs/GH10449/SPEC.md

| Plan / account state | Segment glyph / text | Color tokens | Tooltip | Click action |
|---|---|---|---|---|
| Unlimited plan (`is_unlimited == true`) | `∞` glyph (or "Unlimited" if locale lacks the glyph) | Default fg; thresholds NOT applied | "Unlimited plan · current monthly billing usage: $X" if a USD figure is exposed by the existing billing source, else "Unlimited plan" | Open Settings → Billing & Usage |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] [SECURITY] This reintroduces USD spend into an always-visible credit-count chip and conflicts with the later decision to render only for unlimited plans. Remove the $X tooltip from this spec or move it behind the sibling USD-spend feature so financial data is not exposed here.

Comment thread specs/GH10449/SPEC.md
Comment on lines +230 to +236
- Stale display: when `last_update_time` is older than 30s the
chip switches to the "Stale" row of B1a.i. After ≥5 min of
consecutive failed/missing updates the chip switches to the
"Failed" row of B1a.i (segment hidden); display resumes once
`RequestUsageUpdated` fires successfully. Hide-on-failure is
computed from `AIRequestUsageModel::last_update_time()`, not
from a per-segment counter.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] last_update_time only records the last successful update, so it cannot prove that updates are missing because of consecutive failures or an active retry; idle users could become stale/hidden without any failed fetch. Specify an explicit failure/retry state on AIRequestUsageModel or redefine these states purely by data age.

Comment thread specs/GH10449/SPEC.md
Comment on lines +242 to +244
- When the user switches workspace scope (e.g. "Personal" → "Acme
Workspace"), the segment refetches and re-renders within the
≤2s debounce window.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] This says the segment refetches on workspace switch, but B2 forbids the segment from adding refresh triggers or calling refresh_request_usage_async. Name the upstream owner that refreshes on scope changes, or change this to say the segment only re-renders after the singleton publishes updated data.

Comment thread specs/GH10449/SPEC.md
Comment on lines +458 to +462
- T10. Shared subscription ref-count: spawning N≥2 segments
results in exactly one underlying billing subscription; the
last segment removed disposes the subscription. Verified by
asserting the fetch counter increments at the same cadence as
N=1.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] This test contradicts B2/A10: the spec expects N model-event subscribers on one singleton, not one ref-counted billing subscription that the last chip disposes. Rewrite the test to assert unchanged GraphQL fetch cadence/fetch count, not a single shared subscription object.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed external-contributor Indicates that a PR has been opened by someone outside the Warp team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant