@@ -122,7 +122,7 @@ struct UsageStoreWidgetSnapshotTests {
122122 }
123123
124124 @Test
125- func `widget snapshot includes known Claude model scoped weekly quotas by default `() async throws {
125+ func `widget snapshot includes known Claude model scoped weekly quotas only when enabled `() async throws {
126126 let suite = " UsageStoreWidgetSnapshotTests-claude-model-scoped-weekly-rows "
127127 let defaults = try #require( UserDefaults ( suiteName: suite) )
128128 defaults. removePersistentDomain ( forName: suite)
@@ -164,6 +164,15 @@ struct UsageStoreWidgetSnapshotTests {
164164 store. _test_widgetSnapshotSaveOverride = { widgetSnapshots. append ( $0) }
165165 defer { store. _test_widgetSnapshotSaveOverride = nil }
166166
167+ // The setting is opt-in, so untouched defaults must project the standard quota lanes only.
168+ #expect( settings. claudeModelScopedWeeklyUsageVisible == false )
169+ store. persistWidgetSnapshot ( reason: " claude-model-scoped-weekly-rows-default-test " )
170+ await store. widgetSnapshotPersistTask? . value
171+
172+ let defaultEntry = try #require( widgetSnapshots. last? . entries. first { $0. provider == . claude } )
173+ #expect( defaultEntry. usageRows? . map ( \. id) == [ " primary " , " secondary " ] )
174+
175+ settings. claudeModelScopedWeeklyUsageVisible = true
167176 store. persistWidgetSnapshot ( reason: " claude-model-scoped-weekly-rows-test " )
168177 await store. widgetSnapshotPersistTask? . value
169178
@@ -180,6 +189,94 @@ struct UsageStoreWidgetSnapshotTests {
180189 #expect( hiddenEntry. usageRows? . map ( \. id) == [ " primary " , " secondary " ] )
181190 }
182191
192+ @Test
193+ func `widget snapshot drops persisted Claude model scoped weekly rows once the setting is off`() async throws {
194+ let suite = " UsageStoreWidgetSnapshotTests-claude-model-scoped-weekly-fallback "
195+ let defaults = try #require( UserDefaults ( suiteName: suite) )
196+ defaults. removePersistentDomain ( forName: suite)
197+
198+ let settings = SettingsStore (
199+ userDefaults: defaults,
200+ configStore: testConfigStore ( suiteName: suite) ,
201+ zaiTokenStore: NoopZaiTokenStore ( ) ,
202+ syntheticTokenStore: NoopSyntheticTokenStore ( ) )
203+ settings. statusChecksEnabled = false
204+ settings. claudeModelScopedWeeklyUsageVisible = true
205+
206+ let store = UsageStore (
207+ fetcher: UsageFetcher ( environment: [ : ] ) ,
208+ browserDetection: BrowserDetection ( cacheTTL: 0 ) ,
209+ settings: settings)
210+ let updatedAt = Date ( timeIntervalSince1970: 1_800_000_000 )
211+ store. _setSnapshotForTesting (
212+ UsageSnapshot (
213+ primary: RateWindow ( usedPercent: 25 , windowMinutes: 300 , resetsAt: nil , resetDescription: nil ) ,
214+ secondary: RateWindow ( usedPercent: 50 , windowMinutes: 10080 , resetsAt: nil , resetDescription: nil ) ,
215+ tertiary: nil ,
216+ extraRateWindows: [
217+ NamedRateWindow (
218+ id: " claude-weekly-scoped-fable " ,
219+ title: " Fable only " ,
220+ window: RateWindow (
221+ usedPercent: 30 ,
222+ windowMinutes: 10080 ,
223+ resetsAt: nil ,
224+ resetDescription: nil ) ) ,
225+ ] ,
226+ updatedAt: updatedAt) ,
227+ provider: . claude)
228+
229+ var widgetSnapshots : [ WidgetSnapshot ] = [ ]
230+ store. _test_widgetSnapshotSaveOverride = { widgetSnapshots. append ( $0) }
231+ defer { store. _test_widgetSnapshotSaveOverride = nil }
232+
233+ store. persistWidgetSnapshot ( reason: " claude-model-scoped-weekly-fallback-seed " )
234+ await store. widgetSnapshotPersistTask? . value
235+
236+ let seededEntry = try #require( widgetSnapshots. last? . entries. first { $0. provider == . claude } )
237+ #expect( seededEntry. usageRows? . map ( \. id) == [ " primary " , " secondary " , " claude-weekly-scoped-fable " ] )
238+ let quotaOwnerKey = try #require( seededEntry. quotaOwnerKey)
239+
240+ // No live Claude snapshot: the projection now falls back to the persisted rows above.
241+ store. snapshots. removeValue ( forKey: . claude)
242+ settings. claudeModelScopedWeeklyUsageVisible = false
243+ store. persistWidgetSnapshot ( reason: " claude-model-scoped-weekly-fallback-disabled " )
244+ await store. widgetSnapshotPersistTask? . value
245+
246+ let fallbackEntry = try #require( widgetSnapshots. last? . entries. first { $0. provider == . claude } )
247+ #expect( fallbackEntry. usageRows? . map ( \. id) == [ " primary " , " secondary " ] )
248+ #expect( fallbackEntry. usageRows? . contains { $0. id. hasPrefix ( " claude-weekly-scoped- " ) } == false )
249+
250+ // A persisted entry whose only rows are scoped carve-outs drops out of the projection entirely.
251+ store. lastQueuedWidgetSnapshot = WidgetSnapshot (
252+ entries: [
253+ WidgetSnapshot . ProviderEntry (
254+ provider: . claude,
255+ updatedAt: updatedAt,
256+ primary: nil ,
257+ secondary: nil ,
258+ tertiary: nil ,
259+ usageRows: [
260+ WidgetSnapshot . WidgetUsageRowSnapshot (
261+ id: " claude-weekly-scoped-fable " ,
262+ title: " Fable only " ,
263+ percentLeft: 70 ) ,
264+ ] ,
265+ creditsRemaining: nil ,
266+ codeReviewRemainingPercent: nil ,
267+ tokenUsage: nil ,
268+ dailyUsage: [ ] ,
269+ quotaOwnerKey: quotaOwnerKey) ,
270+ ] ,
271+ enabledProviders: [ . claude] ,
272+ generatedAt: updatedAt)
273+
274+ store. persistWidgetSnapshot ( reason: " claude-model-scoped-weekly-fallback-scoped-only " )
275+ await store. widgetSnapshotPersistTask? . value
276+
277+ #expect( widgetSnapshots. last? . entries. contains { $0. provider == . claude } == false )
278+ }
279+
183280 @Test
184281 func `widget snapshot includes antigravity grouped usage rows`() async throws {
185282 let suite = " UsageStoreWidgetSnapshotTests-antigravity-grouped "
0 commit comments