Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 3 additions & 203 deletions apps/desktop-tauri/src-tauri/src/commands/providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use codexbar::core::HookUsageWindow;
use serde::Serialize;
use std::sync::Arc;

mod reset_backfill;

const MAX_CONCURRENT_PROVIDER_FETCHES: usize = 8;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Expand Down Expand Up @@ -594,7 +596,7 @@ async fn refresh_provider(
.find(|c| c.provider_id == snapshot.provider_id && c.error.is_none())
.cloned();
let mut snapshot = snapshot;
codex_reset_backfill(&mut snapshot, cached.as_ref());
reset_backfill::codex_reset_backfill(&mut snapshot, cached.as_ref());
upsert_provider_cache(&mut guard.provider_cache, snapshot.clone());
if fresh_snapshot {
guard
Expand Down Expand Up @@ -678,82 +680,6 @@ fn dispatch_usage_updated_hook(
);
}

/// F6 (upstream 0.48.0 UsageStore+CodexResetBackfill): backfill missing
/// `resets_at` / `reset_description` on fresh Codex windows from the cached
/// lane data when the cached reset is still future. z.ai five-hour cached
/// resets use the same plausibility bound as the provider parser, so an
/// impossible rejected reset cannot be restored from the cache. Fresh
/// `used_percent` is untouched; only reset metadata is backfilled.
///
/// This remains provider-scoped by design (upstream: "Provider-specific by
/// design"): only Codex and z.ai carry the relevant bounded reset semantics.
///
/// Applies to the bridge snapshot before publishing so every surface (tray,
/// CLI, frontend) sees the backfilled reset instead of a missing one.
pub(super) fn codex_reset_backfill(
snapshot: &mut ProviderUsageSnapshot,
cached: Option<&ProviderUsageSnapshot>,
) {
let Some(cached) = cached else { return };
if !matches!(snapshot.provider_id.as_str(), "codex" | "zai") {
return;
}

// Backfill each slot from the corresponding cached slot.
backfill_slot_window(
&snapshot.provider_id,
&mut snapshot.primary,
&cached.primary,
);
if let (Some(fresh), Some(cached_sec)) = (&mut snapshot.secondary, &cached.secondary) {
backfill_slot_window(&snapshot.provider_id, fresh, cached_sec);
}
// Tertiary (monthly/other): the Codex bridge doesn't normally populate this,
// but the slot exists for forward-compat. Backfill when available.
if let (Some(fresh), Some(cached_ter)) = (&mut snapshot.tertiary, &cached.tertiary) {
backfill_slot_window(&snapshot.provider_id, fresh, cached_ter);
}
}

/// Backfill `resets_at` and `reset_description` on a fresh window from the
/// cached window whose reset is still in the future. `used_percent` is never
/// overwritten (upstream: "fresh used_percent untouched").
fn backfill_slot_window(
provider_id: &str,
fresh: &mut bridge::RateWindowSnapshot,
cached: &bridge::RateWindowSnapshot,
) {
if fresh.resets_at.is_some() {
return;
}
let Some(cached_reset) = &cached.resets_at else {
return;
};
// Only backfill when the cached reset is still future — a stale reset is
// worse than a missing one.
let Ok(cached_dt) = chrono::DateTime::parse_from_rfc3339(cached_reset) else {
return;
};
let now = chrono::Utc::now();
if cached_dt <= now {
return;
}
// A missing z.ai five-hour reset can mean the provider rejected an
// impossible future timestamp. Do not let equally impossible cached
// evidence undo that rejection, but preserve a plausible cached reset.
if provider_id == "zai"
&& fresh.window_minutes == Some(300)
&& cached_dt > now + chrono::Duration::minutes(5 * 60 + 1)
{
return;
}
fresh.resets_at = Some(cached_reset.clone());
fresh.reset_description = fresh
.reset_description
.clone()
.or_else(|| cached.reset_description.clone());
}

#[cfg(test)]
pub(super) fn preserve_last_good_transient_failure(
guard: &mut AppState,
Expand Down Expand Up @@ -1301,129 +1227,3 @@ mod predictive_warning_tests {
}
}
}

