docs/superpowers/plans/2026-07-30-global-low-power-mode.md
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Add a non-destructive global low-power mode that clamps CodexBar's automatic provider, local cost, storage, and OpenAI Web background work while preserving manual refreshes.
Architecture: A pure BackgroundWorkPowerPolicy owns the 30-minute lower bound. SettingsStore persists one default-off toggle, and each automatic scheduling seam asks the shared policy for its effective interval. Manual entry points continue to bypass automatic cooldowns.
Tech Stack: Swift 6.2+, SwiftUI, Observation, Swift Testing, UserDefaults, Swift Package Manager
backgroundWorkLowPowerModeEnabled defaults to false.refreshFrequency or disable cost/storage settings.Files:
Sources/CodexBar/BackgroundWorkPowerPolicy.swiftTests/CodexBarTests/BackgroundWorkPowerPolicyTests.swiftInterfaces:
Produces: BackgroundWorkPowerPolicy.lowPowerMinimumInterval: TimeInterval
Produces: BackgroundWorkPowerPolicy.automaticInterval(_:lowPowerModeEnabled:) -> TimeInterval?
Step 1: Write the failing policy tests
import Foundation
import Testing
@testable import CodexBar
struct BackgroundWorkPowerPolicyTests {
@Test
func `disabled mode preserves requested automatic intervals`() {
#expect(BackgroundWorkPowerPolicy.automaticInterval(nil, lowPowerModeEnabled: false) == nil)
#expect(BackgroundWorkPowerPolicy.automaticInterval(300, lowPowerModeEnabled: false) == 300)
#expect(BackgroundWorkPowerPolicy.automaticInterval(3600, lowPowerModeEnabled: false) == 3600)
}
@Test
func `enabled mode clamps automatic intervals to thirty minutes`() {
#expect(BackgroundWorkPowerPolicy.automaticInterval(nil, lowPowerModeEnabled: true) == nil)
#expect(BackgroundWorkPowerPolicy.automaticInterval(60, lowPowerModeEnabled: true) == 1800)
#expect(BackgroundWorkPowerPolicy.automaticInterval(1800, lowPowerModeEnabled: true) == 1800)
#expect(BackgroundWorkPowerPolicy.automaticInterval(3600, lowPowerModeEnabled: true) == 3600)
}
}
Run the repository's swift test --filter BackgroundWorkPowerPolicyTests command, with the documented local Command Line Tools compatibility flags when full Xcode is unavailable.
Expected: FAIL because BackgroundWorkPowerPolicy does not exist.
import Foundation
enum BackgroundWorkPowerPolicy {
static let lowPowerMinimumInterval: TimeInterval = 30 * 60
static func automaticInterval(
_ requested: TimeInterval?,
lowPowerModeEnabled: Bool) -> TimeInterval?
{
guard let requested else { return nil }
guard lowPowerModeEnabled else { return requested }
return max(requested, self.lowPowerMinimumInterval)
}
}
Expected: both policy tests pass.
Files:
Sources/CodexBar/SettingsStoreState.swiftSources/CodexBar/SettingsStore.swiftSources/CodexBar/SettingsStore+Defaults.swiftSources/CodexBar/SettingsStore+MenuObservation.swiftTests/CodexBarTests/SettingsStoreCoverageTests.swiftInterfaces:
Consumes: SettingsStore.noteBackgroundWorkSettingsChanged()
Produces: SettingsStore.backgroundWorkLowPowerModeEnabled: Bool
Produces: SettingsStore.effectiveOpenAIWebBatterySaverEnabled: Bool
Step 1: Add failing settings persistence tests
Add a test that creates isolated defaults, verifies the new key defaults to false and is absent, toggles it on, verifies backgroundWorkSettingsRevision increments once, reloads SettingsStore, and verifies the value persists. Also assert:
#expect(initial.effectiveOpenAIWebBatterySaverEnabled == false)
initial.backgroundWorkLowPowerModeEnabled = true
#expect(initial.effectiveOpenAIWebBatterySaverEnabled)
initial.backgroundWorkLowPowerModeEnabled = false
initial.openAIWebBatterySaverEnabled = true
#expect(initial.effectiveOpenAIWebBatterySaverEnabled)
Expected: FAIL because the properties do not exist.
Add backgroundWorkLowPowerModeEnabled beside existing background-related settings. Load it with:
let backgroundWorkLowPowerModeEnabled =
userDefaults.object(forKey: "backgroundWorkLowPowerModeEnabled") as? Bool ?? false
The setter stores the value, logs only enabled=0|1, and calls noteBackgroundWorkSettingsChanged(). The effective web saver is the OR of the global and provider-specific values.
Expected: default, persistence, revision, and effective-web assertions pass.
Files:
Sources/CodexBar/UsageStore.swiftSources/CodexBar/UsageStore+AdaptiveRefresh.swiftTests/CodexBarTests/AdaptiveRefreshTimerTests.swiftInterfaces:
Consumes: BackgroundWorkPowerPolicy.automaticInterval(_:lowPowerModeEnabled:)
Consumes: SettingsStore.backgroundWorkLowPowerModeEnabled
Step 1: Add failing timer tests
Add fixed and Adaptive cases proving a requested 300-second fixed interval and an Adaptive 300-second decision both resolve to 1800 seconds when the global setting is on, while 3600 seconds remains unchanged. Keep existing sleep overrides test-only and separate from the policy's computed delay.
Expected: low-power cases observe the old short delay.
In startTimer, clamp frequency.seconds before creating the fixed timer. In nextAdaptiveTimerSleepDuration, clamp the policy decision before assigning adaptiveRefreshScheduledAt and before sleeping. Apply the same clamped value in advanceAdaptiveTimerIfEarlier and normalRefreshIntervalForHeuristics so scheduling metadata does not disagree with the actual timer.
Expected: existing timer behavior stays green and low-power cases resolve to 1800 seconds.
Files:
Sources/CodexBar/UsageStore.swiftSources/CodexBar/UsageStore+ProviderStorage.swiftTests/CodexBarTests/UsageStoreTokenRefreshCadenceTests.swiftTests/CodexBarTests/ProviderStorageFootprintTests.swiftInterfaces:
Consumes: BackgroundWorkPowerPolicy.automaticInterval(_:lowPowerModeEnabled:)
Produces: UsageStore.tokenFetchTTL(for:lowPowerModeEnabled:) -> TimeInterval?
Produces: UsageStore.automaticStorageRefreshInterval(lowPowerModeEnabled:) -> TimeInterval
Step 1: Add failing cadence and manual-refresh tests
Extend token cadence table tests so five minutes becomes 1800 seconds only when low-power mode is enabled, manual remains nil, and one hour remains one hour. Add storage policy assertions for 300 versus 1800 seconds. Extend the existing manual storage refresh test by enabling global low-power mode and proving the explicit second refresh still sees a deleted directory immediately.
Expected: automatic token/storage intervals remain 300 seconds.
Pass the global setting into tokenFetchTTL. Replace the storage constant comparison with automaticStorageRefreshInterval(lowPowerModeEnabled:). Do not add a low-power guard to refreshTokenUsageNow(force:) or refreshStorageFootprintsNow.
Expected: automatic cooldowns clamp and explicit refreshes remain immediate.
Files:
Sources/CodexBar/UsageStore+OpenAIWeb.swiftSources/CodexBar/UsageStore+RefreshEnrichment.swiftSources/CodexBar/UsageStore+Logging.swiftSources/CodexBar/PreferencesGeneralPane.swiftSources/CodexBar/Providers/Codex/CodexProviderImplementation.swiftSources/CodexBar/Resources/en.lproj/Localizable.stringsSources/CodexBar/Resources/zh-Hans.lproj/Localizable.stringsTests/CodexBarTests/CodexBackgroundRefreshCoalescingTests.swiftInterfaces:
Consumes: SettingsStore.effectiveOpenAIWebBatterySaverEnabled
Step 1: Add a failing OpenAI Web gate test
Use existing refresh-policy test seams to set backgroundWorkLowPowerModeEnabled = true,
openAIWebBatterySaverEnabled = false, and verify a routine background Web refresh is suppressed while a forced
user refresh remains eligible.
Expected: routine Web refresh is still allowed.
Use effectiveOpenAIWebBatterySaverEnabled everywhere the runtime creates an OpenAIWebRefreshPolicyContext.
Add the General-pane toggle with the approved title/subtitle. Rename the provider-specific title to
OpenAI web battery saver. Keep the English literals as the built-in fallback and add audited Simplified Chinese
overrides; adding them to en.lproj would make every complete locale require an unaudited translation.
Expected: Web policy and persistence tests pass; UI source compiles.
Files:
docs/superpowers/specs/2026-07-30-global-low-power-mode-design.zh-CN.mdCodexBar-Low-Power-Local.appInterfaces:
Consumes: all prior tasks
Step 1: Restore local-only compatibility exclusions before reviewing Git diff
Restore the five temporarily excluded XCTest files. Confirm the only tracked source compatibility change retained for the local Command Line Tools build is intentionally excluded from the feature commit or split into a separate commit.
Run swiftformat --lint, swiftlint, all changed-area tests, and swift build. When full Xcode is unavailable, record the exact local compatibility flags and distinguish environment limitations from source failures.
Confirm no credentials, account identifiers, local paths, build cache changes, Widget removal, or unrelated provider changes are tracked.
Commit implementation and documentation using repository conventions. Add a Codex/ChatGPT co-author trailer, not a Claude trailer.
Assemble the app from the exact committed executable and resources, disable Sparkle auto-update, omit the Widget only for this local artifact, sign with codesign --sign -, verify the signature, and save a copy under the user's visible project path.
Quit CodexBar, copy the current /Applications/CodexBar.app to a timestamped backup, install the local app, launch it, verify the menu appears, enable Low Power Mode, and confirm existing settings remain readable.
Push codex/global-low-power-mode to Carl723000/CodexBar. Prepare a PR referencing #2508 with the root cause, behavior table, tests, and explicit note that the no-Widget packaging workaround is local-only.