fix(autopilot): give full-cycle dispatch a 30-minute timeout floor (#2852)

Dispatch timeout was derived as interval*2 with a 5-minute floor, tuned
for light per-interval work. A full autopilot cycle routinely needs more
than 10 minutes at common intervals, so healthy full cycles were killed
mid-run. Full-cycle dispatch now gets a 30-minute floor; lighter
dispatches keep the interval-derived budget.

Adds a regression test for the full-cycle floor.
This commit is contained in:
Sanchal Ranjan
2026-07-23 11:38:05 -07:00
committed by GitHub
parent 11659743a2
commit b98fae9b61
3 changed files with 30 additions and 2 deletions
+16
View File
@@ -15,6 +15,7 @@
import { describe, expect, test } from 'bun:test';
import { readFileSync } from 'fs';
import { join } from 'path';
import { resolveAutopilotDispatchTimeoutMs } from '../src/commands/autopilot-timeout.ts';
const AUTOPILOT_SRC = readFileSync(
join(import.meta.dir, '..', 'src', 'commands', 'autopilot.ts'),
@@ -48,6 +49,21 @@ describe('autopilot.ts ↔ dispatchPerSource wiring', () => {
expect(Math.abs(dispatchIdx - fullCycleIdx)).toBeLessThan(3000);
});
test('applies the 30-minute timeout floor only to full-cycle dispatch', () => {
const baseIntervalSeconds = 60;
const intervalDerivedTimeoutMs = Math.max(baseIntervalSeconds * 2 * 1000, 300_000);
expect(resolveAutopilotDispatchTimeoutMs(baseIntervalSeconds, true)).toBeGreaterThanOrEqual(30 * 60_000);
expect(resolveAutopilotDispatchTimeoutMs(baseIntervalSeconds, false)).toBe(intervalDerivedTimeoutMs);
expect(AUTOPILOT_SRC).toContain(
'const timeoutMs = resolveAutopilotDispatchTimeoutMs(baseInterval, false);',
);
expect(AUTOPILOT_SRC).toMatch(
/dispatchPerSource\(engine, queue, \{[\s\S]{0,300}timeoutMs: resolveAutopilotDispatchTimeoutMs\(baseInterval, true\)/,
);
});
test('updates lastFullCycleAt on dispatch (so the 60-min floor is honored)', () => {
// After the dispatchPerSource call, the lastFullCycleAt module var
// must update so the next tick doesn't immediately re-fan-out.