Skip to content

Fix reset refresh timer for resets over 24.8 days away - #720

Draft
Finesssee wants to merge 1 commit into
mainfrom
fix/reset-timer-overflow
Draft

Finesssee wants to merge 1 commit into
mainfrom
fix/reset-timer-overflow

Conversation

@Finesssee

@Finesssee Finesssee commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Usage resets more than about 24.8 days away no longer force an immediate provider refresh.

useProviders arms one setTimeout for the soonest reset across all providers, then calls the forced refreshProviders(), which skips the stale-cache check. WebView2 stores a timer delay as a signed 32-bit integer, so the longest delay it can hold is 2,147,483,647 ms (about 24.8 days):

  • A reset 24.9 to 49.7 days away wraps to a negative delay, so the timer fires at once.
  • A reset 49.7 to 74.6 days away wraps to a shorter positive delay, so it fires early.
  • Each snapshot merge re-arms the timer, so a completed refresh with the same resets can start the next forced refresh straight away.

A monthly plan early in its cycle is enough to hit this. For example, a Cursor- or Copilot-only user in the first days of a billing month.

The #719 proof found this on main:

  • A Cursor seed with a 2099 reset was replaced by two forced Cursor fetches within 0.8 s of launch.
  • In the same WebView2 page, a timer of 2,280,193,515,000 ms fired after 0 ms, while a 7-day control timer did not fire.

Change

  • The hook now waits at most 2,147,483,647 ms per timer. When the reset is further away, the timer re-arms itself with the remaining time, and only the last timer calls refresh().
  • Resets within 24.8 days keep the old timing: one second after the reset, and at least five seconds out.
  • The effect cleanup still clears whichever timer is pending.

Upstream reference

None. This is a Windows-port frontend bug in code from "Port upstream 0.38.0" (3b39f595). The upstream Swift app doesn't use setTimeout.

Ported / Deferred

Nothing is ported or deferred. No other frontend timer takes a date-derived delay: the rest use fixed delays or the refresh interval setting.

Validation

Run in the worker worktree on Rust 1.98.0 and Node 24.

Command Result
pnpm install --frozen-lockfile pass
pnpm exec vitest run src/hooks/useProviders.test.tsx before the fix the new 30-day test fails: refreshProviders was called after 60 s
pnpm exec vitest run src/hooks/useProviders.test.tsx with the fix 16 tests passed
pnpm run check-locale 871 keys match between Rust and TS
pnpm test 65 files, 395 tests passed
pnpm run lint 0 errors. The 11 warnings are all in files this PR doesn't touch.
pnpm run build pass
cargo +1.98.0 fmt --all --check pass
cargo +1.98.0 clippy --workspace --all-targets -- -D warnings pass
cargo +1.98.0 test -p codexbar 2160 passed, 0 failed, 1 ignored
cargo +1.98.0 test -p codexbar-desktop-tauri 459 passed, 1 failed: bootstrap_payload_exposes_every_provider_variant, the non-hermetic #684 test that #711 fixes

New tests in useProviders.test.tsx:

  • Refreshes once the soonest reset has passed: a reset 10 minutes out. There's no refresh at 10 minutes, and one forced refresh a second later.
  • Waits for a reset beyond the 32-bit timer limit: a reset 30 days out. There's no refresh after 60 s, none after a re-armed update and 25 more days, none at the reset itself, and exactly one forced refresh a second after it.

Affected areas

  • apps/desktop-tauri/src/hooks/useProviders.ts: the reset refresh timer.
  • apps/desktop-tauri/src/hooks/useProviders.test.tsx: two new tests.

UI proof

PASS at de9b018e (browser-use over WebView2 CDP, no keyboard, mouse or focus): #720 (comment)

  • In the same WebView2 page, a timer longer than 2,147,483,647 ms fired at once.
  • Control build (main 7695471): a 30-day Codex seed was replaced by a forced fetch 0.9 s after launch.
  • Fixed build: the same 30-day seed stayed for 163 s with no fetch, and a reset 90 s out still refreshed 1.0 s after the reset.
  • No host email or account was visible, the theme stayed dark under auto, and the foreground window never changed.

The red CircleCI check on this head (started 00:39 UTC) failed in codex_source_recovery_keeps_appended_duplicate_unpriced_after_cache_reload and the two paginated fork tests. Those Rust tests fail on any branch between 00:00 and 01:00 UTC, and #721 fixes them. This PR changes no Rust code.

