Summary
Update the default behavior of subscription auto-switch (SUB-003) to trigger after a single failed call instead of requiring 2 consecutive rate-limit errors, and broaden the trigger surface to include subscription/auth failures (not just 429).
Context
Today (see docs/memory/feature-flows/subscription-auto-switch.md), auto-switch only fires when:
- Setting
auto_switch_subscriptions = "true" (opt-in, default false)
- Agent is on a subscription (not API key)
- 2+ consecutive 429 errors on the current subscription
- A viable alternative subscription exists
Two failure modes this misses:
- Single rate-limit hit wastes a full second call before recovery — on long-running schedules that's a guaranteed user-visible failure before the switch can happen.
- Auth / credit-balance / 401 / 403 errors never trigger a switch, even though they strongly indicate the subscription is broken. The scheduler already classifies these via
auth_indicators in src/scheduler/service.py:677-717 (credit balance, unauthorized, 401, 403, oauth, token expired, etc.) but that detection is used only for [AUTH_ERROR] labeling, not for auto-switch.
Proposed Changes
services/subscription_auto_switch.py:52 — drop threshold from consecutive_count < 2 to < 1 (switch on first event).
- Broaden
handle_rate_limit_error(...) (or add a sibling handle_subscription_failure(...)) to accept auth-class failures, reusing the existing auth_indicators list from the scheduler.
- Wire the new trigger into the same three 429 interception points already in place:
services/task_execution_service.py:532
routers/chat.py:431
- background task handler in
routers/chat.py
- Update
docs/memory/feature-flows/subscription-auto-switch.md and docs/requirements/SUB-003-subscription-auto-switch.md.
The existing 2-hour rate-limit-event skip list on alternative subscription selection (db.select_best_alternative_subscription) still prevents thrashing across subs once they've been burned.
Acceptance Criteria
Open Questions
- Setting default: leave
auto_switch_subscriptions opt-in (default "false"), or flip to on-by-default? The phrase "default behavior" is ambiguous — safer default is to keep opt-in and only change the threshold semantics.
- Trigger scope: broaden to auth errors as described, or strictly threshold-only (1 × 429)?
Technical Notes
- Relevant code:
src/backend/services/subscription_auto_switch.py, src/backend/services/task_execution_service.py:520-540, src/backend/routers/chat.py:425-460, src/scheduler/service.py:677-717
- Relevant docs:
docs/memory/feature-flows/subscription-auto-switch.md, docs/requirements/SUB-003-subscription-auto-switch.md
- Related:
docs/memory/feature-flows/subscription-credential-health.md (auth error detection precedent)
Summary
Update the default behavior of subscription auto-switch (SUB-003) to trigger after a single failed call instead of requiring 2 consecutive rate-limit errors, and broaden the trigger surface to include subscription/auth failures (not just 429).
Context
Today (see
docs/memory/feature-flows/subscription-auto-switch.md), auto-switch only fires when:auto_switch_subscriptions = "true"(opt-in, default false)Two failure modes this misses:
auth_indicatorsinsrc/scheduler/service.py:677-717(credit balance,unauthorized,401,403,oauth,token expired, etc.) but that detection is used only for[AUTH_ERROR]labeling, not for auto-switch.Proposed Changes
services/subscription_auto_switch.py:52— drop threshold fromconsecutive_count < 2to< 1(switch on first event).handle_rate_limit_error(...)(or add a siblinghandle_subscription_failure(...)) to accept auth-class failures, reusing the existingauth_indicatorslist from the scheduler.services/task_execution_service.py:532routers/chat.py:431routers/chat.pydocs/memory/feature-flows/subscription-auto-switch.mdanddocs/requirements/SUB-003-subscription-auto-switch.md.The existing 2-hour rate-limit-event skip list on alternative subscription selection (
db.select_best_alternative_subscription) still prevents thrashing across subs once they've been burned.Acceptance Criteria
credit balance/unauthorized/expired token) also triggers auto-switchtests/test_subscription_auto_switch.pycovers: (a) single 429 switch, (b) single 401 switch, (c) no switch when alternative is itself recently rate-limitedOpen Questions
auto_switch_subscriptionsopt-in (default"false"), or flip to on-by-default? The phrase "default behavior" is ambiguous — safer default is to keep opt-in and only change the threshold semantics.Technical Notes
src/backend/services/subscription_auto_switch.py,src/backend/services/task_execution_service.py:520-540,src/backend/routers/chat.py:425-460,src/scheduler/service.py:677-717docs/memory/feature-flows/subscription-auto-switch.md,docs/requirements/SUB-003-subscription-auto-switch.mddocs/memory/feature-flows/subscription-credential-health.md(auth error detection precedent)