fix(primer): refresh Codex usage before primer and allow 1% fresh accounts

Codex primer was skipping with no_eligible_accounts because it read a
stale usage cache and required exactly 0% usage. Re-query usage right
before the primer call and treat freshly-reset 0~1% accounts as eligible
so the 5h window can be anchored at the fixed KST slot. Also hold new
Codex-owner turns at fresh usage until the next primer slot.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
This commit is contained in:
Codex
2026-06-01 16:29:12 +09:00
parent db715c6329
commit f4de795b1e
4 changed files with 233 additions and 6 deletions

77
src/usage-primer.test.ts Normal file
View File

@@ -0,0 +1,77 @@
import { describe, expect, it, vi } from 'vitest';
const callOrder: string[] = [];
const refreshAllCodexAccountUsage = vi.fn(async () => {
callOrder.push('refreshAll');
return { rows: [], fetchedAt: '2026-06-01T00:00:00.000Z' };
});
const refreshActiveCodexUsage = vi.fn(async () => {
callOrder.push('refreshActive');
return { rows: [], fetchedAt: '2026-06-01T00:00:01.000Z' };
});
const runCodexWarmupCycle = vi.fn(async () => {
callOrder.push('warmup');
return { status: 'warmed', accountIndex: 0 };
});
vi.mock('./config.js', () => ({
CODEX_WARMUP_CONFIG: {
enabled: false,
prompt: 'Reply exactly OK. Do not run tools.',
model: 'gpt-5.5',
intervalMs: 300_000,
minIntervalMs: 18_300_000,
staggerMs: 1_800_000,
maxUsagePct: 0,
maxD7UsagePct: 0,
commandTimeoutMs: 120_000,
failureCooldownMs: 21_600_000,
maxConsecutiveFailures: 2,
},
}));
vi.mock('./logger.js', () => ({
logger: {
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
},
}));
vi.mock('./token-rotation.js', () => ({
getAllTokens: vi.fn(() => []),
}));
vi.mock('./codex-usage-collector.js', () => ({
refreshAllCodexAccountUsage,
refreshActiveCodexUsage,
}));
vi.mock('./codex-warmup.js', () => ({
runCodexWarmupCycle,
}));
describe('usage-primer', () => {
it('refreshes Codex usage before primer selection and allows 1% fresh usage', async () => {
callOrder.length = 0;
refreshAllCodexAccountUsage.mockClear();
refreshActiveCodexUsage.mockClear();
runCodexWarmupCycle.mockClear();
const { runCodexPrimerCycle } = await import('./usage-primer.js');
await runCodexPrimerCycle();
expect(callOrder).toEqual(['refreshAll', 'refreshActive', 'warmup']);
expect(runCodexWarmupCycle).toHaveBeenCalledWith(
expect.objectContaining({
enabled: true,
minIntervalMs: 18_000_000,
maxUsagePct: 1,
maxD7UsagePct: 100,
}),
{ ignoreZeroUsageWindow: true },
);
});
});