useProviders armed one setTimeout for the soonest provider reset and then
called the forced refreshProviders(). WebView2 stores a timer delay as a
signed 32-bit integer, so a reset 24.9 to 49.7 days away wrapped to a
negative delay and refreshed at once, and every snapshot merge re-armed it.
A monthly plan early in its cycle is enough to hit this.

The hook now waits at most 2,147,483,647 ms per timer and re-arms with the
remaining time until the reset is close enough. Nearer resets keep the old
timing: one second after the reset, at least five seconds out.

Tests cover the normal reset refresh and a 30-day reset, which refreshed
at once before this change.
@coderabbitai

coderabbitai Bot commented Oct 1, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@Finesssee

Copy link
Copy Markdown
Collaborator Author

UI proof (browser-use)

Result: PASS on build de9b018e5d723e93ec2e447e626118082aa07932, the current PR head "Fix reset refresh timer for resets over 24.8 days away".

  • Control (unfixed main): a Codex seed with a 30-day reset was replaced by a forced Codex fetch 0.9 s after launch.
  • This build: the same seed stayed in place for the whole run, with no fetch.
  • Near resets: a reset 90 s out still refreshed on time, with one forced fetch 1.0 s after the reset.

At the maintainer's direction, this proof drove the app's WebView2 over CDP with the browser-use CLI instead of CUA. It used no keyboard, mouse or focus, and the global shortcut was off in the kit settings.

Setup

  • Builds: each build ran pnpm run tauri:build:debug in the worker worktree, and its exe was copied to the proof kit.
    • bin\ is this PR at de9b018e. Its exe's SHA-256 is 10ea5b3a…77e19cf.
    • bin-before\ is the control: the PR's parent, main at 7695471b. Its exe's SHA-256 is 037c58ff…bbd7fd6.
  • Proof-only patch: both builds got the same patch. It was never committed, and it was reverted after each build.
    • A workspace Cargo.toml [patch.crates-io] dirs shim for an isolated home, plus the resulting Cargo.lock change.
    • A no-focus overlay, because main doesn't include Stop CodexBar from stealing focus #713 yet. It changes only window focus:
      • drops the set_focus calls;
      • builds the windows with focused(false);
      • adds SWP_NOACTIVATE to the DWM frame refresh.
  • Data:
    • Home: every run got its own fresh home under the kit, which covered USERPROFILE, HOME, APPDATA, LOCALAPPDATA and XDG_CONFIG_HOME.
    • Providers: CODEX_HOME, CLAUDE_CONFIG_DIR and GEMINI_HOME pointed at empty kit folders, and the provider API key variables were unset. PATH was cut to the Windows system folders, so no provider CLI could run.
    • Settings: only Codex was enabled. The theme was auto and the float bar was on.
  • Seed: CODEXBAR_SEED_USAGE_JSON held one bridge-shaped Codex snapshot, generated just before each launch with errorState: "ready".
    • Runs A and B: the primary window is 43,200 minutes at 61% used. It resets 30 days after generation, so the old timer delay is 2,592,001,000 ms. The weekly window is 74% used and has no reset.
    • Run C: the primary window is 300 minutes at 61% used and resets 90 s after generation. The weekly window is the same as in A and B.
  • Why fetch lines time the reset timer:
    • While the seed is active, non-forced refreshes skip the fetch (provider_cache_can_skip_refresh), and the backend auto-refresh loop is non-forced. So a Codex fetch happens only on a forced refresh_providers, which is what the reset timer calls.
    • With the empty CODEX_HOME, a fetch fails at once with "Codex auth.json not found", with no network and no keyring. That error then replaces the seed in the cache.
  • Commands:
    • Launches: bash launch.sh before A, bash launch.sh fixed B and bash launch.sh fixed C, one app at a time. Each sets WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS=--remote-debugging-port=9335 ... and CODEXBAR_PROOF_MODE=trayPanel.
    • Checks: BU_CDP_URL=http://127.0.0.1:9335 BU_NAME=worker-720 BH_TAB_MARKER=0 browser-use < bu-check.py.
    • Before each attach, curl /json/version showed Edg/154 WebView2, and the port 9335 listener was a WebView2 child of that run's kit exe.

Results

