@@ -1130,9 +1130,17 @@ func scoreFromWindows(accountID string, windows []accounts.UsageWindow) selectac
11301130 return selectacct .ScoreFromLimitWindows (accountID , 0 , limitWindows )
11311131}
11321132
1133+ // isModelScopedWindow reports whether a window is a per-model quota pool (e.g.
1134+ // Fable), identified by a non-empty Feature. Such a pool being exhausted must
1135+ // not cook the whole account: the scheduler already scores it as its own pool,
1136+ // and the account stays usable for other models (Opus/Sonnet).
1137+ func isModelScopedWindow (window accounts.UsageWindow ) bool {
1138+ return strings .TrimSpace (window .Feature ) != ""
1139+ }
1140+
11331141func cookedFromWindows (windows []accounts.UsageWindow ) (bool , string ) {
11341142 for _ , window := range windows {
1135- if ! isLongQuotaWindow (window ) || clampUsagePercent (window .UsedPercent ) < 100 {
1143+ if isModelScopedWindow ( window ) || ! isLongQuotaWindow (window ) || clampUsagePercent (window .UsedPercent ) < 100 {
11361144 continue
11371145 }
11381146 if window .ResetAfterSeconds > 0 {
@@ -1948,16 +1956,56 @@ func compactPickReason(row srUsageRow) string {
19481956 return "Claude profile"
19491957 }
19501958 left := fmt .Sprintf ("%d%% left" , int (row .score .Headroom * 100 + 0.5 ))
1959+ suffix := exhaustedModelSuffix (row .windows )
19511960 if ! usableForNewSession (row .score ) {
1952- return fmt .Sprintf ("%s, protected < %d%%" , left , int (selectacct .MinNewSessionHeadroom * 100 ))
1961+ return fmt .Sprintf ("%s, protected < %d%%%s " , left , int (selectacct .MinNewSessionHeadroom * 100 ), suffix )
19531962 }
19541963 if row .score .ShortResetAfterSeconds > 0 {
19551964 if usageProvider (row ) == accounts .ProviderClaude {
1956- return fmt .Sprintf ("%s, session reset %s" , left , formatDuration (row .score .ShortResetAfterSeconds ))
1965+ return fmt .Sprintf ("%s, session reset %s%s " , left , formatDuration (row .score .ShortResetAfterSeconds ), suffix )
19571966 }
1958- return fmt .Sprintf ("%s, 5h reset %s" , left , formatDuration (row .score .ShortResetAfterSeconds ))
1967+ return fmt .Sprintf ("%s, 5h reset %s%s" , left , formatDuration (row .score .ShortResetAfterSeconds ), suffix )
1968+ }
1969+ return left + suffix
1970+ }
1971+
1972+ // exhaustedModelSuffix names any per-model quota pools that are fully consumed
1973+ // (e.g. Fable) so the Use column makes clear the account still works for other
1974+ // models even when one model's weekly pool is out.
1975+ func exhaustedModelSuffix (windows []accounts.UsageWindow ) string {
1976+ var out []string
1977+ seen := map [string ]bool {}
1978+ for _ , window := range windows {
1979+ if ! isModelScopedWindow (window ) || clampUsagePercent (window .UsedPercent ) < 100 {
1980+ continue
1981+ }
1982+ label := modelScopedWindowLabel (window )
1983+ if label == "" || seen [label ] {
1984+ continue
1985+ }
1986+ seen [label ] = true
1987+ out = append (out , label )
1988+ }
1989+ if len (out ) == 0 {
1990+ return ""
1991+ }
1992+ return " (" + strings .Join (out , "/" ) + " out)"
1993+ }
1994+
1995+ func modelScopedWindowLabel (window accounts.UsageWindow ) string {
1996+ f := strings .ToLower (window .Feature )
1997+ switch {
1998+ case strings .Contains (f , "fable" ):
1999+ return "Fable"
2000+ case strings .Contains (f , "opus" ):
2001+ return "Opus"
2002+ case strings .Contains (f , "sonnet" ):
2003+ return "Sonnet"
2004+ case strings .Contains (f , "haiku" ):
2005+ return "Haiku"
2006+ default :
2007+ return window .Feature
19592008 }
1960- return left
19612009}
19622010
19632011func usageGridShortWindowCell (row srUsageRow ) usageGridCell {
@@ -1978,6 +2026,9 @@ func usageGridShortNamedWindowCell(row srUsageRow) usageGridCell {
19782026
19792027func longQuotaSaturatedMatching (windows []accounts.UsageWindow , match func (accounts.UsageWindow ) bool ) bool {
19802028 for _ , window := range windows {
2029+ if isModelScopedWindow (window ) {
2030+ continue
2031+ }
19812032 if match (window ) && isLongQuotaWindow (window ) && clampUsagePercent (window .UsedPercent ) >= 100 {
19822033 return true
19832034 }
0 commit comments