fix: update cooldownReason on active-window failures + clear model scope for non-rate-limit

This commit is contained in:
kiranvk2011 2026-03-18 15:36:32 +00:00
parent d88de88962
commit 8ea6f5206c
2 changed files with 56 additions and 1 deletions

View File

@ -796,4 +796,51 @@ describe("markAuthProfileFailure — per-model cooldown metadata", () => {
const stats = store.usageStats?.["github-copilot:github"];
expect(stats?.cooldownModel).toBe("claude-sonnet-4.6");
});
it("updates cooldownReason when auth failure occurs during active rate_limit window", async () => {
const now = 1_000_000;
const store = makeStoreWithCopilot({
"github-copilot:github": {
cooldownUntil: now + 30_000,
cooldownReason: "rate_limit",
cooldownModel: "claude-sonnet-4.6",
errorCount: 1,
lastFailureAt: now - 1000,
},
});
await markAuthProfileFailure({
store,
profileId: "github-copilot:github",
reason: "auth",
modelId: "claude-opus-4.6",
});
const stats = store.usageStats?.["github-copilot:github"];
// Reason should update to the new failure type, not stay as rate_limit
expect(stats?.cooldownReason).toBe("auth");
// Model scope should be cleared — auth failures are profile-wide
expect(stats?.cooldownModel).toBeUndefined();
});
it("clears cooldownModel when non-rate_limit failure hits same model during active window", async () => {
const now = 1_000_000;
const store = makeStoreWithCopilot({
"github-copilot:github": {
cooldownUntil: now + 30_000,
cooldownReason: "rate_limit",
cooldownModel: "claude-sonnet-4.6",
errorCount: 1,
lastFailureAt: now - 1000,
},
});
await markAuthProfileFailure({
store,
profileId: "github-copilot:github",
reason: "auth",
modelId: "claude-sonnet-4.6",
});
const stats = store.usageStats?.["github-copilot:github"];
// Even same-model auth failure should clear model scope (auth is profile-wide)
expect(stats?.cooldownReason).toBe("auth");
expect(stats?.cooldownModel).toBeUndefined();
});
});

View File

@ -479,7 +479,11 @@ function computeNextProfileUsageStats(params: {
typeof params.existing.cooldownUntil === "number" &&
params.existing.cooldownUntil > params.now;
if (existingCooldownActive) {
updatedStats.cooldownReason = params.existing.cooldownReason;
// Always use the latest failure reason so that downstream consumers
// (e.g. isProfileInCooldown model-bypass) see the most recent signal.
// A non-rate_limit failure (auth, billing, …) is profile-wide, so
// upgrading from rate_limit → auth correctly blocks all models.
updatedStats.cooldownReason = params.reason;
// If a different model fails during an active window, widen the scope
// to all models (undefined) so neither model bypasses the cooldown.
if (
@ -488,6 +492,10 @@ function computeNextProfileUsageStats(params: {
params.existing.cooldownModel !== params.modelId
) {
updatedStats.cooldownModel = undefined;
} else if (params.reason !== "rate_limit") {
// Non-rate-limit failures are profile-wide — clear model scope even
// when the same model fails, so that no model can bypass.
updatedStats.cooldownModel = undefined;
} else {
updatedStats.cooldownModel = params.existing.cooldownModel;
}