diff --git a/doc/SPEC-implementation.md b/doc/SPEC-implementation.md index b51a044708..7838de5e83 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 12fb028b09..09738f07c3 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 9b6cdb3172..7c24715eb5 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 3daeec0bc0..3b7e1403c0 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 @@ -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/packages/adapters/openclaw-gateway/src/server/execute.ts b/packages/adapters/openclaw-gateway/src/server/execute.ts index 81a3f2e4ac..8c51ef10a3 100644 --- a/packages/adapters/openclaw-gateway/src/server/execute.ts +++ b/packages/adapters/openclaw-gateway/src/server/execute.ts @@ -401,15 +401,15 @@ function buildWakeText( "1) GET /api/agents/me", `2) Determine issueId: PAPERCLIP_TASK_ID if present, otherwise issue_id (${issueIdHint}).`, "3) If issueId exists:", - " - POST /api/issues/{issueId}/checkout with {\"agentId\":\"$PAPERCLIP_AGENT_ID\",\"expectedStatuses\":[\"todo\",\"backlog\",\"blocked\"]}", + " - POST /api/issues/{issueId}/checkout with {\"agentId\":\"$PAPERCLIP_AGENT_ID\",\"expectedStatuses\":[\"todo\",\"backlog\",\"blocked\",\"in_review\"]}", " - GET /api/issues/{issueId}", " - GET /api/issues/{issueId}/comments", " - Execute the issue instructions exactly.", " - If instructions require a comment, POST /api/issues/{issueId}/comments with {\"body\":\"...\"}.", " - PATCH /api/issues/{issueId} with {\"status\":\"done\",\"comment\":\"what changed and why\"}.", "4) If issueId does not exist:", - " - GET /api/companies/$PAPERCLIP_COMPANY_ID/issues?assigneeAgentId=$PAPERCLIP_AGENT_ID&status=todo,in_progress,blocked", - " - Pick in_progress first, then todo, then blocked, then execute step 3.", + " - GET /api/companies/$PAPERCLIP_COMPANY_ID/issues?assigneeAgentId=$PAPERCLIP_AGENT_ID&status=todo,in_progress,in_review,blocked", + " - Pick in_progress first, then in_review when you were woken by a comment, then todo, then blocked, then execute step 3.", "", "Useful endpoints for issue work:", "- POST /api/issues/{issueId}/comments", diff --git a/server/src/onboarding-assets/ceo/HEARTBEAT.md b/server/src/onboarding-assets/ceo/HEARTBEAT.md index 222fe14e3f..8773823d21 100644 --- a/server/src/onboarding-assets/ceo/HEARTBEAT.md +++ b/server/src/onboarding-assets/ceo/HEARTBEAT.md @@ -24,8 +24,8 @@ If `PAPERCLIP_APPROVAL_ID` is set: ## 4. Get Assignments -- `GET /api/companies/{companyId}/issues?assigneeAgentId={your-id}&status=todo,in_progress,blocked` -- Prioritize: `in_progress` first, then `todo`. Skip `blocked` unless you can unblock it. +- `GET /api/companies/{companyId}/issues?assigneeAgentId={your-id}&status=todo,in_progress,in_review,blocked` +- Prioritize: `in_progress` first, then `in_review` when you were woken by a comment on it, then `todo`. Skip `blocked` unless you can unblock it. - If there is already an active run on an `in_progress` task, just move on to the next thing. - If `PAPERCLIP_TASK_ID` is set and assigned to you, prioritize that task. diff --git a/server/src/routes/issues.ts b/server/src/routes/issues.ts index 5e78af6a3b..4617b7eddb 100644 --- a/server/src/routes/issues.ts +++ b/server/src/routes/issues.ts @@ -1893,6 +1893,7 @@ export function issueRoutes( issueId: currentIssue.id, taskId: currentIssue.id, commentId: comment.id, + wakeCommentId: comment.id, source: "issue.comment.reopen", wakeReason: "issue_reopened_via_comment", reopenedFrom: reopenFromStatus, @@ -1916,6 +1917,7 @@ export function issueRoutes( 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 c260fa32a6..3cd9eb0683 100644 --- a/skills/paperclip/SKILL.md +++ b/skills/paperclip/SKILL.md @@ -38,12 +38,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. @@ -54,7 +55,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.** @@ -314,7 +315,7 @@ PATCH /api/agents/{agentId}/instructions-path | My identity | `GET /api/agents/me` | | My compact inbox | `GET /api/agents/me/inbox-lite` | | Report a user's Mine inbox view | `GET /api/agents/me/inbox/mine?userId=:userId` | -| My assignments | `GET /api/companies/:companyId/issues?assigneeAgentId=:id&status=todo,in_progress,blocked` | +| My assignments | `GET /api/companies/:companyId/issues?assigneeAgentId=:id&status=todo,in_progress,in_review,blocked` | | Checkout task | `POST /api/issues/:issueId/checkout` | | Get task + ancestors | `GET /api/issues/:issueId` | | List issue documents | `GET /api/issues/:issueId/documents` | diff --git a/skills/paperclip/references/api-reference.md b/skills/paperclip/references/api-reference.md index e9a089b284..9ee9301d5a 100644 --- a/skills/paperclip/references/api-reference.md +++ b/skills/paperclip/references/api-reference.md @@ -203,7 +203,7 @@ GET /api/agents/me -> { 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" } @@ -224,7 +224,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", ... }] } @@ -291,7 +291,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 diff --git a/ui/src/api/issues.ts b/ui/src/api/issues.ts index be38f8adf7..2a597f5cc8 100644 --- a/ui/src/api/issues.ts +++ b/ui/src/api/issues.ts @@ -75,7 +75,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`),