diff --git a/apps/server/src/filesystemCheckpointStore.test.ts b/apps/server/src/filesystemCheckpointStore.test.ts index 0938fcb935906390c44a668feb39620e81f7197e..7e78ebdf8568ada263783cfa57fab74dd207d1bd 100644 --- a/apps/server/src/filesystemCheckpointStore.test.ts +++ b/apps/server/src/filesystemCheckpointStore.test.ts @@ -35,6 +35,12 @@ async function createRepo(): Promise { return cwd; } +async function createRepoWithoutHead(): Promise { + const cwd = await mkdtemp(path.join(os.tmpdir(), "t3-fs-checkpoints-empty-test-")); + await runGit(cwd, ["init", "--initial-branch=main"]); + return cwd; +} + describe("FilesystemCheckpointStore", () => { it("captures refs without mutating branch history and restores workspace state", async () => { const cwd = await createRepo(); @@ -211,4 +217,27 @@ describe("FilesystemCheckpointStore", () => { expect(await store.isGitRepository(cwd)).toBe(false); }); + + it("restores checkpoint without reset when repository has no HEAD commit", async () => { + const cwd = await createRepoWithoutHead(); + const store = new FilesystemCheckpointStore(); + const threadId = "thr-no-head"; + + try { + await writeFile(path.join(cwd, "README.md"), "first\n"); + await store.captureCheckpoint({ cwd, threadId, turnCount: 0 }); + + await writeFile(path.join(cwd, "README.md"), "second\n"); + const restored = await store.restoreCheckpoint({ + cwd, + threadId, + turnCount: 0, + }); + + expect(restored).toBe(true); + expect(await readFile(path.join(cwd, "README.md"), "utf8")).toBe("first\n"); + } finally { + await rm(cwd, { recursive: true, force: true }); + } + }); }); diff --git a/apps/server/src/filesystemCheckpointStore.ts b/apps/server/src/filesystemCheckpointStore.ts index 7b517f4fe93d08d40f8e5882e189f0cafaf608ab..41ea5e3c27d5be19c8add20e4ee847fd7ffaf0dc 100644 --- a/apps/server/src/filesystemCheckpointStore.ts +++ b/apps/server/src/filesystemCheckpointStore.ts @@ -136,7 +136,9 @@ export class FilesystemCheckpointStore { await this.runGit(cwd, ["restore", "--source", commitOid, "--worktree", "--staged", "--", "."]); await this.runGit(cwd, ["clean", "-fd", "--", "."]); - await this.runGit(cwd, ["reset", "--quiet", "--", "."]); + if (await this.hasHeadCommit(cwd)) { + await this.runGit(cwd, ["reset", "--quiet", "--", "."]); + } return true; } diff --git a/apps/server/src/providerManager.ts b/apps/server/src/providerManager.ts index 0b2d11cc0e550a1b99f5432252424a8c0baf11d3..532f32f7fa9db1e9d9bd5676da10a83014ad5dc2 100644 --- a/apps/server/src/providerManager.ts +++ b/apps/server/src/providerManager.ts @@ -433,9 +433,6 @@ export class ProviderManager extends EventEmitter { .listSessions() .find((candidate) => candidate.sessionId === sessionId); const candidateCwds = session?.cwd ? [session.cwd] : [process.cwd()]; - if (candidateCwds.length === 0) { - return null; - } await this.withFilesystemLock(sessionId, async () => { const currentCwd = this.sessionCheckpointCwds.get(sessionId); diff --git a/apps/web/src/components/DiffPanel.tsx b/apps/web/src/components/DiffPanel.tsx index 2d030d21fc093af2ff5ddc0d2ffbd0f48a4db921..37a4369a56e18ff3fa58071c3684531a71e15c20 100644 --- a/apps/web/src/components/DiffPanel.tsx +++ b/apps/web/src/components/DiffPanel.tsx @@ -147,7 +147,7 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) { const conversationCheckpointDiff = selectedTurn ? undefined : activeCheckpointDiffQuery.data?.diff; - const isLoadingCheckpointDiff = activeCheckpointDiffQuery.isPending; + const isLoadingCheckpointDiff = activeCheckpointDiffQuery.isLoading; const checkpointDiffError = activeCheckpointDiffQuery.error instanceof Error ? activeCheckpointDiffQuery.error.message