From 258c7ccd21c058fc2be3ca6cd5ba5a8d402e37b6 Mon Sep 17 00:00:00 2001 From: Devin Foley Date: Fri, 3 Apr 2026 09:19:48 -0700 Subject: [PATCH 1/3] fix: ensure agents respond to comments on in_review tasks Root cause: when someone commented on an in_review task, the heartbeat wakeup was triggered but the agent couldn't re-checkout the task because expectedStatuses only included todo/backlog/blocked. The in_review status was never handled in the checkout flow or the heartbeat procedure. Changes: - Add wakeCommentId to issue_commented and issue_reopened_via_comment context snapshots (consistent with issue_comment_mentioned) - Add in_review to checkout expectedStatuses in heartbeat skill - Update Step 3 fallback query to include in_review status - Update Step 4 to prioritize in_review tasks when woken by comment - Add explicit issue_commented wake reason handling in Step 4 Co-Authored-By: Paperclip --- server/src/routes/issues.ts | 2 ++ skills/paperclip/SKILL.md | 9 +++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/server/src/routes/issues.ts b/server/src/routes/issues.ts index d07de42a..c47f572f 100644 --- a/server/src/routes/issues.ts +++ b/server/src/routes/issues.ts @@ -1591,6 +1591,7 @@ export function issueRoutes(db: Db, storage: StorageService) { issueId: currentIssue.id, taskId: currentIssue.id, commentId: comment.id, + wakeCommentId: comment.id, source: "issue.comment.reopen", wakeReason: "issue_reopened_via_comment", reopenedFrom: reopenFromStatus, @@ -1614,6 +1615,7 @@ export function issueRoutes(db: Db, storage: StorageService) { issueId: currentIssue.id, taskId: currentIssue.id, commentId: comment.id, + wakeCommentId: comment.id, source: "issue.comment", wakeReason: "issue_commented", ...(interruptedRunId ? { interruptedRunId } : {}), diff --git a/skills/paperclip/SKILL.md b/skills/paperclip/SKILL.md index 1d319ad3..e70fe2ef 100644 --- a/skills/paperclip/SKILL.md +++ b/skills/paperclip/SKILL.md @@ -35,12 +35,13 @@ Follow these steps every time you wake up: - add a markdown comment explaining why it remains open and what happens next. Always include links to the approval and issue in that comment. -**Step 3 — Get assignments.** Prefer `GET /api/agents/me/inbox-lite` for the normal heartbeat inbox. It returns the compact assignment list you need for prioritization. Fall back to `GET /api/companies/{companyId}/issues?assigneeAgentId={your-agent-id}&status=todo,in_progress,blocked` only when you need the full issue objects. +**Step 3 — Get assignments.** Prefer `GET /api/agents/me/inbox-lite` for the normal heartbeat inbox. It returns the compact assignment list you need for prioritization. Fall back to `GET /api/companies/{companyId}/issues?assigneeAgentId={your-agent-id}&status=todo,in_progress,in_review,blocked` only when you need the full issue objects. -**Step 4 — Pick work (with mention exception).** Work on `in_progress` first, then `todo`. Skip `blocked` unless you can unblock it. +**Step 4 — Pick work (with mention exception).** Work on `in_progress` first, then `in_review` (if you were woken by a comment on it — check `PAPERCLIP_WAKE_COMMENT_ID`), then `todo`. Skip `blocked` unless you can unblock it. **Blocked-task dedup:** Before working on a `blocked` task, fetch its comment thread. If your most recent comment was a blocked-status update AND no new comments from other agents or users have been posted since, skip the task entirely — do not checkout, do not post another comment. Exit the heartbeat (or move to the next task) instead. Only re-engage with a blocked task when new context exists (a new comment, status change, or event-based wake like `PAPERCLIP_WAKE_COMMENT_ID`). If `PAPERCLIP_TASK_ID` is set and that task is assigned to you, prioritize it first for this heartbeat. -If this run was triggered by a comment mention (`PAPERCLIP_WAKE_COMMENT_ID` set; typically `PAPERCLIP_WAKE_REASON=issue_comment_mentioned`), you MUST read that comment thread first, even if the task is not currently assigned to you. +If this run was triggered by a comment on a task you own (`PAPERCLIP_WAKE_COMMENT_ID` set; `PAPERCLIP_WAKE_REASON=issue_commented`), you MUST read that comment, then checkout and address the feedback. This includes `in_review` tasks — if someone comments with feedback, re-checkout the task to address it. +If this run was triggered by a comment mention (`PAPERCLIP_WAKE_COMMENT_ID` set; `PAPERCLIP_WAKE_REASON=issue_comment_mentioned`), you MUST read that comment thread first, even if the task is not currently assigned to you. If that mentioned comment explicitly asks you to take the task, you may self-assign by checking out `PAPERCLIP_TASK_ID` as yourself, then proceed normally. If the comment asks for input/review but not ownership, respond in comments if useful, then continue with assigned work. If the comment does not direct you to take ownership, do not self-assign. @@ -51,7 +52,7 @@ If nothing is assigned and there is no valid mention-based ownership handoff, ex ``` POST /api/issues/{issueId}/checkout Headers: Authorization: Bearer $PAPERCLIP_API_KEY, X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID -{ "agentId": "{your-agent-id}", "expectedStatuses": ["todo", "backlog", "blocked"] } +{ "agentId": "{your-agent-id}", "expectedStatuses": ["todo", "backlog", "blocked", "in_review"] } ``` If already checked out by you, returns normally. If owned by another agent: `409 Conflict` — stop, pick a different task. **Never retry a 409.** From cd2be692e99d16b7c68bab46a389a16270f30a20 Mon Sep 17 00:00:00 2001 From: Devin Foley Date: Sat, 4 Apr 2026 11:20:29 -0700 Subject: [PATCH 2/3] Fix in-review task recheckout guidance Co-Authored-By: Paperclip --- doc/SPEC-implementation.md | 2 +- docs/api/issues.md | 2 +- docs/guides/agent-developer/heartbeat-protocol.md | 6 +++--- docs/guides/agent-developer/task-workflow.md | 6 +++--- packages/adapters/openclaw-gateway/src/server/execute.ts | 6 +++--- server/src/onboarding-assets/ceo/HEARTBEAT.md | 4 ++-- skills/paperclip/SKILL.md | 2 +- skills/paperclip/references/api-reference.md | 2 +- ui/src/api/issues.ts | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/doc/SPEC-implementation.md b/doc/SPEC-implementation.md index b51a0447..7838de5e 100644 --- a/doc/SPEC-implementation.md +++ b/doc/SPEC-implementation.md @@ -491,7 +491,7 @@ All endpoints are under `/api` and return JSON. ```json { "agentId": "uuid", - "expectedStatuses": ["todo", "backlog", "blocked"] + "expectedStatuses": ["todo", "backlog", "blocked", "in_review"] } ``` diff --git a/docs/api/issues.md b/docs/api/issues.md index 12fb028b..09738f07 100644 --- a/docs/api/issues.md +++ b/docs/api/issues.md @@ -73,7 +73,7 @@ POST /api/issues/{issueId}/checkout Headers: X-Paperclip-Run-Id: {runId} { "agentId": "{yourAgentId}", - "expectedStatuses": ["todo", "backlog", "blocked"] + "expectedStatuses": ["todo", "backlog", "blocked", "in_review"] } ``` diff --git a/docs/guides/agent-developer/heartbeat-protocol.md b/docs/guides/agent-developer/heartbeat-protocol.md index 9b6cdb31..7c24715e 100644 --- a/docs/guides/agent-developer/heartbeat-protocol.md +++ b/docs/guides/agent-developer/heartbeat-protocol.md @@ -31,14 +31,14 @@ Close linked issues if the approval resolves them, or comment on why they remain ### Step 3: Get Assignments ``` -GET /api/companies/{companyId}/issues?assigneeAgentId={yourId}&status=todo,in_progress,blocked +GET /api/companies/{companyId}/issues?assigneeAgentId={yourId}&status=todo,in_progress,in_review,blocked ``` Results are sorted by priority. This is your inbox. ### Step 4: Pick Work -- Work on `in_progress` tasks first, then `todo` +- Work on `in_progress` tasks first, then `in_review` when you were woken by a comment on it, then `todo` - Skip `blocked` unless you can unblock it - If `PAPERCLIP_TASK_ID` is set and assigned to you, prioritize it - If woken by a comment mention, read that comment thread first @@ -50,7 +50,7 @@ Before doing any work, you must checkout the task: ``` POST /api/issues/{issueId}/checkout Headers: X-Paperclip-Run-Id: {runId} -{ "agentId": "{yourId}", "expectedStatuses": ["todo", "backlog", "blocked"] } +{ "agentId": "{yourId}", "expectedStatuses": ["todo", "backlog", "blocked", "in_review"] } ``` If already checked out by you, this succeeds. If another agent owns it: `409 Conflict` — stop and pick a different task. **Never retry a 409.** diff --git a/docs/guides/agent-developer/task-workflow.md b/docs/guides/agent-developer/task-workflow.md index 3daeec0b..0aa3d509 100644 --- a/docs/guides/agent-developer/task-workflow.md +++ b/docs/guides/agent-developer/task-workflow.md @@ -11,7 +11,7 @@ Before doing any work on a task, checkout is required: ``` POST /api/issues/{issueId}/checkout -{ "agentId": "{yourId}", "expectedStatuses": ["todo", "backlog", "blocked"] } +{ "agentId": "{yourId}", "expectedStatuses": ["todo", "backlog", "blocked", "in_review"] } ``` This is an atomic operation. If two agents race to checkout the same task, exactly one succeeds and the other gets `409 Conflict`. @@ -82,8 +82,8 @@ This releases your ownership. Leave a comment explaining why. ``` GET /api/agents/me -GET /api/companies/company-1/issues?assigneeAgentId=agent-42&status=todo,in_progress,blocked -# -> [{ id: "issue-101", status: "in_progress" }, { id: "issue-99", status: "todo" }] +GET /api/companies/company-1/issues?assigneeAgentId=agent-42&status=todo,in_progress,in_review,blocked +# -> [{ id: "issue-101", status: "in_progress" }, { id: "issue-100", status: "in_review" }, { id: "issue-99", status: "todo" }] # Continue in_progress work GET /api/issues/issue-101 diff --git a/packages/adapters/openclaw-gateway/src/server/execute.ts b/packages/adapters/openclaw-gateway/src/server/execute.ts index f1c85c11..db369923 100644 --- a/packages/adapters/openclaw-gateway/src/server/execute.ts +++ b/packages/adapters/openclaw-gateway/src/server/execute.ts @@ -390,15 +390,15 @@ function buildWakeText(payload: WakePayload, paperclipEnv: Record { id: "agent-42", companyId: "company-1", ... } # 2. Check inbox -GET /api/companies/company-1/issues?assigneeAgentId=agent-42&status=todo,in_progress,blocked +GET /api/companies/company-1/issues?assigneeAgentId=agent-42&status=todo,in_progress,in_review,blocked -> [ { id: "issue-101", title: "Fix rate limiter bug", status: "in_progress", priority: "high" }, { id: "issue-99", title: "Implement login API", status: "todo", priority: "medium" } diff --git a/ui/src/api/issues.ts b/ui/src/api/issues.ts index 7f0b2b27..73612514 100644 --- a/ui/src/api/issues.ts +++ b/ui/src/api/issues.ts @@ -72,7 +72,7 @@ export const issuesApi = { checkout: (id: string, agentId: string) => api.post(`/issues/${id}/checkout`, { agentId, - expectedStatuses: ["todo", "backlog", "blocked"], + expectedStatuses: ["todo", "backlog", "blocked", "in_review"], }), release: (id: string) => api.post(`/issues/${id}/release`, {}), listComments: (id: string) => api.get(`/issues/${id}/comments`), From d12650e5ac28cd35d468982504837b5c0dda7b5a Mon Sep 17 00:00:00 2001 From: Devin Foley Date: Sat, 4 Apr 2026 11:43:29 -0700 Subject: [PATCH 3/3] fix: update stale single-status checkout examples in worked docs Greptile flagged that worked examples in task-workflow.md and api-reference.md still used ["todo"] instead of the full expectedStatuses array. Aligned them with the rest of the PR. Co-Authored-By: Paperclip --- docs/guides/agent-developer/task-workflow.md | 2 +- skills/paperclip/references/api-reference.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/guides/agent-developer/task-workflow.md b/docs/guides/agent-developer/task-workflow.md index 0aa3d509..3b7e1403 100644 --- a/docs/guides/agent-developer/task-workflow.md +++ b/docs/guides/agent-developer/task-workflow.md @@ -96,7 +96,7 @@ PATCH /api/issues/issue-101 # Pick up next task POST /api/issues/issue-99/checkout -{ "agentId": "agent-42", "expectedStatuses": ["todo"] } +{ "agentId": "agent-42", "expectedStatuses": ["todo", "backlog", "blocked", "in_review"] } # Partial progress PATCH /api/issues/issue-99 diff --git a/skills/paperclip/references/api-reference.md b/skills/paperclip/references/api-reference.md index 13b37895..71d953ce 100644 --- a/skills/paperclip/references/api-reference.md +++ b/skills/paperclip/references/api-reference.md @@ -216,7 +216,7 @@ PATCH /api/issues/issue-101 # 6. Still have time. Checkout the next task. POST /api/issues/issue-99/checkout -{ "agentId": "agent-42", "expectedStatuses": ["todo"] } +{ "agentId": "agent-42", "expectedStatuses": ["todo", "backlog", "blocked", "in_review"] } GET /api/issues/issue-99 -> { ..., ancestors: [{ title: "Build auth system", ... }] } @@ -283,7 +283,7 @@ GET /api/companies/company-1/issues?assigneeAgentId=mgr-1&status=todo,in_progres -> [ { id: "issue-30", title: "Break down Q2 roadmap into tasks", status: "todo" } ] POST /api/issues/issue-30/checkout -{ "agentId": "mgr-1", "expectedStatuses": ["todo"] } +{ "agentId": "mgr-1", "expectedStatuses": ["todo", "backlog", "blocked", "in_review"] } # 6. Create subtasks and delegate. POST /api/companies/company-1/issues