fix(quota): add cooldown + 2-poll confirmation to prevent swap ping-pong
Anthropic HTTP 500 errors surface in the TUI with payloads containing "rate limit" text, which the monitor was matching against quotaPatterns and treating as a real 429 quota hit. With no cooldown and no confirmation, a burst of 500s produced sub-minute ping-pong swaps that tore down user sessions. Two-layer fix: - quota.reactivate_cooldown (already in config, 5m) now gates the monitor too — not just the dispatcher. A completed swap suppresses further detection for the cooldown window. - A hit with no parseable reset time is treated as suspected only on the first poll; a second consecutive poll is required before emitting SwapRequested. Legitimate 429s with "resets in ..." still swap instantly on the first detection. Adds state.RecordSwap / LastSwapInfo for the cooldown, and a forensic log line on every detection: trigger_session, matched pattern, 120-char pane snippet. Tests cover: instant swap with reset, 2-poll confirmation without reset, and suspected-state reset on recovery. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
75b5110748
commit
7c5f8384fa
5 changed files with 246 additions and 25 deletions
|
|
@ -109,8 +109,10 @@ func (a *AccountSwitcher) executeSwitch(req quota.SwitchRequest) {
|
|||
a.killAllPoolSessions()
|
||||
a.recreatePoolSessions()
|
||||
|
||||
// Update active account.
|
||||
// Update active account and record the swap timestamp so the quota
|
||||
// monitor can enforce a cooldown before requesting another one.
|
||||
a.state.SetActiveAccount(target.Name)
|
||||
a.state.RecordSwap(req.From, target.Name)
|
||||
|
||||
// 3. RESUMING — sessions are alive, dispatcher will fill them.
|
||||
a.currentState = StateResuming
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue