CodexBar Refresh Loop Timing and Error Recovery: A Deep Dive into AdaptiveRefreshPolicy
CodexBar uses a pure AdaptiveRefreshPolicy to calculate dynamic refresh intervals based on user interaction patterns and system constraints, while UsageStore implements exponential back-off error recovery that starts at 30 seconds and caps at 10 minutes.
The steipete/CodexBar repository implements a sophisticated refresh architecture that balances real-time data accuracy with system resource conservation. Understanding the refresh loop timing and error recovery mechanisms requires examining how AdaptiveRefreshPolicy makes deterministic scheduling decisions and how UsageStore handles transient failures without user intervention.
Understanding AdaptiveRefreshPolicy
The AdaptiveRefreshPolicy struct in Sources/CodexBar/AdaptiveRefreshPolicy.swift serves as a pure decision engine that calculates the optimal delay before the next automatic refresh. It accepts an Input snapshot containing the current time, last menu interaction timestamp, Low-Power mode status, and thermal state, then returns a Decision containing both the delay duration and a human-readable reason tag.
The Four Interaction-Based Timing Signals
The policy categorizes user engagement into four distinct states, each with specific thresholds and corresponding delays:
- Recent interaction: When the menu was opened within the last 5 minutes (
recentInteractionThreshold), the system waits 120 seconds (recentInteractionDelay) before the next refresh, tagged as.recentInteraction. - Warm: If the last interaction occurred between 5 minutes and 1 hour ago (
warmThreshold), the delay extends to 300 seconds (warmDelay) with reason.warm. - Idle: For interactions between 1 hour and 4 hours ago (
idleThreshold), the delay increases to 900 seconds (idleDelay) marked as.idle. - Long idle: When no interaction has occurred for 4 hours or more, or when
lastMenuOpenAtis nil, the system enters a 1800-second (longIdleDelay) cycle tagged.longIdle.
System Constraint Detection
Beyond time-based signals, the policy monitors hardware conditions through ProcessInfo. When the device enters Low-Power mode or reports a thermal state of .serious or .critical, the policy immediately switches to a constrained delay of 1800 seconds (constrainedDelay), regardless of interaction history. This ensures CodexBar respects system-wide resource limitations.
UsageStore and Refresh Loop Implementation
The actual refresh orchestration occurs in Sources/CodexBar/UsageStore.swift. When UsageStore.refresh() completes—whether successfully or with an error—the store queries AdaptiveRefreshPolicy for the next appropriate interval and schedules execution via DispatchQueue.main.asyncAfter.
The scheduling process follows this flow:
- Construct an
AdaptiveRefreshPolicy.Inputsnapshot with current system variables. - Call
policy.nextDelay(for: input)to receive aDecisioncontaining the delay and reason. - Calculate the actual timer interval based on error state (policy delay vs. retry delay).
- Schedule the next refresh using
DispatchQueue.main.asyncAfter(deadline: .now() + interval).
Because the policy is pure—containing no clock reads or side effects—it guarantees deterministic results for identical inputs, making the system predictable and testable.
Error Recovery and Exponential Back-Off
When network failures, credential expiry, or malformed responses occur, UsageStore implements a resilient error recovery mechanism that prevents aggressive retry storms while ensuring eventual consistency.
The back-off algorithm operates as follows:
- Initial retry delay: Starts at 30 seconds (
retryDelay = .seconds(30)). - Exponential growth: On consecutive failures, the delay doubles (
retryDelay * 2). - Maximum cap: Hard limit at 10 minutes (
maxRetryDelay = .minutes(10)). - Success reset: Immediately returns to 30 seconds upon successful refresh.
Errors are stored in store.lastRefreshError, which the UI observes via MenuCardRefreshMonitor.swift to display visual indicators such as red badges or status messages.
Practical Code Examples
Inspecting the next refresh interval:
import Foundation
let policy = AdaptiveRefreshPolicy()
let input = AdaptiveRefreshPolicy.Input(
now: Date(),
lastMenuOpenAt: Date().addingTimeInterval(-300), // 5 minutes ago
lowPowerModeEnabled: false,
thermalState: .nominal
)
let decision = policy.nextDelay(for: input)
print("Next refresh in \(decision.delay) because of \(decision.reason.rawValue)")
// Output: "Next refresh in 300s because of warm"
Integrating the policy in UsageStore:
final class UsageStore {
private let refreshPolicy = AdaptiveRefreshPolicy()
private var retryDelay = Duration.seconds(30)
func startLoop() {
scheduleRefresh()
}
private func scheduleRefresh() {
let input = AdaptiveRefreshPolicy.Input(
now: Date(),
lastMenuOpenAt: lastMenuOpen,
lowPowerModeEnabled: ProcessInfo.processInfo.isLowPowerModeEnabled,
thermalState: ProcessInfo.processInfo.thermalState
)
let decision = refreshPolicy.nextDelay(for: input)
let delay = lastRefreshError == nil ? decision.delay : retryDelay
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { self.refresh() }
}
private func refresh() async {
do {
try await fetchUsageData()
lastRefreshError = nil
retryDelay = .seconds(30) // Reset back-off
} catch {
lastRefreshError = error
retryDelay = min(retryDelay * 2, .minutes(10))
}
scheduleRefresh() // Schedule next tick
}
}
Logging refresh decisions for diagnostics:
func logRefreshDecision(_ decision: AdaptiveRefreshPolicy.Decision) {
os_log("Refresh scheduled: %{public}s (%{public}s)",
decision.delay.description,
decision.reason.rawValue)
}
Key Source Files
The refresh loop architecture spans several components in the steipete/CodexBar repository:
Sources/CodexBar/AdaptiveRefreshPolicy.swift: Implements the pure decision engine that maps system signals to refresh intervals.Sources/CodexBar/UsageStore.swift: Central store managing the refresh timer, error state, and back-off logic.Sources/CodexBar/MenuCardRefreshMonitor.swift: UI component observingUsageStorestate to update visual indicators.Sources/CodexBar/MenuDescriptor.swift: RecordslastMenuOpenAttimestamps that feed into the adaptive policy.Sources/CodexBar/MemoryPressureMonitor.swift: Detects low-memory conditions that influence the constrained refresh path.
Summary
- AdaptiveRefreshPolicy provides deterministic, pure-function timing decisions based on four interaction states and system constraints.
- Refresh intervals range from 120 seconds during active use to 1800 seconds during long idle periods or thermal constraints.
- UsageStore implements exponential back-off error recovery starting at 30 seconds and capping at 10 minutes.
- The architecture separates policy (when to refresh) from execution (how to recover), enabling testable and observable behavior.
- All timing logic resides in
Sources/CodexBar/AdaptiveRefreshPolicy.swiftwhile loop orchestration lives inSources/CodexBar/UsageStore.swift.
Frequently Asked Questions
How does CodexBar determine the next refresh interval?
CodexBar calculates the next refresh interval using the AdaptiveRefreshPolicy.nextDelay(for:) method, which evaluates four time-based signals (recent interaction, warm, idle, long idle) and system constraints (Low-Power mode, thermal state). The policy returns a delay between 120 and 1800 seconds depending on how recently the user interacted with the menu and the current hardware conditions.
What happens when a refresh fails in CodexBar?
When UsageStore.refresh() encounters an error, the system stores the error in lastRefreshError and switches to exponential back-off mode. The retry delay starts at 30 seconds, doubles after each consecutive failure, and caps at 10 minutes. Once a refresh succeeds, the delay immediately resets to 30 seconds and the system returns to policy-driven scheduling.
How does thermal state affect CodexBar's refresh timing?
If ProcessInfo.processInfo.thermalState reports .serious or .critical, or if Low-Power mode is enabled, AdaptiveRefreshPolicy overrides the standard time-based delays and applies a constrained delay of 1800 seconds. This ensures the application respects thermal throttling and battery conservation priorities.
Where is the refresh loop logic implemented in the CodexBar source code?
The refresh loop spans two primary files: Sources/CodexBar/AdaptiveRefreshPolicy.swift contains the pure logic for calculating delays, while Sources/CodexBar/UsageStore.swift manages the active timer, error handling, and back-off state. The UI observes these changes through Sources/CodexBar/MenuCardRefreshMonitor.swift.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →