Upstream PR router-for-me/CLIProxyAPI#5109 changes session affinity from "purge the binding on any failure and permanently rebind" to "retain the primary binding across transient failures, use a sticky temporary fallback during the primary's cooldown, and purge/rebind only on terminal auth errors (401/402/403/404, revoked OAuth grant, unsupported model)".
An additive port to this tree is not possible: the behavior change contradicts existing tests that pin the current semantics. Concretely, these existing tests assert the old behavior and would fail under the upstream retention rules:
sdk/cliproxy/auth/session_affinity_fix_test.go:74 — asserts HTTP 429 invalidates the binding from the session cache:
expected cacheKey %q to be invalidated on 429 failure, but still bound to %q
- the stream-failure variant in the same file — asserts a stream failure invalidates affinity (
stream failure should invalidate affinity; still bound=%q)
- the reselect-after-failure test in
selector_test.go — asserts Pick() rebinds to a different auth after a rate_limited failure release
TestManagerSessionAffinityPreservesBindingAcrossHigherPriorityRecovery — asserts the failover binding stays after the higher-priority auth recovers (upstream retention instead returns the session to the recovered original)
Those tests encode deliberate Plus-side behavior (see the sibling-scoping work in #188), so flipping them is a product decision, not a mechanical port.
What upstream adds (worth having here if the semantics flip is accepted)
isTerminalSessionAffinityError classification: transient errors (429/5xx, overload) retain the binding; only terminal errors release it.
- A dedicated
fallbackCache *SessionCache on SessionAffinitySelector: while the primary is cooling down, the session sticks to one temporary fallback auth (shared across session alias keys) instead of re-drawing a random one per request; when the primary recovers and succeeds, the fallback binding is invalidated and the session returns to the primary.
SessionCache.Aliases(sessionID) accessor.
- Full test suite to port:
sdk/cliproxy/auth/session_affinity_retention_test.go (14 cases: transient 503 retention, 429 retry-after retention, terminal 401 unbind, sticky fallback across aliases, model-variant suffix collision safety).
Options
- Accept the semantic flip: port the mechanism and update the four pinned tests to the retention expectations (upstream commit 5a4f2086 shows the exact test adjustments).
- Keep current Plus semantics and skip the port.
- Keep current semantics but add only the sticky temporary fallback (no retention on transient errors) — partially reduces failover flapping without touching the pinned tests.
Cross-link: router-for-me/CLIProxyAPI#5109, related Plus work #188.
Upstream PR router-for-me/CLIProxyAPI#5109 changes session affinity from "purge the binding on any failure and permanently rebind" to "retain the primary binding across transient failures, use a sticky temporary fallback during the primary's cooldown, and purge/rebind only on terminal auth errors (401/402/403/404, revoked OAuth grant, unsupported model)".
An additive port to this tree is not possible: the behavior change contradicts existing tests that pin the current semantics. Concretely, these existing tests assert the old behavior and would fail under the upstream retention rules:
sdk/cliproxy/auth/session_affinity_fix_test.go:74— asserts HTTP 429 invalidates the binding from the session cache:expected cacheKey %q to be invalidated on 429 failure, but still bound to %qstream failure should invalidate affinity; still bound=%q)selector_test.go— assertsPick()rebinds to a different auth after arate_limitedfailure releaseTestManagerSessionAffinityPreservesBindingAcrossHigherPriorityRecovery— asserts the failover binding stays after the higher-priority auth recovers (upstream retention instead returns the session to the recovered original)Those tests encode deliberate Plus-side behavior (see the sibling-scoping work in #188), so flipping them is a product decision, not a mechanical port.
What upstream adds (worth having here if the semantics flip is accepted)
isTerminalSessionAffinityErrorclassification: transient errors (429/5xx, overload) retain the binding; only terminal errors release it.fallbackCache *SessionCacheonSessionAffinitySelector: while the primary is cooling down, the session sticks to one temporary fallback auth (shared across session alias keys) instead of re-drawing a random one per request; when the primary recovers and succeeds, the fallback binding is invalidated and the session returns to the primary.SessionCache.Aliases(sessionID)accessor.sdk/cliproxy/auth/session_affinity_retention_test.go(14 cases: transient 503 retention, 429 retry-after retention, terminal 401 unbind, sticky fallback across aliases, model-variant suffix collision safety).Options
Cross-link: router-for-me/CLIProxyAPI#5109, related Plus work #188.