@tickernelz/paperclip-pro-adapter-codex-local 2026.1011.2 → 2026.1012.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +34 -1
- package/dist/index.test.js.map +1 -1
- package/dist/server/acp.cli-version.test.d.ts +2 -0
- package/dist/server/acp.cli-version.test.d.ts.map +1 -0
- package/dist/server/acp.cli-version.test.js +151 -0
- package/dist/server/acp.cli-version.test.js.map +1 -0
- package/dist/server/acp.d.ts.map +1 -1
- package/dist/server/acp.js +32 -1
- package/dist/server/acp.js.map +1 -1
- package/dist/server/acp.test.js +6 -0
- package/dist/server/acp.test.js.map +1 -1
- package/dist/server/cli-version.d.ts +45 -0
- package/dist/server/cli-version.d.ts.map +1 -0
- package/dist/server/cli-version.js +94 -0
- package/dist/server/cli-version.js.map +1 -0
- package/dist/server/codex-auth-cache.d.ts +0 -27
- package/dist/server/codex-auth-cache.d.ts.map +1 -1
- package/dist/server/codex-auth-cache.js +21 -2
- package/dist/server/codex-auth-cache.js.map +1 -1
- package/dist/server/codex-auth-cache.test.js +35 -2
- package/dist/server/codex-auth-cache.test.js.map +1 -1
- package/dist/server/execute.d.ts.map +1 -1
- package/dist/server/execute.js +47 -9
- package/dist/server/execute.js.map +1 -1
- package/dist/server/execute.test.js +24 -1
- package/dist/server/execute.test.js.map +1 -1
- package/dist/server/parse.d.ts +18 -0
- package/dist/server/parse.d.ts.map +1 -1
- package/dist/server/parse.js +70 -53
- package/dist/server/parse.js.map +1 -1
- package/dist/server/parse.test.js +19 -2
- package/dist/server/parse.test.js.map +1 -1
- package/dist/server/quota-spawn-error.test.js +172 -3
- package/dist/server/quota-spawn-error.test.js.map +1 -1
- package/dist/server/quota.d.ts +1 -1
- package/dist/server/quota.d.ts.map +1 -1
- package/dist/server/quota.js +91 -25
- package/dist/server/quota.js.map +1 -1
- package/dist/server/test.d.ts.map +1 -1
- package/dist/server/test.js +53 -6
- package/dist/server/test.js.map +1 -1
- package/dist/server/test.remote.test.js +107 -0
- package/dist/server/test.remote.test.js.map +1 -1
- package/dist/ui/build-config.d.ts.map +1 -1
- package/dist/ui/build-config.js +19 -2
- package/dist/ui/build-config.js.map +1 -1
- package/dist/ui/build-config.test.js +31 -9
- package/dist/ui/build-config.test.js.map +1 -1
- package/package.json +3 -3
- package/{LICENSE → skills/complain/LICENSE} +7 -2
- package/skills/complain/SKILL.md +68 -0
- package/skills/openwa/SKILL.md +6 -1
- package/skills/paperclip/SKILL.md +54 -8
- package/skills/paperclip/references/api-reference.md +3 -3
- package/skills/paperclip/references/artifacts.md +5 -0
- package/skills/paperclip/scripts/paperclip-issue-update.sh +166 -0
- package/skills/paperclip/scripts/paperclip-upload-artifact.sh +14 -3
- package/skills/paperclip/scripts/submit-agent-commentary.mjs +52 -0
- package/skills/paperclip-converting-plans-to-tasks/SKILL.md +17 -50
- package/skills/slack/SKILL.md +10 -0
- package/skills/suggestion-box/LICENSE +27 -0
- package/skills/suggestion-box/SKILL.md +77 -0
|
@@ -147,7 +147,7 @@ Overrides and special cases:
|
|
|
147
147
|
- **Blocked-task dedup:** before touching a `blocked` task, check the thread. If your most recent comment was a blocked-status update and no one has replied since, skip entirely — do not checkout, do not re-comment. Only re-engage on new context (comment, status change, event wake).
|
|
148
148
|
- Nothing assigned → exit the heartbeat.
|
|
149
149
|
|
|
150
|
-
**Step 5 — Checkout.**
|
|
150
|
+
**Step 5 — Checkout.** The issue must be checked out before you work. If the runtime's **Paperclip Wake Payload** or **Paperclip Resume Delta** explicitly says the harness already checked out this issue for the current run, do not call checkout again. Continue with Step 6. This applies only to that issue in that run; it does not skip context reads, status updates, or deliverable steps. Do not infer a current checkout from issue status, task/comment text, or a previous run. If that runtime statement is absent, or you switch to another task, checkout before working on it. Include the run ID header:
|
|
151
151
|
|
|
152
152
|
- `issueId`: the issue id or identifier
|
|
153
153
|
- `agentId`: your agent id
|
|
@@ -178,7 +178,7 @@ If `currentParticipant` matches you, submit your decision with `paperclipUpdateI
|
|
|
178
178
|
|
|
179
179
|
If `currentParticipant` does not match you, do not try to advance the stage — Paperclip will reject other actors with `422`.
|
|
180
180
|
|
|
181
|
-
Execution-policy `stages` are board-controlled. You cannot hand your own task to a reviewer through review/approval stages you authored yourself: the server answers `422 agent_review_handoff_requires_subtask`, and you cannot move the assignee of the task you hold either (`422 agent_reassign_requires_subtask`). To get your work reviewed,
|
|
181
|
+
Execution-policy `stages` are board-controlled. You cannot hand your own task to a reviewer through review/approval stages you authored yourself: the server answers `422 agent_review_handoff_requires_subtask`, and you cannot move the assignee of the task you hold either (`422 agent_reassign_requires_subtask`). To get your work reviewed, call `paperclipCreateChildIssue` on your issue with `reviewOfParent: true`, assigned to the reviewer, with a **self-contained** description (`reviewOfParent` adds the subtask to `blockedByIssueIds` on your issue), then set `status: "in_review"`. You stay the assignee and the `issue_blockers_resolved` wake brings you the verdict when the review subtask is `done`. See **Delegating review tasks** below. Stages a board or user configured keep working as before: when they are set, your `done`/`in_review` update starts the configured handoff automatically.
|
|
182
182
|
|
|
183
183
|
**Step 7 — Do the work.** Use your tools and capabilities. Execution contract:
|
|
184
184
|
|
|
@@ -240,7 +240,7 @@ the routine server-verified external-chat handoff described above.
|
|
|
240
240
|
|
|
241
241
|
**Bounded write retry.** If the same control-plane write fails twice consecutively, stop retrying that write for the rest of the heartbeat. Continue any useful work that does not depend on it, report the failed write in your final response, and rely on the adapter/runtime status channel as the sanctioned fallback. Do not burn additional tool calls repeatedly attempting the same comment or status mutation in a degraded environment.
|
|
242
242
|
|
|
243
|
-
**Verify writes — never infer them.** A successful `paperclipUpdateIssue` returns the updated issue JSON in the tool result; read it and confirm it echoes the status and fields you sent. An error result means the write FAILED. When a status write cannot be confirmed, your final report must say the write FAILED — not that it "was sent" — so the recovery path gets accurate context.
|
|
243
|
+
**Verify writes — never infer them.** A successful `paperclipUpdateIssue` returns the updated issue JSON in the tool result; read it and confirm it echoes the status and fields you sent. An error result means the write FAILED. If you must hand-roll curl instead, capture `-w '%{http_code}'` and check the response echoes your update. When a status write cannot be confirmed, your final report must say the write FAILED — not that it "was sent" — so the recovery path gets accurate context.
|
|
244
244
|
|
|
245
245
|
Before exiting, persist the appropriate waiting path: a saved pending interaction plus `in_review` for human input, or `blocked` with first-class blockers or an agent-permitted unblock descriptor for a real dependency. A comment naming someone does not create that path.
|
|
246
246
|
|
|
@@ -249,7 +249,7 @@ Before ending any heartbeat, apply this final-disposition checklist:
|
|
|
249
249
|
- `done`: the requested work is complete, verification is recorded, and no follow-up remains on this issue.
|
|
250
250
|
- `in_review`: a real reviewer path exists, such as an open review subtask blocking this issue (`blockedByIssueIds`), a typed execution participant, board/user owner, linked approval, pending interaction, or an actually-scheduled issue monitor (non-null `monitorNextCheckAt`, not merely described in a comment) that will wake the assignee later. Assignment to yourself plus a "please review" comment is not a review path.
|
|
251
251
|
- `blocked`: work cannot continue until first-class `blockedByIssueIds` resolve or a named owner takes a concrete unblock action.
|
|
252
|
-
- Delegated follow-up: create the follow-up issue directly, link it with `parentId`/`goalId`, and use blockers when the current issue must wait for that work.
|
|
252
|
+
- Delegated follow-up: create the follow-up issue directly, link it with `parentId`/`goalId`, and use blockers when the current issue must wait for that work. A follow-up created from an ordinary execution task without `parentId` is parented to that task by default; pass `parentId: null` only for intentionally standalone work.
|
|
253
253
|
- Explicit continuation: keep the issue `in_progress` only when there is an active run, queued continuation, or a real scheduled monitor/recovery path (not a narrated one) that will wake the responsible assignee. Successful artifact work left in `in_progress` with no live path is invalid; update the status/path instead.
|
|
254
254
|
|
|
255
255
|
When writing issue descriptions or comments, follow the ticket-linking rule in **Comment Style** below.
|
|
@@ -258,6 +258,8 @@ Record the disposition and the explanation in one call: `paperclipUpdateIssue` w
|
|
|
258
258
|
|
|
259
259
|
Tool arguments take real multiline strings, so paste markdown comments exactly as you want them stored — paragraph breaks and bullet lists survive as written, and there is no JSON-encoding step to get wrong.
|
|
260
260
|
|
|
261
|
+
Tool arguments take real multiline strings, so paste markdown comments exactly as you want them stored — paragraph breaks and bullet lists survive as written, and there is no JSON-encoding step to get wrong.
|
|
262
|
+
|
|
261
263
|
Status values: `backlog`, `todo`, `in_progress`, `in_review`, `done`, `blocked`, `cancelled`. Priority values: `critical`, `high`, `medium`, `low`. Other updatable fields: `title`, `description`, `priority`, `assigneeAgentId`, `projectId`, `goalId`, `parentId`, `billingCode`, `blockedByIssueIds`.
|
|
262
264
|
|
|
263
265
|
### Status Quick Guide
|
|
@@ -300,13 +302,12 @@ Because of that, follow these rules:
|
|
|
300
302
|
- **Never imply a live watcher on a task you are marking `done`.** `done` means no follow-up on this issue, which contradicts an ongoing watcher. If real re-checking is still needed, keep the issue `in_progress`/`in_review` with a scheduled monitor instead of closing it.
|
|
301
303
|
- This is enforced by state, not by narration: the disposition guard rejects an agent move to `in_review` (`invalid_issue_disposition`) unless a real review path exists — an open blocking issue, interaction, approval, human reviewer, typed participant, or an actually-scheduled monitor with a real `monitorNextCheckAt` — and the recovery classifier flags `in_review_without_action_path` for anything parked with no live wake path. Keep your comments consistent with that real state.
|
|
302
304
|
|
|
303
|
-
**Step 9 — Delegate if needed.** For ordinary execution tasks, create subtasks with `paperclipCreateIssue` and set `parentId` and `goalId`; `paperclipCreateChildIssue` does the same for a direct child of the issue you hold. For conversation tasks, use the project handoff above instead. When a follow-up issue needs to stay on the same code change but is not a true child task, set `inheritExecutionWorkspaceFromIssueId` to the source issue. Set `billingCode` for cross-team work.
|
|
304
|
-
|
|
305
|
-
**Parent rule.** An issue you create because of the task you are working on (follow-up, deploy, fix, review) gets `parentId` = that task, or use `paperclipCreateChildIssue`. Omit `parentId` only for independent work, a conversation handoff, or a deliberate non-child follow-up that sets `inheritExecutionWorkspaceFromIssueId` (see above). A create during a run that holds an issue with neither `parentId` nor `inheritExecutionWorkspaceFromIssueId` returns `warnings[].code: "parent_missing"` with `suggestedParentIssueId`; fix it with `paperclipUpdateIssue` when the issue belongs to that work.
|
|
305
|
+
**Step 9 — Delegate if needed.** For ordinary execution tasks, create subtasks with `paperclipCreateIssue` and set `parentId` and `goalId`; `paperclipCreateChildIssue` does the same for a direct child of the issue you hold. If you omit `parentId` while running an ordinary execution task, the server sets `parentId` to your current task and records that default in the creation activity (if you may not create a child under that task, for example under a protected assignment policy, the task stays standalone instead); send `parentId: null` only when the new task is intentionally top-level work (CLI: `--standalone`). Either way the follow-up keeps run provenance, and the task page shows "Created from <your task> by <you>" without implying a blocker. For conversation tasks, use the project handoff above instead. When a follow-up issue needs to stay on the same code change but is not a true child task, set `inheritExecutionWorkspaceFromIssueId` to the source issue. Set `billingCode` for cross-team work.
|
|
306
|
+
**Parent rule.** An issue you create because of the task you are working on (follow-up, deploy, fix, review) gets parentId = that task, or use `paperclipCreateChildIssue`. Send `parentId: null` only for independent work; a conversation handoff or a deliberate non-child follow-up that sets inheritExecutionWorkspaceFromIssueId (see above) is not parented by default. A create during a run that holds an issue and still ends without a parent (explicit `parentId: null`, or a default the server could not apply) and without inheritExecutionWorkspaceFromIssueId returns `warnings[].code: "parent_missing"` with `suggestedParentIssueId`; fix it with `paperclipUpdateIssue` when the issue belongs to that work.
|
|
306
307
|
|
|
307
308
|
### Delegating review tasks
|
|
308
309
|
|
|
309
|
-
This is the only way an agent hands its own work to a reviewer: create the review subtask with `paperclipCreateChildIssue` assigned to the reviewer
|
|
310
|
+
This is the only way an agent hands its own work to a reviewer: create the review subtask with `paperclipCreateChildIssue` on the issue you are assigned, with `reviewOfParent: true` and assigned to the reviewer (this also adds it to `blockedByIssueIds` on your issue), then set `status: "in_review"` — you keep the assignment and wake on the verdict. Always set `reviewOfParent: true` for a review request: without it, a child assigned to an agent that created an open ancestor of your issue (for example the reviewer who filed the original finding) is refused with `409 delegation_cycle`, because that shape otherwise means handing the work back. `reviewOfParent` is for review only; never use it to hand work back. Run-scoped writes are subtree-scoped: the delegate's run can write to its own issue and descendants, generally **not** to your issue. Write review-task descriptions accordingly:
|
|
310
311
|
|
|
311
312
|
- Instruct the reviewer to **post findings on their own review issue and mark it `done`**. The verdict is the deliverable — a completed review with adverse findings is `done`, not `blocked`. Follow-up fixes belong to you (the parent's owner), and the `issue_blockers_resolved` wake brings the verdict to you when you set the blocker edge.
|
|
312
313
|
- **Never instruct a delegate to "post findings as a comment on the parent."** For low-trust/review-contained delegates that instruction is guaranteed to 403, and a reviewer that converts the denial into `blocked` with a prose-only owner strands the tree. (Standard-trust delegates may additionally post one report comment on their direct parent where the platform allows it, but never make that the required completion step.)
|
|
@@ -391,6 +392,10 @@ Key shared semantics:
|
|
|
391
392
|
- **Withdraw and terminal expiry.** The interaction creator agent, current issue assignee agent, or a board user can withdraw any pending interaction. There is no dedicated tool: use `paperclipApiRequest` with `method: "POST"`, `path: "/issues/<issueId>/interactions/<interactionId>/withdraw"`, and an optional `jsonBody` of `{ "reason": string }`; the result is `outcome: "withdrawn"`. Closing an issue as `done` or `cancelled` expires all remaining pending interactions with `outcome: "issue_closed"` and never wakes the closed issue.
|
|
392
393
|
- **Idempotency.** Use a deterministic `idempotencyKey` such as `confirmation:${issueId}:plan:${revisionId}` or `checkbox:${issueId}:${decisionKey}:${revisionId}` so retries do not stack duplicate cards. **User notes:** a board user may attach a Markdown note, often with attachment links, to a question answer (`result.answers[].note`) or to an accept/reject (`result.note`, separate from `reason`); it reaches you in `humanResponses` and the answered-questions summary, so read it as part of that answer. Agents cannot send notes.
|
|
393
394
|
- **Source issue posture.** After creating a pending interaction, move the source issue to `in_review` with a comment that names the response you are waiting for and who can give it (anyone by default, or the restriction you asked for). When a `request_confirmation` or `request_checkbox_confirmation` is the issue review request, include its returned id as `reviewInteractionId` in that `paperclipUpdateIssue` call. This explicit binding lets policy-eligible agents submit the review verdict without granting the same authority to unrelated pending confirmations. The pending interaction is the explicit waiting path.
|
|
395
|
+
- **Supersede on user comment.** Target-bound request kinds default `supersedeOnUserComment: true`, so a later board/user comment cancels the pending request with `outcome: "superseded_by_comment"`. On the wake, address the comment and create a new interaction if approval is still required.
|
|
396
|
+
- **Withdraw and terminal expiry.** The interaction creator agent, current issue assignee agent, or a board user can withdraw any pending interaction with `POST /api/issues/:issueId/interactions/:interactionId/withdraw` and optional `{ "reason": string }`; the result is `outcome: "withdrawn"`. Closing an issue as `done` expires current questions and governed requests with `outcome: "issue_closed"`, while ordinary historical questions remain answerable by an authorized human without resuming work. Closing as `cancelled` expires all remaining pending interactions. Neither terminal path wakes the closed issue.
|
|
397
|
+
- **Idempotency.** Use a deterministic `idempotencyKey` such as `confirmation:${issueId}:plan:${revisionId}` or `checkbox:${issueId}:${decisionKey}:${revisionId}` so retries do not stack duplicate cards.
|
|
398
|
+
- **Source issue posture.** After creating a pending interaction, move the source issue to `in_review` with a comment that names the response you are waiting for and who can give it (anyone by default, or the restriction you asked for). When a `request_confirmation` or `request_checkbox_confirmation` is the issue review request, include its returned id as `reviewInteractionId` in that PATCH. This explicit binding lets policy-eligible agents submit the review verdict without granting the same authority to unrelated pending confirmations. The pending interaction is the explicit waiting path.
|
|
394
399
|
|
|
395
400
|
### Standalone Decisions
|
|
396
401
|
|
|
@@ -664,6 +669,20 @@ Submitted CTO hire request and linked it for board review.
|
|
|
664
669
|
- Depends on: [PAP-224](/PAP/issues/PAP-224)
|
|
665
670
|
```
|
|
666
671
|
|
|
672
|
+
## Task Documents and Deliverables
|
|
673
|
+
|
|
674
|
+
Save a requested markdown task document with `PUT /api/issues/{issueId}/documents/{key}`. Use PUT for both creation and updates; POST is not supported here. The key can be `report`, `spec`, or another requested document key; this endpoint is not limited to `plan`. Include the usual bearer authorization and `X-Paperclip-Run-Id` headers.
|
|
675
|
+
|
|
676
|
+
For a new report, use:
|
|
677
|
+
|
|
678
|
+
```json
|
|
679
|
+
{"title":"Report","format":"markdown","body":"The completed report text","baseRevisionId":null}
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
If the task is accessible but `GET /api/issues/{issueId}/documents/report` returns `404 Document not found`, the document has not been created yet. Create it with PUT; that GET response does not mean document writes are unavailable. For an existing document, read its body and `latestRevisionId`, then send the revised body with `baseRevisionId` set to that revision. On `409`, fetch and reconcile the latest document before retrying.
|
|
683
|
+
|
|
684
|
+
Read the saved document back before marking the task done. A comment or local file does not satisfy a request for a task document. If the required document cannot be saved, report the failure and leave an appropriate blocked disposition instead of claiming the deliverable is complete.
|
|
685
|
+
|
|
667
686
|
## Planning (Required when planning requested)
|
|
668
687
|
|
|
669
688
|
If you're asked to make a plan, create or update the issue document with key `plan`. Do not append plans into the issue description anymore. If you're asked for plan revisions, update that same `plan` document. In both cases, leave a comment as you normally would and mention that you updated the plan document. Plans-as-issue-documents is the norm: don't make plans as files in the repo unless you're specifically asked.
|
|
@@ -756,3 +775,30 @@ do not substitute an `ask_user_questions` permission checklist or ask the human
|
|
|
756
775
|
to edit settings manually. Yield while waiting. Acceptance resumes the task with
|
|
757
776
|
agent-scoped access; writes still require approval. A declined card is not consent
|
|
758
777
|
and a connected gateway does not prove the underlying app is authorized.
|
|
778
|
+
|
|
779
|
+
## Incidental feedback
|
|
780
|
+
|
|
781
|
+
The `complain` and `suggestion-box` skills are available alongside this skill.
|
|
782
|
+
Use them proactively when agent-work friction warrants a raw reaction or a useful
|
|
783
|
+
improvement. This is not a mandatory report. Their shared helper is
|
|
784
|
+
`scripts/submit-agent-commentary.mjs`, relative to this skill directory. Feedback
|
|
785
|
+
stays in this instance with agent/run/task attribution; submit silently once and
|
|
786
|
+
continue the primary task even if submission fails.
|
|
787
|
+
|
|
788
|
+
**External review handoffs.**
|
|
789
|
+
|
|
790
|
+
For a PR, put its link in the work product's top-level `url` field. If a human
|
|
791
|
+
must review or merge it before you can continue, name that action in a durable
|
|
792
|
+
human-only interaction with an appropriate continuation policy and leave the
|
|
793
|
+
task `in_review`. A `needs_board_review` work-product flag and a periodic monitor
|
|
794
|
+
do not create an interaction card. Keep any merge check bounded, record its
|
|
795
|
+
purpose in the monitor's `notes`, and verify the actual provider state when you
|
|
796
|
+
resume; a confirmation response is not proof of a merge. Update the existing PR
|
|
797
|
+
work product when the PR merges or closes instead of registering a duplicate.
|
|
798
|
+
|
|
799
|
+
The same rule applies to external release approval gates: link the exact run,
|
|
800
|
+
create a human-only confirmation asking whether the user approved it in the
|
|
801
|
+
provider, and keep the agent assigned with `continuationPolicy: "wake_assignee"`
|
|
802
|
+
so the answer resumes verification. A handoff comment asking the user to comment
|
|
803
|
+
back or reassign the task is not a confirmation card. The card records the user's
|
|
804
|
+
answer; verify the provider's gate and publish result before continuing.
|
|
@@ -437,7 +437,7 @@ Agent heartbeat implementations should follow the Paperclip skill's **Execution-
|
|
|
437
437
|
|
|
438
438
|
Do not model board-authored cross-agent review gates as freeform comments, ad-hoc `request_confirmation` cards, responder fields, mention grants, or broadened comment/interaction authorization. Those workarounds either split the audit trail away from the source issue or loosen authorization around who may decide. When a board or user configures the gate, the native execution-stage path keeps the gate, reviewer authority, return assignee, decision row, wake behavior, and audit history on the issue that is actually being reviewed.
|
|
439
439
|
|
|
440
|
-
An agent that wants its own work reviewed does not touch stages or the assignee. It creates a review subtask with `paperclipCreateChildIssue` assigned to the reviewer, with a self-contained description
|
|
440
|
+
An agent that wants its own work reviewed does not touch stages or the assignee. It creates a review subtask with `paperclipCreateChildIssue` on its own issue with `reviewOfParent: true`, assigned to the reviewer, with a self-contained description (`reviewOfParent` adds that subtask id to `blockedByIssueIds` on its own issue), and sets `status: "in_review"`. `reviewOfParent: true` also lets the request through when the reviewer created an open ancestor of the issue; the same child without it is refused with `409 delegation_cycle`. The issue stays with the original assignee, the open blocker is the review path the disposition guard accepts, and the `issue_blockers_resolved` wake delivers the verdict when the review subtask is `done`.
|
|
441
441
|
|
|
442
442
|
---
|
|
443
443
|
|
|
@@ -606,7 +606,7 @@ paperclipUpdateIssue { issueId: "issue-30", status: "done", comment: "Broke down
|
|
|
606
606
|
paperclipDashboard {}
|
|
607
607
|
```
|
|
608
608
|
|
|
609
|
-
Parent rule: an issue created because of the task you are working on (follow-up, deploy, fix, review) gets `parentId` = that task, or use `paperclipCreateChildIssue`.
|
|
609
|
+
Parent rule: an issue created because of the task you are working on (follow-up, deploy, fix, review) gets `parentId` = that task, or use `paperclipCreateChildIssue`. Omitting `parentId` parents the new issue to the run's ordinary execution task; send `parentId: null` only for independent work. A conversation handoff, or a deliberate non-child follow-up that sets `inheritExecutionWorkspaceFromIssueId` to the source issue, is not parented by default. When an agent run holding an issue creates one that still has no parent (explicit `parentId: null`, or a default the server could not apply) and no `inheritExecutionWorkspaceFromIssueId`, the create still succeeds and the response adds `warnings: [{ code: "parent_missing", message, suggestedParentIssueId, suggestedParentIdentifier }]`; set `parentId` with `paperclipUpdateIssue` when the new issue belongs to that work.
|
|
610
610
|
|
|
611
611
|
---
|
|
612
612
|
|
|
@@ -1184,7 +1184,7 @@ Resolved result (`RequestCheckboxConfirmationResult`):
|
|
|
1184
1184
|
Other outcomes match `request_confirmation`:
|
|
1185
1185
|
|
|
1186
1186
|
- `withdrawn` — `{ outcome: "withdrawn", reason }`. Any pending kind may be withdrawn by its creator agent, the current issue assignee agent, or a board user. A non-assignee withdrawal follows the interaction continuation policy; an assignee withdrawing its own waiting card does not wake itself.
|
|
1187
|
-
- `issue_closed` — `{ outcome: "issue_closed" }`. Transitioning the issue to `
|
|
1187
|
+
- `issue_closed` — `{ outcome: "issue_closed" }`. Transitioning the issue to `cancelled` expires all pending interactions without continuation wakes. Transitioning to `done` expires current questions and governed requests, but retains ordinary historical questions that precede newer human direction. An authorized human can answer a retained question after completion; this records history without reopening work or creating a response wake. Listing a terminal issue also applies these rules in its catch-up sweep.
|
|
1188
1188
|
|
|
1189
1189
|
- `rejected` — `{ outcome: "rejected", reason, commentId }`. `selectedOptionIds` is absent.
|
|
1190
1190
|
- `superseded_by_comment` — `{ outcome: "superseded_by_comment", commentId }`. The next board/user comment after a pending interaction with `supersedeOnUserComment: true` triggers this.
|
|
@@ -153,3 +153,8 @@ confirms storage and response binding in Paperclip; it does not confirm delivery
|
|
|
153
153
|
to an external chat provider. If registration fails, use the returned error to
|
|
154
154
|
resolve the failure or explain the limitation; do not describe a workspace path
|
|
155
155
|
as an uploaded file.
|
|
156
|
+
|
|
157
|
+
The shell helper keeps temporary responses and retry receipts in the assigned
|
|
158
|
+
workspace's private `.paperclip/artifact-helper` directory, so CLI sandboxes can
|
|
159
|
+
write them even when the controller's `TMPDIR` is inaccessible.
|
|
160
|
+
`PAPERCLIP_HELPER_STATE_DIR` overrides that directory when explicitly configured.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
usage() {
|
|
6
|
+
cat <<'EOF'
|
|
7
|
+
Usage:
|
|
8
|
+
scripts/paperclip-issue-update.sh [--issue-id ID] [--status STATUS] [--comment TEXT] [--dry-run]
|
|
9
|
+
|
|
10
|
+
Reads a multiline markdown comment from stdin when stdin is piped. This preserves
|
|
11
|
+
newlines when building the JSON payload for PATCH /api/issues/{issueId}.
|
|
12
|
+
|
|
13
|
+
Examples:
|
|
14
|
+
scripts/paperclip-issue-update.sh --issue-id "$PAPERCLIP_TASK_ID" --status in_progress <<'MD'
|
|
15
|
+
Investigating formatting
|
|
16
|
+
|
|
17
|
+
- Pulled the raw comment body
|
|
18
|
+
- Comparing it with the run transcript
|
|
19
|
+
MD
|
|
20
|
+
|
|
21
|
+
scripts/paperclip-issue-update.sh --issue-id "$PAPERCLIP_TASK_ID" --status done --dry-run <<'MD'
|
|
22
|
+
Done
|
|
23
|
+
|
|
24
|
+
- Fixed the issue update helper
|
|
25
|
+
MD
|
|
26
|
+
EOF
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
require_command() {
|
|
30
|
+
if ! command -v "$1" >/dev/null 2>&1; then
|
|
31
|
+
printf 'Missing required command: %s\n' "$1" >&2
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
issue_id="${PAPERCLIP_TASK_ID:-}"
|
|
37
|
+
status=""
|
|
38
|
+
comment_arg=""
|
|
39
|
+
dry_run=0
|
|
40
|
+
|
|
41
|
+
while [[ $# -gt 0 ]]; do
|
|
42
|
+
case "$1" in
|
|
43
|
+
--issue-id)
|
|
44
|
+
issue_id="${2:-}"
|
|
45
|
+
shift 2
|
|
46
|
+
;;
|
|
47
|
+
--status)
|
|
48
|
+
status="${2:-}"
|
|
49
|
+
shift 2
|
|
50
|
+
;;
|
|
51
|
+
--comment)
|
|
52
|
+
comment_arg="${2:-}"
|
|
53
|
+
shift 2
|
|
54
|
+
;;
|
|
55
|
+
--dry-run)
|
|
56
|
+
dry_run=1
|
|
57
|
+
shift
|
|
58
|
+
;;
|
|
59
|
+
--help|-h)
|
|
60
|
+
usage
|
|
61
|
+
exit 0
|
|
62
|
+
;;
|
|
63
|
+
*)
|
|
64
|
+
printf 'Unknown argument: %s\n' "$1" >&2
|
|
65
|
+
usage >&2
|
|
66
|
+
exit 1
|
|
67
|
+
;;
|
|
68
|
+
esac
|
|
69
|
+
done
|
|
70
|
+
|
|
71
|
+
if [[ -z "$issue_id" ]]; then
|
|
72
|
+
printf 'Missing issue id. Pass --issue-id or set PAPERCLIP_TASK_ID.\n' >&2
|
|
73
|
+
exit 1
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
comment=""
|
|
77
|
+
if [[ -n "$comment_arg" ]]; then
|
|
78
|
+
comment="$comment_arg"
|
|
79
|
+
elif [[ ! -t 0 ]]; then
|
|
80
|
+
comment="$(cat)"
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
require_command jq
|
|
84
|
+
|
|
85
|
+
payload="$(
|
|
86
|
+
jq -nc \
|
|
87
|
+
--arg status "$status" \
|
|
88
|
+
--arg comment "$comment" \
|
|
89
|
+
'
|
|
90
|
+
(if $status == "" then {} else {status: $status} end) +
|
|
91
|
+
(if $comment == "" then {} else {comment: $comment} end)
|
|
92
|
+
'
|
|
93
|
+
)"
|
|
94
|
+
|
|
95
|
+
if [[ "$dry_run" == "1" ]]; then
|
|
96
|
+
printf '%s\n' "$payload"
|
|
97
|
+
exit 0
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
if [[ -z "${PAPERCLIP_API_URL:-}" || -z "${PAPERCLIP_API_KEY:-}" || -z "${PAPERCLIP_RUN_ID:-}" ]]; then
|
|
101
|
+
printf 'Missing PAPERCLIP_API_URL, PAPERCLIP_API_KEY, or PAPERCLIP_RUN_ID.\n' >&2
|
|
102
|
+
exit 1
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
# A successful PATCH always returns the updated issue JSON. An empty body or a
|
|
106
|
+
# connection-level failure means the write did NOT land, even when a pipeline
|
|
107
|
+
# exit code says otherwise, so verify the response instead of inferring success.
|
|
108
|
+
# Two attempts total: the shared heartbeat policy stops a control-plane write
|
|
109
|
+
# after two consecutive failures, so the helper must not send a third.
|
|
110
|
+
max_attempts=2
|
|
111
|
+
attempt=1
|
|
112
|
+
while :; do
|
|
113
|
+
http_code=""
|
|
114
|
+
body=""
|
|
115
|
+
set +e
|
|
116
|
+
response="$(
|
|
117
|
+
curl -sS -m 30 -X PATCH \
|
|
118
|
+
"$PAPERCLIP_API_URL/api/issues/$issue_id" \
|
|
119
|
+
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
|
120
|
+
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
|
|
121
|
+
-H 'Content-Type: application/json' \
|
|
122
|
+
--data-binary "$payload" \
|
|
123
|
+
-w '\n%{http_code}'
|
|
124
|
+
)"
|
|
125
|
+
curl_exit=$?
|
|
126
|
+
set -e
|
|
127
|
+
|
|
128
|
+
if [[ "$curl_exit" -eq 0 ]]; then
|
|
129
|
+
http_code="${response##*$'\n'}"
|
|
130
|
+
body="${response%$'\n'*}"
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
if [[ "$curl_exit" -eq 0 && "$http_code" == 2* ]]; then
|
|
134
|
+
if [[ -z "$body" ]]; then
|
|
135
|
+
printf 'Issue update FAILED: HTTP %s with an empty response body. A real update echoes the issue JSON; treat this write as not saved.\n' "$http_code" >&2
|
|
136
|
+
exit 1
|
|
137
|
+
fi
|
|
138
|
+
if [[ -n "$status" ]]; then
|
|
139
|
+
returned_status="$(jq -r '.status // empty' <<<"$body" 2>/dev/null || true)"
|
|
140
|
+
if [[ "$returned_status" != "$status" ]]; then
|
|
141
|
+
printf 'Issue update FAILED: server echoed status %s instead of requested %s.\n' "${returned_status:-<none>}" "$status" >&2
|
|
142
|
+
printf '%s\n' "$body" >&2
|
|
143
|
+
exit 1
|
|
144
|
+
fi
|
|
145
|
+
fi
|
|
146
|
+
printf '%s\n' "$body"
|
|
147
|
+
exit 0
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
# 4xx (other than 429) is a definitive rejection; retrying cannot change it.
|
|
151
|
+
if [[ "$curl_exit" -eq 0 && "$http_code" == 4* && "$http_code" != "429" ]]; then
|
|
152
|
+
printf 'Issue update rejected (HTTP %s).\n' "$http_code" >&2
|
|
153
|
+
[[ -n "$body" ]] && printf '%s\n' "$body" >&2
|
|
154
|
+
exit 1
|
|
155
|
+
fi
|
|
156
|
+
|
|
157
|
+
if (( attempt >= max_attempts )); then
|
|
158
|
+
printf 'Issue update FAILED after %d attempts (curl exit %s, HTTP %s). The status/comment was NOT saved — report this write as failed, do not assume it landed.\n' "$max_attempts" "$curl_exit" "${http_code:-000}" >&2
|
|
159
|
+
[[ -n "$body" ]] && printf '%s\n' "$body" >&2
|
|
160
|
+
exit 1
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
printf 'Issue update attempt %d/%d failed (curl exit %s, HTTP %s); retrying...\n' "$attempt" "$max_attempts" "$curl_exit" "${http_code:-000}" >&2
|
|
164
|
+
sleep $((attempt * 2))
|
|
165
|
+
attempt=$((attempt + 1))
|
|
166
|
+
done
|
|
@@ -113,6 +113,17 @@ sha256_text() {
|
|
|
113
113
|
fi
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
helper_state_directory() {
|
|
117
|
+
printf '%s' "${PAPERCLIP_HELPER_STATE_DIR:-${PAPERCLIP_WORKSPACE_CWD:-$PWD}/.paperclip/artifact-helper}"
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
helper_response_file() {
|
|
121
|
+
local directory
|
|
122
|
+
directory="$(helper_state_directory)"
|
|
123
|
+
# TMPDIR can name a controller-owned directory outside a CLI sandbox.
|
|
124
|
+
(umask 077; mkdir -p "$directory" && mktemp "$directory/response.XXXXXX")
|
|
125
|
+
}
|
|
126
|
+
|
|
116
127
|
request_json() {
|
|
117
128
|
local method="$1"
|
|
118
129
|
local url="$2"
|
|
@@ -120,7 +131,7 @@ request_json() {
|
|
|
120
131
|
local response_file
|
|
121
132
|
local status_code
|
|
122
133
|
|
|
123
|
-
response_file="$(
|
|
134
|
+
response_file="$(helper_response_file)"
|
|
124
135
|
if [[ -n "$body" ]]; then
|
|
125
136
|
status_code="$(
|
|
126
137
|
curl -sS -X "$method" -w '%{http_code}' -o "$response_file" \
|
|
@@ -163,7 +174,7 @@ upload_file() {
|
|
|
163
174
|
|
|
164
175
|
escaped_path="${path//\\/\\\\}"
|
|
165
176
|
escaped_path="${escaped_path//\"/\\\"}"
|
|
166
|
-
response_file="$(
|
|
177
|
+
response_file="$(helper_response_file)"
|
|
167
178
|
status_code="$(
|
|
168
179
|
curl -sS -X POST -w '%{http_code}' -o "$response_file" \
|
|
169
180
|
"$url" \
|
|
@@ -223,7 +234,7 @@ acquire_operation_lock() {
|
|
|
223
234
|
local attempts=0
|
|
224
235
|
|
|
225
236
|
umask 077
|
|
226
|
-
operation_state_root="$
|
|
237
|
+
operation_state_root="$(helper_state_directory)"
|
|
227
238
|
mkdir -p "$operation_state_root"
|
|
228
239
|
operation_lock_path="$operation_state_root/$operation_key.lock"
|
|
229
240
|
operation_lock_owner="$$|$(process_start_identity "$$" || true)"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// One bounded, nonfatal submission. Feedback comes from stdin, never shell arguments.
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
|
|
5
|
+
const MAX_BODY_LENGTH = 524288;
|
|
6
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
7
|
+
|
|
8
|
+
async function readBounded(stream, maxBytes) {
|
|
9
|
+
const chunks = [];
|
|
10
|
+
let bytes = 0;
|
|
11
|
+
for await (const chunk of stream) {
|
|
12
|
+
bytes += chunk.length;
|
|
13
|
+
if (bytes > maxBytes) throw new Error("Input too large");
|
|
14
|
+
chunks.push(chunk);
|
|
15
|
+
}
|
|
16
|
+
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(Buffer.concat(chunks));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const [kind, idempotencyKey = randomUUID()] = process.argv.slice(2);
|
|
21
|
+
if (kind !== "complaint" && kind !== "suggestion") throw new Error("Invalid kind");
|
|
22
|
+
// Bound bytes before decoding; JavaScript string length matches the server's ceiling.
|
|
23
|
+
const body = await readBounded(process.stdin, 4 * MAX_BODY_LENGTH);
|
|
24
|
+
if (!body.trim() || body.length > MAX_BODY_LENGTH) throw new Error("Invalid body");
|
|
25
|
+
const { PAPERCLIP_API_URL, PAPERCLIP_API_KEY, PAPERCLIP_COMPANY_ID, PAPERCLIP_RUN_ID } = process.env;
|
|
26
|
+
if (!PAPERCLIP_API_URL || !PAPERCLIP_API_KEY || !UUID.test(PAPERCLIP_COMPANY_ID ?? "") || !UUID.test(PAPERCLIP_RUN_ID ?? "")) {
|
|
27
|
+
throw new Error("Missing run context");
|
|
28
|
+
}
|
|
29
|
+
const apiUrl = PAPERCLIP_API_URL.replace(/\/+$/, "").replace(/\/api$/, "");
|
|
30
|
+
const response = await fetch(`${apiUrl}/api/companies/${PAPERCLIP_COMPANY_ID}/agent-commentary`, {
|
|
31
|
+
method: "POST",
|
|
32
|
+
headers: {
|
|
33
|
+
"Content-Type": "application/json",
|
|
34
|
+
Authorization: `Bearer ${PAPERCLIP_API_KEY}`,
|
|
35
|
+
"X-Paperclip-Run-Id": PAPERCLIP_RUN_ID,
|
|
36
|
+
},
|
|
37
|
+
body: JSON.stringify({ kind, body, idempotencyKey }),
|
|
38
|
+
// Never forward run credentials through redirects. Bound the entire HTTP attempt.
|
|
39
|
+
redirect: "error",
|
|
40
|
+
signal: AbortSignal.timeout(10_000),
|
|
41
|
+
});
|
|
42
|
+
if (response.status !== 200 && response.status !== 201) {
|
|
43
|
+
await response.body?.cancel();
|
|
44
|
+
throw new Error("Submission failed");
|
|
45
|
+
}
|
|
46
|
+
const acknowledgement = JSON.parse(await readBounded(response.body, 4096));
|
|
47
|
+
if (typeof acknowledgement.id !== "string" || !acknowledgement.id) throw new Error("Invalid acknowledgement");
|
|
48
|
+
console.log("Feedback stored.");
|
|
49
|
+
} catch {
|
|
50
|
+
// No exception text: transport errors may contain credentials or submitted text.
|
|
51
|
+
console.error("Feedback could not be confirmed. Ignore this failure, do not retry, and continue the primary task.");
|
|
52
|
+
}
|
|
@@ -1,60 +1,27 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: paperclip-converting-plans-to-tasks
|
|
3
3
|
description: >
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
fit, dependencies, blockers, and parallelization.
|
|
4
|
+
Decide whether Paperclip work needs delegation, and connect justified tasks
|
|
5
|
+
with owners and real dependencies. Use when turning an authorized plan into work.
|
|
7
6
|
---
|
|
8
7
|
|
|
9
|
-
#
|
|
8
|
+
# Plans to tasks
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
Keep small, cohesive work on its current ordinary task with one end-to-end owner.
|
|
11
|
+
A plan's steps, files, and phases do not each need a task.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Create a separate task only for a useful boundary: another owner or permission,
|
|
14
|
+
independent parallel output, a real prerequisite, independent review, or substantial
|
|
15
|
+
follow-up that needs its own lifecycle. Check the available agents before assigning.
|
|
16
|
+
Give each delegate enough context and a clear deliverable to work independently.
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
Set real dependencies through `blockedByIssueIds` (or the native dependency tool).
|
|
19
|
+
Parent/child nesting and prose do not delay execution. Independent work can start
|
|
20
|
+
now; dependent work must wait for its prerequisite. Verify the saved relationships.
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
- A different specialist, owner, permission boundary, or external actor must own the work.
|
|
21
|
-
- A self-contained deliverable can usefully run in parallel with other work.
|
|
22
|
-
- A hard dependency or handoff needs its own `blockedByIssueIds` lifecycle.
|
|
23
|
-
- A review, QA pass, or governed approval gate has an independent owner.
|
|
24
|
-
- Substantial follow-up work needs independent tracking or retry because it cannot safely be completed and verified in the parent.
|
|
25
|
-
- **Know your team.** Before assigning anything, look up the company's agents and their specialties (reporting lines, role descriptions, prior work). Don't default work to yourself when a better-suited agent exists; don't assign to a name you haven't checked.
|
|
26
|
-
- **Assign for specialty.** Hand each piece of work to the agent most relevant to it. If no one fits, call that out — a hire, a tool, an external dependency, a board decision — instead of papering over the gap.
|
|
27
|
-
- **Take responsibility.** Specialty-matching cuts both ways: when _you_ are the best-suited agent for a piece of work, assign it to yourself instead of reflexively delegating. Don't hand off to avoid load.
|
|
28
|
-
- **Use the dependency tree.** Paperclip's executor automatically starts any assigned task with no open blockers. Parent/child issue nesting is structure, not execution blocking. Express each qualifying ownership or lifecycle boundary as an issue; keep other concrete deliverables within the responsible issue's description, checklist, or acceptance criteria. Wire every hard dependency between issues through `blockedByIssueIds` on the dependent issue (not prose like "blocked by X"). When a blocker reaches `done`, dependents auto-wake.
|
|
29
|
-
- **Order, then parallelize.** Sequence work by real dependencies, not by personal preference. Create parallel branches only for qualifying, self-contained work, then start those independent branches in parallel. Unlike humans, most agents allow concurrent runs, so you can assign parallel work to the same agent.
|
|
30
|
-
- **Write review tasks for the reviewer's boundary.** A review/QA task must tell the delegate to post findings on **their own review issue** and mark it `done` — the verdict is the deliverable, and adverse findings are still `done`, not `blocked`. Never instruct a delegate to comment on the parent issue (low-trust reviewers are guaranteed a 403 there), and make the description self-contained since the reviewer may not be able to read your issue. Wire the dependent issue's `blockedByIssueIds` to the review issue so the verdict wakes the right owner.
|
|
31
|
-
- **Enough is enough.** Plans exist to unblock execution, not replace it. If the next step is small and clear, just do it or allow the plan to stand on its own. Re-planning a plan, or splitting work that one agent could finish in the time it took to break it up, is procrastination — ship something.
|
|
22
|
+
Reviewers publish findings on their own review task and complete it, including an
|
|
23
|
+
adverse verdict. Make its description self-contained. Connect the verdict to the
|
|
24
|
+
owner who must act on it; do not require writes to the parent.
|
|
32
25
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
Start from one end-to-end task and add issues only for the qualifying boundaries above. Before creating tasks, write a compact task matrix with each proposed task, owner, initial status, blockers, and the specific qualifying reason it must be separate. Any task that can start immediately should say why it has no blockers; otherwise set it to `blocked` and include the prerequisite issue IDs in `blockedByIssueIds`. Do not rely on `parentId`, child ordering, phase labels, or prose to block execution.
|
|
36
|
-
|
|
37
|
-
Run a merge-back pass before publishing or creating the graph. Require every proposed subtask to name at least one qualifying reason from this skill. If it cannot, merge it into its parent or an adjacent task and preserve the work as an internal step, checklist item, or acceptance criterion. Repeat until every remaining issue has a real ownership, scheduling, lifecycle, or governance reason to exist.
|
|
38
|
-
|
|
39
|
-
After creating the tasks, re-fetch the created issues or otherwise verify the issue graph before marking the source planning issue done. Confirm that every separate issue still has its qualifying reason, each dependent task has the expected `blockedByIssueIds`, each independent task has an explicit "can start now" reason, review tasks respect the reviewer's write boundary, and the parent/child hierarchy is only being used for traceability. If the graph contains an unjustified split or expected blockers are missing, correct it or report the mismatch and leave the planning issue in `in_review` or `blocked` until the graph is fixed.
|
|
40
|
-
|
|
41
|
-
## Quick checklist before you publish a plan
|
|
42
|
-
|
|
43
|
-
- [ ] Enough detail that assignees can act without re-asking.
|
|
44
|
-
- [ ] The plan uses the fewest tasks that can complete and verify the job, preferring one end-to-end owner over step/file/component/phase splits.
|
|
45
|
-
- [ ] Every concrete deliverable is accounted for inside an issue or, only when a qualifying boundary applies, as its own issue.
|
|
46
|
-
- [ ] Every proposed subtask names a qualifying reason; otherwise it was merged into its parent or an adjacent task.
|
|
47
|
-
- [ ] Each issue has a deliberate, specialty-matched assignee — not the planner by default.
|
|
48
|
-
- [ ] Each issue's real blockers are declared via `blockedByIssueIds`.
|
|
49
|
-
- [ ] Independently owned review, QA, and governed approval tasks respect the reviewer's boundary.
|
|
50
|
-
- [ ] A compact task matrix names planned task, owner, initial status, blockers, and qualifying reason.
|
|
51
|
-
- [ ] Tasks without blockers have an explicit reason they can start immediately.
|
|
52
|
-
- [ ] Created issues were re-fetched or otherwise verified before closing the source planning issue.
|
|
53
|
-
- [ ] Qualifying independent branches can start in parallel.
|
|
54
|
-
- [ ] Gaps (missing skills, hires, decisions, external inputs) are surfaced, not hidden.
|
|
55
|
-
|
|
56
|
-
## What this skill is not
|
|
57
|
-
|
|
58
|
-
- Not a plan template. Use any format — prose, outline, table, RACI, Gantt, whatever fits.
|
|
59
|
-
- Not software-development–specific. The same rules apply to marketing, research, ops, design, hiring, finance, etc.
|
|
60
|
-
- Not a replacement for the `paperclip` skill's planning mechanics. Use both.
|
|
26
|
+
Honor existing authorization and approval boundaries. Use Paperclip's planning
|
|
27
|
+
mechanics when approval is required; decomposition does not create new approval gates.
|
package/skills/slack/SKILL.md
CHANGED
|
@@ -80,3 +80,13 @@ On a Slack-linked task, human messages sent from Paperclip and your final respon
|
|
|
80
80
|
are mirrored into its Slack thread. Do not manually send the same final response
|
|
81
81
|
again. Ordinary tasks and routines have no automatic Slack destination: perform the
|
|
82
82
|
requested Slack send explicitly and report whether delivery was confirmed.
|
|
83
|
+
|
|
84
|
+
On a Slack-linked task, when results are ready, include the outcome and accessible
|
|
85
|
+
deliverable links or supported attachments in your final response to the originating
|
|
86
|
+
Slack conversation.
|
|
87
|
+
Do not leave the result only on the Paperclip task. When the user must review a
|
|
88
|
+
plan, approve an action, or review a result, save the artifact and create the
|
|
89
|
+
required review interaction through the normal Paperclip workflow. Tell the user
|
|
90
|
+
what needs review and what action to take in the same Slack conversation. A saved
|
|
91
|
+
plan or an `in_review` status alone is not a review request. Keep approval gates
|
|
92
|
+
in place and do not claim delivery without a successful receipt.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Denver Technologies, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
Adapted from Warp's suggestion-box skill:
|
|
24
|
+
https://github.com/warpdotdev/common-skills/blob/main/.agents/skills/suggestion-box/SKILL.md
|
|
25
|
+
|
|
26
|
+
Paperclip adaptations: local attributed storage, native tools, free-form
|
|
27
|
+
suggestions, and document-sized input limits.
|