From cb11cd5c2858535d97d21a35a73a486b631b88ff Mon Sep 17 00:00:00 2001 From: Dotta Date: Mon, 4 May 2026 15:17:06 -0500 Subject: [PATCH] Preserve plugin checkout policy compatibility Co-Authored-By: Paperclip --- .../__tests__/plugin-scoped-api-routes.test.ts | 17 ++++++++++------- server/src/routes/plugins.ts | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/server/src/__tests__/plugin-scoped-api-routes.test.ts b/server/src/__tests__/plugin-scoped-api-routes.test.ts index 52b924aef60..4e2ce297bc0 100644 --- a/server/src/__tests__/plugin-scoped-api-routes.test.ts +++ b/server/src/__tests__/plugin-scoped-api-routes.test.ts @@ -253,7 +253,7 @@ describe.sequential("plugin scoped API routes", () => { })); }); - it("rejects non-owning agents on in-progress required checkout routes before worker dispatch", async () => { + it("allows non-assignee agents on in-progress required checkout routes without claiming checkout ownership", async () => { const apiRoutes = manifest([ { routeKey: "issue.advance", @@ -271,9 +271,6 @@ describe.sequential("plugin scoped API routes", () => { status: "in_progress", assigneeAgentId: agentId, }); - const conflict = new Error("Issue run ownership conflict") as Error & { status?: number }; - conflict.status = 409; - mockIssueService.assertCheckoutOwner.mockRejectedValue(conflict); const { app, workerManager } = await createApp({ actor: { type: "agent", @@ -294,9 +291,15 @@ describe.sequential("plugin scoped API routes", () => { .post(`/api/plugins/${pluginId}/api/issues/${issueId}/advance`) .send({ step: "next" }); - expect(res.status).toBe(409); - expect(mockIssueService.assertCheckoutOwner).toHaveBeenCalledWith(issueId, peerAgentId, runId); - expect(workerManager.call).not.toHaveBeenCalled(); + expect(res.status).toBe(200); + expect(mockIssueService.assertCheckoutOwner).not.toHaveBeenCalled(); + expect(workerManager.call).toHaveBeenCalledWith(pluginId, "handleApiRequest", expect.objectContaining({ + routeKey: "issue.advance", + params: { issueId }, + body: { step: "next" }, + actor: expect.objectContaining({ actorType: "agent", agentId: peerAgentId, runId }), + companyId, + })); }); it("rejects checkout-protected agent routes without a run id before worker dispatch", async () => { diff --git a/server/src/routes/plugins.ts b/server/src/routes/plugins.ts index 269666e5f71..35b60812609 100644 --- a/server/src/routes/plugins.ts +++ b/server/src/routes/plugins.ts @@ -507,7 +507,7 @@ export function pluginRoutes( throw notFound("Issue not found"); } if (policy === "required-for-agent-in-progress") { - if (issue.status !== "in_progress") return; + if (issue.status !== "in_progress" || issue.assigneeAgentId !== req.actor.agentId) return; } const runId = req.actor.runId?.trim(); if (!runId) {