Distribute Sessions Across Accounts
The setting that spreads new sessions across all your logged-in accounts by how much headroom each has, instead of filling one account at a time — what it does when on, how to see it working, and how to stop it.
What it is
One-paragraph answer: "Distribute Sessions Across Accounts" is a setting (Settings → Accounts) that spreads new Claude Code sessions across all of your login accounts based on how much each has been used, instead of piling every session onto a single active account until it hits its limit. With it on, each new session starts on the account that currently has the most headroom — with a gentle preference for an account whose usage window is about to reset — weighted most toward the weekly (7-day) window, since that is the bigger paid-for chunk — so capacity that would otherwise expire unused gets harvested first; each session then stays on its own account; and when one account hits its rate limit, only that account's sessions move elsewhere — there is no synchronized "switch everything at once." If one account gets busy while others sit idle, Omniscio also proactively moves a couple of its sessions to a freer account before they stall, parks a session that keeps bouncing across a fully-maxed pool, splits each account's chip into how many sessions are actively running vs. parked waiting on you (and shows a maxed account's sessions as a muted "waiting" instead of a misleading green "live") It is off by default since 2026-08-29 (fresh installs run a single account; installs from before that date keep the on-by-default setting they had — turn it on in Settings → Accounts with 2+ login accounts), needs 2+ login accounts to do anything, and when off, account behavior is exactly the same as it has always been.
Why it exists
Normally Omniscio funnels every new session onto one "active" account. If you run many sessions in parallel, that one account's 5-hour usage window drains fast, and when it caps, Omniscio flips your whole fleet over to the next account at once. With several accounts, that means you only ever use one window at a time and you get a big synchronized stall at each cap.
Load balancing changes the economics: with 3 login accounts, new sessions fan out across all three, so you get roughly 3× the effective 5-hour window, each account drains at about a third the rate, and there is no fleet-wide mass switch — caps happen one account at a time and only affect that account's sessions.
The goal is to use as much of your total capacity as possible before it resets. Spreading evenly is most of that; the soon-to-reset preference (below) is the rest — it steers real work toward capacity that's about to cycle so less of it expires unused, weighted hardest toward the weekly (7-day) window because a whole week's unused budget is the bigger thing to forfeit. It does not manufacture work: with only a couple of sessions running you can't fill every account, and it won't try to.
Where to find it
Requirements and where to find it
- Settings → Accounts, a toggle labeled "Distribute Sessions Across Accounts."
- The toggle only appears when you have 2 or more login (OAuth) accounts. API-key accounts do not participate in load balancing (they remain a last-resort fallback exactly as before), so a single login account plus an API key still does not show the toggle.
- Default: off since 2026-08-29 — fresh installs run a single account; turn it on anytime with 2+ login accounts. Installs from before that date are preserved: a config that never saved the setting keeps the old on behavior, and any explicit choice you made stays exactly as you left it.
How it behaves
What it does when ON — five behaviors
Spread at spawn, with a soon-to-reset preference. When a new session starts, Omniscio picks the least-loaded login account that still has capacity (lowest of its 5-hour / 7-day utilization), rather than always using the pinned/active account. On top of that, it gives a bounded preference to an account whose usage window is about to reset and still has room — so capacity that's about to reset (and would otherwise go unused) gets harvested first. This runs for both windows: the 5-hour window (a small nudge over its final hour) and — weighted about twice as hard and over roughly three days' runway — the weekly (7-day) window, because the weekly budget is the bigger paid-for chunk and the 5-hour limit throttles how fast you can draw it down, so draining a week's leftover needs a multi-day head start (the runway was widened from one day to three on 2026-07-05, because a single day often wasn't long enough to use up a big weekly balance before it reset). Each preference is capped so it can win a close call but never overrides a clearly-fresher account, and both are measured against the higher of the account's 5-hour and 7-day usage, so they never push an account toward its scarcer weekly limit or into an early weekly lockout. (If an account doesn't report a reset time, it simply gets no preference — even spread as before. The weekly harvest has its own off switch:
AMC_DISABLE_LB_SEVEN_DAY_HARVEST=1.) Omniscio also weighs how many sessions each account is already running (the same in-use count the account dropdown chip shows, split there into active + waiting on you): each live session makes its account a little less preferred, so new sessions fan out toward the quieter accounts instead of stacking on one — even when several accounts look equally fresh and the sessions are started minutes apart (not just in a tight burst). That preference is capped (after roughly six sessions an account stops looking progressively worse), so it never sends work to an almost-maxed account just because another one is busy — real usage always wins at the extreme. A hard ceiling stops a pile-up. That per-session preference only slows how fast one account fills; once an account is the clear favorite on usage it can still absorb a long run of back-to-back sessions (in practice one account was seen climbing to ~20 live sessions while others sat nearly empty). So on top of it, once an account is carrying more than its fair share of live sessions, Omniscio stops handing it new work and fans the next sessions out to other accounts that have room — unless the only accounts with room are themselves near their limit, in which case it keeps using the busy-but-fresh account (it never trades an even spread for a spawn onto an almost-maxed account). The ceiling scales with how busy you are, so an even spread is never disturbed; it only engages when one account has genuinely run ahead. It counts sessions that are still spinning up, not just ones already fully started (the same way the near-limit cap below does), so a rapid burst of new sessions can't all pile onto one fresh account before its usage numbers catch up — the gap that used to let one account reach ~14 live sessions while healthy accounts sat idle. (Emergency off switch:AMC_DISABLE_LB_SESSION_CEILING=1.) During a sudden burst (e.g. you launch 30 sessions at once, before fresh usage numbers have come back), an additional short-lived "just spawned here" nudge makes the burst fan out evenly instead of all landing on whichever account looked freshest for a split second. A near-limit account is held to one session at a time. As a final safety rail, once any account crosses 95% of its weekly (7-day) budget, Omniscio lets it run only a single session at once and sends any additional new session elsewhere — so an account about to exhaust its scarce weekly allowance can't have several sessions pile onto it and cap together. This is stricter than the ceiling above (which only steers toward comfortable accounts and otherwise gives up and uses the busiest); it is the one rule that reliably keeps a nearly-maxed weekly account from absorbing a whole burst. It counts sessions still spinning up (not just fully-started ones), so a rapid burst can't slip past it. If every account is in that state at once, Omniscio still starts the session rather than leaving you stuck. (Emergency off switch:AMC_DISABLE_LB_HOT_ACCOUNT_CAP=1.)Stay put (per-session account affinity). Each session normally keeps using its own account across turns. Omniscio does not force an idle session to follow the global active account on its next message, so a session that started on account B usually keeps using account B. Exceptions: if you remove account B from Omniscio, or if that home account is already carrying well above its fair share while another comfortable login has room, the next respawn can re-home the session instead of piling even more work onto the overloaded account.
Re-spread on cap-out. When one account hits its rate limit, only that account's sessions are affected, and each independently re-picks another account that still has room — instead of dumping them all onto one other account (which would just recreate the pile-up). There is no global account switch and no synchronized cascade. If every account is capped, sessions wait for the soonest reset exactly as they do today, then resume on whichever account recovered.
Proactively drain an over-loaded account before it stalls. Re-spread (behavior 3) only moves a session after it personally hits a limit — so one account could keep many sessions on it, each capping one at a time over a long stretch, while other accounts sit nearly idle. About every 5 minutes (when fresh usage numbers come in), Omniscio drains an over-loaded account onto one with real room — never more than two sessions per check, and never onto an account that's itself nearly full — in either (or both) of two independent cases per check:
- Near its limit and carrying more than its fair share → it moves a couple of its running sessions over before they cap (they are the actual load about to stall).
- Far over its fair share by session count, even while its usage still looks calm → a big pile on one account never trips a rate limit as a whole, yet it repeatedly maxes its own 5-hour window and leaves its sessions "waiting" while other accounts idle. Here Omniscio drains gently: it re-homes only sessions that can move for free — never one that's actively working, and never one that's waiting on your answer — so nothing in progress is interrupted. The pile evens out over the next few checks as its running sessions finish and become movable, while behavior 1's ceiling keeps new work off it. This calm-pile case now runs on its own each check — it is no longer skipped whenever some other account happens to be near its limit (which, with many accounts, was almost always). Before, that meant a calm-but-over-piled account — the exact thing that stranded ~20+ sessions on one account — could sit undrained until it capped on its own.
(Emergency off switches:
AMC_DISABLE_LB_REBALANCE=1disables all draining;AMC_DISABLE_LB_COUNT_REBALANCE=1disables just the second, count-based case while leaving the near-limit drain on.)Stop a session from bouncing forever (anti-thrash). When every account is hot, a single session can ping-pong — run, hit a limit, move, hit a limit, move — every minute or so for hours without ever finishing its turn. After it has been bounced a few times in a 15-minute window without settling, Omniscio parks it to wait for the next reset instead of burning another restart, and lets it try again once the pool cools off.
Seeing it work — the header indicator
When load balancing is on, the account pill in the top toolbar (the small control at the top-right that normally shows one account's usage %) changes to report the pool instead of a single account:
- A balance-scale icon followed by a number — the number is how many of your accounts are currently in use (running at least one live session right now). "In use" counts sessions that are running, starting, stalled, or waiting on you; it excludes idle pre-warmed blanks, silent background recipe sessions, and errored (dead) sessions — a session that crashed and is sitting red has no running process, so it doesn't count as "live" (since 2026-06-10; before that, a pile of errored sessions could inflate the count). The number matches what your sidebar shows.
- The old single-account percentage is replaced by a small pool-health glyph: a green check means "your whole pool won't run out before its limits reset," an amber triangle means "the pool is projected to run low," and a red ✕ means "almost out / every account is capped." This replaces the percentage because under load balancing that number was just the pinned account's usage — usually your busiest one — so it looked alarming even when ten other accounts had plenty of headroom. The glyph is the same verdict shown in the account dropdown's forecast row, so the two never disagree. (During the forecast's cold-start window, or if you've turned the usage forecast off, the glyph is simply omitted and the pill shows just the scale icon + count.)
- Hovering the pill adds a "Load balancing on · N accounts in use" line to the tooltip, plus the pool-health verdict in words.
- Opening the dropdown shows a "Balancing · N in use" line under the Accounts
header, and each account that's running work gets a chip that splits its sessions into
how many are actively running versus parked — green "N running · M waiting"
(or just "N running" when all are running, or "M waiting"
when none are). "Running" means actually streaming tokens right now; "waiting"
is the rest — parked for your reply, spinning up, or stalled — which burn
nothing, so a big count next to 0% usage is no longer a mystery. One click
answers not just how many accounts but which ones, and how busy each is. An account that is at capacity (its 5-hour or weekly window maxed,
no extra credits) instead shows a single muted "N waiting" chip — all its
sessions are stranded: they can't run until the limit resets or they move to a
freer account. The active + waiting numbers always add up to the same in-use
total the load balancer uses (account-switch invariant
standing-per-session-load-spreads-spawns) — only the presentation is split. Balancing also orders the dropdown by which account it would use next — the active account leads, but once it is at capacity (the same "N waiting" state) it is no longer pinned to the top; it drops to its real position so accounts with room lead.
On a phone / narrow mobile layout the mobile account chip (the account control in the mobile header) shows a more compact version: just the balance-scale icon, and the icon's own COLOUR is the pool-health verdict — green when your pool won't run out before its limits reset, amber when it's projected to run low, red when it's out of capacity (change of 2026-07-10; this replaced the older separate green-check / amber-triangle / red-X glyph that used to sit beside the scale). The accounts-in-use number was already dropped from the chip face to save room on the narrow bar (2026-07-09); it still rides the chip's tooltip/label, and opening the chip shows the full "Balancing · N accounts in use" line plus the same running/waiting split chip ("N running · M waiting") on each login account. When there's no verdict yet (a cold-start window, or the usage forecast is off) the scale is a neutral grey and highlights in the accent colour while the account menu is open. Mobile and desktop read the exact same session/forecast state through shared code, so their verdicts can never disagree — the colour is the same one the desktop pill and the Settings forecast row show; mobile just carries it on the scale icon itself instead of a separate glyph.
When load balancing is off, the pill (and the mobile chip) is exactly as it was before — the single-account percentage and status dot — with none of the above.
Seeing it work — inside a session
The header pill is the pool-wide view; each individual session also explains itself, so an ember/orange session in the sidebar is never a mystery:
- A parked session shows a pinned banner. While a session is waiting on a rate limit (its "waiting" status — ember orange), a persistent "Waiting for a Claude account with available usage — resumes automatically, no action needed" banner sits at the top of that session. It is pinned (not a message that scrolls away), clears itself the instant the session resumes, and has nothing to click — the wait needs nothing from you.
- A moved session shows a caption in its log. When a rate-limited session resumes on a different account (re-spread on cap-out, or the proactive drain), the message log shows a small "Switched to a Claude account with available usage to keep going" divider right where the turn picked back up — so the failover is visible instead of buried in the collapsed activity. A same-account resume (the limit simply cleared, nothing moved) shows no caption.
- The "waiting" state is reserved for a GENUINE pool-wide shortage. A session only
turns ember "waiting" (and only then does the "Sessions waiting on account
capacity" inbox alert appear) when every account is genuinely out of usage. If a
session briefly bounces off a temporary "slow down" from Anthropic (a burst throttle,
which can hit an account that still has plenty of usage left) while other accounts have
room, it stays silent and simply re-routes to a free account — no orange badge, no
"out of capacity" alert. So that alert firing genuinely means the whole pool is capped,
not that one account hiccuped. (Safety net: a session somehow stuck for 10+ minutes
still surfaces so nothing hides forever; emergency revert
AMC_DISABLE_CAPACITY_FLEET_GATE=1restores the old always-surface behavior.) - A one-login pile-up warns you on its own (2026-07-27). If one login ends up carrying a
large, SUSTAINED pile-up of actively-running sessions while another account still has room,
AMC raises a single dismissible inbox note ("Your login is carrying N active sessions…")
so you notice the imbalance instead of catching it by screenshot. It is deliberately rare —
it needs a genuine, sustained pile-up on a login that is ALSO climbing toward its limit while
a cooler login sits idle — and it clears itself as the load evens out. Nothing stops working;
you may just see more rate-limit waiting on that one login until its window resets. This
warning now fires whenever load balancing is on (it is no longer gated behind the
in-development capacity-ledger flag). Env revert
AMC_DISABLE_LB_CONCENTRATION_ALERT=1silences just the alert; the underlying metric keeps logging.
What "pinning / Activate an account" means while it's ON
The manual Activate action (clicking an account to make it active) still works and still moves your existing sessions — that is treated as deliberate user intent. But for new spawns, load balancing intentionally ignores the pinned account and spreads instead; the pinned account just becomes the display default. If you want the old "everything on one account" behavior back, turn the toggle off.
When OFF
Behavior is byte-for-byte identical to the old single-account model: new sessions use the pinned/active account, sessions are force-migrated to follow the active account, and a rate limit triggers the normal auto-switch + fleet cascade (governed by the separate "Auto-Switch Account on Rate Limit" setting). Turning load balancing off fully restores this.
Kill switch
Set the environment variable AMC_DISABLE_DISTRIBUTE_SESSIONS=1 to force the feature off regardless of the setting (the env kill switch always wins). This is an emergency escape hatch; normal users just use the toggle.
Relationship to "Auto-Switch Account on Rate Limit"
These are different settings. "Auto-Switch" (when on) moves your whole active account to a fresh one when the active account caps. Load balancing instead keeps sessions distributed and moves only the affected account's sessions. When load balancing is on, the automated global auto-switch is suppressed (so the two don't fight); the manual Activate switch is unaffected.
For agents
For agents with repo access — where this lives
- Feature flag + kill switch: src/main/services/distribute-sessions-flag.ts (
isDistributeSessionsEnabled(settings),AMC_DISABLE_DISTRIBUTE_SESSIONS). - Setting:
distributeSessionsAcrossAccountsinAppSettings(defaultfalseas of 2026-08-29; wastrue2026-05-31→2026-08-29) + Zod field in src/shared/ipc-schemas/update-settings.ts; UI toggle in src/renderer/src/features/settings/sections/accounts/AccountSettings.tsx. Pre-flip installs are preserved by load-save's pre-parse seed (a raw config missing the key persists the oldtrue; explicit choices are never rewritten) — the former force-ON one-shot migration is retired.isDistributeSessionsEnabledand every UI read-site fall back?? false, matching the default. - Spread-at-spawn picker:
pickLoadBalancedAccountin src/shared/account-pool.ts; wired intopickActiveAccount()in src/main/process/process-manager.ts. Effective load =max(5h,7d) + standing-session penalty + in-flight penalty − 5h-reset bias − 7d-reset bias. Standing per-session penalty:min(liveSessions × LOAD_BALANCE_SESSION_STEP, LOAD_BALANCE_SESSION_CAP)(5/session, cap 30), exact ties broken by fewer live sessions;liveSessionscomes fromgetLiveSessionCountsByAccount()(src/main/db/queries-sessions/reads.ts), which sharesIN_USE_STATUSES+isSilentlyHiddenwith the header via src/shared/session-live.ts (the rendererlib/silent-session.tsre-exports it) so the picker's count equals the "N live" chips; the call is try/caught (degrades to usage-only on failure). Per-account live-session ceiling: above the soft session cap, the picker narrows the ranked set to accounts that are BOTH undermax(LB_SESSION_CEILING_MIN = 8, ceil(totalPoolLive / poolSize) + LB_SESSION_CEILING_SLACK = 2)live AND comfortable (primaryScore < LB_COMFORTABLE_PCT = 80), falling back to the full pool when no comfortable under-ceiling account exists (never blocks a spawn, never routes onto a near-maxed account — preserves the cap's invariant-24 guarantee); the ranking itself is unchanged, only the candidate set is narrowed. The caller passessessionCeilingEnabledfrom theAMC_DISABLE_LB_SESSION_CEILING=1kill switch. Contract invariant 27. Hot-account 7-day session cap: ahead of — and feeding — the ceiling narrowing, a login at/aboveLB_HOT_ACCOUNT_SEVEN_DAY_PCT = 95on its 7-day window that already carries (live + in-flight)≥ LB_HOT_ACCOUNT_SESSION_CAP = 1session is dropped from the candidate set; the ceiling'scomfortableUnderthen filters this narrowed set rather than the raw pool, and both fall back to the full pool when every account is hot-and-at-cap (never strands). Because 95 >LB_COMFORTABLE_PCT(80) a hot account already fails the comfort gate, so this is load-bearing only in the ceiling's "nothing comfortable → use the raw pool" branch. The caller (pickViaLoadBalancerin src/main/process/account-selection.ts) passeshotAccountCapEnabledfrom theAMC_DISABLE_LB_HOT_ACCOUNT_CAP=1kill switch. Contract invariant 28. In-flight counter: transient burst spread (src/main/process/spawn-load-tracker.ts, 20s TTL). Soon-to-reset urgency bias (per window): the sharedresetUrgencyBonus(account, resetMs, horizonMs, cap)is subtracted once per window — 5-hour (LOAD_BALANCE_URGENCY_CAP = 15,LOAD_BALANCE_URGENCY_HORIZON_MS = 1h) and 7-day (LOAD_BALANCE_SEVEN_DAY_URGENCY_CAP = 30,LOAD_BALANCE_SEVEN_DAY_URGENCY_HORIZON_MS = 72h/ ~3 days, widened from 24h on 2026-07-05 — weighted 2× over a much longer runway because the weekly budget is the bigger forfeited chunk and 24h often wasn't long enough to drain it before reset; HORIZON is the "harvest harder" lever, the CAP stays put so a burst still fans out). Both useheadroom = 100 − max(5h,7d)(protects the 7-day budget + avoids an early weekly lockout) and stack when both windows are imminent. Reset distances areAccountWithUsage.fiveHourResetMs/sevenDayResetMs, computed inbuildAccountsWithUsagefromusage.{fiveHour,sevenDay}.resetsAt. The 7-day term is gated by thesevenDayHarvestEnabledoption from theAMC_DISABLE_LB_SEVEN_DAY_HARVEST=1kill switch (5-hour unaffected); the rebalancer (pickBestAccount) is NOT a consumer. Contract invariant 22. - Per-session affinity + cap-out re-spread: src/main/services/rate-limit-recovery-service.ts (
tryAutoSwitchno-op under LB;lb-respawnper-session trigger; the consume-once exclude-account hint,markExcludeAccountOnNextSpawn→ src/main/process/next-spawn-directives.ts). Resume affinity is routed through src/main/process/spawn-cluster-manager.ts + src/shared/account-pool.ts (preferAccountId), and it now yields to the live-session ceiling when another comfortable account has room. - Proactive rebalance (drain an over-subscribed account before it caps):
rebalanceOverloadedAccountsin src/main/services/rate-limit-recovery-service.ts, called fromonUsageRefreshedunder LB. Two INDEPENDENT SOURCE triggers viapickDrainSources, each draining its OWN source per cycle (HEAT first, then COUNT) through the shareddrainOneSource: HEAT =utilOf ≥ LB_REBALANCE_HIGH_WATER_PCT(80) + over fair share → drains RUNNING-first candidates; COUNT =liveCount ≥ LB_REBALANCE_COUNT_RATIO (2) × fairShareeven below the high-water → GENTLE, candidates filtered to non-running/startingand!hasActionablePending(the shared predicaterestartSession's needs_you guard uses), so no active turn or pending question is ever moved. The COUNT trigger is NO LONGER gated on!heatSource(2026-07-22): that preemption starved it whenever ANY account was hot — the busy-fleet norm — so a calm, over-piled account sat undrained (the ~27-live active-account pile-up). It now only EXCLUDES the heat account so one account is never drained twice; being restart-free (idle re-point) it adds no kills, so running both per cycle keeps process kills ≤LB_REBALANCE_MAX_PER_CYCLE(heat path only).LB_REBALANCE_COUNT_RATIOmirrorsCONCENTRATION_ALERT_RATIO(kept a local constant to avoid a service import cycle). Bounded byLB_REBALANCE_MAX_PER_CYCLE; moves via thelb-rebalancetrigger +markExcludeAccountOnNextSpawn; candidate sessions fromgetLiveSessionsForAccount(src/main/db/queries-sessions/reads.ts, same filter asgetLiveSessionCountsByAccount). Kill switches:AMC_DISABLE_LB_REBALANCE=1(all draining) /AMC_DISABLE_LB_COUNT_REBALANCE=1(the count trigger only). Locked by tests/unit/db/get-live-sessions-for-account.test.ts + theproactive rebalanceblock (incl. thecount-concentration drainsub-block) in rate-limit-recovery-load-balance.test.ts. Contract invariant 25. - Anti-thrash backstop (park a session bouncing across a saturated pool):
lbRespawnTimestamps+LB_RESPAWN_THRASH_MAX/LB_RESPAWN_THRASH_WINDOW_MSinonLbSessionRateLimited(same file); contract invariant 26. - Header indicator (display-only): the desktop toolbar pill src/renderer/src/components/ui/AccountIndicator.tsx renders the scale icon + accounts-in-use count + pool-health glyph; the mobile chip src/renderer/src/components/ui/MobileAccountChip.tsx renders a compact scale icon + pool-health glyph only — the accounts-in-use count is dropped from the narrow chip face (it stays in the chip's aria-label + the dropdown's "Balancing · N in use" line), and the neutral scale icon turns accent while the account menu is open (2026-07-09; deliberate mobile divergence from the desktop pill). The per-account live count comes from
computeLiveSessionsByAccount(src/renderer/src/lib/account-live-sessions.ts, "in use" =LIVE_STATUSESminusready/terminating/error— dead sessions are not load, silent-excluded) and the glyph frompickForecastGlyph(src/renderer/src/lib/forecast-glyph.ts, mirrorsGlobalUsageForecastRow). The two surfaces shareEMPTY_LIVE_BY_ACCOUNT(the frozen LB-off subscription guard, inaccount-live-sessions.ts),ForecastGlyphIcon(glyph→icon), andLiveSessionChip(the active/waiting split chip — its active count comes from the sharedcountStreamingSessionsByAccount) so they can't drift. Pure reads of session state + the existing usage forecast — no spawn/recovery change. The chip's at-capacity ("waiting") state is decided byisAccountAtCapacity(src/renderer/src/lib/account-live-sessions.ts), which wraps the sharedusageHasBudgetHeadroom(src/shared/account-pool.ts) — the SAME capacity rule the spawn picker uses, so "waiting" can never disagree with which accounts the load balancer treats as full. Locked by tests/unit/components/LiveSessionChip.test.tsx + theisAccountAtCapacityblock in tests/unit/lib/account-live-sessions.test.ts. - In-session surface (display-only): the parked-session banner lives in src/renderer/src/features/sessions/SessionPanel/SessionStatusBanners.tsx (gated on
session.status === 'waiting', no action button). The account-switch caption is src/renderer/src/features/sessions/RateLimitRecoveryCaption.tsx driven byderiveRateLimitRecoveryCaptionin src/renderer/src/features/sessions/recovery-captions.ts, which gates onACCOUNT_MOVE_RECOVERY_TRIGGERS(read via itsisAccountMoveTriggerhelper) from src/shared/rate-limit-recovery-triggers.ts — that shared module is the source of truth for the trigger set, andcoalesce-system-rows.tsonly re-exports the caption helper. Fires only for the account-MOVE triggersaccount-switch/lb-respawn/lb-rebalance; mirrors the auth-refreshRecoveryCaption, mounted in src/renderer/src/features/sessions/TurnGroup.tsx. When ONE turn carries BOTH this switch caption AND the auth-refreshRecoveryCaption, the two render in chronological (firstTimestamp) order viaorderRecoveryCaptions— the earlier event on top — not a fixed recovery-then-switch order (2026-07-23 fix: a 12:52 AM switch was drawing below a 1:58 AM refresh). Both are additive — the in-flight inline marker is unchanged. Locked by tests/unit/features/sessions/session-status-banners-rate-limit.test.tsx + tests/unit/features/sessions/rate-limit-recovery-caption.test.tsx + tests/unit/features/sessions/TurnGroup-recovery-caption-order.test.tsx. Contract: .claude/memory/contracts/account-capacity-silent-recovery-contract.md the-park-and-switch-have-a-visible-in-session-surface. - Capacity-wait fleet gate (
the-fleet-capacity-gate): the bounded-window sweep in src/main/services/rate-limit-recovery/capacity-wait-visibility.ts (sweep/readFleetCapacity/maybeRaiseWave/nudgeOnce) surfaces the emberwaitingstate + raises the coalesced "Sessions waiting on account capacity" inbox alert ONLY when the fleet is genuinely exhausted — it probesfleetHasFreeCapacity(the sharedanyEligibleAccountHasCapacity=pickBestAccounton cached usage, in src/main/services/rate-limit-recovery-service.ts) once per tick; while any account has room a past-window session stays silent + is nudged to re-route. A 10-min absolute backstop (CAPACITY_WAIT_ABSOLUTE_SILENCE_MS) still surfaces a wedged session (bounded silence) without the false alarm; fail-visible on a probe throw; kill switchAMC_DISABLE_CAPACITY_FLEET_GATE=1. Locked by tests/unit/services/rate-limit-capacity-wait.test.ts (the 5 the-fleet-capacity-gate cases). Contract: .claude/memory/contracts/account-capacity-silent-recovery-contract.md the-fleet-capacity-gate. - Invariants + safe-change rules: .claude/memory/contracts/account-switch-contract.md (see the "Header surfacing" note).
Related
Accounts themselves — adding one, signing in, and what happens when one hits its rate limit — are covered by the accounts documentation in this library.
Last verified 2026-10-01