mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-10 12:37:55 +01:00
fix(issues): do not wake the assignee for an issue created already closed (#19961)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Creating an issue with an agent assignee queues an `issue_assigned`
wake for that agent
> - The create path skips the wake only for `backlog` and the onboarding
first task
> - An issue created already `done` or `cancelled` is a record of
finished work, but it still wakes the assignee
> - The update path already refuses this case ("Restoring an assignee on
completed work is not a reopen request"), and create-time fast responses
already skip `done` and `cancelled`
> - This pull request applies the same closed-status guard to the
create-time assignment wake
> - The benefit is that recording finished work does not start a run,
spend tokens, or queue behind the agent's current work
## Linked Issues or Issue Description
**What happened**
In a chat eval, setup created a historical issue with `status: "done"`
assigned to an agent that was already running a task. The create queued
a second run for that agent (`invocationSource: assignment`,
`wakeReason: issue_assigned`, `source: issue.create`). The run waited
behind the active run, and then was cancelled or failed. The eval
expected no work for a finished record.
**Expected behavior**
Creating an issue that is already `done` or `cancelled` does not wake
its assignee. This matches the update path, where an assignee change on
closed work is not a reopen request.
**Steps to reproduce**
1. Create an agent.
2. `POST /api/companies/<companyId>/issues` with
`{"title":"Record","status":"done","assigneeAgentId":"<agentId>"}`.
3. See a new `agent_wakeup_requests` row and a queued heartbeat run for
the agent.
## What Changed
- `server/src/routes/issues.ts`: the create route does not call
`queueIssueAssignmentWakeup` when the created issue status is `done` or
`cancelled` (`isClosedIssueStatus`).
- `server/src/__tests__/issue-onboarding-first-task-routes.test.ts`: a
regression test for `done` and `cancelled` creates. The existing test
that an ordinary assigned create still wakes is unchanged.
## Verification
- `cd server && pnpm exec vitest run
src/__tests__/issue-onboarding-first-task-routes.test.ts`: 11 passed.
- Negative control: with the route file from `origin/master`, both new
cases fail.
## Risks
- Low risk. Only creates in a closed status change. A caller that relied
on a create-time wake for a closed issue no longer gets one. To start
work, the caller creates the issue in an open status or reopens it,
which still wakes the assignee.
## Model Used
- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), tool use and code
execution in Claude Code.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c6781f5b2c
commit
02898b98de
2 files changed
+23
-2
No files matched your search
@@ -342,6 +342,25 @@ describeEmbeddedPostgres("issue create onboarding first-task routes", () => {
|
||||
expect(mockAdapterExecute).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each(["done", "cancelled"])("does not queue an assignment wake for an issue created already %s", async (status) => {
|
||||
const companyId = await seedCompany();
|
||||
const agentId = await seedAgent(companyId);
|
||||
const app = createApp();
|
||||
|
||||
await request(app)
|
||||
.post(`/api/companies/${companyId}/issues`)
|
||||
.send({ title: "Historical record", status, assigneeAgentId: agentId })
|
||||
.expect(201);
|
||||
|
||||
await drainHeartbeatRunsToQuiescence(db, heartbeatService(db));
|
||||
const wakeups = await db
|
||||
.select()
|
||||
.from(agentWakeupRequests)
|
||||
.where(eq(agentWakeupRequests.companyId, companyId));
|
||||
expect(wakeups).toHaveLength(0);
|
||||
expect(mockAdapterExecute).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("still queues an assignment wake for an ordinary assigned issue", async () => {
|
||||
const companyId = await seedCompany();
|
||||
const agentId = await seedAgent(companyId);
|
||||
|
||||
@@ -13319,9 +13319,11 @@ export function issueRoutes(
|
||||
// Do not auto-wake the onboarding first task. Nothing should run and no
|
||||
// token should be spent until the user types: the greeting is posted above
|
||||
// (deterministic, no LLM) and the user's first comment wakes the assignee
|
||||
// through the normal comment path. Every other create path keeps its wake.
|
||||
// through the normal comment path. A task created already done or
|
||||
// cancelled is a record, not work: like restoring an assignee on closed
|
||||
// work, it must not wake the assignee. Every other create keeps its wake.
|
||||
|
||||
if (!isOnboardingFirstTask) {
|
||||
if (!isOnboardingFirstTask && !isClosedIssueStatus(issue.status)) {
|
||||
void queueIssueAssignmentWakeup({
|
||||
heartbeat,
|
||||
issue,
|
||||
|
||||
Reference in new issue
Block a user