# Upgrade Guide — vNEXT

<!-- assembled-by: assemble-next-md -->
<!-- bump: minor -->

## What Changed

Adds the **proactive pre-limit swap** — the missing half of the Subscription &
Auth Standard's continuity guarantee. A new `ProactiveSwapMonitor` moves a session
OFF an account BEFORE it walls, at a lag-aware measured threshold (`thresholdPct`,
default 80 — below the real limit because the polled reading trails real usage).
Critically, it covers the session you actually use: an UNTAGGED session (the
primary interactive session running on the default login) is resolved to its real
account via `InUseAccountResolver`, so it is moved pre-emptively instead of wedging
at the wall. Bounded by a per-cycle cap, per-session cooldown, an
alternate-must-be-below-threshold anti-thrash guard, and a near-the-wall poll
refresh. Two routes: `GET /subscription-pool/proactive-swap` (status) and
`POST /subscription-pool/proactive-swap/check` (run one pass on demand). Gated
OFF by default behind `subscriptionPool.proactiveSwap.enabled` (moving live
sessions is real authority — same authority as the reactive `autoSwapOnRateLimit`,
just an earlier trigger). Disabled → strict no-op; the routes answer
`200 { enabled:false }`.

## What to Tell Your User

Your accounts now swap *before* one runs out, not just after. Before, the agent
would ride an account all the way to its limit and then either blip onto a fresh
account — or, if it was the main session you chat with, get stuck until you
manually switched accounts. Now, when an account crosses ~80% of its limit, I move
the session to one with room to spare while preserving the conversation, with
margin to spare (your real usage runs a few points ahead of what I can poll). It's
off by default; just tell me to turn on proactive swapping and I'll enable it, and
you can ask me to run a check on demand to watch it work.

## Summary of New Capabilities

| Capability | How to Use |
|-----------|-----------|
| Proactive pre-limit swap (before the wall) | set `subscriptionPool.proactiveSwap.enabled: true` (restart to apply) |
| Tune the trigger | `subscriptionPool.proactiveSwap.thresholdPct` (default 80), `maxSwapsPerCycle`, `cooldownMs`, `tickMs`, `watchMarginPct` |
| See status | `GET /subscription-pool/proactive-swap` |
| Run one pass now | `POST /subscription-pool/proactive-swap/check` |

## Evidence

Reproduction (live, 2026-06-09): the operator's interactive session was running
on the default login (pointed at SageMind - Adriana). Adriana's 5-hour window
filled; the session wedged. The reactive auto-swap could not rescue it — it only
fires after a rate-limit escalation, AND the session carried no
`subscriptionAccountId` (the main interactive session is untagged), so the swap
engine had nothing to move. The operator had to swap the default login manually to
unblock it.

After the fix: the monitor resolves the untagged session's effective account from
the default login and pre-emptively swaps it at the measured threshold. The e2e
test (`subscription-proactive-swap-lifecycle.test.ts`) drives exactly this case
end-to-end over HTTP — an untagged session on an at-pressure default login is
swapped before the wall, conversation preserved. The unit suite asserts both sides
of every boundary (at-pressure vs. below, tagged vs. untagged-via-default,
alternate-exists vs. anti-thrash, cap, cooldown, recency ordering, watch-zone poll
refresh). tsc + lint clean; sibling subscription/quota suites and all 358
PostUpdateMigrator tests green.
