diff --git a/devlog/_plan/260906_release_244_followups/052_shutdown_fixture.md b/devlog/_plan/260906_release_244_followups/052_shutdown_fixture.md new file mode 100644 index 0000000000..3242fea9a0 --- /dev/null +++ b/devlog/_plan/260906_release_244_followups/052_shutdown_fixture.md @@ -0,0 +1,19 @@ +# Shutdown fallback fixture clock + +CI34021352755 on the documentation-only combo closeout failed one macOS test: +shutdown drain cap expiry enters the synchronous spill fallback. The run had +10,050 passes and one failure. This file and production state.ts were unchanged +from the previously green e1f5a5b8d runtime. + +The fixture freezes ACL and spill clocks but the shutdown reserve uses Date.now. +An 80 ms reserve therefore still races host disk/scheduling latency (the failure +was ETIMEDOUT inside fallbackPendingResponseSpills). Freeze that third clock only +around flush, using the existing spy pattern from the neighboring ordering test. +The real 40 ms drain timer still expires while the async publication gate stays +held; positive synchronous-call, empty-pending and installed-stub assertions remain. +Release the gate, await the publication tail and restore the clock in finally. + +This C1 verifier repair changes one fixture, no production timeout, skip or retry +policy. Budget-exhaustion/watchdog cases remain untouched. Land as a separate +prerequisite PR and cascade the combo branch. Independent fixture review and new +exact parent/child hosted CI are required; no local suite/typecheck/build runs. diff --git a/tests/responses/responses-state.test.ts b/tests/responses/responses-state.test.ts index 5442910aec..8ba5da86b2 100644 --- a/tests/responses/responses-state.test.ts +++ b/tests/responses/responses-state.test.ts @@ -1427,16 +1427,27 @@ describe("Responses previous_response_id state", () => { return { success: true, exitCode: 0, timedOut: false, stdout: "" }; }); setResponseStateByteCapForTests(1_024); - rememberLarge("resp_shutdown_fallback", "f".repeat(2 * 1024 * 1024 + 4_096)); - await started; - + let restoreClock: (() => void) | undefined; try { + rememberLarge("resp_shutdown_fallback", "f".repeat(2 * 1024 * 1024 + 4_096)); + await started; + // ACL/spill clocks alone do not control the shutdown reserve: state.ts + // uses Date.now(). Keep its 80 ms budget independent of real disk latency. + // The real 40 ms drain timer still fires while publication stays gated. + const shutdownNow = Date.now(); + const nowSpy = spyOn(Date, "now").mockReturnValue(shutdownNow); + restoreClock = () => { nowSpy.mockRestore(); }; await flushResponseState(); expect(synchronousCalls).toBeGreaterThan(0); expect(pendingResponseSpillMetricsForTests()).toEqual({ count: 0, bytes: 0 }); expect(responseStateMetrics()).toMatchObject({ residentCount: 0, spillStubCount: 1 }); } finally { release(); + try { + await awaitResponseSpillPublicationTailForTests(); + } finally { + restoreClock?.(); + } } });