Skip to content

Add a pace verdict and re-expose predictive warnings (#190) - #203

Merged
tsouth89 merged 2 commits into
mainfrom
feat/pace-verdict
Aug 5, 2026
Merged

Add a pace verdict and re-expose predictive warnings (#190)#203
tsouth89 merged 2 commits into
mainfrom
feat/pace-verdict

Conversation

@tsouth89

@tsouth89 tsouth89 commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Closes #190.

it would be really helpful if we had an "estimator" that would estimate if we are using too much usage and would not last until the end of week

Stacked on #202. Review the third commit.

Why nobody could see this

Ceiling already computed the answer. It just could not be reached:

  • The detail pace section is display: none in the tray surface, which is exactly where the reporter was looking. The two-bar expected/actual block is too tall for the tray, which is a fair call and the reason for a compact version rather than deleting the rule.
  • The warning that fires on a predicted shortfall was retired as experimental: pinned to false on every settings load, with its toggle removed from the UI and a test asserting it stayed gone. It was unreachable regardless of what a user stored.
  • Even when enabled, it was restricted to Claude and Codex.

Visual

PaceVerdict: a headline, its consequence, and one slim bar whose tick marks where usage should be by this point in the window. Same conclusion as the expected-vs-actual pair at roughly a third of the height. Shown in the tray; the taller breakdown stays for the window view.

A pace that still runs out early never renders in the calm "slow" colour, since the shortfall is what matters, not that you were technically behind.

Warning

  • Stop pinning the setting off on load; restore the toggle under Settings > Notifications. It stays opt-in.
  • Let any provider raise it. Providers that never report an account were skipped for want of a dedupe identity, and now fall back to the source label.
  • Name the window by real cadence. Warnings were labelled by slot, so a monthly quota would have announced itself as a Session limit once ungated.

Verification

797 rust, 474 tauri, 362 vitest, clippy clean. New tests cover the verdict states, tick/fill placement, out-of-range clamping, ETA formatting, the settings round trip, and cadence naming.

Copilot AI lite review requested due to automatic review settings August 5, 2026 01:16
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
ceiling 20fa394 Commit Preview URL

Branch Preview URL
Aug 05 2026, 01:56 AM

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 14 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 27c3e0d0-a055-4cfc-be4f-de08bb059b0e

📥 Commits

Reviewing files that changed from the base of the PR and between f31fd2f and 20fa394.

📒 Files selected for processing (15)
  • apps/desktop-tauri/src-tauri/src/commands/providers.rs
  • apps/desktop-tauri/src/components/MenuCard.tsx
  • apps/desktop-tauri/src/components/PaceVerdict.test.tsx
  • apps/desktop-tauri/src/components/PaceVerdict.tsx
  • apps/desktop-tauri/src/i18n/keys.ts
  • apps/desktop-tauri/src/styles.css
  • apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.test.tsx
  • apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.tsx
  • rust/src/locale.rs
  • rust/src/locale/en-US.ftl
  • rust/src/locale/zh-CN.ftl
  • rust/src/notifications.rs
  • rust/src/settings.rs
  • rust/src/settings/raw.rs
  • rust/src/settings/tests.rs

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

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.

Pull request overview

This PR reintroduces user-facing “predictive pace” guidance across the Tauri desktop UI by adding a compact pace verdict in the tray and re-exposing the predictive pace warning setting (previously pinned off) so users can opt in to exhaustion predictions.

Changes:

  • Add a new compact PaceVerdict tray component (headline + consequence + single bar with expected tick).
  • Re-enable persistence + UI toggle for predictive pace warnings (opt-in), and broaden warning window labeling to include monthly cadence.
  • Update locale keys/strings and tray CSS so the tray shows the compact verdict while hiding the taller pace breakdown.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
rust/src/settings/tests.rs Adds a regression test ensuring the predictive warning opt-in survives JSON round-trip.
rust/src/settings/raw.rs Stops pinning predictive_pace_warning_enabled to false on load; restores persisted value.
rust/src/settings.rs Updates the predictive warning field docs to reflect opt-in and provider-agnostic behavior.
rust/src/notifications.rs Extends predictive warning window enum to include Monthly and localizes it.
rust/src/locale/en-US.ftl Updates predictive warning helper text; adds PaceVerdict* strings.
rust/src/locale/zh-CN.ftl Updates predictive warning helper text; adds PaceVerdict* strings.
rust/src/locale.rs Registers new PaceVerdict* locale keys.
apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.tsx Re-adds the predictive pace warnings toggle under Notifications.
apps/desktop-tauri/src/surfaces/settings/tabs/GeneralTab.test.tsx Updates tests to assert the toggle exists/works and is disabled when notifications are off.
apps/desktop-tauri/src/styles.css Keeps the verdict in the tray while hiding the pace header + tall expected/actual bars.
apps/desktop-tauri/src/i18n/keys.ts Adds PaceVerdict* keys to the frontend locale key list.
apps/desktop-tauri/src/components/PaceVerdict.tsx New compact verdict component + ETA formatting and percent clamping.
apps/desktop-tauri/src/components/PaceVerdict.test.tsx Adds unit tests for verdict states, tick/fill placement, clamping, and ETA formatting.
apps/desktop-tauri/src/components/MenuCard.tsx Replaces the prior “runs out / will last” blocks with <PaceVerdict />.
apps/desktop-tauri/src-tauri/src/commands/providers.rs Removes provider gating for predictive warnings, improves identity fallback, and labels warning cadence by actual window minutes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apps/desktop-tauri/src-tauri/src/commands/providers.rs
Comment thread apps/desktop-tauri/src/components/PaceVerdict.tsx
@tsouth89
tsouth89 force-pushed the feat/cursor-on-demand-spend branch from b584b65 to 4f79477 Compare August 5, 2026 01:31
@tsouth89
tsouth89 force-pushed the feat/pace-verdict branch from 868b515 to ddb7bdc Compare August 5, 2026 01:31
@tsouth89
tsouth89 changed the base branch from feat/cursor-on-demand-spend to main August 5, 2026 01:35
Ceiling already predicted whether usage would outlast its window, but the
answer was unreachable: the detail section is hidden in the tray surface,
and the warning that fires on a predicted shortfall had been retired as
experimental, pinned off on every settings load with no UI to enable it.

Visual:
- Add `PaceVerdict`: a headline, its consequence, and one slim bar whose
  tick marks where usage should be by this point in the window. It carries
  the same conclusion as the expected-vs-actual pair at roughly a third of
  the height, which is what made the old section too heavy for the tray.
- Show it in the tray and keep the taller breakdown for the window view.
- A pace that still runs out early never renders in the calm "slow" colour,
  since the shortfall is the part that matters.

Warning:
- Stop pinning `predictive_pace_warning_enabled` to false on load and
  restore its toggle under Settings > Notifications. It stays opt-in.
- Let any provider raise the warning. It was restricted to Claude and
  Codex, and providers that never report an account were skipped entirely
  for want of a dedupe identity.
- Name the window by its real cadence. Warnings were labelled by slot, so
  a monthly quota would have announced itself as a "Session" limit.
@tsouth89
tsouth89 force-pushed the feat/pace-verdict branch from ddb7bdc to 9e1ad8e Compare August 5, 2026 01:47
- Drop the Claude/Codex check in `record_predictive_observation`. Two other
  gates were removed for #190, but this third one sat in the dedupe path,
  so enabling the setting would still have warned nobody else. The identity
  check stays: a warning with no account to attribute it to is dropped.
- Derive the spare-capacity figure in `PaceVerdict` from the clamped
  percentage. A non-finite value off the bridge rendered "NaN% to spare"
  next to a bar that had already clamped it away.
@tsouth89

tsouth89 commented Aug 5, 2026

Copy link
Copy Markdown
Owner Author

Both correct, fixed in 20fa394.

  1. Third provider gate — this was the important one. I removed the gate in commands/providers.rs and generalized the identity function, but missed record_predictive_observation in notifications.rs, which hard-blocked everything except Claude and Codex inside the dedupe path. The PR's central claim was false as written: enabling the setting would have warned nobody else. Gate removed; the identity.is_empty() check stays, since a warning with no account to attribute it to should still be dropped. Covered by any_provider_can_raise_a_predictive_warning and predictive_warnings_still_need_an_identity.

  2. remaining computed from the unclamped value — right, and it would have disagreed with the bar beside it. Now derived from the clamped percentage, so a non-finite value off the bridge reads "100% to spare" instead of "NaN% to spare". Two tests added for the NaN and overshoot cases.

801 rust / 474 tauri / 364 vitest, clippy clean.

@tsouth89
tsouth89 merged commit 0e431c1 into main Aug 5, 2026
11 checks passed
@tsouth89
tsouth89 deleted the feat/pace-verdict branch August 5, 2026 01:59
@tsouth89 tsouth89 mentioned this pull request Aug 5, 2026
tsouth89 added a commit that referenced this pull request Aug 5, 2026
Prepares **Ceiling 1.5.23**, carrying the two feature requests merged
since v1.5.22 plus two cleanups found while verifying them.

- **#202** — Show Cursor on-demand spend in dollars (closes #191)
- **#203** — Add a pace verdict and re-expose predictive warnings
(closes #190)

## Also in this PR

### Removed the Cursor "Promotional" lane

The percentage was `plan.used - breakdown.included`, clamped to the
bonus. The included lane is its own closed set — `plan.used ==
breakdown.included == plan.limit` on every real payload — so the
subtraction was identically zero and the meter read **0% permanently**.
Confirmed against a live account: `Promotional 0.00%`.

The `PromoSignal` badge it fed took its expiry from the billing cycle
end, while Cursor credits expire on their own schedule (the dashboard
shows a 2027 date on my account). So it was telling users their bonus
expired about a year early.

Neither number is recoverable: `breakdown.bonus` is bonus *consumed*,
and the grant it would be metered against is not in the payload. Removed
rather than guessed at.

### Stopped mutating process environment in cost-scanner tests

`unsafe { env::set_var(...) }` on `CODEX_HOME`, `CLAUDE_CONFIG_DIR`, and
`GROK_HOME` is undefined behaviour while any other thread reads those
variables, and nine modules read them constantly. Unrelated scans
intermittently resolved the wrong home.

The failure was also disguised: one panicking test poisoned the shared
mutex, and the three tests that then failed on `.expect()` made a single
fault look like four.

`CostScanner` now takes an injected `ambient_home_override`, so no test
touches process env. The mutex and its now-dead helper constructor are
gone. Measured before/after: **~1 failing run in 5 → 12 consecutive
clean runs.**

## Not included

A sweep of ~36 providers turned up three more meters that may rest on
inferred denominators (`commandcode`, `minimax`, `manus`). None could be
verified without accounts on those services, and guessing at field
semantics is what produced the bug this PR removes. Left alone
deliberately; worth a follow-up issue for someone who can check them.

## Verification

796 rust / 474 tauri / 364 vitest, clippy clean, tsc clean.

---------

Co-authored-by: tsouth89 <tsouth89@users.noreply.github.com>
tsouth89 added a commit that referenced this pull request Aug 5, 2026
…#205)

Follow-up to #203 and #202, prepared as **1.5.24**.

## Why this exists

#203 put a pace verdict in `MenuCard`. `<MenuCard` renders in exactly
one place — `TrayPanel.tsx:466`. The main app window is `PopOutPanel`,
which renders `ProviderDetailView`, a component that change never
touched. So the pace answer, and the Cursor on-demand dollars from #202,
never appeared in the window most people use.

## The marker

Every weekly and monthly bar in the Overview and in a provider's detail
view now shows where usage *should* be. One rule everywhere: **the
marker is where the bar's edge should be right now.**

Overspending fills the span between edge and marker with a striped band,
so it reads as a distance rather than as a line you have to decode. Bars
showing remaining capacity mirror the marker, and `ahead` is computed
from usage rather than bar geometry so it means the same thing in both
modes — pinned by a test asserting both modes agree on the same window.

Derived from `elapsed / duration`, not from `PaceSnapshot`. That means
no provider support is needed and it applies to every long window,
rather than the single window pace is computed for. Windows under 12h
are skipped: a 5-hour session is not spent evenly, so a marker there
would sweep across the bar and mean nothing.

## Cursor on-demand

`ProviderDetailView` filtered it out by name:

```ts
.filter((metric) => !/promotional|on-demand/i.test(metric.label))
```

So #202's plumbing was landing correctly and being discarded at the last
step. Filter removed; the **On-demand** row now shows with its spend
beside it. (`promotional` no longer exists at all after #204.)

## Two bugs only running the app could find

1. **`.usage-bar` collided with an existing component** at
`styles.css:1831`, which sets `display: flex`. My bar became a flex
item, collapsed to zero width, and vanished — while the
absolutely-positioned marker kept rendering, leaving markers floating
with no bars. Renamed to `.pace-overlay*`.
2. **Red on red was invisible.** On an exhausted bar the fill is already
red, so a red marker and band disappeared into it. Both now use the text
colour.

Neither was reachable from unit tests or a static mock.

## Verification

Run against live provider accounts and screenshotted, plus 801 rust /
375 vitest, clippy clean, tsc clean, and `local-check.ps1 -All -Version
1.5.24` green.

One stale test updated: `ProviderDetailView.test.tsx` asserted the
filtered lane was absent. Its fixture now carries an on-demand window
with money and asserts it renders.

---------

Co-authored-by: tsouth89 <tsouth89@users.noreply.github.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.

[Feature]: On track // Too much usage

2 participants