From 3388d315b6244d6a556d0db3660e8f97c4323b42 Mon Sep 17 00:00:00 2001 From: Frank Leng Date: Wed, 26 Aug 2026 20:04:12 -0700 Subject: [PATCH] fix(server): support local-only worktree bases Starting a thread from a local-only branch failed when the stored start-from-origin preference tried to resolve a remote-tracking ref that did not exist. Fall back to the selected local branch after a successful fetch when that remote ref cannot be resolved. --- apps/server/src/server.test.ts | 195 +++++++++++++++++---------------- apps/server/src/ws.ts | 23 ++-- 2 files changed, 114 insertions(+), 104 deletions(-) diff --git a/apps/server/src/server.test.ts b/apps/server/src/server.test.ts index a9a2c3fa10d6..043bbfa66524 100644 --- a/apps/server/src/server.test.ts +++ b/apps/server/src/server.test.ts @@ -7766,108 +7766,113 @@ it.layer(NodeServices.layer)("server router seam", (it) => { }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); - it.effect( - "falls back to the local base branch when startFromOrigin is set but no origin remote exists", - () => - Effect.gen(function* () { - const dispatchedCommands: Array = []; - const remoteExists = vi.fn( - (_: Parameters[0]) => - Effect.succeed(false), - ); - const fetchRemote = vi.fn( - (_: Parameters[0]) => Effect.void, - ); - const resolveRemoteTrackingCommit = vi.fn( - (_: Parameters[0]) => - Effect.succeed({ - commitSha: "0123456789abcdef0123456789abcdef01234567", - remoteRefName: "origin/main", - }), - ); - const createWorktree = vi.fn( - (_: Parameters[0]) => - Effect.succeed({ - worktree: { - refName: "t3code/bootstrap-refName", - path: "/tmp/bootstrap-worktree", - }, + it.effect.each([ + { label: "origin does not exist", originExists: false }, + { label: "the branch does not exist on origin", originExists: true }, + ])("falls back to the local base branch when $label", ({ originExists }) => + Effect.gen(function* () { + const dispatchedCommands: Array = []; + const remoteExists = vi.fn( + (_: Parameters[0]) => + Effect.succeed(originExists), + ); + const fetchRemote = vi.fn( + (_: Parameters[0]) => Effect.void, + ); + const resolveRemoteTrackingCommit = vi.fn( + (_: Parameters[0]) => + Effect.fail( + new GitCommandError({ + operation: "GitVcsDriver.resolveRemoteTrackingCommit", + command: "git rev-parse --verify refs/remotes/origin/main^{commit}", + cwd: "/tmp/project", + detail: "Git command exited with a non-zero status.", }), - ); - - yield* buildAppUnderTest({ - layers: { - gitVcsDriver: { - remoteExists, - fetchRemote, - resolveRemoteTrackingCommit, - createWorktree, - }, - orchestrationEngine: { - dispatch: (command) => - Effect.sync(() => { - dispatchedCommands.push(command); - return { sequence: dispatchedCommands.length }; - }), - readEvents: () => Stream.empty, + ), + ); + const createWorktree = vi.fn( + (_: Parameters[0]) => + Effect.succeed({ + worktree: { + refName: "t3code/bootstrap-refName", + path: "/tmp/bootstrap-worktree", }, + }), + ); + + yield* buildAppUnderTest({ + layers: { + gitVcsDriver: { + remoteExists, + fetchRemote, + resolveRemoteTrackingCommit, + createWorktree, }, - }); + orchestrationEngine: { + dispatch: (command) => + Effect.sync(() => { + dispatchedCommands.push(command); + return { sequence: dispatchedCommands.length }; + }), + readEvents: () => Stream.empty, + }, + }, + }); - const createdAt = "2026-01-01T00:00:00.000Z"; - const wsUrl = yield* getWsServerUrl("/ws"); - yield* Effect.scoped( - withWsRpcClient(wsUrl, (client) => - client[ORCHESTRATION_WS_METHODS.dispatchCommand]({ - type: "thread.turn.start", - commandId: CommandId.make("cmd-bootstrap-turn-start-no-origin"), - threadId: ThreadId.make("thread-bootstrap-no-origin"), - message: { - messageId: MessageId.make("msg-bootstrap-no-origin"), - role: "user", - text: "hello", - attachments: [], + const createdAt = "2026-01-01T00:00:00.000Z"; + const wsUrl = yield* getWsServerUrl("/ws"); + yield* Effect.scoped( + withWsRpcClient(wsUrl, (client) => + client[ORCHESTRATION_WS_METHODS.dispatchCommand]({ + type: "thread.turn.start", + commandId: CommandId.make("cmd-bootstrap-turn-start-no-origin"), + threadId: ThreadId.make("thread-bootstrap-no-origin"), + message: { + messageId: MessageId.make("msg-bootstrap-no-origin"), + role: "user", + text: "hello", + attachments: [], + }, + modelSelection: defaultModelSelection, + runtimeMode: "full-access", + interactionMode: "default", + bootstrap: { + createThread: { + projectId: defaultProjectId, + title: "Bootstrap Thread", + modelSelection: defaultModelSelection, + runtimeMode: "full-access", + interactionMode: "default", + branch: "main", + worktreePath: null, + createdAt, }, - modelSelection: defaultModelSelection, - runtimeMode: "full-access", - interactionMode: "default", - bootstrap: { - createThread: { - projectId: defaultProjectId, - title: "Bootstrap Thread", - modelSelection: defaultModelSelection, - runtimeMode: "full-access", - interactionMode: "default", - branch: "main", - worktreePath: null, - createdAt, - }, - prepareWorktree: { - projectCwd: "/tmp/project", - baseBranch: "main", - branch: "t3code/bootstrap-refName", - startFromOrigin: true, - }, + prepareWorktree: { + projectCwd: "/tmp/project", + baseBranch: "main", + branch: "t3code/bootstrap-refName", + startFromOrigin: true, }, - createdAt, - }), - ), - ); + }, + createdAt, + }), + ), + ); - assert.deepEqual(remoteExists.mock.calls[0]?.[0], { - cwd: "/tmp/project", - remoteName: "origin", - }); - assert.equal(fetchRemote.mock.calls.length, 0); - assert.equal(resolveRemoteTrackingCommit.mock.calls.length, 0); - assert.deepEqual(createWorktree.mock.calls[0]?.[0], { - cwd: "/tmp/project", - refName: "main", - newRefName: "t3code/bootstrap-refName", - baseRefName: "main", - path: null, - }); - }).pipe(Effect.provide(NodeHttpServer.layerTest)), + assert.deepEqual(remoteExists.mock.calls[0]?.[0], { + cwd: "/tmp/project", + remoteName: "origin", + }); + assert.equal(fetchRemote.mock.calls.length, originExists ? 1 : 0); + assert.equal(resolveRemoteTrackingCommit.mock.calls.length, originExists ? 1 : 0); + assert.deepEqual(createWorktree.mock.calls[0]?.[0], { + cwd: "/tmp/project", + refName: "main", + newRefName: "t3code/bootstrap-refName", + baseRefName: "main", + path: null, + }); + }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); it.effect("records setup-script failures without aborting bootstrap turn start", () => diff --git a/apps/server/src/ws.ts b/apps/server/src/ws.ts index 226c82cdb1ac..2ed86415b895 100644 --- a/apps/server/src/ws.ts +++ b/apps/server/src/ws.ts @@ -1016,9 +1016,10 @@ const makeWsRpcLayer = ( if (bootstrap?.prepareWorktree) { let worktreeBaseRef = bootstrap.prepareWorktree.baseBranch; - // "Start from origin" is a stored default; repos without an - // origin remote fall back to the local base branch instead of - // failing the whole bootstrap on `git fetch origin`. + // "Start from origin" is a stored default. Repos without an + // origin remote, or whose selected base branch only exists + // locally, fall back to the local branch instead of failing the + // whole bootstrap. const startFromOrigin = bootstrap.prepareWorktree.startFromOrigin === true && (yield* gitWorkflow.remoteExists({ @@ -1030,12 +1031,16 @@ const makeWsRpcLayer = ( cwd: bootstrap.prepareWorktree.projectCwd, remoteName: "origin", }); - const resolvedRemoteBase = yield* gitWorkflow.resolveRemoteTrackingCommit({ - cwd: bootstrap.prepareWorktree.projectCwd, - refName: bootstrap.prepareWorktree.baseBranch, - fallbackRemoteName: "origin", - }); - worktreeBaseRef = resolvedRemoteBase.commitSha; + const resolvedRemoteBase = yield* gitWorkflow + .resolveRemoteTrackingCommit({ + cwd: bootstrap.prepareWorktree.projectCwd, + refName: bootstrap.prepareWorktree.baseBranch, + fallbackRemoteName: "origin", + }) + .pipe(Effect.orElseSucceed(() => null)); + if (resolvedRemoteBase) { + worktreeBaseRef = resolvedRemoteBase.commitSha; + } } const worktree = yield* gitWorkflow.createWorktree({ cwd: bootstrap.prepareWorktree.projectCwd,