diff --git a/src/resources/extensions/gsd/auto.ts b/src/resources/extensions/gsd/auto.ts index fe21ed438..f39edba93 100644 --- a/src/resources/extensions/gsd/auto.ts +++ b/src/resources/extensions/gsd/auto.ts @@ -1164,8 +1164,9 @@ export async function startAuto( s.activeRunDir = meta.activeRunDir ?? null; s.originalBasePath = meta.originalBasePath || base; s.stepMode = meta.stepMode ?? requestedStepMode; + s.autoStartTime = meta.autoStartTime || Date.now(); s.paused = true; - try { unlinkSync(pausedPath); } catch { /* non-fatal */ } + try { unlinkSync(pausedPath); } catch (e) { logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" }); } ctx.ui.notify( `Resuming paused custom workflow${meta.activeRunDir ? ` (${meta.activeRunDir})` : ""}.`, "info", @@ -1197,21 +1198,24 @@ export async function startAuto( s.pausedSessionFile = meta.sessionFile ?? null; s.pausedUnitType = meta.unitType ?? null; s.pausedUnitId = meta.unitId ?? null; + s.autoStartTime = meta.autoStartTime || Date.now(); s.paused = true; - try { unlinkSync(pausedPath); } catch { /* non-fatal */ } + try { unlinkSync(pausedPath); } catch (e) { logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" }); } ctx.ui.notify( `Resuming paused session for ${meta.milestoneId}${meta.worktreePath && existsSync(meta.worktreePath) ? ` (worktree)` : ""}.`, "info", ); } } else if (existsSync(pausedPath)) { - try { unlinkSync(pausedPath); } catch { /* non-fatal */ } + try { unlinkSync(pausedPath); } catch (e) { logWarning("session", `stale pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" }); } } } } catch (err) { // Malformed or missing — proceed with fresh bootstrap logWarning("session", `paused-session restore failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" }); } + // Guard against zero/missing autoStartTime after resume (#3585) + if (!s.autoStartTime || s.autoStartTime <= 0) s.autoStartTime = Date.now(); } if (!s.paused) { diff --git a/src/resources/extensions/gsd/guided-flow.ts b/src/resources/extensions/gsd/guided-flow.ts index 451a9011c..7ba292d38 100644 --- a/src/resources/extensions/gsd/guided-flow.ts +++ b/src/resources/extensions/gsd/guided-flow.ts @@ -1314,8 +1314,8 @@ export async function showSmartEntry( if (interrupted.pausedSession) { try { unlinkSync(join(gsdRoot(basePath), "runtime", "paused-session.json")); - } catch { - // Non-fatal stale metadata cleanup. + } catch (e) { + logWarning("guided", `stale pause file cleanup failed: ${(e as Error).message}`, { file: "guided-flow.ts" }); } } } else if (interrupted.classification === "recoverable") { diff --git a/src/resources/extensions/gsd/interrupted-session.ts b/src/resources/extensions/gsd/interrupted-session.ts index 5dae6f52c..8c6274a05 100644 --- a/src/resources/extensions/gsd/interrupted-session.ts +++ b/src/resources/extensions/gsd/interrupted-session.ts @@ -33,6 +33,7 @@ export interface PausedSessionMetadata { unitId?: string; activeEngineId?: string; activeRunDir?: string | null; + autoStartTime?: number; } export interface InterruptedSessionAssessment {