diff --git a/src/features/boulder-state/storage.test.ts b/src/features/boulder-state/storage.test.ts index 63e43faff..2fe0438ad 100644 --- a/src/features/boulder-state/storage.test.ts +++ b/src/features/boulder-state/storage.test.ts @@ -622,6 +622,27 @@ describe("boulder-state", () => { expect(completedState?.works?.[secondWorkId]?.status).not.toBe("completed") expect(existsSync(join(SISYPHUS_DIR, "boulder.json"))).toBe(true) }) + + test("should keep first completion timing when completeBoulder is called repeatedly", () => { + // given + const initialState = createBoulderState( + join(TEST_DIR, ".sisyphus/plans/plan-idempotent.md"), + "session-a", + ) + writeBoulderState(TEST_DIR, initialState) + const workId = initialState.active_work_id! + + // when + const firstCompletedState = completeBoulder(TEST_DIR, workId, "2026-01-01T00:01:00Z") + const secondCompletedState = completeBoulder(TEST_DIR, workId, "2026-01-01T01:00:00Z") + + // then + expect(firstCompletedState?.works?.[workId]?.ended_at).toBe("2026-01-01T00:01:00Z") + expect(secondCompletedState?.works?.[workId]?.ended_at).toBe("2026-01-01T00:01:00Z") + expect(secondCompletedState?.works?.[workId]?.elapsed_ms).toBe( + Date.parse("2026-01-01T00:01:00Z") - Date.parse(secondCompletedState!.works![workId]!.started_at), + ) + }) }) describe("readCurrentTopLevelTask", () => { diff --git a/src/features/boulder-state/storage.ts b/src/features/boulder-state/storage.ts index f5f03109c..a07f2a40e 100644 --- a/src/features/boulder-state/storage.ts +++ b/src/features/boulder-state/storage.ts @@ -955,6 +955,10 @@ export function completeBoulder(directory: string, workId?: string, endedAt?: st return null } + if (work.status === "completed" && work.ended_at !== undefined && work.elapsed_ms !== undefined) { + return state + } + const endAt = endedAt ?? nowIsoString() work.ended_at = endAt work.elapsed_ms = getElapsedMs(work.started_at, endAt)