From 4de90ede60c1bf70d3289679391b57f1543a1674 Mon Sep 17 00:00:00 2001 From: Daniel Ehrhardt Date: Wed, 21 Oct 2026 11:23:59 +0200 Subject: [PATCH 1/2] Update feed: never serve an older release because the GitHub API was busy Without a token the API refuses after 60 calls an hour, and the feed then answered with the last known release, kept for a week. After v0.2.22 the desktop apps kept seeing 0.2.20 and found no update. Now github.com's latest-release redirect (no API quota) says which release is the latest; the kept copy answers only while it is still that one. --- apps/website/src/lib/releases.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/website/src/lib/releases.ts b/apps/website/src/lib/releases.ts index b0944ae5..ffcea9ca 100644 --- a/apps/website/src/lib/releases.ts +++ b/apps/website/src/lib/releases.ts @@ -18,7 +18,9 @@ const cached = (body: string, seconds: number) => new Response(body, { headers: /** * Latest published release, cached at the edge for five minutes. Without a token the API allows 60 calls an hour - * per Cloudflare IP, so when it refuses, the last known release (kept a week) or the public release pages answer. + * per Cloudflare IP, so when it refuses, github.com says which release is the latest (no API call): the last known + * copy (kept a week) answers while it is still that one, the public release pages once a newer one is out. Never an + * older release just because the API was busy: the app would not see the update. */ export async function latestRelease(): Promise { const cache = await caches.open('godmode-releases'); @@ -32,9 +34,11 @@ export async function latestRelease(): Promise { await Promise.all([cache.put(fresh, cached(body, 300)), cache.put(known, cached(body, 604800))]); return JSON.parse(body); } - const old = await cache.match(known); - if (old) return old.json(); - const release = await releaseFromPages(await latestTag()); + const old = (await cache.match(known)?.then((r) => r?.json() as Promise | undefined)) ?? null; + const tag = await latestTag().catch(() => null); + if (old && (!tag || old.tag_name === tag)) return old; + if (!tag) throw new Error(`GitHub latest release: API ${res?.status ?? 'unreachable'}, release page unreadable`); + const release = await releaseFromPages(tag); await cache.put(fresh, cached(JSON.stringify(release), 300)); return release; } From 244e12115d18961819095a40500f7693414e4c96 Mon Sep 17 00:00:00 2001 From: Daniel Ehrhardt Date: Wed, 21 Oct 2026 11:36:23 +0200 Subject: [PATCH 2/2] Scheduler test: the stand-in catch-up only counts its own automations (it failed when an earlier test's one came due at that hour) --- packages/core/test/scheduler.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/core/test/scheduler.test.ts b/packages/core/test/scheduler.test.ts index 5a43dd81..9bfdbfa8 100644 --- a/packages/core/test/scheduler.test.ts +++ b/packages/core/test/scheduler.test.ts @@ -362,6 +362,8 @@ describe("triggering", () => { }); test("a computer that starts standing in runs the automations that came due since the primary went, once", async () => { + // Only this test's automations: earlier ones may come due at the time of day the suite runs. + exec("UPDATE routines SET enabled = 0"); const hourly = createRoutine({ agentId: agent.id, name: "Hourly", cron: "0 * * * *", timezone: "UTC", prompt: "hourly" }); const ranThere = createRoutine({ agentId: agent.id, name: "Ran there", cron: "0 * * * *", timezone: "UTC", prompt: "done there" }); const before = createRoutine({ agentId: agent.id, name: "Before", cron: "0 0 1 1 *", timezone: "UTC", prompt: "yearly" });