# Assertion Result
T0 No real email or account from the host is visible. DOM scans of the tray panel and the float bar in all five checks found no @ addresses and no host user name. Each scan ran before any screenshot. PASS
T1 Each run used the intended frontend. The control loads index-DGyH24zP.js, which has no 2147483647 constant. This build loads index-CSxmLyT3.js, which has one. PASS
T2 WebView2 wraps timer delays above 2^31-1 ms. In-page checks in runs A and B: a 2,592,001,000 ms (30-day) setTimeout fired after 0 ms, and a 2,147,483,647 ms one didn't fire within 500 ms. Both timers were cleared afterwards. PASS
T3 The control reproduces the bug (run A, main at 7695471b). The seed loaded at 00:48:05.560Z. Codex fetch lines followed at +0.886 s and +0.920 s, and there were none in the rest of the run (about 2 minutes). At +24 s, get_cached_providers held the auth error (needsAuthentication, no usage). The panel showed that error, and the float bar showed "Sign-in required". PASS (bug reproduced)
T4 This build keeps a 30-day reset from refreshing (run B). The seed loaded at 00:50:40.860Z, and the log has no Codex fetch line for the whole run. At the late check (00:53:24Z, 163 s after the seed), the cache still held the seed: sourceLabel seed, ready, 61% and 74%. The panel showed "30-Day 61% used" and "Weekly 74% used", and the float bar showed "74%". PASS
T5 Near resets still refresh on time (run C, this build). The seed's reset was at 00:55:15Z. At 00:54:05Z the cache held the seed. The only fetch line came at 00:55:16.008Z, 1.009 s after the reset. At 00:55:40Z the cache held the auth error. PASS
T6 Dark under theme auto in every check. The tray panel has data-theme=dark, and its body is rgb(28, 28, 30) with text rgb(245, 245, 247). The float bar has data-theme=dark, a transparent background and text rgba(255, 255, 255, 0.95). PASS
T7 The proof never took the foreground. fg-monitor.ps1 sampled the foreground window every 15 ms: 9,053 samples in run A, 11,257 in B and 7,973 in C. None of them were on the proof's process tree. Both app pages report document.hasFocus() as false. PASS
— Native paths Not applicable: this change is frontend-only.

Validation at de9b018e

Run in the worker worktree on Rust 1.98.0 and Node 24:

Command Result
pnpm install --frozen-lockfile pass
pnpm exec vitest run src/hooks/useProviders.test.tsx without the fix The new 30-day test fails: refreshProviders was called after 60 s.
pnpm exec vitest run src/hooks/useProviders.test.tsx with the fix 16 tests passed
pnpm run check-locale 871 keys match between Rust and TS
pnpm test 65 files, 395 tests passed
pnpm run lint 0 errors. The 11 warnings are all in files this PR doesn't touch.
pnpm run build pass
cargo +1.98.0 fmt --all --check pass
cargo +1.98.0 clippy --workspace --all-targets -- -D warnings pass
cargo +1.98.0 test -p codexbar 2160 passed, 0 failed, 1 ignored
cargo +1.98.0 test -p codexbar-desktop-tauri 459 passed, 1 failed: bootstrap_payload_exposes_every_provider_variant, the non-hermetic #684 test that #711 fixes

Screenshots

All paths are under C:\Users\FSOS\AppData\Local\Win-CodexBar\port-audit\proof\720\shots\.

File Run When What it shows
bu-A-early-traypanel.png Control (A) 24 s after launch The Codex card shows the auth error instead of the seeded usage.
bu-B-early-traypanel.png This build (B) 21 s after launch "30-Day 61% used" and "Weekly 74% used"
bu-B-late-traypanel.png This build (B) 163 s after launch Byte-identical to the 21 s screenshot.
bu-C-early-traypanel.png This build (C) 70 s before the reset "Session 61% used" and "Weekly 74% used"
bu-C-late-traypanel.png This build (C) 24 s after the reset The auth error, byte-identical to bu-A-early-traypanel.png

Not blocking (found on main, not caused by this PR)

  • The card's "Updated just now" label doesn't age while the panel stays open.
    • In run B the card still read "Updated just now" at the late check, 163 s after the seed's updatedAt.
    • MenuCard formats the label with Date.now() during render, and nothing re-renders the card header on a timer. So the label changes only when provider data changes, and between refreshes it can understate the data's age.
  • An empty global_shortcut logs WARN codexbar_desktop_tauri::shortcut_bridge: Could not parse global shortcut: at every launch.

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