fix: enforce tier lock during quota preflight (#1550)

Tier lock now fails closed in quota preflight when no healthy locked-tier account is available, instead of falling through cross-tier.
This commit is contained in:
Kai (Tam Nhu) Tran authored and GitHub committed 2026-06-16 08:45:35 -04:00
1 parent 06e83091be
commit 361328c788
2 files changed
+51 -3

No files matched your search

+8 -3
View File
@@ -658,8 +658,8 @@ export async function preflightCheck(provider: CLIProxyProvider): Promise<Prefli
// match the locked tier. If it doesn't, route to a healthy account in the
// locked tier instead. Locks are per-provider: locking "agy" to "ultra"
// does NOT constrain "claude", "codex", "gemini", or "ghcp".
// Graceful degradation: if no locked-tier account is available, fall through
// to the default (don't block the request entirely).
// This is intentionally strict: if no locked-tier account is available, do
// not fail open to a cross-tier default.
const tierLock = getTierLockForProvider(quotaConfig.manual, provider);
if (tierLock !== null && (defaultAccount.tier || 'unknown') !== tierLock) {
const lockedTierAccount = await findHealthyAccount(provider, []);
@@ -673,7 +673,12 @@ export async function preflightCheck(provider: CLIProxyProvider): Promise<Prefli
reason: `Tier lock: selected ${tierLock} account`,
};
}
// No locked-tier account available — fall through and use default
return {
proceed: false,
accountId: '',
reason: `Tier lock: no healthy ${tierLock} account available`,
};
}
// Check if default is paused
@@ -352,6 +352,49 @@ describe('quota-manager findHealthyAccount — tier_lock', () => {
expect(result).toBeNull();
});
// -------------------------------------------------------------------------
// preflightCheck must enforce the same strict tier lock as findHealthyAccount
// -------------------------------------------------------------------------
it('preflightCheck blocks a cross-tier default when no healthy locked-tier account exists', async () => {
writeMinimalConfig(tempHome, { agy: 'pro' });
_mockAccounts = [ULTRA_ACCOUNT, PRO_ACCOUNT];
const uid = `preflight-lock-pro-exhausted-${Date.now()}-${Math.random()}`;
const { preflightCheck, setCachedQuota } = await import(
`../../../src/cliproxy/quota/quota-manager?${uid}`
);
// Default is ultra, but agy is locked to pro. The only pro account is
// exhausted, so preflight must not fail open to the healthy ultra default.
setCachedQuota('agy', ULTRA_ACCOUNT.id, HEALTHY_QUOTA as never);
setCachedQuota('agy', PRO_ACCOUNT.id, EXHAUSTED_QUOTA as never);
const result = await preflightCheck('agy');
expect(result.proceed).toBe(false);
expect(result.accountId).not.toBe(ULTRA_ACCOUNT.id);
expect(result.reason).toBe('Tier lock: no healthy pro account available');
});
it('preflightCheck switches a cross-tier default to a healthy locked-tier account', async () => {
writeMinimalConfig(tempHome, { agy: 'pro' });
_mockAccounts = [ULTRA_ACCOUNT, PRO_ACCOUNT];
const uid = `preflight-lock-pro-healthy-${Date.now()}-${Math.random()}`;
const { preflightCheck, setCachedQuota } = await import(
`../../../src/cliproxy/quota/quota-manager?${uid}`
);
setCachedQuota('agy', ULTRA_ACCOUNT.id, HEALTHY_QUOTA as never);
setCachedQuota('agy', PRO_ACCOUNT.id, HEALTHY_QUOTA as never);
const result = await preflightCheck('agy');
expect(result.proceed).toBe(true);
expect(result.accountId).toBe(PRO_ACCOUNT.id);
expect(result.switchedFrom).toBe(ULTRA_ACCOUNT.id);
expect(result.reason).toBe('Tier lock: selected pro account');
});
// -------------------------------------------------------------------------
// Baseline: no tier_lock — any available healthy account is returned.
//