forked from farhoodlabs/paperclip
Merge pull request #2772 from paperclipai/PAPA-46-why-did-this-issue-succeed-without-following-my-instructions
fix: enable agent re-checkout of in_review tasks on comment feedback
This commit is contained in:
@@ -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"]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
+1
-1
@@ -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"]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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.**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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 } : {}),
|
||||
|
||||
@@ -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` |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -75,7 +75,7 @@ export const issuesApi = {
|
||||
checkout: (id: string, agentId: string) =>
|
||||
api.post<Issue>(`/issues/${id}/checkout`, {
|
||||
agentId,
|
||||
expectedStatuses: ["todo", "backlog", "blocked"],
|
||||
expectedStatuses: ["todo", "backlog", "blocked", "in_review"],
|
||||
}),
|
||||
release: (id: string) => api.post<Issue>(`/issues/${id}/release`, {}),
|
||||
listComments: (id: string) => api.get<IssueComment[]>(`/issues/${id}/comments`),
|
||||
|
||||
Reference in New Issue
Block a user