Files
steipete--codexbar/docs/refresh-loop.md
T
2026-07-13 12:22:33 +08:00

56 lines
3.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
summary: "Refresh cadence, background updates, and error handling."
read_when:
- Changing refresh cadence, background tasks, or refresh triggers
- Investigating refresh timing or stale data behavior
---
# Refresh loop
## Cadence
- `RefreshFrequency`: Manual, 1m, 2m, 5m (default), 15m, 30m, Adaptive.
- Stored in `UserDefaults` via `SettingsStore`. The default stays 5m for new and existing users; nothing
auto-migrates an existing fixed selection to Adaptive.
## Behavior
- Background refresh runs off-main and updates `UsageStore` (usage + credits + optional web scrape).
- Manual “Refresh now” always available in the menu.
- Stale/error states dim the icon and surface status in-menu.
- Optional provider-storage scans run only when “Show provider storage usage” is enabled. They are scheduled in the
background, coalesced/throttled during automatic refreshes, and forced by manual refresh without blocking the usage
refresh path.
## Adaptive mode
- `AdaptiveRefreshPolicy` (`Sources/CodexBar/AdaptiveRefreshPolicy.swift`) is a pure function of an `Input`
(current time, last menu-open time, Low Power Mode, thermal state) that returns the next delay and a
stable `Reason`. It reads no clock and no `ProcessInfo` state itself — `UsageStore.startTimer()` gathers
those impure signals immediately before each tick.
- Policy table, first match wins:
| Condition | Delay | Reason |
|---|---:|---|
| Low Power Mode enabled, or thermal state `.serious`/`.critical` | 30 min | `constrained` |
| Menu opened within the last 5 min (including future/clock-adjusted timestamps) | 2 min | `recentInteraction` |
| Menu opened 5 min1 h ago | 5 min | `warm` |
| Menu opened 14 h ago | 15 min | `idle` |
| No recorded menu open, or opened 4+ h ago | 30 min | `longIdle` |
- Every decision falls in the 230 min range by construction. Deliberately excludes quota, latency, error,
account, and time-of-day signals.
- `UsageStore` tracks `lastMenuOpenAt` in memory only (never persisted; resets on launch). `noteMenuOpened(at:)`
is called from `StatusItemController.menuWillOpen(_:)`. A menu open can bring a pending adaptive tick
forward to the recent-interaction cadence, but never postpones an earlier tick or refreshes synchronously.
- Each adaptive tick recomputes the delay after the previous refresh completes, sleeps, then calls the same
`UsageStore.refresh()` used by fixed-interval mode, so the existing `isRefreshing` coalescing guard still
applies — only one provider-batch refresh runs at a time regardless of cadence mode.
- Selected delay and reason are logged (e.g. `reason=warm delay=300s` in the `adaptive-refresh` category) through
the existing local logger; never provider identity, account, email, workspace, path, credentials, or response data.
- Interval-derived heuristics (reset-boundary refresh, OpenAI web staleness, persistent-CLI-session idle windows)
read `UsageStore.normalRefreshIntervalForHeuristics()`, which resolves adaptive mode to the current decision's
delay — they stay active in adaptive mode rather than degrading to manual, whose interval is nil.
## Optional future
- Auto-seed a log if none exists via `codex exec --skip-git-repo-check --json "ping"` (currently not executed).
See also: `docs/status.md`, `docs/ui.md`.