pub struct AccountStateMap {
pub ordered_keys: Vec<AccountKey>,
pub by_key: HashMap<AccountKey, AccountState>,
}Fields§
§ordered_keys: Vec<AccountKey>§by_key: HashMap<AccountKey, AccountState>Implementations§
Source§impl AccountStateMap
impl AccountStateMap
Sourcepub fn empty_for_test() -> Self
pub fn empty_for_test() -> Self
Empty map for the testing feature’s Workspace::testing_stub.
Production code paths reach this map only through the gateway’s
selection walk, which a test fixture should never exercise.
pub fn new(accounts: &[LoadedAccount]) -> Self
Sourcepub fn seed_from_cache(&mut self, cached: &BTreeMap<String, UsageSnapshot>)
pub fn seed_from_cache(&mut self, cached: &BTreeMap<String, UsageSnapshot>)
Seed the in-memory map from a previously-persisted cache.
Each known account that appears in cached gets its usage
populated; unknown cache entries (account removed from
forge.toml) are ignored. Does NOT clear last_error or
next_probe_at - the cache is purely seed data; the live
poller still drives backoff. Used by Workspace::new so
usage reads have data on cold boot.
Sourcepub fn snapshots_for_cache(&self) -> BTreeMap<String, UsageSnapshot>
pub fn snapshots_for_cache(&self) -> BTreeMap<String, UsageSnapshot>
Snapshot the per-account usage for writing to the on-disk
cache. Accounts with no live snapshot are omitted so the cache
file doesn’t grow placeholders.
Sourcepub fn env(&self, key: &AccountKey) -> Option<&HashMap<String, String>>
pub fn env(&self, key: &AccountKey) -> Option<&HashMap<String, String>>
The account’s env map: global [env] extended by its own
[accounts.env]. Consumed by the spawn path (stamped onto the
child) and the usage poller / loader (base url, bearer, setup
token). None for unknown keys.
Sourcepub fn provider(&self, key: &AccountKey) -> Option<Provider>
pub fn provider(&self, key: &AccountKey) -> Option<Provider>
Declared forge_primitives::account::Provider for key, the input the probe plan and the
preflight repair copy both branch on. None for unknown keys.
Sourcepub fn auth(&self, key: &AccountKey) -> Option<AccountAuth>
pub fn auth(&self, key: &AccountKey) -> Option<AccountAuth>
How the account proves who it is, which is the only thing that
changes what an auth-repair hint tells the user to do. Base-url
wins: such an account authenticates with the flat token beside
its base_url. Every other account’s credential is its setup
token, so both its token-mode and its token-less shapes repair
the same way. None for unknown keys.
Sourcepub fn set_usage(&mut self, key: &AccountKey, snapshot: UsageSnapshot)
pub fn set_usage(&mut self, key: &AccountKey, snapshot: UsageSnapshot)
Replace the cached usage snapshot for key and clear any
stale last_error. Resets the consecutive-failure counter
and clears the next-probe gate so this account returns to
the default cadence. Also transitions loading to Ready -
a successful probe is the terminal-success state for the
boot-time loading task. Silent no-op when key isn’t
registered (defensive - invariant says every poller key was
inserted in new()).
Sourcepub fn set_loading(&mut self, key: &AccountKey, loading: LoadingState)
pub fn set_loading(&mut self, key: &AccountKey, loading: LoadingState)
Drive a LoadingState transition for key, used by the
boot-time loading task. Setting Bailed clears the cached
usage so no stale bar is drawn (replaces the PR #238
3-strike counter; bailed accounts have no live snapshot by
construction).
Sourcepub fn set_limit_reading(&mut self, key: &AccountKey, snapshot: UsageSnapshot)
pub fn set_limit_reading(&mut self, key: &AccountKey, snapshot: UsageSnapshot)
Keep the windows a failed probe still carried. It never touches
loading or last_error: a rate-limited probe does not heal the
account, and nothing selection or a cooldown reads may move.
Sourcepub fn latest_reading(&self, key: &AccountKey) -> Option<&UsageSnapshot>
pub fn latest_reading(&self, key: &AccountKey) -> Option<&UsageSnapshot>
The newest reading the account gave, for a view’s bars: a rate-limited response’s windows when one landed after the last successful probe, else that probe’s snapshot. Absent while both are.
Sourcepub fn disarm_override(&mut self, key: &AccountKey)
pub fn disarm_override(&mut self, key: &AccountKey)
Disarm the scheduler hook for key after firing an override
probe attempt. One-shot semantics: the hook fires once per
arming (i.e. once per fresh snapshot). Subsequent stale-reset
state must wait for the next successful probe (which re-arms
via set_usage) or until the existing backoff timer
(next_probe_at) elapses.
Sourcepub fn set_last_error(
&mut self,
key: &AccountKey,
status: UsageFetchStatus,
retry_after: Option<Duration>,
)
pub fn set_last_error( &mut self, key: &AccountKey, status: UsageFetchStatus, retry_after: Option<Duration>, )
Record the latest poll-attempt failure for key and schedule
the next probe.
retry_after (when Some) is the server-provided hold-down
duration - typically Anthropic’s Retry-After header on 429.
We honour it verbatim because the server knows when its
per-account bucket will reset; guessing with our own backoff
either over- or under-shoots and keeps the limit hot. When
None (network failures, unknown HTTP status, server didn’t
send Retry-After), fall back to a local exponential schedule.
The cached usage snapshot is preserved so the panel keeps
showing the last known-good bars (a fresh 429 doesn’t blank
them out).
Sourcepub fn should_probe_now(&self, key: &AccountKey) -> bool
pub fn should_probe_now(&self, key: &AccountKey) -> bool
true when the poller may probe key now. false when the
account is in an active backoff window from a recent failure.
Cold-cache accounts (no last_error) always return true.
Sourcepub fn all_loaded(&self) -> bool
pub fn all_loaded(&self) -> bool
true when every account in the map has reached a terminal
LoadingState (Ready or Bailed). The boot wave and the cron
spawn path gate on this. Empty maps return true (vacuous; no
accounts means no work to wait on - relevant in the testing-stub
path). A retired account counts as settled: the edit removed it, so
nothing will ever load it.
Sourcepub fn is_saturated(&self, key: &AccountKey) -> bool
pub fn is_saturated(&self, key: &AccountKey) -> bool
True when key’s loaded usage snapshot shows it at-or-beyond
the plan cap - the same saturation signal unusable_reason
classifies on. A Ready-but-saturated account logs in
fine but trips the rate limit on its next request, so the
selection walk prefers other accounts when one is available.
Sourcepub fn unusable_reason(&self, key: &AccountKey) -> Option<Unusable>
pub fn unusable_reason(&self, key: &AccountKey) -> Option<Unusable>
Why key is out of the usable tier, None when pickable: what
the walk reads to demote an account. Bailed
outranks the probe classes: it is the loading task’s terminal
verdict, and a bailed account has no cached windows to saturate
with. A snapshot whose source is not the account backend’s is
treated as absent, the same refusal budget() applies - the
redb cache survives a provider edit and is re-seeded at boot,
and without this a stale windowed snapshot would classify a
spend row as saturated.
Sourcepub fn loading_state(&self, key: &AccountKey) -> LoadingState
pub fn loading_state(&self, key: &AccountKey) -> LoadingState
Snapshot the current LoadingState for key. Returns
LoadingState::Loading by default for unknown keys
(defensive - a caller may hold a key that hasn’t been
registered in the map).
Sourcepub fn scheduler_should_probe(&self, key: &AccountKey) -> bool
pub fn scheduler_should_probe(&self, key: &AccountKey) -> bool
Combined scheduler signal: probe key on the current cycle if
the normal backoff gate (should_probe_now) is open OR the
one-shot reset-clear override hook (has_just_cleared_cap_window)
is firing. Both sides of the OR carry through to the workspace
poller, which calls this method to decide which accounts to
probe each tick. Centralising the OR here keeps a future
refactor from accidentally dropping one half of the signal -
the unit test pins the truth table that the scheduler relies
on, regardless of whether the call site spells out both
predicates inline.
Sourcepub fn has_just_cleared_cap_window(&self, key: &AccountKey) -> bool
pub fn has_just_cleared_cap_window(&self, key: &AccountKey) -> bool
true when at least one cached window shows the account just
transitioned out of its cap (utilization at-or-above 100%,
resets_at now in the past) AND the override hook is armed.
The scheduler ORs this with should_probe_now so a fresh probe
lands on the next poll cycle after the reset moment, instead of
waiting through the remainder of an active backoff window.
Without the hook, an account 429’d with a multi-hour
Retry-After keeps painting the stale “100%” bar for hours
past the actual reset because the next probe is gated until the
backoff timer elapses.
The override_armed gate enforces one-shot semantics: each
successful probe arms the hook exactly once, and the scheduler
disarms it after firing. A persistently failing probe series
won’t keep tripping the override every cycle.
Sourcepub fn usage(&self, key: &AccountKey) -> Option<&UsageSnapshot>
pub fn usage(&self, key: &AccountKey) -> Option<&UsageSnapshot>
Look up the cached usage snapshot for key. None when the
poller hasn’t yet succeeded for this account.
Sourcepub fn usage_error(&self, key: &AccountKey) -> Option<UsageFetchStatus>
pub fn usage_error(&self, key: &AccountKey) -> Option<UsageFetchStatus>
Look up the latest poll-attempt failure for key. None
when the most recent attempt succeeded (or no attempt has
been made yet).
Sourcepub fn clear_last_error(&mut self, key: &AccountKey)
pub fn clear_last_error(&mut self, key: &AccountKey)
Drop the recorded failure for key, so a bail that never
recorded its own cause falls back to the unrecorded default
rather than wearing a stale class.