fix(core): pick up checkpoints when git is initialized mid-session (#13026)

ensureGitRepository cached a negative probe for the lifetime of the hook
instance, so a session started in a non-git folder never got checkpoints
even after the user ran git init. Cache only the positive answer and
re-probe otherwise; the probe runs at most once per user turn.
This commit is contained in:
Saoud Rizwan
2026-08-07 15:25:11 -07:00
committed by GitHub
parent adabfc6bd5
commit a5f90e0d53
2 changed files with 47 additions and 2 deletions
@@ -341,6 +341,47 @@ describe("createCheckpointHooks", () => {
}
});
it("starts checkpointing after the user runs git init mid-session", async () => {
const cwd = await mkdtemp(join(tmpdir(), "core-checkpoint-nogit-"));
let metadata: Record<string, unknown> | undefined;
try {
const hooks = createCheckpointHooks({
cwd,
sessionId: "sess_git_init_later",
readSessionMetadata: async () => metadata,
writeSessionMetadata: async (next) => {
metadata = next;
},
});
// First turn: no git repo yet, so no checkpoint is written.
await writeFile(join(cwd, "note.txt"), "before-init\n", "utf8");
await runCheckpointHooks(hooks);
expect(metadata?.checkpoint).toBeUndefined();
// The user initializes git mid-session.
await runGit(cwd, "init");
await runGit(cwd, "config", "user.name", "Codex Test");
await runGit(cwd, "config", "user.email", "codex@example.com");
await runGit(cwd, "add", "note.txt");
await runGit(cwd, "commit", "-m", "initial");
// Next turn on the same hook instance picks up the new repo and
// checkpoints from here forward.
await writeFile(join(cwd, "note.txt"), "after-init\n", "utf8");
await runCheckpointHooks(hooks, {
messages: [userMessage("first request"), userMessage("second request")],
});
const checkpoint = metadata?.checkpoint as CheckpointMetadata;
expect(checkpoint.history).toHaveLength(1);
expect(checkpoint.latest.runCount).toBe(2);
expect(checkpoint.latest.kind).toBe("stash");
} finally {
await rm(cwd, { recursive: true, force: true });
}
});
it("skips checkpoint creation for subagents", async () => {
const cwd = await createGitRepo();
let writes = 0;
@@ -321,8 +321,12 @@ export function createCheckpointHooks(
let rootRunMessageStart: number | undefined;
const ensureGitRepository = async (): Promise<boolean> => {
if (repoSupported !== undefined) {
return repoSupported;
// Only cache the positive answer: a cwd that is not a git repo can
// become one mid-session (the user runs `git init`), and this probe
// fires at most once per user turn, so re-checking is cheap. Once the
// repo is detected, checkpoints start applying from that turn onward.
if (repoSupported === true) {
return true;
}
try {
const result = await runGit(options.cwd, [