diff --git a/AGENTS.md b/AGENTS.md index dc5856e2..8a1a52d1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -85,7 +85,7 @@ Prefer additive updates. Keep `doc/SPEC.md` and `doc/SPEC-implementation.md` ali When you are creating a plan file in the repository itself, new plan documents belong in `doc/plans/` and should use `YYYY-MM-DD-slug.md` filenames. This does not replace Paperclip issue planning: if a Paperclip issue asks for a plan, update the issue `plan` document per the `paperclip` skill instead of creating a repo markdown file. 6. Attach inspectable generated artifacts. -When your task produces a user-inspectable file, upload it to the current issue before final disposition. Use `scripts/paperclip-upload-artifact.sh` so the file is available through the Paperclip API, create/update an artifact work product when the file is the deliverable, link the uploaded artifact in the final issue comment, and then set status. Do not rely on local filesystem paths as the only access path. See `doc/AGENT-ARTIFACTS.md` for `.mp4` and `.webm` examples. +When your task produces a user-inspectable file, follow the Paperclip skill's "Generated Artifacts and Work Products" workflow before final disposition. In this repo, prefer `scripts/paperclip-upload-artifact.sh` so the file is available through the Paperclip API, create/update an artifact work product when the file is the deliverable, link the uploaded artifact in the final issue comment, and then set status. Do not rely on local filesystem paths as the only access path. See `doc/AGENT-ARTIFACTS.md` for `.mp4` and `.webm` examples. ## 6. Database Change Workflow diff --git a/doc/AGENT-ARTIFACTS.md b/doc/AGENT-ARTIFACTS.md index 68f63f2e..7cd8c367 100644 --- a/doc/AGENT-ARTIFACTS.md +++ b/doc/AGENT-ARTIFACTS.md @@ -78,7 +78,7 @@ curl -sS -X POST \ "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/issues/$PAPERCLIP_TASK_ID/attachments" \ -H "Authorization: Bearer $PAPERCLIP_API_KEY" \ -H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \ - -F "file=@dist/demo.mp4;type=video/mp4" + -F 'file=@"dist/demo.mp4";type=video/mp4' ``` Then create a work product when the uploaded file is the deliverable: @@ -92,6 +92,6 @@ curl -sS -X POST \ --data-binary @artifact-work-product.json ``` -Use `type: "artifact"`, `provider: "paperclip"`, and metadata containing -`attachmentId`, `contentType`, `byteSize`, `contentPath`, `openPath`, -`downloadPath`, and `originalFilename`. +Use `type: "artifact"`, `provider: "paperclip"`, and metadata containing the +uploaded `attachmentId`. The server canonicalizes `contentType`, `byteSize`, +`contentPath`, `openPath`, `downloadPath`, and `originalFilename`. diff --git a/server/src/__tests__/paperclip-skill-utils.test.ts b/server/src/__tests__/paperclip-skill-utils.test.ts index b2fa08ba..41402991 100644 --- a/server/src/__tests__/paperclip-skill-utils.test.ts +++ b/server/src/__tests__/paperclip-skill-utils.test.ts @@ -43,6 +43,16 @@ describe("paperclip skill utils", () => { expect(entries[1]?.source).toBe(path.join(root, "skills", "paperclip-create-agent")); }); + it("documents artifact uploads in the installed Paperclip skill", async () => { + const skillBody = await fs.readFile(path.resolve("skills/paperclip/SKILL.md"), "utf8"); + + expect(skillBody).toContain("Generated Artifacts and Work Products"); + expect(skillBody).toContain("scripts/paperclip-upload-artifact.sh"); + expect(skillBody).toContain("POST"); + expect(skillBody).toContain("/api/companies/$PAPERCLIP_COMPANY_ID/issues/$PAPERCLIP_TASK_ID/attachments"); + expect(skillBody).toContain("/api/issues/$PAPERCLIP_TASK_ID/work-products"); + }); + it("marks skills with required: false in SKILL.md frontmatter as optional", async () => { const root = await makeTempDir("paperclip-skill-optional-"); cleanupDirs.add(root); diff --git a/server/src/onboarding-assets/default/AGENTS.md b/server/src/onboarding-assets/default/AGENTS.md index 62b639f5..23acfbd2 100644 --- a/server/src/onboarding-assets/default/AGENTS.md +++ b/server/src/onboarding-assets/default/AGENTS.md @@ -5,7 +5,7 @@ You are an agent at Paperclip company. - Start actionable work in the same heartbeat. Do not stop at a plan unless the issue explicitly asks for planning. - Keep the work moving until it is done. If you need QA to review it, ask them. If you need your boss to review it, ask them. - Leave durable progress in task comments, documents, or work products, then update the issue to a clear final disposition before you exit. -- When your work produces a user-inspectable file, upload it to the issue before final disposition. Use `scripts/paperclip-upload-artifact.sh` when working in this repo, create/update an artifact work product when the file is the deliverable, and link the uploaded attachment in the final comment. Do not rely on local filesystem paths as the only access path. +- When your work produces a user-inspectable file, follow the Paperclip skill's "Generated Artifacts and Work Products" workflow before final disposition. Use `scripts/paperclip-upload-artifact.sh` when working in this repo, create/update an artifact work product when the file is the deliverable, and link the uploaded attachment in the final comment. Do not rely on local filesystem paths as the only access path. - Comments, documents, screenshots, work products, and `Remaining` bullets are evidence, not valid liveness paths by themselves. - Final disposition checklist: mark `done` when complete and verified; use `in_review` only with a real reviewer, approval, interaction, or monitor path; use `blocked` only with first-class blockers or a named unblock owner/action; create delegated follow-up issues with blockers when another agent owns the next step; keep `in_progress` only when a live continuation path exists. - Use child issues for parallel or long delegated work instead of polling agents, sessions, or processes. diff --git a/skills/paperclip/SKILL.md b/skills/paperclip/SKILL.md index dc57aeed..a825fb9c 100644 --- a/skills/paperclip/SKILL.md +++ b/skills/paperclip/SKILL.md @@ -94,6 +94,51 @@ If `currentParticipant` does not match you, do not try to advance the stage — - If blocked, move the issue to `blocked` with the unblock owner and exact action needed. - Respect budget, pause/cancel, approval gates, execution policy stages, and company boundaries. +### Generated Artifacts and Work Products + +When work produces a user-inspectable file, upload it to the current issue before final disposition. Local filesystem paths are not enough because board users, reviewers, and cloud operators may not have access to the agent workspace. + +Use the repo helper when it exists in the active workspace: + +```bash +scripts/paperclip-upload-artifact.sh path/to/output.webm \ + --title "Walkthrough render" \ + --summary "Rendered walkthrough for review" +``` + +The helper uses `PAPERCLIP_API_URL`, `PAPERCLIP_API_KEY`, `PAPERCLIP_COMPANY_ID`, `PAPERCLIP_TASK_ID`, and `PAPERCLIP_RUN_ID`. It uploads the file as an issue attachment, creates an attachment-backed artifact work product by default, and prints issue-safe markdown links for your final comment. + +If the helper is unavailable, use the Paperclip API directly: + +```bash +curl -sS -X POST \ + "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/issues/$PAPERCLIP_TASK_ID/attachments" \ + -H "Authorization: Bearer $PAPERCLIP_API_KEY" \ + -H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \ + -F 'file=@"path/to/output.webm";type=video/webm' +``` + +Then create a work product when the file is the deliverable. The server canonicalizes attachment-backed artifact metadata from the `attachmentId`: + +```bash +curl -sS -X POST \ + "$PAPERCLIP_API_URL/api/issues/$PAPERCLIP_TASK_ID/work-products" \ + -H "Authorization: Bearer $PAPERCLIP_API_KEY" \ + -H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \ + -H "Content-Type: application/json" \ + --data-binary '{ + "type": "artifact", + "provider": "paperclip", + "title": "Walkthrough render", + "status": "ready_for_review", + "reviewState": "needs_board_review", + "isPrimary": true, + "metadata": { "attachmentId": "" } + }' +``` + +In your final issue comment, link the uploaded attachment or work product and describe what it contains. Do not leave artifact-producing work `in_progress` with only a local path or a `Remaining` note. + **Step 8 — Update status and communicate.** Always include the run ID header. If you are blocked at any point, you MUST update the issue to `blocked` before exiting the heartbeat, with a comment that explains the blocker and who needs to act.