This commit is contained in:
Waseem Ilyas 2026-09-13 12:08:45 +00:00 committed by GitHub
commit 1c12c8687a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 56 additions and 1 deletions

View File

@ -308,6 +308,56 @@ describe.sequential("closed isolated workspace issue routes", () => {
expect(mockIssueService.addComment).not.toHaveBeenCalled();
});
it("accepts a plain comment on a done issue without touching the closed workspace", async () => {
// A comment that does not resume a terminal issue is a pure record: it
// must not rebuild the worktree, and it must not be blocked by a
// workspace it never needed.
mockIssueService.getById.mockResolvedValue({ ...makeIssue(), status: "done", assigneeAgentId: null });
mockIssueService.addComment.mockResolvedValue({ id: "comment-1", body: "decision recorded" });
const res = await request(createApp())
.post(`/api/issues/${issueId}/comments`)
.send({ body: "decision recorded" });
expect(res.status).toBe(201);
expect(mockIssueService.addComment).toHaveBeenCalledTimes(1);
expect(mockExecutionWorkspaceService.reopenClosedIsolatedExecutionWorkspaceForIssue).not.toHaveBeenCalled();
});
it("accepts a plain comment on a done issue even when the workspace cannot be reopened", async () => {
// The audit comment is not resuming anything, so a workspace that is
// un-reopenable must not turn it into a 409.
mockIssueService.getById.mockResolvedValue({ ...makeIssue(), status: "done", assigneeAgentId: null });
mockIssueService.addComment.mockResolvedValue({ id: "comment-1", body: "decision recorded" });
mockExecutionWorkspaceService.reopenClosedIsolatedExecutionWorkspaceForIssue.mockResolvedValue({
ok: false,
code: "not_reopenable",
message: "Execution workspace is not reopenable",
});
const res = await request(createApp())
.post(`/api/issues/${issueId}/comments`)
.send({ body: "decision recorded" });
expect(res.status).toBe(201);
expect(mockIssueService.addComment).toHaveBeenCalledTimes(1);
});
it("still reopens the closed workspace for a comment that resumes a done issue", async () => {
// `reopen: true` on a terminal issue is a resume: it needs the worktree,
// so the reopen still runs and still blocks when the rebuild fails.
mockIssueService.getById.mockResolvedValue({ ...makeIssue(), status: "done", assigneeAgentId: null });
mockIssueService.update.mockResolvedValue({ ...makeIssue(), status: "todo" });
mockIssueService.addComment.mockResolvedValue({ id: "comment-1", body: "hello" });
const res = await request(createApp())
.post(`/api/issues/${issueId}/comments`)
.send({ body: "please continue", reopen: true });
expect(mockExecutionWorkspaceService.reopenClosedIsolatedExecutionWorkspaceForIssue).toHaveBeenCalledTimes(1);
expect(res.status).toBe(201);
});
it("returns 503 and blocks the checkout when the rebuild fails", async () => {
mockExecutionWorkspaceService.reopenClosedIsolatedExecutionWorkspaceForIssue.mockResolvedValue({
ok: false,

View File

@ -17232,9 +17232,14 @@ export function issueRoutes(
// blocker, and run-cap gate passes. A rejected comment must not rebuild and
// republish the workspace as active, because the issue stays terminal and the
// reaper then skips the leaked workspace.
//
// A comment on a terminal issue that does not resume the work is a plain
// record: it never needs the worktree. Rebuilding one for it would either
// churn a workspace the reaper immediately reaps again or block an audit
// comment with a 409 the workspace was never needed for.
let reopenedWorkspace: Pick<ExecutionWorkspace, "id"> | null = null;
let reopenedGeneration: number | null = null;
if (closedExecutionWorkspace) {
if (closedExecutionWorkspace && (!isClosed || effectiveMoveToTodoRequested)) {
const reopenOutcome =
await reopenClosedIssueExecutionWorkspaceOrRespond(
req,