Add a pace verdict and re-expose predictive warnings (#190) - #203
Conversation
Deploying with
|
| 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 |
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 14 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (15)
Comment |
There was a problem hiding this comment.
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
PaceVerdicttray 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.
b584b65 to
4f79477
Compare
868b515 to
ddb7bdc
Compare
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.
ddb7bdc to
9e1ad8e
Compare
- 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.
|
Both correct, fixed in 20fa394.
801 rust / 474 tauri / 364 vitest, clippy clean. |
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>
…#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>
Closes #190.
Stacked on #202. Review the third commit.
Why nobody could see this
Ceiling already computed the answer. It just could not be reached:
display: nonein 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.falseon 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.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
Sessionlimit 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.