Make merged-Overview provider limit user-configurable - #2339
Conversation
Was hardcoded to 3 (docs/ui.md, issue steipete#2107). Now backed by UserDefaults (static var, since it's used as a default-parameter value elsewhere and Swift disallows self in those) with a Settings stepper (1-12, default 3 for no behavior change).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3017da8f53
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| private static let maxOverviewProviders = SettingsStore.mergedOverviewProviderLimit | ||
| private static var maxOverviewProviders: Int { SettingsStore.mergedOverviewProviderLimit } | ||
|
|
||
| @AppStorage("mergedOverviewProviderLimit") private var overviewProviderLimitSetting = 6 |
There was a problem hiding this comment.
Invalidate menus when overview limit changes
When this setting changes after the merged Overview menu has already been rendered, only UserDefaults.standard is updated. StatusItemController invalidates/rebuilds menu content via observation of settings.menuObservationToken, and that token never reads this new key, so menuNeedsUpdate can consider the existing menu fresh and keep showing the old number of Overview rows until an unrelated observed setting/store change or app restart. Please route the limit through SettingsStore/defaultsState and include it in menu observation, or otherwise invalidate the merged menu when the value changes.
Useful? React with 👍 / 👎.
|
Codex review: needs real behavior proof before merge. Reviewed August 2, 2026, 11:52 AM ET / 15:52 UTC. ClawSweeper reviewWhat this changesAdds a persisted Settings stepper that lets users choose one to twelve providers for the merged Overview tab, while retaining six as the default. Merge readiness⛔ Blocked until real behavior proof is added - 5 items remain Keep this PR open for a maintainer product-direction choice and fresh real-behavior proof. The code is a focused, plausible implementation, but the related Overview work now has competing per-provider-list and unified-card directions. Likely related people
Priority: P3 Review scores
Verification
How this fits togetherThe merged menu reads enabled providers and the user’s Overview selection, resolves a capped list, then renders it as Overview rows. This PR replaces the fixed cap with a setting stored in the app’s configured defaults store and observed by already-open menus. flowchart LR
A[Enabled providers] --> C[Overview selection resolver]
B[Menu Bar settings stepper] --> D[Persisted provider limit]
D --> C
C --> E[Merged menu controller]
E --> F[Overview provider rows]
D --> G[Menu refresh observation]
G --> E
Decision needed
Why: The requested setting is technically bounded, but it controls a UI model that the linked work is actively reconsidering; code review cannot determine which long-lived user-facing configuration should exist. Before merge
Agent review detailsSecurityNone. Review metrics
Merge-risk optionsMaintainer options:
Technical reviewBest possible solution: Choose the intended Overview layout first; if the per-provider list remains supported, land this narrow preference only after fresh-bundle proof shows the stepper updates both newly opened and already-open Overview menus. Do we have a high-confidence way to reproduce the issue? No. The PR body claims a debug build but leaves its manual verification unchecked, and it contains no screenshot, recording, runtime output, or redacted log showing the stepper changes actual Overview rows on a fresh bundle. Is this the best way to solve the issue? Unclear. The implementation is a narrow way to configure the current list, but #2107 and #2578 leave the durable Overview layout and configuration direction unresolved. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 2bd6a6c37eff. LabelsLabel changes:
Label justifications:
EvidenceWhat I checked:
Likely related people:
Rank-up movesOptional improvements that raise the rating; they are not merge blockers.
Rating scale
Overall follows the weaker of proof and patch quality. Workflow
HistoryReview history (8 earlier review cycles)
|
…er changes Addresses clawsweeper P2 finding on steipete#2339: the Overview-limit Stepper wrote straight to UserDefaults via @AppStorage, bypassing SettingsStore entirely, so menuObservationToken (which drives cached-menu invalidation) never changed and an already-open Overview menu kept its old row count. Adds mergedOverviewProviderLimitRevision, an @Observable-tracked instance counter bumped by a new instance accessor for the setting, and touches it in menuObservationToken. The Stepper now binds through settings.mergedOverviewProviderLimit instead of a raw @AppStorage.
…rDefaults Addresses clawsweeper P2 finding on steipete#2339: mergedOverviewProviderLimit read and wrote UserDefaults.standard directly instead of the store's own configured userDefaults, so isolated test stores (and any future non-standard suite) couldn't observe or reset it independently of the real app. Replaces the static get/set with a plain mergedOverviewProviderLimitDefault constant (used only where Swift requires a self-independent default parameter value) and routes every real read/write through the existing mergedOverviewProviderLimit instance accessor, backed by self.userDefaults like the rest of SettingsStore's persisted settings.
CI lint gate flagged spaceAroundOperators/wrapMultilineStatementBraces on the overview-limit Stepper introduced in this PR.
The stepper's title/subtitle were raw English strings while every adjacent Menu Bar settings row uses L(...) (ClawSweeper finding on PR steipete#2339).
…er changes Addresses clawsweeper P2 finding on steipete#2339: the Overview-limit Stepper wrote straight to UserDefaults via @AppStorage, bypassing SettingsStore entirely, so menuObservationToken (which drives cached-menu invalidation) never changed and an already-open Overview menu kept its old row count. Adds mergedOverviewProviderLimitRevision, an @Observable-tracked instance counter bumped by a new instance accessor for the setting, and touches it in menuObservationToken. The Stepper now binds through settings.mergedOverviewProviderLimit instead of a raw @AppStorage.
…rDefaults Addresses clawsweeper P2 finding on steipete#2339: mergedOverviewProviderLimit read and wrote UserDefaults.standard directly instead of the store's own configured userDefaults, so isolated test stores (and any future non-standard suite) couldn't observe or reset it independently of the real app. Replaces the static get/set with a plain mergedOverviewProviderLimitDefault constant (used only where Swift requires a self-independent default parameter value) and routes every real read/write through the existing mergedOverviewProviderLimit instance accessor, backed by self.userDefaults like the rest of SettingsStore's persisted settings.
The stepper's title/subtitle were raw English strings while every adjacent Menu Bar settings row uses L(...) (ClawSweeper finding on PR steipete#2339).
Manual testing found the stepper completely unresponsive. Root cause: mergedOverviewProviderLimit's getter read straight from self.userDefaults, which is @ObservationIgnored — so SwiftUI recorded zero dependencies for that read and never re-rendered the Stepper/Text after a click updated the stored value. Reading the already-tracked mergedOverviewProviderLimitRevision first gives the getter an observable dependency, same technique already used in menuObservationToken. Also moves the stepper's numeric readout out of the Stepper's own label into a LabeledContent accessory (matching CostHistoryDaysEditor's existing pattern) so the +/- hit target isn't sharing space with a three-line label.
Summary
docs/ui.md, issue Allow more than 3 providers in the Overview tab (compact density mode) #2107). This makes it user-configurable via a Settings stepper (1-12), backed byUserDefaults.main, which already raised the hardcoded default from 3 to 6 — kept that as the new configurable default so existing users see no behavior change.Test plan
swift build --product CodexBar -c debugsucceeds