Symptom
A seat that is blocked by both a credits flag and an exhausted usage window is never offered the free reset that would unblock it. The grant sits unused until it expires.
Live example from 2026-09-20: backup1 shows
backup1 team 100% · in 1h23m (Mon 00:09) 31% · in 6d15h none RESETS 1 cooling (credits) until 11:45 PM
backup1: backend reports workspace_member_credits_depleted
It holds 1 grant expiring 2026-10-04, and its 5h window is exhausted — precisely what a "Full reset (Weekly + 5 hr)" lifts — yet it is filtered out of reset_eligible_seats.
Cause
src/seat.rs:189-221:
let cooling = st.cooldown_until.is_some_and(|u| u > now);
let reason = CooldownReason::parse(st.cooldown_reason.as_deref().unwrap_or(""));
if cooling && !reason.is_lifted_by_reset() {
// A per-model cap, credits or a spend cap all survive a reset.
return None;
}
is_lifted_by_reset is true only for RateLimit (src/ratelimit.rs:51). The comment's reasoning is sound when credits are the only block, but verdict() checks rate_limit_reached_type before the windows (src/usage.rs:1066), so a seat that is window-exhausted and credit-flagged records credits and the window exhaustion underneath becomes invisible to the eligibility filter.
This is the same error as #37 in the opposite direction: credits only matter once included quota is gone, so restoring the quota makes the credits block irrelevant.
Suggested direction
Treat a seat as reset-eligible when its own enforced window is at 100% with a reset still ahead, regardless of whether the recorded cooldown reason is credits — because a reset lifts that half, and with quota restored the seat no longer needs credits.
What needs verifying first
I have not confirmed that the backend stops reporting workspace_member_credits_depleted once the window resets. That assumption is what the fix rests on. seat reset --dry-run lists grants without redeeming, so it cannot settle it; only redeeming can, and a failed attempt still spends a finite grant.
Mitigating factor: clear_window_cooldown_after_reset already declines to clear when the refreshed reading still says exhausted, so the machinery fails safe — it would just have wasted the grant.
Symptom
A seat that is blocked by both a credits flag and an exhausted usage window is never offered the free reset that would unblock it. The grant sits unused until it expires.
Live example from 2026-09-20:
backup1showsIt holds 1 grant expiring 2026-10-04, and its 5h window is exhausted — precisely what a "Full reset (Weekly + 5 hr)" lifts — yet it is filtered out of
reset_eligible_seats.Cause
src/seat.rs:189-221:is_lifted_by_resetis true only forRateLimit(src/ratelimit.rs:51). The comment's reasoning is sound when credits are the only block, butverdict()checksrate_limit_reached_typebefore the windows (src/usage.rs:1066), so a seat that is window-exhausted and credit-flagged recordscreditsand the window exhaustion underneath becomes invisible to the eligibility filter.This is the same error as #37 in the opposite direction: credits only matter once included quota is gone, so restoring the quota makes the credits block irrelevant.
Suggested direction
Treat a seat as reset-eligible when its own enforced window is at 100% with a reset still ahead, regardless of whether the recorded cooldown reason is
credits— because a reset lifts that half, and with quota restored the seat no longer needs credits.What needs verifying first
I have not confirmed that the backend stops reporting
workspace_member_credits_depletedonce the window resets. That assumption is what the fix rests on.seat reset --dry-runlists grants without redeeming, so it cannot settle it; only redeeming can, and a failed attempt still spends a finite grant.Mitigating factor:
clear_window_cooldown_after_resetalready declines to clear when the refreshed reading still says exhausted, so the machinery fails safe — it would just have wasted the grant.