#[cfg(test)]
mod reset_backfill_tests {
use super::*;
use crate::commands::bridge::{ProviderUsageSnapshot, RateWindowSnapshot};

fn win(used: f64, resets_at: Option<&str>) -> RateWindowSnapshot {
RateWindowSnapshot {
used_percent: used,
remaining_percent: 100.0 - used,
window_minutes: Some(300),
resets_at: resets_at.map(String::from),
reset_description: None,
is_exhausted: false,
is_informational: false,
reserve_percent: None,
reserve_description: None,
reserve_will_last_to_reset: false,
reserve_eta_seconds: None,
}
}

fn codex_snapshot(primary: RateWindowSnapshot) -> ProviderUsageSnapshot {
ProviderUsageSnapshot {
provider_id: "codex".into(),
display_name: "Codex".into(),
primary,
primary_label: None,
secondary: None,
secondary_label: None,
model_specific: None,
tertiary: None,
tertiary_label: None,
extra_rate_windows: Vec::new(),
inventory: Vec::new(),
display_details: Vec::new(),
cost: None,
plan_name: None,
account_email: None,
subscription: None,
source_label: String::new(),
has_successful_claude_cli_quota: false,
updated_at: "2026-01-01T00:00:00Z".into(),
error: None,
error_state: codexbar::core::ProviderStateKind::Ready,
pace: None,
account_organization: None,
tray_status_label: None,
fetch_duration_ms: None,
wayfinder_usage: None,
session_equivalent_forecast: None,
}
}

#[test]
fn f6_backfills_future_cached_reset() {
// Cached has a future resets_at; fresh has none → backfilled.
let future = (chrono::Utc::now() + chrono::Duration::hours(2)).to_rfc3339();
let cached = codex_snapshot(win(50.0, Some(&future)));
let mut fresh = codex_snapshot(win(30.0, None));
codex_reset_backfill(&mut fresh, Some(&cached));
assert_eq!(fresh.primary.resets_at.as_deref(), Some(future.as_str()));
// used_percent is NOT overwritten.
assert!((fresh.primary.used_percent - 30.0).abs() < f64::EPSILON);
}

#[test]
fn f6_does_not_backfill_stale_cached_reset() {
// Cached reset is in the past → not backfilled.
let past = (chrono::Utc::now() - chrono::Duration::hours(2)).to_rfc3339();
let cached = codex_snapshot(win(50.0, Some(&past)));
let mut fresh = codex_snapshot(win(30.0, None));
codex_reset_backfill(&mut fresh, Some(&cached));
assert!(
fresh.primary.resets_at.is_none(),
"stale reset not backfilled"
);
}

#[test]
fn f6_does_not_overwrite_existing_resets_at() {
// Fresh already has resets_at → cached not applied.
let future1 = (chrono::Utc::now() + chrono::Duration::hours(3)).to_rfc3339();
let future2 = (chrono::Utc::now() + chrono::Duration::hours(5)).to_rfc3339();
let cached = codex_snapshot(win(50.0, Some(&future2)));
let mut fresh = codex_snapshot(win(30.0, Some(&future1)));
codex_reset_backfill(&mut fresh, Some(&cached));
assert_eq!(fresh.primary.resets_at.as_deref(), Some(future1.as_str()));
}

#[test]
fn f6_skips_non_codex_provider() {
let future = (chrono::Utc::now() + chrono::Duration::hours(2)).to_rfc3339();
let mut cached = codex_snapshot(win(50.0, Some(&future)));
cached.provider_id = "claude".into();
let mut fresh = codex_snapshot(win(30.0, None));
fresh.provider_id = "claude".into();
codex_reset_backfill(&mut fresh, Some(&cached));
assert!(fresh.primary.resets_at.is_none(), "non-codex skip");
}

#[test]
fn zai_five_hour_backfill_rejects_impossible_cached_reset() {
for (offset, should_backfill) in [
(chrono::Duration::hours(1), true),
(chrono::Duration::hours(10), false),
] {
let future = (chrono::Utc::now() + offset).to_rfc3339();
let mut cached = codex_snapshot(win(50.0, Some(&future)));
cached.provider_id = "zai".into();
let mut fresh = codex_snapshot(win(30.0, None));
fresh.provider_id = "zai".into();
fresh.primary.reset_description = Some("5-hour".into());
codex_reset_backfill(&mut fresh, Some(&cached));
assert_eq!(fresh.primary.resets_at.is_some(), should_backfill);
assert!((fresh.primary.used_percent - 30.0).abs() < f64::EPSILON);
}
}

#[test]
fn f6_skips_when_no_cached_snapshot() {
let mut fresh = codex_snapshot(win(30.0, None));
codex_reset_backfill(&mut fresh, None);
assert!(fresh.primary.resets_at.is_none());
}
}
Loading