docs(FN-3538): clarify pause-safe stuck timeout behavior

- Update enginePaused setting docs to specify stuck-task timers are suspended while pauses are active
- Document that paused wall-clock time does not count toward taskStuckTimeoutMs, including shared globalPause windows
- Clarify that unpausing restores scheduling and grants active sessions a fresh stuck-task grace window before detection resumes

Fusion-Task-Id: FN-3538
This commit is contained in:
Fusion
2026-05-05 23:17:47 -07:00
committed by gsxdsm
parent 81bf882c81
commit 193be58dba
6 changed files with 326 additions and 10 deletions

View File

@@ -604,6 +604,75 @@ describe("StuckTaskDetector", () => {
});
});
describe("pause lifecycle", () => {
it("skips stuck evaluation while paused", async () => {
const getSettings = vi.fn().mockResolvedValue({ taskStuckTimeoutMs: 60000 });
store = createMockStore({ getSettings });
const onStuck = vi.fn();
const customDetector = new StuckTaskDetector(store, { onStuck });
const session = createMockSession();
vi.useFakeTimers({ shouldAdvanceTime: true });
customDetector.trackTask("FN-001", session);
customDetector.pause();
vi.advanceTimersByTime(61_000);
await customDetector.checkNow();
expect(getSettings).not.toHaveBeenCalled();
expect(onStuck).not.toHaveBeenCalled();
expect(session.dispose).not.toHaveBeenCalled();
expect(customDetector.trackedCount).toBe(1);
vi.useRealTimers();
});
it("resets tracked timing on resume so paused interval is not immediately stuck", async () => {
store = createMockStore({
getSettings: vi.fn().mockResolvedValue({ taskStuckTimeoutMs: 60000 }),
});
const onStuck = vi.fn();
const customDetector = new StuckTaskDetector(store, { onStuck });
const session = createMockSession();
vi.useFakeTimers({ shouldAdvanceTime: true });
customDetector.trackTask("FN-001", session);
customDetector.pause();
vi.advanceTimersByTime(120_000);
customDetector.resume();
await customDetector.checkNow();
expect(onStuck).not.toHaveBeenCalled();
vi.advanceTimersByTime(61_000);
await customDetector.checkNow();
expect(onStuck).toHaveBeenCalledWith(
expect.objectContaining({ taskId: "FN-001", reason: "inactivity" }),
);
vi.useRealTimers();
});
it("does not refresh tracked timing when resume is called while already unpaused", async () => {
store = createMockStore({
getSettings: vi.fn().mockResolvedValue({ taskStuckTimeoutMs: 60000 }),
});
const onStuck = vi.fn();
const customDetector = new StuckTaskDetector(store, { onStuck });
const session = createMockSession();
vi.useFakeTimers({ shouldAdvanceTime: true });
customDetector.trackTask("FN-001", session);
vi.advanceTimersByTime(61_000);
customDetector.resume();
await customDetector.checkNow();
expect(onStuck).toHaveBeenCalledWith(
expect.objectContaining({ taskId: "FN-001", reason: "inactivity" }),
);
vi.useRealTimers();
});
});
describe("checkNow", () => {
it("checks stuck tasks immediately and disposes session", async () => {
store = createMockStore({