diff --git a/ratelimit.go b/ratelimit.go index 0e34709..639036c 100644 --- a/ratelimit.go +++ b/ratelimit.go @@ -228,10 +228,8 @@ func (rl *RateLimiter) loadSQLite() error { if err != nil { return err } - // Persisted quotas are authoritative when present; otherwise keep config defaults. - if len(quotas) > 0 { - rl.Quotas = maps.Clone(quotas) - } + // Persisted quotas are always authoritative, even when empty. + rl.Quotas = maps.Clone(quotas) state, err := rl.sqlite.loadState() if err != nil { diff --git a/sqlite_test.go b/sqlite_test.go index 7026261..0122d28 100644 --- a/sqlite_test.go +++ b/sqlite_test.go @@ -788,6 +788,33 @@ func TestSQLiteLoadReplacesPersistedSnapshot_Good(t *testing.T) { assert.Equal(t, 1, rl2.Stats("model-b").RPD) } +func TestSQLiteLoadReplacesQuotasWithEmptyPersistedSnapshot_Good(t *testing.T) { + dbPath := filepath.Join(t.TempDir(), "empty-quotas.db") + rl, err := NewWithSQLiteConfig(dbPath, Config{ + Providers: []Provider{ProviderLocal}, + }) + require.NoError(t, err) + + require.Empty(t, rl.Quotas) + rl.RecordUsage("usage-only-model", 10, 20) + + require.NoError(t, rl.Persist()) + require.NoError(t, rl.Close()) + + rl2, err := NewWithSQLiteConfig(dbPath, Config{ + Providers: []Provider{ProviderGemini}, + }) + require.NoError(t, err) + defer rl2.Close() + + require.NotEmpty(t, rl2.Quotas, "constructor should start with Gemini defaults") + require.NoError(t, rl2.Load()) + + assert.Empty(t, rl2.Quotas, "persisted empty quotas should replace configured defaults") + assert.NotContains(t, rl2.Quotas, "gemini-3-pro-preview") + assert.Equal(t, 1, rl2.Stats("usage-only-model").RPD) +} + func TestSQLitePersistAtomic_Good(t *testing.T) { dbPath := filepath.Join(t.TempDir(), "persist-atomic.db") rl, err := NewWithSQLiteConfig(dbPath, Config{