From c6d1bdd1cc383a237301cb4d2ef6820ec1f59bc6 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Tue, 17 Mar 2026 09:48:38 -0400 Subject: [PATCH] fix: compare gsdVersion instead of syncedAt for resource staleness check (#804) (#829) Every new pi session writes a fresh syncedAt timestamp to managed-resources.json, causing a running auto-mode session to falsely detect a GSD update and stop. The actual version (gsdVersion) only changes on real upgrades. Switch the staleness check from syncedAt (timestamp) to gsdVersion (semver string) so that launching a second session no longer triggers a false positive. --- src/resources/extensions/gsd/auto.ts | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/resources/extensions/gsd/auto.ts b/src/resources/extensions/gsd/auto.ts index ae2f8f9a5..827172e10 100644 --- a/src/resources/extensions/gsd/auto.ts +++ b/src/resources/extensions/gsd/auto.ts @@ -261,28 +261,30 @@ const MAX_CONSECUTIVE_SKIPS = 3; /** Persisted completed-unit keys — survives restarts. Loaded from .gsd/completed-units.json. */ const completedKeySet = new Set(); -/** Resource sync timestamp captured at auto-mode start. If the managed-resources - * manifest changes mid-session (e.g. /gsd:update or dev edit + copy-resources), +/** Resource version captured at auto-mode start. If the managed-resources + * manifest version changes mid-session (e.g. npm update -g gsd-pi), * templates on disk may expect variables the in-memory code doesn't provide. - * Detect this and stop gracefully instead of crashing. */ -let resourceSyncedAtOnStart: number | null = null; + * Detect this and stop gracefully instead of crashing. + * Uses gsdVersion (semver) instead of syncedAt (timestamp) so that + * launching a second session doesn't falsely trigger staleness (#804). */ +let resourceVersionOnStart: string | null = null; -function readResourceSyncedAt(): number | null { +function readResourceVersion(): string | null { const agentDir = process.env.GSD_CODING_AGENT_DIR || join(homedir(), ".gsd", "agent"); const manifestPath = join(agentDir, "managed-resources.json"); try { const manifest = JSON.parse(readFileSync(manifestPath, "utf-8")); - return typeof manifest?.syncedAt === "number" ? manifest.syncedAt : null; + return typeof manifest?.gsdVersion === "string" ? manifest.gsdVersion : null; } catch { return null; } } function checkResourcesStale(): string | null { - if (resourceSyncedAtOnStart === null) return null; - const current = readResourceSyncedAt(); + if (resourceVersionOnStart === null) return null; + const current = readResourceVersion(); if (current === null) return null; - if (current !== resourceSyncedAtOnStart) { + if (current !== resourceVersionOnStart) { return "GSD resources were updated since this session started. Restart gsd to load the new code."; } return null; @@ -1075,7 +1077,7 @@ export async function startAuto( restoreHookState(base); resetProactiveHealing(); autoStartTime = Date.now(); - resourceSyncedAtOnStart = readResourceSyncedAt(); + resourceVersionOnStart = readResourceVersion(); completedUnits = []; pendingQuickTasks = []; currentUnit = null;