$ cat blog/provider-scoped-usage-cache

Cache Claude and Codex Usage Per Provider

A shared cache file can hold both providers, but a successful Claude refresh must never make an old Codex reading look new, or vice versa.

A desktop usage view needs a cache for cold starts and temporary network failures. The dangerous shortcut is to give the whole snapshot one freshness timestamp. If Claude refreshes successfully while Codex fails, rewriting the file with a new timestamp can silently extend the life of stale Codex data.

The fix is to model freshness at the same boundary as failure: one timestamp per provider, plus a conservative rule for what counts as cacheable data.

Store values and freshness together

Agent Island v1.7.1 defines a snapshot with Claude data, Codex data, an overall compatibility timestamp, and separate claudeUpdatedAt and codexUpdatedAt fields. Older snapshots can fall back to the legacy timestamp, while new writes retain each provider's real age.

snapshot = {
  claude,
  codex,
  updatedAt,
  claudeUpdatedAt,
  codexUpdatedAt
}

The overall timestamp is still useful for decoding old records and quick rejection, but it does not authorize both provider values to restore.

Do not cache errors as fresh data

A provider result is cacheable only when the relevant windows have no error and at least one real signal exists: a positive percentage or a reset timestamp. A plan label with two zero-percent windows is not enough. Without that guard, an authentication failure or empty response can be saved as a clean-looking 0% reading.

Error-bearing values are also rejected even when they preserve percentages from a previous successful fetch. Those percentages may be useful for the current screen, but saving them under the new time would launder stale data into a fresh cache entry.

A partial success may preserve, but not renew

Suppose Claude returns a fresh result and Codex times out. The snapshot may save Claude's new value while carrying forward the last valid Codex value. The timestamps must tell the truth:

claude value: new          claudeUpdatedAt: now
codex value: preserved     codexUpdatedAt: old timestamp

If neither provider produced fresh cacheable data, no new snapshot is written. This prevents repeated failed refreshes from extending the cache indefinitely. An unfetched provider follows the same rule: it can retain a valid existing value, but it never receives a fresh timestamp simply because its peer was fetched.

Expire each provider independently

Restore checks compare each provider timestamp with the maximum age. One provider may restore while the other becomes empty. The cache remains useful during a partial outage without presenting both values with the same confidence.

This policy also handles schema evolution. Codex's July 2026 response can contain one meaningful usage window while marking the secondary window as absent. That shape remains cacheable when the real window has usage or reset data. Requiring two fully populated windows would turn a valid provider response into a permanent cache miss.

Policy tests that catch timestamp laundering

  1. Error-bearing preserved usage cannot create or renew a snapshot.
  2. Plan-only zero-percent data cannot cache or restore as healthy usage.
  3. Real usage keeps its percent and reset time while cached errors are stripped.
  4. A fresh Claude result preserves an older Codex value without changing the Codex timestamp.
  5. An unfetched provider never gets a fresh timestamp.
  6. Restore can keep one provider and expire the other.
  7. A valid single-window provider shape remains cacheable.

Product and measurement boundary

The cache policy exists in the tagged macOS and Windows v1.7.1 source and has corresponding tests. The user-visible macOS usage surface is verified. The existence of the Windows policy does not, by itself, prove every Windows usage-screen behavior; that requires separate UI verification.

Usage percentages and local API-value estimates are also not subscription invoices, savings, revenue, user counts, or installs. Cache correctness improves the honesty of the display, not the meaning of the underlying metric.

The implementation is available in the public Agent Island v1.7.1 source. The reusable rule is simple: scope freshness to the failing dependency, never renew data you did not fetch, and require evidence before turning an empty response into 0%.