diff --git a/packages/coding-agent/src/tools/github-cache.ts b/packages/coding-agent/src/tools/github-cache.ts index 62a41bb0f..76cd0b59d 100644 --- a/packages/coding-agent/src/tools/github-cache.ts +++ b/packages/coding-agent/src/tools/github-cache.ts @@ -22,6 +22,7 @@ import * as os from "node:os"; import * as path from "node:path"; import { getGithubCacheDbPath, logger } from "@oh-my-pi/pi-utils"; import type { Settings } from "../config/settings"; +import { ToolAbortError } from "./tool-errors"; // ──────────────────────────────────────────────────────────────────────────── // Storage layer @@ -619,6 +620,7 @@ export async function getOrFetchView(options: CacheLookupOptions): Promise storeResult(authKey, options.repo, options.kind, options.number, options.includeComments, fresh, fetchedAt); return { ...fresh, status: "refreshed", fetchedAt }; } catch (err) { + if (err instanceof ToolAbortError) throw err; logger.debug("github cache: synchronous refresh failed; returning stale view", { err: String(err), repo: options.repo, diff --git a/packages/coding-agent/test/tools/github-cache.test.ts b/packages/coding-agent/test/tools/github-cache.test.ts index e6a904a82..c800e25c6 100644 --- a/packages/coding-agent/test/tools/github-cache.test.ts +++ b/packages/coding-agent/test/tools/github-cache.test.ts @@ -19,6 +19,7 @@ import { putCached, resetForTests as resetCacheForTests, } from "@oh-my-pi/pi-coding-agent/tools/github-cache"; +import { ToolAbortError, throwIfAborted } from "@oh-my-pi/pi-coding-agent/tools/tool-errors"; import * as git from "@oh-my-pi/pi-coding-agent/utils/git"; const TEST_REPO = "owner/example"; @@ -372,6 +373,43 @@ describe("getOrFetchView (TTL semantics)", () => { expect(result.rendered).toBe("old"); expect(fetchFresh).toHaveBeenCalledTimes(1); }); + it("propagates aborts during a soft-expired synchronous refresh", async () => { + const settings = Settings.isolated({ + "github.cache.softTtlSec": 60, + "github.cache.hardTtlSec": 86400, + }); + const controller = new AbortController(); + controller.abort(); + const fetchFresh = vi.fn(async () => { + throwIfAborted(controller.signal); + return { + rendered: "never", + sourceUrl: undefined, + payload: { number: 53 }, + }; + }); + putCached({ + repo: TEST_REPO, + kind: "issue", + number: 53, + includeComments: true, + payload: { number: 53 }, + rendered: "stale-after-abort", + fetchedAt: Date.now() - 5 * 60_000, + }); + + await expect( + getOrFetchView({ + repo: TEST_REPO, + kind: "issue", + number: 53, + includeComments: true, + fetchFresh, + settings, + }), + ).rejects.toThrow(ToolAbortError); + expect(fetchFresh).toHaveBeenCalledTimes(1); + }); it("keeps PR diff rows stale-first past soft TTL", async () => { const settings = Settings.isolated({