From 87814a2722be092b1001b0fdfe4866e2445129e8 Mon Sep 17 00:00:00 2001 From: Yash Singh Date: Sun, 6 Sep 2026 20:03:03 -0500 Subject: [PATCH 1/2] fix(server): follow placeholder branches after checkout updates - Update the thread branch and refresh pull requests when a saved placeholder branch becomes real - Preserve shared-worktree safety for unrelated branch drift --- .../Layers/CheckpointReactor.test.ts | 47 +++++++++++++++---- .../orchestration/Layers/CheckpointReactor.ts | 5 +- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/apps/server/src/orchestration/Layers/CheckpointReactor.test.ts b/apps/server/src/orchestration/Layers/CheckpointReactor.test.ts index 9e141600713f..b88f7f012d46 100644 --- a/apps/server/src/orchestration/Layers/CheckpointReactor.test.ts +++ b/apps/server/src/orchestration/Layers/CheckpointReactor.test.ts @@ -1002,23 +1002,22 @@ describe("CheckpointReactor", () => { expect(thread?.branch).toBe("t3code/renamed-by-agent"); }); - it("does not adopt a drifted checkout when the worktree is shared by another thread", async () => { + it("follows a checkout from a saved placeholder branch and refreshes its pull request", async () => { const pullRequestRefreshCalls: string[] = []; const harness = await createHarness({ seedFilesystemCheckpoints: false, - threadBranch: "t3code/original-branch", - localStatusRefName: "t3code/renamed-by-agent", - secondThreadSharingWorktree: true, + threadBranch: "t3code/fd9cbe0e", + localStatusRefName: "fix/mobile-tool-detail-expansion", pullRequestRefreshCalls, }); harness.provider.emit({ type: "turn.completed", - eventId: EventId.make("evt-turn-completed-branch-drift-shared"), + eventId: EventId.make("evt-turn-completed-placeholder-drift"), provider: ProviderDriverKind.make("codex"), createdAt: "2026-01-01T00:00:00.000Z", threadId: ThreadId.make("thread-1"), - turnId: asTurnId("turn-branch-drift-shared"), + turnId: asTurnId("turn-placeholder-drift"), payload: { state: "completed" }, }); @@ -1026,10 +1025,42 @@ describe("CheckpointReactor", () => { const snapshot = await harness.readModel(); const thread = snapshot.threads.find((entry) => entry.id === ThreadId.make("thread-1")); - expect(thread?.branch).toBe("t3code/original-branch"); - expect(pullRequestRefreshCalls).toEqual([]); + expect(thread?.branch).toBe("fix/mobile-tool-detail-expansion"); + expect(thread?.worktreePath).toBe(harness.cwd); + expect(pullRequestRefreshCalls).toEqual([harness.cwd]); }); + it.each(["t3code/original-branch", "t3code/fd9cbe0e"])( + "does not adopt a drifted checkout from %s when the worktree is shared by another thread", + async (threadBranch) => { + const pullRequestRefreshCalls: string[] = []; + const harness = await createHarness({ + seedFilesystemCheckpoints: false, + threadBranch, + localStatusRefName: "t3code/renamed-by-agent", + secondThreadSharingWorktree: true, + pullRequestRefreshCalls, + }); + + harness.provider.emit({ + type: "turn.completed", + eventId: EventId.make("evt-turn-completed-branch-drift-shared"), + provider: ProviderDriverKind.make("codex"), + createdAt: "2026-01-01T00:00:00.000Z", + threadId: ThreadId.make("thread-1"), + turnId: asTurnId("turn-branch-drift-shared"), + payload: { state: "completed" }, + }); + + await harness.drain(); + + const snapshot = await harness.readModel(); + const thread = snapshot.threads.find((entry) => entry.id === ThreadId.make("thread-1")); + expect(thread?.branch).toBe(threadBranch); + expect(pullRequestRefreshCalls).toEqual([]); + }, + ); + it("does not adopt a temporary placeholder checkout as the thread branch", async () => { const harness = await createHarness({ seedFilesystemCheckpoints: false, diff --git a/apps/server/src/orchestration/Layers/CheckpointReactor.ts b/apps/server/src/orchestration/Layers/CheckpointReactor.ts index eb77348180ff..2bdb77a7ca0f 100644 --- a/apps/server/src/orchestration/Layers/CheckpointReactor.ts +++ b/apps/server/src/orchestration/Layers/CheckpointReactor.ts @@ -558,13 +558,14 @@ const make = Effect.gen(function* () { const thread = yield* projectionSnapshotQuery .getThreadShellById(input.threadId) .pipe(Effect.map(Option.getOrUndefined)); + // A saved placeholder can outlive automatic naming. Once the checkout + // has a real branch, follow it even if the recorded name is temporary. if ( !thread || thread.branch === null || thread.branch === checkedOutBranch || thread.worktreePath === null || - thread.worktreePath !== input.cwd || - isTemporaryWorktreeBranch(thread.branch) + thread.worktreePath !== input.cwd ) { return; } From a68c63a77d1a922b47da59e7f0015d91c5447712 Mon Sep 17 00:00:00 2001 From: Yash Singh Date: Sun, 6 Sep 2026 20:05:21 -0500 Subject: [PATCH 2/2] refactor(server): remove obsolete checkpoint branch comment --- apps/server/src/orchestration/Layers/CheckpointReactor.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/server/src/orchestration/Layers/CheckpointReactor.ts b/apps/server/src/orchestration/Layers/CheckpointReactor.ts index 2bdb77a7ca0f..d4d6b9409808 100644 --- a/apps/server/src/orchestration/Layers/CheckpointReactor.ts +++ b/apps/server/src/orchestration/Layers/CheckpointReactor.ts @@ -558,8 +558,6 @@ const make = Effect.gen(function* () { const thread = yield* projectionSnapshotQuery .getThreadShellById(input.threadId) .pipe(Effect.map(Option.getOrUndefined)); - // A saved placeholder can outlive automatic naming. Once the checkout - // has a real branch, follow it even if the recorded name is temporary. if ( !thread || thread.branch === null ||