fix: stop prompts from encouraging subtasks (#20362)

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents read runtime prompts, tool help, skills, and role
instructions.
> - Several of these texts tell agents to create child tasks as a normal
next step.
> - One accepted-plan prompt even forbids work on the current task.
> - Task parentage is useful, but it does not need a prompt that
encourages task creation.
> - This pull request removes those prompts and keeps the task APIs
available.

## Linked Issues or Issue Description

Refs #10642. This change adjusts prompt wording; it does not add
delegation-cycle guards.

**Current behavior**
Prompts tell agents to create children after plan approval, use children
for long work, and create a task just to pass context. Onboarding
requires a child even for a single request.

**Proposed behavior**
Continue work on the current task. Keep explicit assignments, optional
task creation, parentage, dependencies, and approval gates. Use the
separate-task recipes when the user requests that structure or when
continuing existing work.

**Reason and benefit**
Reduce needless task trees without removing subtask support. Keep the
patch limited to wording, existing test expectations, and generated
catalog data.

## What Changed

- Align accepted-plan prompts and remove direct child-creation advice
from runtime and error text.
- Make task tool descriptions neutral. Keep their fields, defaults, and
handlers.
- Update the main skill, onboarding, CEO instructions, company
templates, specialist workflows, and matching guides.
- Keep historical release snapshots intact. Refresh the explicitly
seeded schema fixture's catalog digest; the server still verifies
historical release snapshot hashes.
- Refresh the affected generated catalogs, capability inventory,
semantic contracts, and fixture manifest.

## Verification

- Final tested head: `c6e304d0c105cdbfbe0b2ab0a233307fcd91a04b`.
- All 66 check runs are complete: 60 pass, one is neutral, and five are
skipped by CI conditions. Snyk also passes. No checks fail or remain
pending.
- [PR
CI](https://github.com/paperclipai/paperclip/actions/runs/42846226554)
passes typecheck, build, server/general tests, all eight E2E shards,
Runner support, Runner Rust, and the aggregate verify gate.
[Windows](https://github.com/paperclipai/paperclip/actions/runs/42846226546)
and [Docker
Runner](https://github.com/paperclipai/paperclip/actions/runs/42846226108)
also pass.
-
[Greptile](https://github.com/paperclipai/paperclip/pull/20362#issuecomment-6751419835)
and [Paperclip
review](https://github.com/paperclipai/paperclip/pull/20362#issuecomment-6751454010)
give this exact head 5/5. All six inline threads are resolved. Neither
current summary has findings.
- Merged master `bf0220d34a156c81853755c3d1365ddc527037de`. Resolved
three generated-file conflicts with the canonical generators.
- Local verification passes: 473 focused prompt, continuation, UI,
catalog, runtime and native-execution tests; Runner TypeScript build;
generated metadata; UI token gates; and diff checks.
- The prior head `c22018cb0f` failed three SDK evidence tests because
two source hashes were stale. Reproduced all three failures locally. The
existing generator refreshed only the hashes for the denial-copy file
and its test. All 53 affected evidence/source-pin tests then passed,
including private-value rejection. No scanner logic, assertions, or
runtime dependency pins changed. Current CI confirms the support suite
passes.
- A prior commit failed the Rust process reaping test. Its cause remains
unproven. No Rust code or assertions changed. Later Rust runs pass; this
does not establish the cause of the earlier failure.
- Local preflight built shared/plugin SDK packages, then stopped because
`cargo` is absent. Current-head CI supplies Rust and full build
coverage. `pnpm verify --explain` selects broad required CI; full local
suites were not repeated.

## Risks

- Wording changes guide the model; they do not enforce a ban on
subtasks.
- Existing installed skill versions and saved custom instructions need
their normal refresh process. This PR does not migrate company
configuration.
- Existing approved task assignments and approval gates remain in
effect.

## Model Used

- OpenAI GPT-6, through Codex, with reasoning, tool use, and code
execution. The exact routed model ID and context-window size were not
exposed in this session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip authored and GitHub committed 2026-11-10 09:58:54 -06:00
1 parent 79e28dc0d0
commit ce1ee9e89f
56 files changed
+169 -172

No files matched your search

@@ -863,7 +863,7 @@ describeEmbeddedPostgres("accepted plan workspace refresh", () => {
otherActiveClaimIdentifier: "PAP-9302",
}));
expect(adapterInput.context.paperclipTaskMarkdown).toContain("Make the plan only.");
expect(adapterInput.context.paperclipTaskMarkdown).not.toContain("Create child issues from the approved plan only");
expect(adapterInput.context.paperclipTaskMarkdown).not.toContain("Implement the accepted plan on this issue.");
}, 20_000);
it("guards cross-issue accepted-plan retries even when the waking issue is standard work mode", async () => {
@@ -1025,7 +1025,7 @@ describeEmbeddedPostgres("accepted plan workspace refresh", () => {
otherActiveClaimIdentifier: "PAP-9402",
}));
expect(adapterInput.context.paperclipTaskMarkdown).toContain("Issue: \"PAP-9401\"");
expect(adapterInput.context.paperclipTaskMarkdown).not.toContain("Create child issues from the approved plan only");
expect(adapterInput.context.paperclipTaskMarkdown).not.toContain("Implement the accepted plan on this issue.");
}, 20_000);
it("preserves accepted-plan instructions but replaces a pre-identity session when the wake issue owns the in-flight claim", async () => {
@@ -1163,7 +1163,7 @@ describeEmbeddedPostgres("accepted plan workspace refresh", () => {
expect(adapterInput.runtime.sessionId).toBeNull();
expect(adapterInput.context.acceptedPlanWakeRouting).toBeUndefined();
expect(adapterInput.context.paperclipTaskMarkdown).toContain(
"Implement the accepted plan on this issue when the work is small and cohesive.",
"Implement the accepted plan on this issue.",
);
}, 20_000);
});
@@ -519,7 +519,7 @@ describe("buildPaperclipTaskMarkdown", () => {
});
expect(acceptedConfirmation).toContain(
"Implement the accepted plan on this issue when the work is small and cohesive.",
"Implement the accepted plan on this issue.",
);
expect(acceptedConfirmation).not.toContain("Make the plan only.");
});
@@ -538,7 +538,7 @@ describe("buildPaperclipTaskMarkdown", () => {
expect(acceptedConfirmation).toContain("Accepted plan directive:");
expect(acceptedConfirmation).toContain(
"Implement the accepted plan on this issue when the work is small and cohesive.",
"Implement the accepted plan on this issue.",
);
expect(acceptedConfirmation).not.toContain("- Work mode: \"planning\"");
});
@@ -646,7 +646,7 @@ describe("buildPaperclipTaskMarkdown", () => {
});
expect(commentWake).toContain("Update the plan only. Do not write code or perform implementation work.");
expect(commentWake).not.toContain("Create child issues from the approved plan only");
expect(commentWake).not.toContain("Implement the accepted plan on this issue.");
});
});
@@ -1062,7 +1062,7 @@ describe("issue comment reopen routes", () => {
// Plan §6: name the boundary, who can act, and the sanctioned path.
expect(res.body.error).toContain("Issue visibility");
expect(res.body.error).toContain("Who can act:");
expect(res.body.details.sanctionedPath).toContain("child issue");
expect(res.body.details.sanctionedPath).toContain("Ask the board to widen visibility");
expect(mockAccessService.decide).toHaveBeenCalledWith(
expect.objectContaining({ action: "issue:comment" }),
);
@@ -21,7 +21,7 @@ async function nativeProposalRecipe(kind: "confirmation" | "checkbox", planRevis
const recipes = [...skill.matchAll(/^ {0,3}```json\n([\s\S]*?)^ {0,3}```/gm)].map(match => JSON.parse(match[1]!) as Record<string, unknown>);
const matches = recipes.filter(recipe => recipe.interactionKind === kind);
expect(matches, `one executable native ${kind} recipe must ship with the first-task skill`).toHaveLength(1);
return JSON.parse(JSON.stringify(matches[0]).replaceAll("<child-title>", "Approved child")
return JSON.parse(JSON.stringify(matches[0]).replaceAll("<task-title>", "Approved work")
.replaceAll("<plan-revision-id>", planRevisionId)) as Record<string, unknown>;
}
@@ -42,6 +42,6 @@ Server-enforced target keys:
- Start concrete work in the same heartbeat when the issue is actionable; do not stop at a plan unless planning was requested.
- Leave durable progress in comments, issue documents, or draft files, with a clear next action owner.
- Use child issues for long or parallel delegated work instead of polling.
- Continue work on the current issue; do not busy-poll.
- If blocked, mark the issue blocked and name the unblock owner and exact action needed.
- Respect budget, pause/cancel, approval gates, execution policy stages, and company boundaries.
@@ -47,9 +47,9 @@ Status quick guide:
## 6. Delegation
- Create subtasks with `POST /api/companies/{companyId}/issues`. Always set `parentId` and `goalId`. Each subtask gets its own workspace from the project base, never the parent's checkout or unmerged changes.
- When you know the needed work and owner, create those subtasks directly. When the board/user must choose from a proposed task tree, answer structured questions, or confirm a proposal before you can proceed, create an issue-thread interaction on the current issue with `POST /api/issues/{issueId}/interactions` using `kind: "suggest_tasks"`, `kind: "ask_user_questions"`, or `kind: "request_confirmation"` and `continuationPolicy: "wake_assignee"` when the answer should wake you.
- For plan approval, update the `plan` document first, create `request_confirmation` targeting the latest `plan` revision, use an idempotency key like `confirmation:{issueId}:plan:{revisionId}`, set the source issue to `in_review`, and do not create implementation subtasks until the board/user accepts it.
- Optional task creation uses `POST /api/companies/{companyId}/issues`, with `parentId` and `goalId` available. Each subtask gets its own workspace from the project base, never the parent's checkout or unmerged changes.
- Continue work on the current issue. When the board/user must choose from a proposed task tree, answer structured questions, or confirm a proposal before you can proceed, create an issue-thread interaction on the current issue with `POST /api/issues/{issueId}/interactions` using `kind: "suggest_tasks"`, `kind: "ask_user_questions"`, or `kind: "request_confirmation"` and `continuationPolicy: "wake_assignee"` when the answer should wake you.
- For plan approval, update the `plan` document first, create `request_confirmation` targeting the latest `plan` revision, use an idempotency key like `confirmation:{issueId}:plan:{revisionId}`, set the source issue to `in_review`, and do not start implementation until the board/user accepts it.
- `ask_user_questions` and confirmations default `supersedeOnUserComment` to `false`, so a later board/user comment keeps the pending card open while discussion continues. Set it to `true` when a new comment should replace the pending request. If you are woken by a superseding comment, revise the question set or proposal and create a fresh interaction if input is still needed.
- Use `paperclip-create-agent` skill when hiring new agents.
- Assign work to the right agent for the job.
+1 -1
View File
@@ -16,7 +16,7 @@ You are the CEO.
- Create organizational clarity. If priorities are unclear, it's on you; repeat strategy until it sticks.
- Pull for bad news and reward candor. If problems stop surfacing, you've lost your information edge.
- Stay close to the customer. Dashboards help, but regular firsthand conversations keep you honest.
- Be replaceable in operations and irreplaceable in judgment. Delegate execution; keep your time for strategy, capital allocation, key hires, and existential risk.
- Be replaceable in operations and irreplaceable in judgment. Balance execution with strategy, capital allocation, key hires, and existential risk.
## Voice and Tone
@@ -33,19 +33,19 @@ Work in this order.
- Determine the requested outcome before choosing an approval boundary. The interview path and requests to build, improve, or deliver something are execution requests: the plan is an intermediate artifact. Treat a request as plan-only only when the user explicitly limits the deliverable to planning. Asking to review a plan before work begins does not make it plan-only. Preserve any separate hiring or execution approvals the user explicitly requests and any applicable company policies.
- If the user asks to continue onboarding in your chat with them instead, ask your questions there. Your first question in the chat replaces this task's pending question cards, and Paperclip closes this task with a link to the chat. From then on the chat replaces this task for the rest of this workflow: save the plan and post proposal cards in the chat, create accepted child tasks from the chat, and report their results there. Skip the steps below that keep this task `in_review`, save documents on it, or make it wait for a child. Do not reopen this task or ask the user what should happen to it.
- If the user asks to continue onboarding in your chat with them instead, ask your questions there. Your first question in the chat replaces this task's pending question cards, and Paperclip closes this task with a link to the chat. From then on the chat replaces this task for the rest of this workflow: save the plan and post proposal cards in the chat, create accepted execution tasks from the chat, and report their results there. Skip the steps below that keep this task `in_review`, save documents on it, or make it wait for a child. Do not reopen this task or ask the user what should happen to it.
2. Propose, then wait for acceptance.
- Choose the proposal form from the user’s request first: an explicit plan request or the interview path always requires a saved plan, even when the task description says `confirmation`.
- For the interview path or an explicit plan request, save a `plan` document on this onboarding task describing the goal, scope, steps, proposed team, and what done means. Post one `request_checkbox_confirmation` targeting the saved plan revision. A card or thread message alone is not a saved plan. This applies to explicit plan requests regardless of the single-task proposal mode. Proposing a team alone does not authorize hiring it; the recorded acceptance authorizes only the team and work actually included in the accepted scope, subject to normal hiring and budget policies.
- For an execution request, make the saved plan and approval card say "Approve this plan and start the described work." Describe the tasks and any proposed hires the user is approving. Do not add a separate implementation-approval requirement unless the user or an applicable policy requires it. For an explicitly plan-only request, make the card say "Approve this plan only; do not start implementation."
- If they want one thing done, propose exactly one child task with a clear outcome and scope. Ask them to accept it before creating the child. Do not produce the requested finished work inside the proposal, even when it is quick to do.
- For a single-task proposal, follow the `Single-task proposal mode` saved in the task description: `confirmation` means one `request_confirmation` card describing the child task, without a plan document; `plan` means save a short `plan` document describing that same child task and post one `request_checkbox_confirmation` targeting its saved revision.
- If they want one thing done, propose completing it on this task with a clear outcome and scope. Ask them to accept the scope before execution. Do not produce the requested finished work inside the proposal, even when it is quick to do.
- For a single-task proposal, follow the `Single-task proposal mode` saved in the task description: `confirmation` means one `request_confirmation` card describing the work, without a plan document; `plan` means save a short `plan` document describing that same work and post one `request_checkbox_confirmation` targeting its saved revision.
- With native tools, create those proposal cards using `request_human_input`: `interactionKind: "confirmation"` creates `request_confirmation`; `interactionKind: "checkbox"` creates `request_checkbox_confirmation`. Use `continuationPolicy: "wake_assignee"` so acceptance and rejection both return the decision to you; only a saved acceptance authorizes the approved work. Use `interactionKind: "questions"` only to collect scope or clarification answers. An Approve/Revise choice question is still a question, so answering it does not record proposal acceptance or authorize child creation. Present the proposal through the matching confirmation kind instead.
- Keep this task `in_review` while waiting. You may clarify, research for planning, and save or revise a plan/proposal before acceptance. Do not hire, create execution tasks, perform the deliverable, save finished output, or claim completion yet.
For a native single-task confirmation, call `request_human_input` with arguments like these. Replace `<child-title>` with the proposed title and describe its actual scope in the prompt. Use a new idempotency key for each changed or revised proposal. Reuse a key only for the exact same proposal when reconciling an interrupted call; the example key below is for the first proposal.
For a native single-task confirmation, call `request_human_input` with arguments like these. Replace `<task-title>` with the proposed title and describe its actual scope in the prompt. Use a new idempotency key for each changed or revised proposal. Reuse a key only for the exact same proposal when reconciling an interrupted call; the example key below is for the first proposal.
The tool's root fields are `idempotencyKey`, `interactionKind`, `title`, `prompt`, `continuationPolicy`, and `payload`, with optional `targetRevisionId` and `resolverPolicy` as advertised. Put `version` and `supersedeOnUserComment` inside `payload`; neither is a root argument. If validation rejects a proposal, correct its argument placement and keep the confirmation or checkbox approval kind. Switching it to a question form cannot authorize the proposed work. Wait for a successful saved card receipt before claiming you requested approval.
@@ -55,8 +55,8 @@ Work in this order.
{
"idempotencyKey": "first-task-proposal-confirmation",
"interactionKind": "confirmation",
"title": "Approve child task",
"prompt": "Approve creating exactly one child task named \"<child-title>\" for the scope described above?",
"title": "Approve work",
"prompt": "Approve completing \"<task-title>\" on this task for the scope described above?",
"continuationPolicy": "wake_assignee",
"payload": {
"version": 1,
@@ -68,7 +68,7 @@ Work in this order.
}
```
For a native single-task proposal in plan mode, save the plan first, then call `request_human_input` with a checkbox card bound to its actual revision. Replace `<child-title>` and `<plan-revision-id>`; make the options describe the scope the user is approving. For an explicitly plan-only request, change the prompt and option to approve the plan alone without implementation or hiring.
For a native single-task proposal in plan mode, save the plan first, then call `request_human_input` with a checkbox card bound to its actual revision. Replace `<task-title>` and `<plan-revision-id>`; make the options describe the scope the user is approving. For an explicitly plan-only request, change the prompt and option to approve the plan alone without implementation or hiring.
```json
{
@@ -80,7 +80,7 @@ Work in this order.
"payload": {
"version": 1,
"supersedeOnUserComment": false,
"options": [{ "id": "scope", "label": "Approve the plan and start <child-title>" }],
"options": [{ "id": "scope", "label": "Approve the plan and start <task-title>" }],
"defaultSelectedOptionIds": ["scope"],
"minSelected": 1,
"maxSelected": 1,
@@ -100,10 +100,13 @@ Work in this order.
4. Carry out the accepted scope.
- For a plan-only request, retain the accepted plan on this task. Do not start its implementation or hire the proposed team without authorization to do that work.
- For an accepted interview execution plan, create or resume the minimum execution tasks needed for the approved outcome. Link them to this onboarding task and assign each to yourself or an approved agent, ready to run. Use normal hiring controls for any approved hires. Check for existing tasks from this proposal first so later wakes and retries continue the same work.
- For an accepted interview execution plan, carry out the approved work on this task unless the accepted plan explicitly assigns work elsewhere. Use normal hiring controls for any approved hires. Check for existing tasks from this proposal first so later wakes and retries continue the same work.
- A recorded acceptance of an execution proposal authorizes starting its selected work. Start that work without asking the user to approve the same scope again. Honor an accepted proposal that explicitly reserved a later approval; never expand its authorization retroactively. Missing credentials or connections block only the steps that require them; continue independent approved work.
- For an accepted single task, check for an existing child from this proposal before creating anything. Create exactly one child linked to this onboarding task and assign it to yourself. On later wakes, continue that same child instead of creating another.
- For an accepted single task, complete the work here. Save the output document and publish any requested file on this task before marking it done. If the accepted proposal already specified a separate task, honor that assignment and reuse any task already created for it.
- A saved, approved plan alone does not complete an execution request. Follow the execution and result-delivery steps below for the approved tasks, and report the actual output before completing onboarding. An explicitly plan-only request is complete when its accepted plan is retained.
The following handoff steps apply only when the accepted proposal explicitly includes a separate task. They do not require a new task for work you can complete here.
- If the accepted child work requires a file deliverable, include the approved scope, document requirements, and requested filenames in a self-contained child description. Delegate the work to the child's own Paperclip execution, even when the child is assigned to yourself. That execution must save the document and publish the requested file on the child before marking it done. Do not create or register that file from the onboarding run, or mark the child done before its execution delivers the file. Native `register_deliverable` is bound to its current task.
- Copy required input payloads verbatim, including encoded bytes, nonces, constraints, and requested filenames, from the accepted request and its accepted corrections into the child description. The child has its own execution context and does not inherit this conversation. Do not leave references such as "provided above" or "same as the parent" in place of actual inputs. When an input is not inline, include its actual retrievable source reference as an identifier link such as `[BRA-2](/BRA/issues/BRA-2#document-plan)`, not a raw UUID, and ensure the child is authorized to read it. Keep credentials and secrets out of handoff text.
- Parent-child linkage is not a dependency. Do not put this onboarding task in the child's `blockedByTaskIds`: the child must execute before this task can finish. Create the child ready to run, then make this onboarding task wait for the child.
@@ -257,7 +257,7 @@ export function buildPaperclipTaskMarkdown(input: {
}
if (acceptedPlanContinuation) {
directive =
"Implement the accepted plan on this issue when the work is small and cohesive. Use the paperclip-converting-plans-to-tasks skill to decide whether decomposition is justified. Create the minimum child issue graph only for qualifying ownership, parallelism, dependency, review, or lifecycle boundaries. Do not create a child merely because a plan was accepted.";
"Implement the accepted plan on this issue. Follow any task assignments explicitly included in the approved plan. Plan acceptance alone does not require subtasks.";
}
lines.push(
`- Work mode: ${quoteTaskScalar("planning")}`,
@@ -276,7 +276,7 @@ export function buildPaperclipTaskMarkdown(input: {
lines.push(
"",
"Accepted plan directive:",
"Implement the accepted plan on this issue when the work is small and cohesive. Use the paperclip-converting-plans-to-tasks skill to decide whether decomposition is justified. Create the minimum child issue graph only for qualifying ownership, parallelism, dependency, review, or lifecycle boundaries. Do not create a child merely because a plan was accepted.",
"Implement the accepted plan on this issue. Follow any task assignments explicitly included in the approved plan. Plan acceptance alone does not require subtasks.",
);
}
if (rejectedPlan) {
@@ -153,10 +153,10 @@ describe("first-task proposal mode policy", () => {
const skill = await readFile(new URL("../onboarding-assets/first-task/skills/first-task/SKILL.md", import.meta.url), "utf8");
expect(skill).toContain("For a plan-only request, retain the accepted plan on this task");
expect(skill).toContain("Do not start its implementation or hire the proposed team without authorization");
expect(skill).toContain("Create exactly one child linked to this onboarding task");
expect(skill).toContain("For an accepted single task, complete the work here.");
expect(skill).toContain("For document-only child work, you may execute it directly");
expect(skill).toContain("Save the finished output as a document on the child task and mark that child done");
expect(skill).toContain("On later wakes, continue that same child instead of creating another");
expect(skill).toContain("reuse any task already created for it");
});
});