diff --git a/packages/agent/src/adapters/local-tools/tools/signed-commit.test.ts b/packages/agent/src/adapters/local-tools/tools/signed-commit.test.ts index 33378c3f1f..5502dacd3b 100644 --- a/packages/agent/src/adapters/local-tools/tools/signed-commit.test.ts +++ b/packages/agent/src/adapters/local-tools/tools/signed-commit.test.ts @@ -117,6 +117,41 @@ describe("signed-commit tool handler", () => { }); }); + it("persists the branch when cwd uses an equivalent path representation", async () => { + await signedCommitTool.handler( + { + cwd: "/tmp/workspace/repos/posthog/code/.", + token: "ghs_x", + taskId: "task-1", + taskRunId: "run-1", + }, + { message: "chore: bump", cwd: "." }, + ); + + expect(reportTaskRunBranch).toHaveBeenCalledWith({ + taskId: "task-1", + taskRunId: "run-1", + branch: "posthog-code/feature", + }); + }); + + it("does not persist a branch created in a sibling repository", async () => { + await signedCommitTool.handler( + { + cwd: "/tmp/workspace/repos/posthog/code", + token: "ghs_x", + taskId: "task-1", + taskRunId: "run-1", + }, + { + message: "chore: bump", + cwd: "/tmp/workspace/repos/posthog/grafana-dashboards", + }, + ); + + expect(reportTaskRunBranch).not.toHaveBeenCalled(); + }); + it("returns the no-token error without invoking createSignedCommit", async () => { const savedGh = process.env.GH_TOKEN; const savedGithub = process.env.GITHUB_TOKEN; diff --git a/packages/agent/src/adapters/local-tools/tools/signed-git-tool.ts b/packages/agent/src/adapters/local-tools/tools/signed-git-tool.ts index 0b72c25875..cdeae219d0 100644 --- a/packages/agent/src/adapters/local-tools/tools/signed-git-tool.ts +++ b/packages/agent/src/adapters/local-tools/tools/signed-git-tool.ts @@ -43,10 +43,14 @@ export function defineSignedGitTool(opts: { string, unknown >; - const cwd = argCwd ? path.resolve(ctx.cwd, argCwd) : ctx.cwd; + const taskRepositoryCwd = path.resolve(ctx.cwd); + const cwd = argCwd + ? path.resolve(taskRepositoryCwd, argCwd) + : taskRepositoryCwd; return opts.run( { cwd, + taskRepositoryCwd, token, taskId: ctx.taskId, taskRunId: ctx.taskRunId, diff --git a/packages/agent/src/adapters/signed-commit-shared.ts b/packages/agent/src/adapters/signed-commit-shared.ts index aa7e8523b4..2cdd83eb7d 100644 --- a/packages/agent/src/adapters/signed-commit-shared.ts +++ b/packages/agent/src/adapters/signed-commit-shared.ts @@ -135,7 +135,11 @@ export interface SignedCommitToolResult { [key: string]: unknown; } -export type SignedCommitToolCtx = SignedCommitCtx & { taskRunId?: string }; +export type SignedCommitToolCtx = SignedCommitCtx & { + taskRunId?: string; + /** The task repository cwd, before a tool-call `cwd` override is applied. */ + taskRepositoryCwd: string; +}; async function runSignedTool( toolName: string, @@ -176,11 +180,17 @@ export function runSignedCommitTool( SIGNED_COMMIT_TOOL_NAME, async (c, a: SignedCommitInput) => { const result = await createSignedCommit(c, a); - await reportTaskRunBranch({ - taskId: ctx.taskId, - taskRunId: ctx.taskRunId, - branch: result.branch, - }); + // TaskRun.branch is the branch that provisioning checks out in the task's + // repository on resume. A task can also commit to sibling repositories by + // passing `cwd`; persisting one of those branches here makes the next run + // try to clone the task repository at a branch that only exists elsewhere. + if (ctx.cwd === ctx.taskRepositoryCwd) { + await reportTaskRunBranch({ + taskId: ctx.taskId, + taskRunId: ctx.taskRunId, + branch: result.branch, + }); + } // The "commit hook": every pushed commit becomes a `commit` artefact on the signal // reports this task is associated with. Best-effort and awaited inside the tool's // try/catch-free success path — reportCommitArtefacts never throws, so a failed